169 lines
4.7 KiB
Go
169 lines
4.7 KiB
Go
|
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
|
||
|
}
|
||
|
}
|