Browse Source

Initial Commit

hkremer/rebranding
Ichaboc 4 years ago
commit
95a757ee75
  1. 3
      .gitignore
  2. 200
      config.go
  3. 363
      main.go
  4. 29
      types.go

3
.gitignore vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
disgord-thanos
config.json
start.sh

200
config.go

@ -0,0 +1,200 @@ @@ -0,0 +1,200 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"time"
"os"
"github.com/bwmarrin/discordgo"
)
func status(s *discordgo.Session) {
defer log.PanicSafe()
monChan, _ := s.Channel(config.MonitorChann)
roles, _ := s.GuildRoles(config.GuildID)
var monRole discordgo.Role
var veriRole discordgo.Role
for _, role := range roles {
if role.ID == config.MonitorRole {
monRole = *role
}
if role.ID == config.VerifiedRole {
veriRole = *role
}
}
status := fmt.Sprintf("Uptime: %+v\n", time.Since(startupTime))
status += fmt.Sprintf("Monitor role: %+v\n", monRole.Mention())
status += fmt.Sprintf("Monitor chann: %+v\n", monChan.Mention())
status += fmt.Sprintf("Verified role: %+v\n", veriRole.Mention())
status += fmt.Sprintf("Last bump: %+v\n", time.Since(config.BumpTime))
status += fmt.Sprintf("Last bumper: <@%+v>\n", config.LastBumper)
status += fmt.Sprintf("Bump needed: %+v\n", bump)
if len(config.Unverified) > 0 {
status += "Unverified users:\n```"
for k, v := range config.Unverified {
uvUser := userFromID(s, k)
status += fmt.Sprintf("\n%+v will be removed in %+v", uvUser.Username, time.Until(v.Add(1*time.Hour)))
}
status += "```"
} else {
status += "There are no unverified users.\n"
}
if len(config.Verifications) > 0 {
status += "Pending verifications:\n"
status += "```"
for _, v := range config.Verifications {
status += fmt.Sprintf("%+v has submitted a verification.", v.Username)
}
status += "```"
} else {
status += "There are no pending verifications."
}
if len(config.Probations) > 0 {
status += "\nThe following users are on probation: \n```"
for uid, join := range config.Probations {
probationUser := userFromID(s, uid)
status += fmt.Sprintf("%+v for another %+v\n", probationUser.Username, time.Until(join.Add(2*time.Hour)))
}
status += "```"
}
s.ChannelMessageSend(config.AdminChannel, status)
statistics := "```"
for k, v := range config.Stats {
adminUser, err := s.GuildMember(config.GuildID, k)
if err == nil {
statistics += fmt.Sprintf("\n%+v: %+v", adminUser.User.Username, v+1)
} else {
log.LogErrorType(err)
}
}
statistics += "\n```"
log.LogInfo(fmt.Sprintf("Private statistics: %+v", statistics))
go runPurge(s)
return
}
func loadConfig() {
var c Config
confFile, _ := ioutil.ReadFile(configFile)
err := json.Unmarshal([]byte(confFile), &c)
if err != nil {
log.LogErrorType(err)
return
}
config = c
if time.Since(config.BumpTime) < (2 * time.Hour) {
bump = false
} else {
bump = true
}
if config.Stats == nil {
config.Stats = make(map[string]int)
}
if config.Unverified == nil {
config.Unverified = make(map[string]time.Time)
}
if config.Verifications == nil {
config.Verifications = make(map[string]Verification)
}
if config.Probations == nil {
config.Probations = make(map[string]time.Time)
}
log.LogInfo("Setup completed using config file.")
}
func saveConfig() {
defer log.PanicSafe()
file, err := json.Marshal(config)
if err != nil {
log.LogErrorType(err)
}
err = ioutil.WriteFile(configFile, file, 0600)
if err != nil {
log.LogErrorType(err)
}
}
func setAdminChannel(s *discordgo.Session, m *discordgo.MessageCreate) {
config.AdminChannel = m.ChannelID
if len(m.MentionRoles) != 1 {
s.ChannelMessageSend(config.AdminChannel, "Invalid verified role")
return
}
config.VerifiedRole = m.MentionRoles[0]
s.ChannelMessageDelete(m.ChannelID, m.ID)
msg, _ := s.ChannelMessageSend(config.AdminChannel, "Run !setup in the monitored channel to finish setup.")
setupMsg = msg.ID
}
func setMonitorChann(s *discordgo.Session, m *discordgo.MessageCreate) {
config.MonitorChann = m.ChannelID
if len(m.MentionRoles) != 1 {
s.ChannelMessageSend(config.AdminChannel, "Invalid monitor role")
return
}
config.MonitorRole = m.MentionRoles[0]
s.ChannelMessageDelete(m.ChannelID, m.ID)
s.ChannelMessageSend(config.AdminChannel, "Setup completed. Run !status for status.")
s.ChannelMessageDelete(config.AdminChannel, setupMsg)
go purgeTimer(s)
saveConfig()
}
func bumpTimer(s *discordgo.Session) {
if !bump {
return
}
bump = false
config.BumpTime = time.Now()
time.Sleep(2 * time.Hour)
if time.Since(lastActiveTime) < (5*time.Minute) && lastActiveChan != config.AdminChannel {
s.ChannelMessageSend(lastActiveChan, "!d bump is ready, please use it. (say \"!d bump\" without the quotes)")
}
s.ChannelMessageSend(config.AdminChannel, "!d bump is ready.")
bump = true
}
func purgeTimer(s *discordgo.Session) {
for {
runPurge(s)
saveConfig()
if time.Since(lastActiveTime) > 4 * time.Hour && time.Since(startupTime) > 12 * time.Hour {
saveConfig()
os.Exit(0)
}
time.Sleep(20 * time.Minute)
}
}
func (v Verification) prettyPrint() string {
ret := ""
ret += fmt.Sprintf("```%+v has marked %+v's verification as %+v\n", v.Admin, v.Username, v.Status)
ret += fmt.Sprintf("Submitted: %+v\nClosed: %+v\n", v.Submitted, v.Closed)
ret += fmt.Sprintf("Turnaround time: %+v```", time.Since(v.Submitted))
ret += fmt.Sprintf("\n%+v", v.Photo)
return ret
}
func userFromID(s *discordgo.Session, i string) discordgo.User {
u, err := s.GuildMember(config.GuildID, i)
if err != nil {
log.LogErrorType(err)
return discordgo.User{}
}
return *u.User
}
func adminInteraction(s *discordgo.Session, m string) {
admin, _ := s.GuildMember(config.GuildID, m)
counter, ok := config.Stats[admin.User.ID]
if !ok {
config.Stats[admin.User.ID] = 0
} else {
config.Stats[admin.User.ID] = counter + 1
}
}

363
main.go

@ -0,0 +1,363 @@ @@ -0,0 +1,363 @@
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()
}
}
}

29
types.go

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package main
import "time"
// Config struct used for bot
type Config struct {
GuildID string
AdminChannel string
AdminRole string
MonitorRole string
MonitorChann string
VerifiedRole string
BumpTime time.Time
LastBumper string
Stats map[string]int
Unverified map[string]time.Time
Verifications map[string]Verification
Probations map[string]time.Time
}
type Verification struct {
UserID string
Username string
Photo string
Submitted time.Time
Status string
Admin string
Closed time.Time
}
Loading…
Cancel
Save