commit e52eb3a5531bb344ef2f3b6ce6e2559b87cf229a Author: Luther Wen Xu Date: Mon May 11 13:30:07 2020 +0800 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f6539a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Milen +bot.db +config.toml diff --git a/commands/commands.go b/commands/commands.go new file mode 100644 index 0000000..9f1c78d --- /dev/null +++ b/commands/commands.go @@ -0,0 +1,39 @@ +package commands + +import ( + "strings" + + "github.com/bwmarrin/discordgo" + + "gitea.teamortix.com/chanbakjsd/Milen/db" + "gitea.teamortix.com/chanbakjsd/Milen/util" +) + +func Event(dg *discordgo.Session, m *discordgo.MessageCreate) { + if !strings.HasPrefix(m.Content, "milen ") { + return + } + split := strings.Split(m.Content, " ") + switch split[1] { + case "autorole": + util.RequireAdmin(dg, m.Author.ID, m.ChannelID, func() { + if len(split) < 6 { + util.SendCheckError(dg, m.ChannelID, "❌ Incorrect usage. Usage: `milen autorole `") + return + } + role := util.ParseRole(split[2]) + if role == "" { + util.SendCheckError(dg, m.ChannelID, "❌ Incorrect usage. Usage: `milen autorole `") + return + } + emoji := util.ParseEmoji(split[5]) + db.CreateReactRole(split[4], emoji, role) + err := dg.MessageReactionAdd(split[3], split[4], emoji) + if err != nil { + util.ReportError(dg, err) + return + } + util.SendCheckError(dg, m.ChannelID, "✅ Auto-role registered.") + }) + } +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..6f6d012 --- /dev/null +++ b/config.go @@ -0,0 +1,25 @@ +package main + +import ( + "io/ioutil" + + "github.com/BurntSushi/toml" +) + +type config struct { + Token string + ReportTarget string +} + +func LoadConfig() config { + bytes, err := ioutil.ReadFile("config.toml") + if err != nil { + panic(err) + } + var cfg config + err = toml.Unmarshal(bytes, &cfg) + if err != nil { + panic(err) + } + return cfg +} diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..16ff3d1 --- /dev/null +++ b/db/db.go @@ -0,0 +1,48 @@ +package db + +import ( + "fmt" + + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" +) + +type ReactRole struct { + MessageID string + EmojiID string + RoleID string +} + +var db *gorm.DB + +func init() { + var err error + db, err = gorm.Open("sqlite3", "bot.db") + if err != nil { + panic(err) + } + db.AutoMigrate(&ReactRole{}) +} + +func Close() { + db.Close() +} + +func CreateReactRole(messageID, emojiID, roleID string) { + fmt.Println("CreateReactRole", messageID, emojiID, roleID) + db.Create(&ReactRole{ + MessageID: messageID, + EmojiID: emojiID, + RoleID: roleID, + }) +} + +func GetReactRole(messageID, emojiID string) string { + fmt.Println("GetReactRole", messageID, emojiID) + result := ReactRole{ + MessageID: messageID, + EmojiID: emojiID, + } + db.Where(&result).First(&result) + return result.RoleID +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..633f366 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module gitea.teamortix.com/chanbakjsd/Milen + +go 1.14 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/bwmarrin/discordgo v0.20.3 + github.com/jinzhu/gorm v1.9.12 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d8615ac --- /dev/null +++ b/go.sum @@ -0,0 +1,31 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bwmarrin/discordgo v0.20.3 h1:AxjcHGbyBFSC0a3Zx5nDQwbOjU7xai5dXjRnZ0YB7nU= +github.com/bwmarrin/discordgo v0.20.3/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= +github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= +github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..dff08d7 --- /dev/null +++ b/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/bwmarrin/discordgo" + + "gitea.teamortix.com/chanbakjsd/Milen/commands" + "gitea.teamortix.com/chanbakjsd/Milen/db" + "gitea.teamortix.com/chanbakjsd/Milen/reactrole" + "gitea.teamortix.com/chanbakjsd/Milen/util" +) + +func main() { + cfg := LoadConfig() + util.ReportTarget = cfg.ReportTarget + + dg, err := discordgo.New("Bot " + cfg.Token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + dg.AddHandler(commands.Event) + dg.AddHandler(reactrole.EventAdd) + dg.AddHandler(reactrole.EventRemove) + + err = dg.Open() + if err != nil { + fmt.Println("error opening connection,", err) + return + } + fmt.Println("Bot is now running. Press CTRL-C to exit.") + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) + <-sc + dg.Close() + db.Close() +} diff --git a/reactrole/listener.go b/reactrole/listener.go new file mode 100644 index 0000000..712e483 --- /dev/null +++ b/reactrole/listener.go @@ -0,0 +1,30 @@ +package reactrole + +import ( + "github.com/bwmarrin/discordgo" + + "gitea.teamortix.com/chanbakjsd/Milen/db" + "gitea.teamortix.com/chanbakjsd/Milen/util" +) + +func EventAdd(dg *discordgo.Session, r *discordgo.MessageReactionAdd) { + role := db.GetReactRole(r.MessageID, r.Emoji.APIName()) + if role == "" { + return + } + err := dg.GuildMemberRoleAdd(r.GuildID, r.UserID, role) + if err != nil { + util.ReportError(dg, err) + } +} + +func EventRemove(dg *discordgo.Session, r *discordgo.MessageReactionRemove) { + role := db.GetReactRole(r.MessageID, r.Emoji.APIName()) + if role == "" { + return + } + err := dg.GuildMemberRoleRemove(r.GuildID, r.UserID, role) + if err != nil { + util.ReportError(dg, err) + } +} diff --git a/util/error.go b/util/error.go new file mode 100644 index 0000000..5e9439f --- /dev/null +++ b/util/error.go @@ -0,0 +1,40 @@ +package util + +import ( + "fmt" + "runtime/debug" + + "github.com/bwmarrin/discordgo" +) + +var ReportTarget string + +func ReportError(dg *discordgo.Session, toReport error) { + msg := "```\n" + toReport.Error() + "\n" + string(debug.Stack()) + "```" + + if dg == nil { + fmt.Println(msg) + fmt.Println("DiscordGo not provided") + return + } + + channel, err := dg.UserChannelCreate(ReportTarget) + if err != nil { + fmt.Println(msg) + fmt.Println(err) + return + } + _, err = dg.ChannelMessageSend(channel.ID, msg) + if err != nil { + fmt.Println(msg) + fmt.Println(err) + } +} + +func SendCheckError(dg *discordgo.Session, channelID string, message string) { + _, err := dg.ChannelMessageSend(channelID, message) + if err != nil { + ReportError(dg, err) + return + } +} diff --git a/util/magic.go b/util/magic.go new file mode 100644 index 0000000..12c0464 --- /dev/null +++ b/util/magic.go @@ -0,0 +1,19 @@ +package util + +import ( + "strings" +) + +func ParseRole(roleString string) string { + if !strings.HasPrefix(roleString, "<@&") || !strings.HasSuffix(roleString, ">") { + return "" + } + return roleString[3 : len(roleString)-1] +} + +func ParseEmoji(emojiString string) string { + if !strings.HasPrefix(emojiString, "<:") || !strings.HasSuffix(emojiString, ">") { + return emojiString + } + return emojiString[2 : len(emojiString)-1] +} diff --git a/util/perm.go b/util/perm.go new file mode 100644 index 0000000..8bd7f1c --- /dev/null +++ b/util/perm.go @@ -0,0 +1,18 @@ +package util + +import ( + "github.com/bwmarrin/discordgo" +) + +func RequireAdmin(dg *discordgo.Session, userID string, channelID string, hasAdmin func()) { + perm, err := dg.UserChannelPermissions(userID, channelID) + if err != nil { + ReportError(dg, err) + return + } + if perm&discordgo.PermissionAdministrator == 0 { + SendCheckError(dg, channelID, "❌ You do not have permission to use this command.") + return + } + hasAdmin() +}