Thumbnail

rani/matterbridge.git

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

Viewing file on branch master

1package bdiscord
2
3import (
4 "time"
5 "strings"
6
7 "github.com/bwmarrin/discordgo"
8 "github.com/davecgh/go-spew/spew"
9 "github.com/matterbridge-org/matterbridge/bridge/config"
10 "github.com/matterbridge-org/matterbridge/bridge/helper"
11)
12
13func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam
14 if m.GuildID != b.guildID {
15 b.Log.Debugf("Ignoring messageDelete because it originates from a different guild")
16 return
17 }
18 rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EventMsgDelete, Text: config.EventMsgDelete}
19 rmsg.Channel = b.getChannelName(m.ChannelID)
20
21 b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
22 b.Log.Debugf("<= Message is %#v", rmsg)
23 b.Remote <- rmsg
24}
25
26// TODO(qaisjp): if other bridges support bulk deletions, it could be fanned out centrally
27func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageDeleteBulk) { //nolint:unparam
28 if m.GuildID != b.guildID {
29 b.Log.Debugf("Ignoring messageDeleteBulk because it originates from a different guild")
30 return
31 }
32 for _, msgID := range m.Messages {
33 rmsg := config.Message{
34 Account: b.Account,
35 ID: msgID,
36 Event: config.EventMsgDelete,
37 Text: config.EventMsgDelete,
38 Channel: b.getChannelName(m.ChannelID),
39 }
40
41 b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
42 b.Log.Debugf("<= Message is %#v", rmsg)
43 b.Remote <- rmsg
44 }
45}
46
47func (b *Bdiscord) messageEvent(s *discordgo.Session, m *discordgo.Event) {
48 b.Log.Debug(spew.Sdump(m.Struct))
49}
50
51func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
52 if m.GuildID != b.guildID {
53 b.Log.Debugf("Ignoring messageTyping because it originates from a different guild")
54 return
55 }
56 if !b.GetBool("ShowUserTyping") {
57 return
58 }
59
60 // Ignore our own typing messages
61 if m.UserID == b.userID {
62 return
63 }
64
65 rmsg := config.Message{Account: b.Account, Event: config.EventUserTyping}
66 rmsg.Channel = b.getChannelName(m.ChannelID)
67 b.Remote <- rmsg
68}
69
70func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) { //nolint:unparam
71 if m.GuildID != b.guildID {
72 b.Log.Debugf("Ignoring messageUpdate because it originates from a different guild")
73 return
74 }
75 if b.GetBool("EditDisable") {
76 return
77 }
78 // only when message is actually edited
79 if m.Message.EditedTimestamp != nil {
80 // don't relay old edits
81 // there are often spurrious "ghost" messages
82 delay := time.Now().Sub(*m.Message.EditedTimestamp)
83 if delay >= time.Duration(6) * time.Hour {
84 return
85 }
86 b.Log.Debugf("Sending edit message")
87 m.Content += b.GetString("EditSuffix")
88 msg := &discordgo.MessageCreate{
89 Message: m.Message,
90 }
91 b.messageCreate(s, msg)
92 }
93}
94
95func (b *Bdiscord) handleQuote(s *discordgo.Session, m *discordgo.Message, msg string) string {
96 if b.GetBool("QuoteDisable") {
97 return msg
98 }
99 if m.MessageReference == nil {
100 return msg
101 }
102 refMsgRef := m.MessageReference
103 refMsg, err := s.ChannelMessage(refMsgRef.ChannelID, refMsgRef.MessageID)
104 if err != nil {
105 b.Log.Errorf("Error getting quoted message %s:%s: %s", refMsgRef.ChannelID, refMsgRef.MessageID, err)
106 return msg
107 }
108
109 quoteMessage := refMsg.Content
110 quoteNick := b.getNick(refMsg.Author, refMsg.GuildID)
111 fromWebhook := m.WebhookID != ""
112 if !fromWebhook && b.GetBool("UseDiscriminator") {
113 quoteNick += "#" + refMsg.Author.Discriminator
114
115 }
116
117 format := b.GetString("quoteformat")
118 if format == "" {
119 format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})"
120 }
121 quoteMessagelength := len([]rune(quoteMessage))
122 if b.GetInt("QuoteLengthLimit") != 0 && quoteMessagelength >= b.GetInt("QuoteLengthLimit") {
123 runes := []rune(quoteMessage)
124 quoteMessage = string(runes[0:b.GetInt("QuoteLengthLimit")])
125 if quoteMessagelength > b.GetInt("QuoteLengthLimit") {
126 quoteMessage += "..."
127 }
128 }
129
130 replacer := strings.NewReplacer(
131 "{MESSAGE}", msg,
132 "{QUOTENICK}", quoteNick,
133 "{QUOTEMESSAGE}", quoteMessage,
134 )
135 format = replacer.Replace(format)
136 return format
137}
138
139func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { //nolint:unparam
140 if m.GuildID != b.guildID {
141 b.Log.Debugf("Ignoring messageCreate because it originates from a different guild")
142 return
143 }
144 var err error
145
146 // not relay our own messages
147 if m.Author.Username == b.nick {
148 return
149 }
150 // if using webhooks, do not relay if it's ours
151 if m.Author.Bot && b.transmitter.HasWebhook(m.Author.ID) {
152 return
153 }
154
155 rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", UserID: m.Author.ID, ID: m.ID, Extra: make(map[string][]interface{})}
156
157 // add the url of the attachments to content
158 if len(m.Attachments) > 0 {
159 first := true
160 for _, attach := range m.Attachments {
161 if b.alwaysDownloadFiles {
162 var url, name, caption string
163 var data *[]byte
164
165 url = attach.URL
166 name = attach.Filename
167
168 err = helper.HandleDownloadSize(b.Log, &rmsg, name, int64(attach.Size), b.General)
169 if err != nil {
170 return
171 }
172 data, err = helper.DownloadFile(url)
173 if err != nil {
174 return
175 }
176
177 if first {
178 caption = m.Content
179 if caption == "" {
180 caption = name
181 }
182 first = false
183 } else {
184 caption = ""
185 }
186
187 helper.HandleDownloadData(b.Log, &rmsg, name, caption, "", data, b.General)
188 } else {
189 m.Content = m.Content + "\n" + attach.URL
190 }
191 }
192 }
193
194 b.Log.Debugf("== Receiving event %#v", m.Message)
195
196 if m.Content != "" {
197 m.Message.Content = b.replaceChannelMentions(m.Message.Content)
198 rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c)
199 if err != nil {
200 b.Log.Errorf("ContentWithMoreMentionsReplaced failed: %s", err)
201 rmsg.Text = m.ContentWithMentionsReplaced()
202 }
203 }
204
205 // set channel name
206 rmsg.Channel = b.getChannelName(m.ChannelID)
207
208 fromWebhook := m.WebhookID != ""
209 if !fromWebhook && !b.GetBool("UseUserName") {
210 rmsg.Username = b.getNick(m.Author, m.GuildID)
211 } else {
212 rmsg.Username = m.Author.Username
213 if !fromWebhook && b.GetBool("UseDiscriminator") {
214 rmsg.Username += "#" + m.Author.Discriminator
215 }
216 }
217
218 // if we have embedded content add it to text
219 if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil {
220 for _, embed := range m.Message.Embeds {
221 rmsg.Text += handleEmbed(embed)
222 }
223 }
224
225 // no empty messages
226 if rmsg.Text == "" && len(rmsg.Extra["file"]) == 0 {
227 return
228 }
229
230 // do we have a /me action
231 var ok bool
232 rmsg.Text, ok = b.replaceAction(rmsg.Text)
233 if ok {
234 rmsg.Event = config.EventUserAction
235 }
236
237 // Replace emotes
238// rmsg.Text = replaceEmotes(rmsg.Text)
239
240 // Handle Reply thread
241 rmsg.Text = b.handleQuote(s, m.Message, rmsg.Text)
242
243 // Add our parent id if it exists, and if it's not referring to a message in another channel
244 if ref := m.MessageReference; ref != nil && ref.ChannelID == m.ChannelID {
245 rmsg.ParentID = ref.MessageID
246 }
247
248 b.Log.Debugf("<= Sending message from %s on %s to gateway", m.Author.Username, b.Account)
249 b.Log.Debugf("<= Message is %#v", rmsg)
250 b.Remote <- rmsg
251}
252
253func (b *Bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
254 if m.GuildID != b.guildID {
255 b.Log.Debugf("Ignoring memberUpdate because it originates from a different guild")
256 return
257 }
258 if m.Member == nil {
259 b.Log.Warnf("Received member update with no member information: %#v", m)
260 }
261
262 b.membersMutex.Lock()
263 defer b.membersMutex.Unlock()
264
265 if currMember, ok := b.userMemberMap[m.Member.User.ID]; ok {
266 b.Log.Debugf(
267 "%s: memberupdate: user %s (nick %s) changes nick to %s",
268 b.Account,
269 m.Member.User.Username,
270 b.userMemberMap[m.Member.User.ID].Nick,
271 m.Member.Nick,
272 )
273 delete(b.nickMemberMap, currMember.User.Username)
274 delete(b.nickMemberMap, currMember.Nick)
275 delete(b.userMemberMap, m.Member.User.ID)
276 }
277 b.userMemberMap[m.Member.User.ID] = m.Member
278 b.nickMemberMap[m.Member.User.Username] = m.Member
279 if m.Member.Nick != "" {
280 b.nickMemberMap[m.Member.Nick] = m.Member
281 }
282}
283
284func (b *Bdiscord) memberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
285 if m.GuildID != b.guildID {
286 b.Log.Debugf("Ignoring memberAdd because it originates from a different guild")
287 return
288 }
289 if b.GetBool("nosendjoinpart") {
290 return
291 }
292 if m.Member == nil {
293 b.Log.Warnf("Received member update with no member information: %#v", m)
294 return
295 }
296 username := m.Member.User.Username
297 if m.Member.Nick != "" {
298 username = m.Member.Nick
299 }
300
301 rmsg := config.Message{
302 Account: b.Account,
303 Event: config.EventJoinLeave,
304 Username: "system",
305 Text: username + " joins",
306 }
307 b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
308 b.Log.Debugf("<= Message is %#v", rmsg)
309 b.Remote <- rmsg
310}
311
312func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
313 if m.GuildID != b.guildID {
314 b.Log.Debugf("Ignoring memberRemove because it originates from a different guild")
315 return
316 }
317 if b.GetBool("nosendjoinpart") {
318 return
319 }
320 if m.Member == nil {
321 b.Log.Warnf("Received member update with no member information: %#v", m)
322 return
323 }
324 username := m.Member.User.Username
325 if m.Member.Nick != "" {
326 username = m.Member.Nick
327 }
328
329 rmsg := config.Message{
330 Account: b.Account,
331 Event: config.EventJoinLeave,
332 Username: "system",
333 Text: username + " leaves",
334 }
335 b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
336 b.Log.Debugf("<= Message is %#v", rmsg)
337 b.Remote <- rmsg
338}
339
340func handleEmbed(embed *discordgo.MessageEmbed) string {
341 var t []string
342 var result string
343
344 t = append(t, embed.Title)
345 t = append(t, embed.Description)
346
347 for _, f := range embed.Fields {
348 field := f.Name + ": " + f.Value
349 t = append(t, field)
350 }
351
352 t = append(t, embed.URL)
353 if embed.Footer != nil {
354 t = append(t, embed.Footer.Text)
355 }
356
357 i := 0
358 for _, e := range t {
359 if e == "" {
360 continue
361 }
362
363 i++
364 if i == 1 {
365 result += " embed: " + e
366 continue
367 }
368
369 result += "\n" + e
370 }
371
372 if result != "" {
373 result += "\n"
374 }
375
376 return result
377}
378