2020-06-09 13:04:44 +00:00
|
|
|
package discord
|
|
|
|
|
|
|
|
import (
|
2020-10-25 20:33:21 +00:00
|
|
|
"fmt"
|
2020-06-09 13:04:44 +00:00
|
|
|
"strings"
|
2020-07-02 20:00:22 +00:00
|
|
|
"time"
|
2020-06-09 13:04:44 +00:00
|
|
|
|
|
|
|
"go.etztech.xyz/sedbot/config"
|
|
|
|
"go.etztech.xyz/sedbot/database"
|
|
|
|
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
|
|
"go.jolheiser.com/beaver"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Register commands to this map
|
2020-06-12 05:17:58 +00:00
|
|
|
var (
|
|
|
|
commands = make(map[string]command)
|
|
|
|
messageRoleMap = make(map[string]map[string]string)
|
2020-07-02 20:00:22 +00:00
|
|
|
|
|
|
|
memeRateLimit *rateLimit
|
2020-06-12 05:17:58 +00:00
|
|
|
)
|
2020-06-09 13:04:44 +00:00
|
|
|
|
|
|
|
type commandInit struct {
|
|
|
|
session *discordgo.Session
|
|
|
|
message *discordgo.Message
|
|
|
|
config *config.Config
|
|
|
|
database *database.Database
|
|
|
|
}
|
|
|
|
|
|
|
|
type command struct {
|
|
|
|
validate func(cmd commandInit) bool
|
2020-06-12 05:17:58 +00:00
|
|
|
run func(cmd commandInit) (string, error)
|
2020-06-09 13:04:44 +00:00
|
|
|
help string
|
|
|
|
}
|
|
|
|
|
|
|
|
func Bot(cfg *config.Config, db *database.Database) (*discordgo.Session, error) {
|
|
|
|
bot, err := discordgo.New("Bot " + cfg.Token)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-12 05:17:58 +00:00
|
|
|
for _, messageRole := range cfg.MessageRoles {
|
|
|
|
if messageRoleMap[messageRole.MessageID] == nil {
|
|
|
|
messageRoleMap[messageRole.MessageID] = make(map[string]string)
|
|
|
|
}
|
|
|
|
_ = bot.MessageReactionAdd(messageRole.ChannelID, messageRole.MessageID, messageRole.Emoji)
|
|
|
|
messageRoleMap[messageRole.MessageID][messageRole.Emoji] = messageRole.RoleID
|
|
|
|
}
|
2020-06-09 13:04:44 +00:00
|
|
|
|
2020-06-12 16:38:18 +00:00
|
|
|
Echo(cfg)
|
2020-06-12 17:01:17 +00:00
|
|
|
bot.AddHandler(readyHandler())
|
2020-10-25 20:33:21 +00:00
|
|
|
bot.AddHandler(leaveHandler(cfg))
|
2020-06-09 13:04:44 +00:00
|
|
|
bot.AddHandler(commandHandler(cfg, db))
|
|
|
|
bot.AddHandler(messageHandler(cfg, db))
|
2020-06-12 05:17:58 +00:00
|
|
|
bot.AddHandler(reactionAddHandler())
|
|
|
|
bot.AddHandler(reactionRemoveHandler())
|
2020-06-09 13:04:44 +00:00
|
|
|
|
2020-07-02 20:00:22 +00:00
|
|
|
// Rate limits
|
|
|
|
d, err := time.ParseDuration(cfg.MemeRate)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
memeRateLimit = NewRateLimit(d)
|
|
|
|
|
2020-10-25 20:33:21 +00:00
|
|
|
// Intents
|
|
|
|
bot.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAll)
|
|
|
|
|
2020-06-09 13:04:44 +00:00
|
|
|
return bot, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendTyping(s *discordgo.Session, channelID string) {
|
|
|
|
if err := s.ChannelTyping(channelID); err != nil {
|
|
|
|
beaver.Errorf("could not send typing status: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 20:43:43 +00:00
|
|
|
func sendMessage(s *discordgo.Session, channelID, content string, scrub bool) *discordgo.Message {
|
|
|
|
var msg *discordgo.Message
|
|
|
|
var err error
|
|
|
|
if scrub {
|
|
|
|
msg, err = s.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{
|
2020-07-02 21:52:20 +00:00
|
|
|
Content: content,
|
2020-07-02 20:43:43 +00:00
|
|
|
AllowedMentions: &discordgo.MessageAllowedMentions{},
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
msg, err = s.ChannelMessageSend(channelID, content)
|
|
|
|
}
|
2020-06-09 13:04:44 +00:00
|
|
|
if err != nil {
|
|
|
|
beaver.Errorf("could not send message: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return msg
|
|
|
|
}
|
|
|
|
|
2020-07-02 20:00:22 +00:00
|
|
|
func sendEmbed(s *discordgo.Session, channelID string, embed *discordgo.MessageEmbed) *discordgo.Message {
|
|
|
|
msg, err := s.ChannelMessageSendEmbed(channelID, embed)
|
|
|
|
if err != nil {
|
|
|
|
beaver.Errorf("could not send embed: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return msg
|
|
|
|
}
|
|
|
|
|
2020-06-09 13:04:44 +00:00
|
|
|
func isStaff(authorRoleIDs, staffRoleIDs []string) bool {
|
|
|
|
for _, aRole := range authorRoleIDs {
|
|
|
|
for _, sRole := range staffRoleIDs {
|
|
|
|
if aRole == sRole {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-06-12 17:01:17 +00:00
|
|
|
func readyHandler() func(s *discordgo.Session, m *discordgo.Ready) {
|
|
|
|
return func(s *discordgo.Session, r *discordgo.Ready) {
|
|
|
|
beaver.Infof("https://discord.com/api/oauth2/authorize?client_id=%s&permissions=0&redirect_uri=https://birbmc.com&scope=bot", r.User.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 13:04:44 +00:00
|
|
|
func commandHandler(cfg *config.Config, db *database.Database) func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
|
|
return func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
|
|
// Ignore bots
|
|
|
|
if m.Author.Bot {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check prefix
|
|
|
|
if !strings.HasPrefix(m.Content, cfg.Prefix) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
content := m.Content[1:]
|
|
|
|
args := strings.Fields(content)
|
|
|
|
if len(args) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdArg := strings.ToLower(args[0])
|
|
|
|
|
|
|
|
cmd, ok := commands[cmdArg]
|
2020-06-12 05:17:58 +00:00
|
|
|
if !ok {
|
2020-06-09 13:04:44 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdInit := commandInit{
|
|
|
|
session: s,
|
|
|
|
message: m.Message,
|
|
|
|
config: cfg,
|
|
|
|
database: db,
|
|
|
|
}
|
|
|
|
if !cmd.validate(cmdInit) {
|
2020-07-02 20:43:43 +00:00
|
|
|
sendMessage(s, m.ChannelID, "You cannot run this command.", false)
|
2020-06-09 13:04:44 +00:00
|
|
|
return
|
|
|
|
}
|
2020-06-12 05:17:58 +00:00
|
|
|
feedback, err := cmd.run(cmdInit)
|
|
|
|
if err != nil {
|
|
|
|
feedback = "Internal error"
|
|
|
|
beaver.Errorf("error while running %s: %v", cmdArg, err)
|
|
|
|
}
|
|
|
|
if len(feedback) > 0 {
|
2020-07-02 20:43:43 +00:00
|
|
|
sendMessage(s, m.ChannelID, feedback, false)
|
2020-06-09 13:04:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func messageHandler(cfg *config.Config, db *database.Database) func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
|
|
return func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
|
|
// Ignore bots
|
|
|
|
if m.Author.Bot {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// [FIRED] increment
|
|
|
|
for _, role := range m.MentionRoles {
|
|
|
|
if cfg.FiredRole == role {
|
|
|
|
if err := db.IncrementPing(cfg.FiredRole); err != nil {
|
|
|
|
beaver.Errorf("could not increment ping for %s: %v", cfg.FiredRole, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-12 05:17:58 +00:00
|
|
|
|
|
|
|
func reactionAddHandler() func(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
|
|
|
|
return func(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
|
|
|
|
reactionHandler(true, s, m.MessageReaction)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func reactionRemoveHandler() func(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
|
|
|
|
return func(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
|
|
|
|
reactionHandler(false, s, m.MessageReaction)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func reactionHandler(add bool, s *discordgo.Session, m *discordgo.MessageReaction) {
|
|
|
|
if _, ok := messageRoleMap[m.MessageID]; ok {
|
2020-06-12 14:57:01 +00:00
|
|
|
if r, ok := messageRoleMap[m.MessageID][m.Emoji.APIName()]; ok {
|
2020-06-12 05:17:58 +00:00
|
|
|
var roleCmd func(guildID, userID, roleID string) (err error)
|
|
|
|
if add {
|
|
|
|
roleCmd = s.GuildMemberRoleAdd
|
|
|
|
} else {
|
|
|
|
roleCmd = s.GuildMemberRoleRemove
|
|
|
|
}
|
|
|
|
if err := roleCmd(m.GuildID, m.UserID, r); err != nil {
|
|
|
|
beaver.Errorf("could not modify role %s for user %s", r, m.UserID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-25 20:33:21 +00:00
|
|
|
|
|
|
|
func leaveHandler(cfg *config.Config) func(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
|
|
|
return func(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
|
|
|
sendMessage(s, cfg.LeaveChannel, fmt.Sprintf("%s left the server. :sob:", m.Mention()), true)
|
|
|
|
}
|
|
|
|
}
|