diff options
author | Qais Patankar <qaisjp@gmail.com> | 2020-11-30 05:47:02 +0000 |
---|---|---|
committer | Wim <wim@42.be> | 2020-12-13 23:19:48 +0100 |
commit | 52e2f926f423295dbf95463218bed6abd94d574a (patch) | |
tree | 18b7f76ab647c73a631c257ef806513dd04e90ed /bridge/discord/discord.go | |
parent | 611fb279bc3680ef9e241e913589dc9056c2f5bd (diff) | |
download | matterbridge-msglm-52e2f926f423295dbf95463218bed6abd94d574a.tar.gz matterbridge-msglm-52e2f926f423295dbf95463218bed6abd94d574a.tar.bz2 matterbridge-msglm-52e2f926f423295dbf95463218bed6abd94d574a.zip |
Add initial transmitter implementation (discord)
This has been tested with one webhook in one channel.
Sends, edits and deletions work fine
Diffstat (limited to 'bridge/discord/discord.go')
-rw-r--r-- | bridge/discord/discord.go | 194 |
1 files changed, 78 insertions, 116 deletions
diff --git a/bridge/discord/discord.go b/bridge/discord/discord.go index 6e43c99d..1a3af929 100644 --- a/bridge/discord/discord.go +++ b/bridge/discord/discord.go @@ -8,6 +8,7 @@ import ( "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" + "github.com/42wim/matterbridge/bridge/discord/transmitter" "github.com/42wim/matterbridge/bridge/helper" "github.com/matterbridge/discordgo" ) @@ -19,12 +20,9 @@ type Bdiscord struct { c *discordgo.Session - nick string - userID string - guildID string - webhookID string - webhookToken string - canEditWebhooks bool + nick string + userID string + guildID string channelsMutex sync.RWMutex channels []*discordgo.Channel @@ -33,6 +31,10 @@ type Bdiscord struct { membersMutex sync.RWMutex userMemberMap map[string]*discordgo.Member nickMemberMap map[string]*discordgo.Member + + // Webhook specific logic + useAutoWebhooks bool + transmitter *transmitter.Transmitter } func New(cfg *bridge.Config) bridge.Bridger { @@ -40,9 +42,17 @@ func New(cfg *bridge.Config) bridge.Bridger { b.userMemberMap = make(map[string]*discordgo.Member) b.nickMemberMap = make(map[string]*discordgo.Member) b.channelInfoMap = make(map[string]*config.ChannelInfo) - if b.GetString("WebhookURL") != "" { - b.Log.Debug("Configuring Discord Incoming Webhook") - b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL")) + + // If WebhookURL is set to anything, we assume preference for autoWebhooks + // + // Legacy note: WebhookURL used to have an actual webhook URL that we would edit, + // but we stopped doing that due to Discord making rate limits more aggressive. + // + // We're keeping the same setting for now, and we will late deprecate this setting + // in favour of a new setting, something like "AutoWebhooks=true" + b.useAutoWebhooks = b.GetString("WebhookURL") != "" + if b.useAutoWebhooks { + b.Log.Debug("Using automatic webhooks") } return b } @@ -137,36 +147,44 @@ func (b *Bdiscord) Connect() error { return err } - b.channelsMutex.RLock() - if b.GetString("WebhookURL") == "" { - for _, channel := range b.channels { - b.Log.Debugf("found channel %#v", channel) - } - } else { - manageWebhooks := discordgo.PermissionManageWebhooks - var channelsDenied []string - for _, info := range b.Channels { - id := b.getChannelID(info.Name) // note(qaisjp): this readlocks channelsMutex - b.Log.Debugf("Verifying PermissionManageWebhooks for %s with ID %s", info.ID, id) - - perms, permsErr := b.c.UserChannelPermissions(userinfo.ID, id) - if permsErr != nil { - b.Log.Warnf("Failed to check PermissionManageWebhooks in channel \"%s\": %s", info.Name, permsErr.Error()) - } else if perms&manageWebhooks == manageWebhooks { - continue + // Initialise webhook management + b.transmitter = transmitter.New(b.c, b.guildID, "matterbridge", b.useAutoWebhooks) + b.transmitter.Log = b.Log + + var webhookChannelIDs []string + for _, channel := range b.Channels { + channelID := b.getChannelID(channel.Name) // note(qaisjp): this readlocks channelsMutex + + // If a WebhookURL was not explicitly provided for this channel, + // there are two options: just a regular bot message (ugly) or this is should be webhook sent + if channel.Options.WebhookURL == "" { + // If it should be webhook sent, we should enforce this via the transmitter + if b.useAutoWebhooks { + webhookChannelIDs = append(webhookChannelIDs, channelID) } - channelsDenied = append(channelsDenied, fmt.Sprintf("%#v", info.Name)) + continue + } + + whID, whToken, ok := b.splitURL(channel.Options.WebhookURL) + if !ok { + return fmt.Errorf("failed to parse WebhookURL %#v for channel %#v", channel.Options.WebhookURL, channel.ID) } - b.canEditWebhooks = len(channelsDenied) == 0 - if b.canEditWebhooks { - b.Log.Info("Can manage webhooks; will edit channel for global webhook on send") - } else { - b.Log.Warn("Can't manage webhooks; won't edit channel for global webhook on send") - b.Log.Warn("Can't manage webhooks in channels: ", strings.Join(channelsDenied, ", ")) + b.transmitter.AddWebhook(channelID, &discordgo.Webhook{ + ID: whID, + Token: whToken, + GuildID: b.guildID, + ChannelID: channelID, + }) + } + + if b.useAutoWebhooks { + err = b.transmitter.RefreshGuildWebhooks(webhookChannelIDs) + if err != nil { + b.Log.WithError(err).Println("transmitter could not refresh guild webhooks") + return err } } - b.channelsMutex.RUnlock() // Obtaining guild members and initializing nickname mapping. b.membersMutex.Lock() @@ -223,23 +241,9 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { msg.Text = "_" + msg.Text + "_" } - // use initial webhook configured for the entire Discord account - isGlobalWebhook := true - wID := b.webhookID - wToken := b.webhookToken - - // check if have a channel specific webhook - b.channelsMutex.RLock() - if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { - if ci.Options.WebhookURL != "" { - wID, wToken = b.splitURL(ci.Options.WebhookURL) - isGlobalWebhook = false - } - } - b.channelsMutex.RUnlock() - // Use webhook to send the message - if wID != "" && msg.Event != config.EventMsgDelete { + useWebhooks := b.shouldMessageUseWebhooks(&msg) + if useWebhooks && msg.Event != config.EventMsgDelete { // skip events if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { return "", nil @@ -260,32 +264,18 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { if msg.ID != "" { b.Log.Debugf("Editing webhook message") - uri := discordgo.EndpointWebhookToken(wID, wToken) + "/messages/" + msg.ID - _, err := b.c.RequestWithBucketID("PATCH", uri, discordgo.WebhookParams{ + err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{ Content: msg.Text, Username: msg.Username, - }, discordgo.EndpointWebhookToken("", "")) + }) if err == nil { return msg.ID, nil } b.Log.Errorf("Could not edit webhook message: %s", err) } - b.Log.Debugf("Broadcasting using Webhook") - - // if we have a global webhook for this Discord account, and permission - // to modify webhooks (previously verified), then set its channel to - // the message channel before using it. - if isGlobalWebhook && b.canEditWebhooks { - b.Log.Debugf("Setting webhook channel to \"%s\"", msg.Channel) - _, err := b.c.WebhookEdit(wID, "", "", channelID) - if err != nil { - b.Log.Errorf("Could not set webhook channel: %s", err) - return "", err - } - } b.Log.Debugf("Processing webhook sending for message %#v", msg) - msg, err := b.webhookSend(&msg, wID, wToken) + msg, err := b.webhookSend(&msg, channelID) if err != nil { b.Log.Errorf("Could not broadcast via webook for message %#v: %s", msg, err) return "", err @@ -339,46 +329,6 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { return res.ID, nil } -// useWebhook returns true if we have a webhook defined somewhere -func (b *Bdiscord) useWebhook() bool { - if b.GetString("WebhookURL") != "" { - return true - } - - b.channelsMutex.RLock() - defer b.channelsMutex.RUnlock() - - for _, channel := range b.channelInfoMap { - if channel.Options.WebhookURL != "" { - return true - } - } - return false -} - -// isWebhookID returns true if the specified id is used in a defined webhook -func (b *Bdiscord) isWebhookID(id string) bool { - if b.GetString("WebhookURL") != "" { - wID, _ := b.splitURL(b.GetString("WebhookURL")) - if wID == id { - return true - } - } - - b.channelsMutex.RLock() - defer b.channelsMutex.RUnlock() - - for _, channel := range b.channelInfoMap { - if channel.Options.WebhookURL != "" { - wID, _ := b.splitURL(channel.Options.WebhookURL) - if wID == id { - return true - } - } - } - return false -} - // handleUploadFile handles native upload of files func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) { var err error @@ -401,10 +351,26 @@ func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (stri return "", nil } +// shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks +func (b *Bdiscord) shouldMessageUseWebhooks(msg *config.Message) bool { + if b.useAutoWebhooks { + return true + } + + b.channelsMutex.RLock() + defer b.channelsMutex.RUnlock() + if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { + if ci.Options.WebhookURL != "" { + return true + } + } + return false +} + // webhookSend send one or more message via webhook, taking care of file // uploads (from slack, telegram or mattermost). // Returns messageID and error. -func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*discordgo.Message, error) { +func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) { var ( res *discordgo.Message err error @@ -427,10 +393,8 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d // We can't send empty messages. if msg.Text != "" { - res, err = b.c.WebhookExecute( - webhookID, - token, - true, + res, err = b.transmitter.Send( + channelID, &discordgo.WebhookParams{ Content: msg.Text, Username: msg.Username, @@ -454,10 +418,8 @@ func (b *Bdiscord) webhookSend(msg *config.Message, webhookID, token string) (*d if msg.Text == "" { content = fi.Comment } - _, e2 := b.c.WebhookExecute( - webhookID, - token, - false, + _, e2 := b.transmitter.Send( + channelID, &discordgo.WebhookParams{ Username: msg.Username, AvatarURL: msg.Avatar, |