urizen/internal/rolereactions/rolereactions.go

169 lines
4.7 KiB
Go
Raw Normal View History

2020-07-16 06:24:19 +00:00
package rolereactions
import (
"io/ioutil"
"log"
"git.annabunch.es/annabunches/urizen/lib/util"
"github.com/bwmarrin/discordgo"
"gopkg.in/yaml.v2"
)
type roleReactionConfig struct {
Channel string
Message string
MessageText string `yaml:"message_text"`
RoleMap map[string]emojiData `yaml:"role_map"`
}
type emojiData struct {
CustomEmojiID string `yaml:"custom_emoji_id"`
RoleID string `yaml:"role_id"`
}
var config []roleReactionConfig
// Init initializes the module, installing the handlers into the provided
// discord session and setting up the role mapping via the config file.
func Init(session *discordgo.Session, configFile string) {
// TODO: should config be added/stored dynamically?
config = make([]roleReactionConfig, 0)
util.ParseFile(configFile, initConfig)
changed := initMessages(session)
if changed {
writeConfig(configFile)
}
session.AddHandler(handleReactAdd)
session.AddHandler(handleReactRemove)
}
func initConfig(data []byte) int {
err := yaml.Unmarshal(data, &config)
if err != nil {
log.Panicf("[RoleReactions] Failed to parse role reaction config: %v", err)
}
return 1
}
func writeConfig(filename string) {
data, err := yaml.Marshal(&config)
if err != nil {
log.Printf("[RoleReactions] Failed to convert config back to yaml. Couldn't save config. This will probably break something: %v", err)
return
}
err = ioutil.WriteFile(filename, data, 0644)
if err != nil {
log.Printf("[RoleReactions] Failed to save changes to config. This will probably break something on future runs: %v", err)
return
}
log.Printf("[RoleReactions] Wrote new config.")
}
// Iterate over the message IDs, first removing all reactions from the
// bot, then adding the available reactions.
// This will log an error if a message ID is not found, but program
// execution will continue.
func initMessages(session *discordgo.Session) bool {
changed := false
for i, data := range config {
// if message ID is empty, create a new message and flag that we need to write
// out the new config
if data.Message == "" {
msgID, err := postMessage(session, data)
if err != nil {
log.Printf("[RoleReactions] Couldn't post message to channel '%s'. This config entry will be ignored.", data.Channel)
continue
}
config[i].Message = msgID
data.Message = msgID // we have to set this one so that the rest of this loop iteration works as expected
changed = true
}
// delete any self-made reactions from previous runs. We iterate over all of them
// in case the options have changed
deleteOwnReactions(session, data.Message, data.Channel)
// Add the new reacts
for name, eData := range data.RoleMap {
emojiString := name
if eData.CustomEmojiID != "" {
emojiString += ":" + eData.CustomEmojiID
}
err := session.MessageReactionAdd(data.Channel, data.Message, emojiString)
if err != nil {
log.Printf("[RoleReactions] Couldn't add reaction '%s' on message '%s': %v", emojiString, data.Message, err)
continue
}
}
log.Printf("[RoleReactions] listening to message %s", data.Message)
log.Printf("[RoleReactions] role map: %v", data.RoleMap)
}
return changed
}
func postMessage(session *discordgo.Session, data roleReactionConfig) (string, error) {
log.Printf("Posting a new message to channel '%s'", data.Channel)
msg, err := session.ChannelMessageSend(data.Channel, data.MessageText)
if err != nil {
return "", err
}
return msg.ID, nil
}
func deleteOwnReactions(session *discordgo.Session, message string, channel string) {
// Clean up the old reacts
msg, err := session.ChannelMessage(channel, message)
if err != nil {
log.Printf("[RoleReactions] Error retrieving message to clean reacts: %v", err)
return
}
for _, react := range msg.Reactions {
if react.Me != true {
continue
}
session.MessageReactionRemove(channel, message, react.Emoji.Name, "@me")
}
}
func handleReactAdd(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
for _, target := range config {
if m.MessageID != target.Message {
continue
}
eData, ok := target.RoleMap[m.Emoji.Name]
if !ok {
log.Printf("[RoleReactions] Unmapped react added to message '%s': %s", target.Message, m.Emoji.Name)
return
}
// add the role to the user
s.GuildMemberRoleAdd(m.GuildID, m.UserID, eData.RoleID)
log.Printf("[RoleReactions] Added role '%s' to user '%s'", m.UserID, eData.RoleID)
return
}
}
func handleReactRemove(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
for _, target := range config {
if m.MessageID != target.Message {
continue
}
eData, ok := target.RoleMap[m.Emoji.Name]
if !ok {
return
}
s.GuildMemberRoleRemove(m.GuildID, m.UserID, eData.RoleID)
log.Printf("[RoleReactions] Removed role '%s' from user '%s'", m.UserID, eData.RoleID)
return
}
}