package main import ( "fmt" "strconv" "strings" "time" "github.com/bwmarrin/discordgo" "github.com/google/uuid" ) var session *discordgo.Session var pendingV []Verification var rebootTS time.Time const alpha = "abcdefghijklmnopqrstuvwxyz1234567890" func configureDiscord() { dg, err := discordgo.New("Bot " + config.DiscordToken) if err != nil { log.LogErrorType(err) log.LogPanic("Unable to create bot using token.") } dg.AddHandler(ready) dg.AddHandler(readReaction) dg.AddHandler(messageCreate) //dg.AddHandler(guildMemberAdd) session = dg err = dg.Open() if err != nil { log.LogErrorType(err) log.LogPanic("Unable to open websocket") } log.LogInfo("Discord link is now online.") } func discordExit() { log.LogCritical("Closing Discord (closing Session)") session.Close() } func sendDiscordMessage(msg string) { session.ChannelMessageSend(config.VerifiedChan, fixMentions(msg)) } func sendAdminDiscordMessage(msg string) { session.ChannelMessageSend(config.AdminChan, msg) } func ready(s *discordgo.Session, m *discordgo.Ready) { s.UpdateStatus(0, "Minecraft") } func banUser(s *discordgo.Session, u discordgo.User) { s.GuildBanCreate(config.GuildID, u.ID, 0) } func readReaction(s *discordgo.Session, m *discordgo.MessageReactionAdd) { defer log.PanicSafe() if m.Emoji.Name != "⚠️" && m.Emoji.Name != "❗" { return } log.LogDebug(fmt.Sprintf("Report emoji: ```%+v```", *m)) msg, err := s.ChannelMessage(m.ChannelID, m.MessageID) log.LogDebug(fmt.Sprintf("Message retrieved: %+v", *msg)) if err != nil { s.ChannelMessageSend(config.AdminChan, "A message was reported that I could not detect.") return } reporter, _ := s.GuildMember(config.GuildID, m.UserID) violator, _ := s.GuildMember(config.GuildID, msg.Author.ID) s.ChannelMessageSend(config.AdminChan, fmt.Sprintf("A message was reported! %+v reported %+v's message: %+v\n```%+v```", reporter.Nick, violator.Nick, msg.Content, *msg)) var r Report var violatorUser User violatorUser.DiscordID = m.UserID violatorUser.Retrieve() var reporterUser User reporterUser.DiscordID = msg.Author.ID reporterUser.Retrieve() reporterUser.Write() // Update last seen r.Time = time.Now() r.Reporter = reporterUser r.Abuser = violatorUser r.Msg = msg.Content r.Write() } func guildMemberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) { defer log.PanicSafe() s.GuildMemberRoleAdd(config.GuildID, m.User.ID, config.MonitorRole) st, _ := s.UserChannelCreate(m.User.ID) // Get the ID of PM channel id := uuid.New() // Generate new UUID4 var v Verification // Set up new Verification v.DiscordID = m.User.ID // ID is what really matters, username is garbage v.Created = time.Now() // Setup the created time for reasons v.Code = id.String() // Generate Random UUID pendingV = append(pendingV, v) s.ChannelMessageSend(st.ID, "Hello! You have not yet verified with me. Type !sitnet [your sitnet ID here] to verify who you are. You will get an email explaining what to do next. https://web.cs.sunyit.edu/polymc") } func fixMentions(s string) string { defer log.PanicSafe() var ret string if !strings.Contains(s, "@") { return s } parts := strings.Split(s, " ") for _, part := range parts { if strings.HasPrefix(part, "@") { var u User u.Email = strings.Replace(part, "@", "", -1) u.Retrieve() ret += fmt.Sprintf("<@!%+v>", u.DiscordID) } else { ret += part } ret += " " } return ret } func setupVerifications(s *discordgo.Session, m *discordgo.MessageCreate) { defer log.PanicSafe() st, _ := s.UserChannelCreate(m.Author.ID) // Get the ID of PM channel id := uuid.New() // Generate new UUID4 var v Verification // Set up new Verification v.DiscordID = m.Author.ID // ID is what really matters, username is garbage v.Created = time.Now() // Setup the created time for reasons v.Code = id.String() // Generate Random UUID pendingV = append(pendingV, v) s.ChannelMessageSend(st.ID, "Hello! You have not yet verified with me. Type !sitnet [your sitnet ID here] to verify who you are. You will get an email explaining what to do next. https://web.cs.sunyit.edu/polymc") } func alphaOnly(s string) bool { for _, char := range s { if !strings.Contains(alpha, strings.ToLower(string(char))) { return false } } return true } func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { defer log.PanicSafe() if m.Author.ID == s.State.User.ID { return } if m.GuildID == "" { return } if m.ChannelID == config.MonitorChan && strings.HasPrefix(m.Content, "!verify") { for _, v := range pendingV { if v.DiscordID == m.Author.ID { return } } setupVerifications(s, m) return } if m.ChannelID == config.MonitorChan { return } var u User u.DiscordID = m.Author.ID u.Retrieve() u.Write() // Update last seen if strings.HasPrefix(m.Content, "!verify") && m.ChannelID != config.AdminChan { s.ChannelMessageSend(m.ChannelID, "That command ONLY works for unverified users in the unverified channel. Instead, for account resets try !resetme") return } if strings.HasPrefix(m.Content, "!resetme") { mcCommand(fmt.Sprintf("whitelist remove %+v", u.McUser)) u.Delete() s.GuildMemberRoleAdd(config.GuildID, m.Author.ID, config.MonitorRole) s.GuildMemberRoleRemove(config.GuildID, m.Author.ID, config.VerifiedRole) return } if strings.HasPrefix(m.Content, "!online") { if len(onlineUsers) == 0 { s.ChannelMessageSend(m.ChannelID, "There are no users online.") return } str := fmt.Sprintf("The following %+v users are online:\n```", len(onlineUsers)) for _, usr := range onlineUsers { str += fmt.Sprintf("%+v : %+v\n", usr.Email, usr.McUser) } str += "```" s.ChannelMessageSend(m.ChannelID, str) return } if m.ChannelID == config.VerifiedChan { log.LogDebug(fmt.Sprintf("Discord message from retrieved user %+v", u)) // if debug message shows complete user, you may replace userStr with u.SITNresET if m.Member.Nick != u.Email { s.GuildMemberNickname(config.GuildID, m.Author.ID, u.Email) } mcSay(fmt.Sprintf("[%+v]: %+v", u.Email, m.Content)) return } if m.ChannelID == config.AdminChan { go handleAdmin(s, m) } } func manualVerifyUser(s *discordgo.Session, m *discordgo.MessageCreate) { parts := strings.Split(m.Content, " ") if len(parts) != 4 { sendAdminDiscordMessage("Usage !verify $SITNET $McUser $DiscordID") sendAdminDiscordMessage("Please note, DiscordID is the numerical ID, not their username.") } var u User u.Email = parts[1] u.McUser = parts[2] u.DiscordID = parts[3] u.Write() } func handleAdmin(s *discordgo.Session, m *discordgo.MessageCreate) { if !strings.HasPrefix(m.Content, "!") { return } if strings.HasPrefix(m.Content, "!verify") { manualVerifyUser(s, m) } if strings.HasPrefix(m.Content, "!mc") { mcCommand(strings.Replace(m.Content, "!mc", "", -1)) return } if strings.HasPrefix(m.Content, "!wb") { if strings.Contains(m.Content, "stop") { config.WbFinished = true } else { config.WbFinished = false } } if strings.HasPrefix(m.Content, "!reboot") { if serverRebooting { s.ChannelMessageSend(config.AdminChan, fmt.Sprintf("Reboot pending: %+v", time.Until(rebootTS))) return } serverRebooting = true rebootTS = time.Now().Add(30 * time.Minute) parts := strings.Split(m.Content, " ") if len(parts) == 1 { s.ChannelMessageSend(config.AdminChan, "A reboot has been queued. I will poll for when users log out and then execute.") timeout := 120 for { if len(onlineUsers) == 0 || timeout == 0 { mcExit() discordExit() log.LogPanic("Shutting down server from queued reboot.") } time.Sleep(15 * time.Second) timeout-- } return } if len(parts) != 2 && len(parts) != 3 { s.ChannelMessageSend(config.AdminChan, "You dumbass. I take one or two arguments.") return } when, err := strconv.Atoi(parts[1]) if err != nil { s.ChannelMessageSend(config.AdminChan, "The first argument must be an int, number of minutes.") return } reminders := 0 if len(parts) == 3 { reminders, err = strconv.Atoi(parts[2]) if err != nil { s.ChannelMessageSend(config.AdminChan, "You supplied a second argument.. So that also has to be an int, number of reminders.") return } } mcReboot(when, reminders) } }