Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

Viewing file on branch master

1package bdiscord
2
3import (
4 "bytes"
5 "strings"
6
7 "github.com/bwmarrin/discordgo"
8 "github.com/matterbridge-org/matterbridge/bridge/config"
9 "github.com/matterbridge-org/matterbridge/bridge/helper"
10)
11
12// shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks
13func (b *Bdiscord) shouldMessageUseWebhooks(msg *config.Message) bool {
14 if b.useAutoWebhooks {
15 return true
16 }
17
18 b.channelsMutex.RLock()
19 defer b.channelsMutex.RUnlock()
20 if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok {
21 if ci.Options.WebhookURL != "" {
22 return true
23 }
24 }
25 return false
26}
27
28// maybeGetLocalAvatar checks if UseLocalAvatar contains the message's
29// account or protocol, and if so, returns the Discord avatar (if exists)
30func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string {
31 for _, val := range b.GetStringSlice("UseLocalAvatar") {
32 if msg.Protocol != val && msg.Account != val {
33 continue
34 }
35
36 member, err := b.getGuildMemberByNick(msg.Username)
37 if err != nil {
38 return ""
39 }
40
41 return member.User.AvatarURL("")
42 }
43 return ""
44}
45
46func (b *Bdiscord) webhookSendTextOnly(msg *config.Message, channelID string) (string, error) {
47 msgParts := helper.ClipOrSplitMessage(msg.Text, MessageLength, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount"))
48 msgIds := []string{}
49 for _, msgPart := range msgParts {
50 res, err := b.transmitter.Send(
51 channelID,
52 &discordgo.WebhookParams{
53 Content: msgPart,
54 Username: msg.Username,
55 AvatarURL: msg.Avatar,
56 AllowedMentions: b.getAllowedMentions(),
57 },
58 )
59 if err != nil {
60 return "", err
61 } else {
62 msgIds = append(msgIds, res.ID)
63 }
64 }
65 // Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";".
66 return strings.Join(msgIds, ";"), nil
67}
68
69func (b *Bdiscord) webhookSendFilesOnly(msg *config.Message, channelID string) error {
70 for _, f := range msg.Extra["file"] {
71 fi := f.(config.FileInfo) //nolint:forcetypeassert
72 file := discordgo.File{
73 Name: fi.Name,
74 ContentType: "",
75 Reader: bytes.NewReader(*fi.Data),
76 }
77 content := fi.Comment
78
79 // Cannot use the resulting ID for any edits anyway, so throw it away.
80 // This has to be re-enabled when we implement message deletion.
81 _, err := b.transmitter.Send(
82 channelID,
83 &discordgo.WebhookParams{
84 Username: msg.Username,
85 AvatarURL: msg.Avatar,
86 Files: []*discordgo.File{&file},
87 Content: content,
88 AllowedMentions: b.getAllowedMentions(),
89 },
90 )
91 if err != nil {
92 b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, err)
93 return err
94 }
95 }
96 return nil
97}
98
99// webhookSend send one or more message via webhook, taking care of file
100// uploads (from slack, telegram or mattermost).
101// Returns messageID and error.
102func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (string, error) {
103 var (
104 res string
105 err error
106 )
107
108 // If avatar is unset, mutate the message to include the local avatar (but only if settings say we should do this)
109 if msg.Avatar == "" {
110 msg.Avatar = b.maybeGetLocalAvatar(msg)
111 }
112
113 // WebhookParams can have either `Content` or `File`.
114
115 // We can't send empty messages.
116 if msg.Text != "" {
117 res, err = b.webhookSendTextOnly(msg, channelID)
118 }
119
120 if err == nil && msg.Extra != nil {
121 err = b.webhookSendFilesOnly(msg, channelID)
122 }
123
124 return res, err
125}
126
127func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (string, error) {
128 // skip events
129 if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
130 return "", nil
131 }
132
133 // skip empty messages
134 if msg.Text == "" && (msg.Extra == nil || len(msg.Extra["file"]) == 0) {
135 b.Log.Debugf("Skipping empty message %#v", msg)
136 return "", nil
137 }
138
139 // discord username must be [0..32] max
140 if len(msg.Username) > 32 {
141 msg.Username = msg.Username[0:32]
142 }
143
144 if msg.ID != "" {
145 // Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";".
146 msgIds := strings.Split(msg.ID, ";")
147 msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds))
148 for len(msgParts) < len(msgIds) {
149 msgParts = append(msgParts, "((obsoleted by edit))")
150 }
151 b.Log.Debugf("Editing webhook message")
152 var editErr error = nil
153 for i := range msgParts {
154 // In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates.
155 // TODO: Optimize away noop-updates of un-edited messages
156 editErr = b.transmitter.Edit(channelID, msgIds[i], &discordgo.WebhookParams{
157 Content: msgParts[i],
158 Username: msg.Username,
159 AllowedMentions: b.getAllowedMentions(),
160 })
161 if editErr != nil {
162 break
163 }
164 }
165 if editErr == nil {
166 return msg.ID, nil
167 }
168 b.Log.Errorf("Could not edit webhook message(s): %s; sending as new message(s) instead", editErr)
169 }
170
171 b.Log.Debugf("Processing webhook sending for message %#v", msg)
172 msg.Text = b.replaceUserMentions(msg.Text)
173 msgID, err := b.webhookSend(msg, channelID)
174 if err != nil {
175 b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msgID, err)
176 return "", err
177 }
178 return msgID, nil
179}
180