package main import ( "flag" "fmt" "math/rand" "os" "os/signal" "syscall" "time" "github.com/bwmarrin/discordgo" "github.com/rudi9719/loggy" ) var ( startupTime time.Time setupToken = fmt.Sprintf("%+v", rand.Intn(9999)+1000) rebootToken = fmt.Sprintf("%+v", rand.Intn(9999)+1000) bump = true config Config log = loggy.NewLogger(config.LogOpts) lastActiveTime time.Time token string configFile string dg *discordgo.Session lastPM = make(map[string]time.Time) introMsg = make(map[string]string) quotes = []string{"The hardest choices require the strongest wills.", "You're strong, but I could snap my fingers and you'd all cease to exist.", "Fun isn't something one considers when balancing the universe. But this... does put a smile on my face.", "Perfectly balanced, as all things should be.", "I am inevitable."} gitCommit string commands []Command ) func init() { flag.StringVar(&token, "t", "", "Bot Token") flag.StringVar(&configFile, "c", "", "Config file") flag.Parse() } func main() { go runWeb() defer log.PanicSafe() if configFile == "" { configFile = "config.json" } else { loadConfig() } log = loggy.NewLogger(config.LogOpts) startupTime = time.Now() lastActiveTime = time.Now() if token == "" { log.LogPanic("No token provided. Please run: disgord-thanos -t ") } log.LogCritical("SetupToken: %+v\nRebootToken: %+v", setupToken, rebootToken) var err error dg, err = discordgo.New("Bot " + token) if err != nil { log.LogErrorType(err) log.LogPanic("Unable to create bot using token.") } dg.AddHandler(ready) dg.AddHandler(guildMemberRemove) dg.AddHandler(guildMemberAdd) dg.AddHandler(guildMemberBanned) go setupCommands() dg.AddHandler(messageCreate) dg.AddHandler(readReaction) dg.AddHandler(guildMemberUpdate) dg.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAll) err = dg.Open() if err != nil { log.LogErrorType(err) log.LogPanic("Unable to open websocket.") } log.LogInfo("Thanos is now running. Press CTRL-C to exit.") go purgeTimer(dg) go rebootBump() sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) <-sc saveConfig() dg.Close() } func exit(s *discordgo.Session) { s.Close() saveConfig() os.Exit(0) } func runPurge(s *discordgo.Session) { defer log.PanicSafe() if time.Since(config.BumpTime) > 2*time.Hour { bump = true } for uid, join := range config.Probations { if time.Since(join) > 2*time.Hour { delete(config.Probations, uid) s.ChannelMessageDelete(config.IntroChann, introMsg[uid]) } } for k, v := range config.Unverified { isUnverified := false m, err := s.GuildMember(config.GuildID, k) if err != nil { delete(config.Unverified, k) continue } for _, role := range m.Roles { if role == config.MonitorRole { isUnverified = true } } if isUnverified { if val, ok := lastPM[k]; ok && time.Since(val) < 5*time.Minute { continue } lastPM[k] = time.Now() pmChann, _ := s.UserChannelCreate(k) s.ChannelMessageSend(pmChann.ID, fmt.Sprintf("This is a reminder that you have not verified with me and will be removed at . You may reply to this message for verification instructions.", v.Add(1*time.Hour).Unix())) if time.Since(v) > (time.Hour * 1) { s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v was removed.", m.Mention())) s.GuildMemberDeleteWithReason(config.GuildID, k, fmt.Sprintf("Unverified user %+v.", v)) } } else { delete(config.Unverified, k) } } messages, _ := s.ChannelMessages(config.MonitorChann, 100, "", "", "") for _, message := range messages { found := false for user := range config.Unverified { if message.Author.ID == user { found = true } for _, mention := range message.Mentions { if mention.ID == user { found = true } } } if !found { s.ChannelMessageDelete(config.MonitorChann, message.ID) } } go cleanSocials(s) saveConfig() } func cleanSocials(s *discordgo.Session) { for _, channel := range config.SocialChanns { go func(channel string, s *discordgo.Session) { messages, _ := s.ChannelMessages(channel, 100, "", "", "") for _, message := range messages { _, err := s.GuildMember(config.GuildID, message.Author.ID) if err != nil { s.ChannelMessageDelete(channel, message.ID) } } }(channel, s) } } func verifyMember(s *discordgo.Session, u discordgo.User) { defer log.PanicSafe() log.LogDebug("Adding verified roll") s.GuildMemberRoleAdd(config.GuildID, u.ID, config.VerifiedRole) log.LogDebug("Removing monitor role") s.GuildMemberRoleRemove(config.GuildID, u.ID, config.MonitorRole) log.LogDebug("Creating PM channel") st, err := s.UserChannelCreate(u.ID) if err != nil { log.LogErrorType(err) } log.LogDebug("Sending acceptance message!") s.ChannelMessageSend(st.ID, "Your verification has been accepted, welcome!") log.LogDebug("Sending Intro message") m, err := s.ChannelMessageSend(config.IntroChann, fmt.Sprintf("Welcome %+v please introduce yourself! :) feel free to check out <#710557387937022034> to tag your roles. Also please mute any channels you are not interested in!", u.Mention())) if err != nil { log.LogErrorType(err) } log.LogDebug("Storing introMsg ID to be deleted later") introMsg[u.ID] = m.ID } func rejectVerification(s *discordgo.Session, u discordgo.User) { defer log.PanicSafe() st, err := s.UserChannelCreate(u.ID) if err != nil { log.LogErrorType(err) } if st != nil { s.ChannelMessageSend(st.ID, fmt.Sprintf("Your verification has been rejected. This means it did not clearly show your face, with your pinkie finger held to the corner of your mouth, or the photo looked edited/filtered. No filters will be accepted.\n\nPlease try again before ", time.Now().Add(1*time.Hour).Unix())) } config.Unverified[u.ID] = time.Now() } func requestAge(s *discordgo.Session, u discordgo.User) { defer log.PanicSafe() st, err := s.UserChannelCreate(u.ID) if err != nil { log.LogErrorType(err) } s.ChannelMessageSend(st.ID, "What is your ASL? (Age/Sex/Language) Please note, this is NOT requesting your gender, but your biological sex. Gender is a social construct, sex is measurable and in the context of pornographic images more important.") } func requestReupload(s *discordgo.Session, u discordgo.User) { defer log.PanicSafe() st, err := s.UserChannelCreate(u.ID) if err != nil { log.LogErrorType(err) } s.ChannelMessageSend(st.ID, "Hello! Your verification has been denied because it failed to load. Please try again! The instructions will follow this message:") rejectVerification(s, u) }