364 lines
12 KiB
364 lines
12 KiB
4 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"os"
|
||
|
"os/signal"
|
||
|
"strings"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
|
||
|
"github.com/bwmarrin/discordgo"
|
||
|
"github.com/rudi9719/loggy"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// Logging Setup
|
||
|
logOpts = loggy.LogOpts{
|
||
|
UseStdout: true,
|
||
|
Level: 5,
|
||
|
KBTeam: "nightmarehaus.logs.fetiche",
|
||
|
KBChann: "general",
|
||
|
ProgName: "disgord-thanos",
|
||
|
}
|
||
|
log = loggy.NewLogger(logOpts)
|
||
|
startupTime time.Time
|
||
|
setupToken = "!setup "
|
||
|
rebootToken = "!reboot "
|
||
|
bump = true
|
||
|
config Config
|
||
|
lastActiveChan string
|
||
|
lastActiveTime time.Time
|
||
|
token string
|
||
|
configFile string
|
||
|
setupMsg string
|
||
|
lastPM = make(map[string]time.Time)
|
||
|
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."}
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
flag.StringVar(&token, "t", "", "Bot Token")
|
||
|
flag.StringVar(&configFile, "c", "", "Config file")
|
||
|
flag.Parse()
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
defer log.PanicSafe()
|
||
|
startupTime = time.Now()
|
||
|
lastActiveTime = time.Now()
|
||
|
lastActiveChan = "627620309754839070"
|
||
|
if token == "" {
|
||
|
log.LogPanic("No token provided. Please run: disgord-thanos -t <bot token>")
|
||
|
}
|
||
|
defer log.PanicSafe()
|
||
|
if configFile == "" {
|
||
|
configFile = "config.json"
|
||
|
setupToken = fmt.Sprintf("!setup %+v", rand.Intn(9999)+1000)
|
||
|
rebootToken = fmt.Sprintf("!reboot %+v", rand.Intn(9999)+1000)
|
||
|
log.LogCritical(fmt.Sprintf("SetupToken: %+v\nRebootToken: %+v", setupToken, rebootToken))
|
||
|
} else {
|
||
|
loadConfig()
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
dg.AddHandler(messageCreate)
|
||
|
dg.AddHandler(readReaction)
|
||
|
|
||
|
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)
|
||
|
sc := make(chan os.Signal, 1)
|
||
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||
|
<-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)
|
||
|
}
|
||
|
}
|
||
|
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 in %+v. You may reply to this message for verification instructions.", time.Until(v.Add(1*time.Hour))))
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
saveConfig()
|
||
|
}
|
||
|
|
||
|
func ready(s *discordgo.Session, event *discordgo.Ready) {
|
||
|
// Set the playing status.
|
||
|
s.UpdateStatus(0, "DreamDaddy v0.5")
|
||
|
}
|
||
|
|
||
|
func guildMemberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
||
|
defer log.PanicSafe()
|
||
|
config.Unverified[m.User.ID] = time.Now()
|
||
|
config.Probations[m.User.ID] = time.Now()
|
||
|
s.GuildMemberRoleAdd(config.GuildID, m.User.ID, config.MonitorRole)
|
||
|
s.ChannelMessageSend(config.MonitorChann, fmt.Sprintf("Welcome %+v, you may PM me your verification, or I will ban you in an hour!\nSay \"!rules\" in this channel, without quotes for the rules. You may private/direct message me for verification instructions.\n\nYou will not be able to read/see other channels or users until you verify.", m.User.Mention()))
|
||
|
}
|
||
|
|
||
|
func guildMemberBanned(s *discordgo.Session, m *discordgo.GuildBanAdd) {
|
||
|
defer log.PanicSafe()
|
||
|
for uid, _ := range config.Probations {
|
||
|
if m.User.Email == uid {
|
||
|
delete(config.Probations, uid)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func guildMemberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
||
|
defer log.PanicSafe()
|
||
|
go runPurge(s)
|
||
|
banned:= false
|
||
|
for uid, join := range config.Probations {
|
||
|
if time.Since(join) < 2*time.Hour {
|
||
|
if m.User.ID == uid {
|
||
|
banned = true
|
||
|
s.GuildBanCreateWithReason(config.GuildID, m.User.ID, fmt.Sprintf("Left within 2 hours of joining. %+v", time.Since(join)), 0)
|
||
|
delete(config.Probations, uid)
|
||
|
}
|
||
|
} else {
|
||
|
delete(config.Probations, uid)
|
||
|
}
|
||
|
}
|
||
|
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v (@%+v) has left, ban: %+v", m.User.ID, m.User.Username, banned))
|
||
|
}
|
||
|
|
||
|
func verifyMember(s *discordgo.Session, u discordgo.User) {
|
||
|
defer log.PanicSafe()
|
||
|
s.GuildMemberRoleAdd(config.GuildID, u.ID, config.VerifiedRole)
|
||
|
s.GuildMemberRoleRemove(config.GuildID, u.ID, config.MonitorRole)
|
||
|
st, _ := s.UserChannelCreate(u.ID)
|
||
|
s.ChannelMessageSend(st.ID, "Your verification has been accepted, welcome!")
|
||
|
s.ChannelMessageSend("627620309754839070", fmt.Sprintf("Welcome %+v please introduce yourself! :)", u.Mention()))
|
||
|
}
|
||
|
|
||
|
func rejectVerification(s *discordgo.Session, u discordgo.User) {
|
||
|
defer log.PanicSafe()
|
||
|
st, _ := s.UserChannelCreate(u.ID)
|
||
|
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 %+v", time.Until(time.Now().Add(1*time.Hour))))
|
||
|
}
|
||
|
config.Unverified[u.ID] = time.Now()
|
||
|
}
|
||
|
|
||
|
func requestAge(s *discordgo.Session, u discordgo.User) {
|
||
|
defer log.PanicSafe()
|
||
|
st, _ := s.UserChannelCreate(u.ID)
|
||
|
s.ChannelMessageSend(st.ID, "What is your ASL? (Age/Sex/Language)")
|
||
|
|
||
|
}
|
||
|
|
||
|
func handlePM(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||
|
defer log.PanicSafe()
|
||
|
if strings.Contains(m.Content, "Rule") || strings.Contains(m.Content, "rule") {
|
||
|
s.ChannelMessageSend(m.ChannelID, "I specifically said to say \"!rules\" without quotes in the unverified channel for the rules.")
|
||
|
}
|
||
|
for _, uid := range config.Verifications {
|
||
|
user := userFromID(s, uid.UserID)
|
||
|
if m.Author.ID == user.ID {
|
||
|
s.ChannelMessageSend(m.ChannelID, "Your verification is pending. An admin will respond to it when they are available.")
|
||
|
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
if len(m.Attachments) != 1 {
|
||
|
s.ChannelMessageSend(m.ChannelID, "```I am a bot and this is an autoreply.\n\nUntil you send a verification, I will always say the following message:```\nYou may only send me your verification (and nothing else) to be passed to the admins (and no one else). Verification is a full face pic, with your pinky finger held to the corner of your mouth.")
|
||
|
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||
|
return
|
||
|
}
|
||
|
delete(config.Unverified, m.Author.ID)
|
||
|
msg, _ := s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v\n%+v", m.Author.Username, m.Attachments[0].ProxyURL))
|
||
|
var v Verification
|
||
|
v.Submitted = time.Now()
|
||
|
v.UserID = m.Author.ID
|
||
|
v.Username = m.Author.Username
|
||
|
v.Photo = m.Attachments[0].ProxyURL
|
||
|
v.Status = "Submitted"
|
||
|
config.Verifications[msg.ID] = v
|
||
|
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👎")
|
||
|
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👍")
|
||
|
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👶")
|
||
|
s.MessageReactionAdd(config.AdminChannel, msg.ID, "⛔")
|
||
|
}
|
||
|
|
||
|
func readReaction(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
|
||
|
defer log.PanicSafe()
|
||
|
if m.ChannelID != config.AdminChannel || m.UserID == s.State.User.ID {
|
||
|
return
|
||
|
}
|
||
|
admin, _ := s.GuildMember(config.GuildID, m.UserID)
|
||
|
adminInteraction(s, admin.User.ID)
|
||
|
verification, ok := config.Verifications[m.MessageID]
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
verification.Admin = admin.User.Username
|
||
|
verification.Closed = time.Now()
|
||
|
user := userFromID(s, verification.UserID)
|
||
|
if user.ID == "" {
|
||
|
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v, that user was not found, they might have left.", admin.Mention()))
|
||
|
delete(config.Verifications, m.MessageID)
|
||
|
return
|
||
|
}
|
||
|
if m.Emoji.Name == "👎" {
|
||
|
rejectVerification(s, user)
|
||
|
verification.Status = "Rejected"
|
||
|
} else if m.Emoji.Name == "👍" {
|
||
|
verifyMember(s, user)
|
||
|
verification.Status = "Accepted"
|
||
|
} else if m.Emoji.Name == "👶" {
|
||
|
requestAge(s, user)
|
||
|
log.LogInfo(fmt.Sprintf("%+v has requested ASL for user %+v.", admin.User.Username, user.Username))
|
||
|
return
|
||
|
} else if m.Emoji.Name == "⛔" {
|
||
|
s.GuildBanCreateWithReason(config.GuildID, user.ID, fmt.Sprintf("Underage or too many failed verifications. %+v", admin.User.Username), 5)
|
||
|
verification.Status = "Banned"
|
||
|
} else {
|
||
|
return
|
||
|
}
|
||
|
log.LogInfo(fmt.Sprintf("%+v", verification.prettyPrint()))
|
||
|
delete(config.Verifications, m.MessageID)
|
||
|
}
|
||
|
|
||
|
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||
|
defer log.PanicSafe()
|
||
|
if m.Author.ID == s.State.User.ID || m.Author.Bot {
|
||
|
return
|
||
|
}
|
||
|
if m.GuildID == "" {
|
||
|
handlePM(s, m)
|
||
|
return
|
||
|
}
|
||
|
if m.ChannelID != config.MonitorChann && time.Since(config.BumpTime) > 2 * time.Hour && !strings.Contains(m.Content, "!d bump") {
|
||
|
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v please say \"!d bump\" without the quotes to bump our server :)", m.Author.Mention()))
|
||
|
}
|
||
|
for role := range m.Member.Roles {
|
||
|
if fmt.Sprintf("%+v", role) == config.AdminRole {
|
||
|
adminInteraction(s, m.Author.ID)
|
||
|
}
|
||
|
}
|
||
|
if m.ChannelID != config.AdminChannel {
|
||
|
lastActiveChan = m.ChannelID
|
||
|
lastActiveTime = time.Now()
|
||
|
}
|
||
|
if m.ChannelID == config.MonitorChann {
|
||
|
if strings.Contains(m.Content, "erif") && !m.Author.Bot {
|
||
|
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v send me a private message for verification.", m.Author.Mention()))
|
||
|
}
|
||
|
}
|
||
|
if strings.HasPrefix(m.Content, "!d bump") {
|
||
|
if time.Since(config.BumpTime) < 2 * time.Hour {
|
||
|
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Sorry, <@%+v> already claimed the bump. Better luck next time!", config.LastBumper))
|
||
|
return
|
||
|
}
|
||
|
config.LastBumper = m.Author.ID
|
||
|
go bumpTimer(s)
|
||
|
return
|
||
|
}
|
||
|
if config.AdminChannel == "" {
|
||
|
if !strings.HasPrefix(m.Content, setupToken) {
|
||
|
return
|
||
|
}
|
||
|
config.GuildID = m.GuildID
|
||
|
setAdminChannel(s, m)
|
||
|
return
|
||
|
} else if config.MonitorChann == "" {
|
||
|
if !strings.HasPrefix(m.Content, setupToken) {
|
||
|
return
|
||
|
}
|
||
|
setMonitorChann(s, m)
|
||
|
return
|
||
|
}
|
||
|
if m.ChannelID == config.AdminChannel {
|
||
|
if strings.HasPrefix(m.Content, rebootToken) {
|
||
|
exit(s)
|
||
|
}
|
||
|
if strings.HasPrefix(m.Content, "!quote") {
|
||
|
quotes = append(quotes, strings.ReplaceAll(m.Content, "!quote", ""))
|
||
|
pmChann, _ := s.UserChannelCreate(m.Author.ID)
|
||
|
s.ChannelMessageSend(pmChann.ID, fmt.Sprintf("Your quote was added.\n %+v", quotes))
|
||
|
return
|
||
|
}
|
||
|
if strings.HasPrefix(m.Content, "!snap") || strings.HasPrefix(m.Content, "!purge") {
|
||
|
go runPurge(s)
|
||
|
s.ChannelMessageSend(config.AdminChannel, quotes[rand.Intn(len(quotes))])
|
||
|
return
|
||
|
}
|
||
|
if strings.HasPrefix(m.Content, "!st") {
|
||
|
go status(s)
|
||
|
saveConfig()
|
||
|
}
|
||
|
}
|
||
|
}
|