Thumbnail

rani/matterbridge.git

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

Viewing file on branch master

1package bwhatsapp
2
3import (
4 "context"
5 "fmt"
6 "mime"
7 "strings"
8
9 "github.com/matterbridge-org/matterbridge/bridge/config"
10 "github.com/matterbridge-org/matterbridge/bridge/helper"
11
12 "go.mau.fi/whatsmeow/binary/proto"
13 "go.mau.fi/whatsmeow/types"
14 "go.mau.fi/whatsmeow/types/events"
15)
16
17// nolint:gocritic
18func (b *Bwhatsapp) eventHandler(evt interface{}) {
19 switch e := evt.(type) {
20 case *events.Message:
21 b.handleMessage(e)
22 case *events.GroupInfo:
23 b.handleGroupInfo(e)
24 }
25}
26
27func (b *Bwhatsapp) handleGroupInfo(event *events.GroupInfo) {
28 b.Log.Debugf("Receiving event %#v", event)
29
30 switch {
31 case event.Join != nil:
32 b.handleUserJoin(event)
33 case event.Leave != nil:
34 b.handleUserLeave(event)
35 case event.Topic != nil:
36 b.handleTopicChange(event)
37 }
38}
39
40func (b *Bwhatsapp) handleUserJoin(event *events.GroupInfo) {
41 for _, joinedJid := range event.Join {
42 senderName := b.getSenderNameFromJID(joinedJid)
43
44 rmsg := config.Message{
45 UserID: joinedJid.String(),
46 Username: senderName,
47 Channel: event.JID.String(),
48 Account: b.Account,
49 Protocol: b.Protocol,
50 Event: config.EventJoinLeave,
51 Text: "joined chat",
52 }
53
54 b.Remote <- rmsg
55 }
56}
57
58func (b *Bwhatsapp) handleUserLeave(event *events.GroupInfo) {
59 for _, leftJid := range event.Leave {
60 senderName := b.getSenderNameFromJID(leftJid)
61
62 rmsg := config.Message{
63 UserID: leftJid.String(),
64 Username: senderName,
65 Channel: event.JID.String(),
66 Account: b.Account,
67 Protocol: b.Protocol,
68 Event: config.EventJoinLeave,
69 Text: "left chat",
70 }
71
72 b.Remote <- rmsg
73 }
74}
75
76func (b *Bwhatsapp) handleTopicChange(event *events.GroupInfo) {
77 msg := event.Topic
78 senderJid := msg.TopicSetBy
79 senderName := b.getSenderNameFromJID(senderJid)
80
81 text := msg.Topic
82 if text == "" {
83 text = "removed topic"
84 }
85
86 rmsg := config.Message{
87 UserID: senderJid.String(),
88 Username: senderName,
89 Channel: event.JID.String(),
90 Account: b.Account,
91 Protocol: b.Protocol,
92 Event: config.EventTopicChange,
93 Text: "Topic changed: " + text,
94 }
95
96 b.Remote <- rmsg
97}
98
99func (b *Bwhatsapp) handleMessage(message *events.Message) {
100 msg := message.Message
101 switch {
102 case msg == nil, message.Info.IsFromMe, message.Info.Timestamp.Before(b.startedAt):
103 return
104 }
105
106 b.Log.Debugf("Receiving message %#v", msg)
107
108 switch {
109 case msg.Conversation != nil || msg.ExtendedTextMessage != nil:
110 b.handleTextMessage(message.Info, msg)
111 case msg.VideoMessage != nil:
112 b.handleVideoMessage(message)
113 case msg.AudioMessage != nil:
114 b.handleAudioMessage(message)
115 case msg.DocumentMessage != nil:
116 b.handleDocumentMessage(message)
117 case msg.ImageMessage != nil:
118 b.handleImageMessage(message)
119 case msg.ProtocolMessage != nil && *msg.ProtocolMessage.Type == proto.ProtocolMessage_REVOKE:
120 b.handleDelete(msg.ProtocolMessage)
121 }
122}
123
124// nolint:funlen
125func (b *Bwhatsapp) handleTextMessage(messageInfo types.MessageInfo, msg *proto.Message) {
126 senderJID := messageInfo.Sender
127 channel := messageInfo.Chat
128
129 senderName := b.getSenderName(messageInfo)
130
131 if msg.GetExtendedTextMessage() == nil && msg.GetConversation() == "" {
132 b.Log.Debugf("message without text content? %#v", msg)
133 return
134 }
135
136 var text string
137
138 // nolint:nestif
139 if msg.GetExtendedTextMessage() == nil {
140 text = msg.GetConversation()
141 } else if msg.GetExtendedTextMessage().GetContextInfo() == nil {
142 // Handle pure text message with a link preview
143 // A pure text message with a link preview acts as an extended text message but will not contain any context info
144 text = msg.GetExtendedTextMessage().GetText()
145 } else {
146 text = msg.GetExtendedTextMessage().GetText()
147 ci := msg.GetExtendedTextMessage().GetContextInfo()
148
149 if senderJID == (types.JID{}) && ci.Participant != nil {
150 senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
151 }
152
153 if ci.MentionedJID != nil {
154 // handle user mentions
155 for _, mentionedJID := range ci.MentionedJID {
156 numberAndSuffix := strings.SplitN(mentionedJID, "@", 2)
157
158 // mentions comes as telephone numbers and we don't want to expose it to other bridges
159 // replace it with something more meaninful to others
160 mention := b.getSenderNotify(types.NewJID(numberAndSuffix[0], types.DefaultUserServer))
161
162 text = strings.Replace(text, "@"+numberAndSuffix[0], "@"+mention, 1)
163 }
164 }
165 }
166
167 parentID := ""
168 if msg.GetExtendedTextMessage() != nil {
169 ci := msg.GetExtendedTextMessage().GetContextInfo()
170 parentID = getParentIdFromCtx(ci)
171 }
172
173 rmsg := config.Message{
174 UserID: senderJID.String(),
175 Username: senderName,
176 Text: text,
177 Channel: channel.String(),
178 Account: b.Account,
179 Protocol: b.Protocol,
180 Extra: make(map[string][]interface{}),
181 ID: getMessageIdFormat(senderJID, messageInfo.ID),
182 ParentID: parentID,
183 }
184
185 if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
186 rmsg.Avatar = avatarURL
187 }
188
189 b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
190 b.Log.Debugf("<= Message is %#v", rmsg)
191
192 b.Remote <- rmsg
193}
194
195// HandleImageMessage sent from WhatsApp, relay it to the brige
196func (b *Bwhatsapp) handleImageMessage(msg *events.Message) {
197 imsg := msg.Message.GetImageMessage()
198
199 senderJID := msg.Info.Sender
200 senderName := b.getSenderName(msg.Info)
201 ci := imsg.GetContextInfo()
202
203 if senderJID == (types.JID{}) && ci.Participant != nil {
204 senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
205 }
206
207 rmsg := config.Message{
208 UserID: senderJID.String(),
209 Username: senderName,
210 Channel: msg.Info.Chat.String(),
211 Account: b.Account,
212 Protocol: b.Protocol,
213 Extra: make(map[string][]interface{}),
214 ID: getMessageIdFormat(senderJID, msg.Info.ID),
215 ParentID: getParentIdFromCtx(ci),
216 }
217
218 if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
219 rmsg.Avatar = avatarURL
220 }
221
222 fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
223 if err != nil {
224 b.Log.Errorf("Mimetype detection error: %s", err)
225
226 return
227 }
228
229 // rename .jfif to .jpg https://github.com/42wim/matterbridge/issues/1292
230 if fileExt[0] == ".jfif" {
231 fileExt[0] = ".jpg"
232 }
233
234 // rename .jpe to .jpg https://github.com/42wim/matterbridge/issues/1463
235 if fileExt[0] == ".jpe" {
236 fileExt[0] = ".jpg"
237 }
238
239 filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
240
241 b.Log.Debugf("Trying to download %s with type %s", filename, imsg.GetMimetype())
242
243 data, err := b.wc.Download(context.Background(), imsg)
244 if err != nil {
245 b.Log.Errorf("Download image failed: %s", err)
246
247 return
248 }
249
250 // Move file to bridge storage
251 helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
252
253 b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
254 b.Log.Debugf("<= Message is %#v", rmsg)
255
256 b.Remote <- rmsg
257}
258
259// HandleVideoMessage downloads video messages
260func (b *Bwhatsapp) handleVideoMessage(msg *events.Message) {
261 imsg := msg.Message.GetVideoMessage()
262
263 senderJID := msg.Info.Sender
264 senderName := b.getSenderName(msg.Info)
265 ci := imsg.GetContextInfo()
266
267 if senderJID == (types.JID{}) && ci.Participant != nil {
268 senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
269 }
270
271 rmsg := config.Message{
272 UserID: senderJID.String(),
273 Username: senderName,
274 Channel: msg.Info.Chat.String(),
275 Account: b.Account,
276 Protocol: b.Protocol,
277 Extra: make(map[string][]interface{}),
278 ID: getMessageIdFormat(senderJID, msg.Info.ID),
279 ParentID: getParentIdFromCtx(ci),
280 }
281
282 if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
283 rmsg.Avatar = avatarURL
284 }
285
286 fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
287 if err != nil {
288 b.Log.Errorf("Mimetype detection error: %s", err)
289
290 return
291 }
292
293 if len(fileExt) == 0 {
294 fileExt = append(fileExt, ".mp4")
295 }
296
297 // Prefer .mp4 extension, otherwise fallback to first index
298 fileExtIndex := 0
299 for i, n := range fileExt {
300 if ".mp4" == n {
301 fileExtIndex = i
302 break
303 }
304 }
305
306 filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[fileExtIndex])
307
308 b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, imsg.GetFileLength(), imsg.GetMimetype())
309
310 data, err := b.wc.Download(context.Background(), imsg)
311 if err != nil {
312 b.Log.Errorf("Download video failed: %s", err)
313
314 return
315 }
316
317 // Move file to bridge storage
318 helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
319
320 b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
321 b.Log.Debugf("<= Message is %#v", rmsg)
322
323 b.Remote <- rmsg
324}
325
326// HandleAudioMessage downloads audio messages
327func (b *Bwhatsapp) handleAudioMessage(msg *events.Message) {
328 imsg := msg.Message.GetAudioMessage()
329
330 senderJID := msg.Info.Sender
331 senderName := b.getSenderName(msg.Info)
332 ci := imsg.GetContextInfo()
333
334 if senderJID == (types.JID{}) && ci.Participant != nil {
335 senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
336 }
337 rmsg := config.Message{
338 UserID: senderJID.String(),
339 Username: senderName,
340 Channel: msg.Info.Chat.String(),
341 Account: b.Account,
342 Protocol: b.Protocol,
343 Extra: make(map[string][]interface{}),
344 ID: getMessageIdFormat(senderJID, msg.Info.ID),
345 ParentID: getParentIdFromCtx(ci),
346 }
347
348 if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
349 rmsg.Avatar = avatarURL
350 }
351
352 fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
353 if err != nil {
354 b.Log.Errorf("Mimetype detection error: %s", err)
355
356 return
357 }
358
359 if len(fileExt) == 0 {
360 fileExt = append(fileExt, ".ogg")
361 }
362
363 filename := fmt.Sprintf("%v%v", msg.Info.ID, fileExt[0])
364
365 b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, imsg.GetFileLength(), imsg.GetMimetype())
366
367 data, err := b.wc.Download(context.Background(), imsg)
368 if err != nil {
369 b.Log.Errorf("Download video failed: %s", err)
370
371 return
372 }
373
374 // Move file to bridge storage
375 helper.HandleDownloadData(b.Log, &rmsg, filename, "audio message", "", &data, b.General)
376
377 b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
378 b.Log.Debugf("<= Message is %#v", rmsg)
379
380 b.Remote <- rmsg
381}
382
383// HandleDocumentMessage downloads documents
384func (b *Bwhatsapp) handleDocumentMessage(msg *events.Message) {
385 imsg := msg.Message.GetDocumentMessage()
386
387 senderJID := msg.Info.Sender
388 senderName := b.getSenderName(msg.Info)
389 ci := imsg.GetContextInfo()
390
391 if senderJID == (types.JID{}) && ci.Participant != nil {
392 senderJID = types.NewJID(ci.GetParticipant(), types.DefaultUserServer)
393 }
394
395 rmsg := config.Message{
396 UserID: senderJID.String(),
397 Username: senderName,
398 Channel: msg.Info.Chat.String(),
399 Account: b.Account,
400 Protocol: b.Protocol,
401 Extra: make(map[string][]interface{}),
402 ID: getMessageIdFormat(senderJID, msg.Info.ID),
403 ParentID: getParentIdFromCtx(ci),
404 }
405
406 if avatarURL, exists := b.userAvatars[senderJID.String()]; exists {
407 rmsg.Avatar = avatarURL
408 }
409
410 fileExt, err := mime.ExtensionsByType(imsg.GetMimetype())
411 if err != nil {
412 b.Log.Errorf("Mimetype detection error: %s", err)
413
414 return
415 }
416
417 filename := fmt.Sprintf("%v", imsg.GetFileName())
418
419 b.Log.Debugf("Trying to download %s with extension %s and type %s", filename, fileExt, imsg.GetMimetype())
420
421 data, err := b.wc.Download(context.Background(), imsg)
422 if err != nil {
423 b.Log.Errorf("Download document message failed: %s", err)
424
425 return
426 }
427
428 // Move file to bridge storage
429 helper.HandleDownloadData(b.Log, &rmsg, filename, imsg.GetCaption(), "", &data, b.General)
430
431 b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
432 b.Log.Debugf("<= Message is %#v", rmsg)
433
434 b.Remote <- rmsg
435}
436
437func (b *Bwhatsapp) handleDelete(messageInfo *proto.ProtocolMessage) {
438 sender, _ := types.ParseJID(*messageInfo.Key.Participant)
439
440 rmsg := config.Message{
441 Account: b.Account,
442 Protocol: b.Protocol,
443 ID: getMessageIdFormat(sender, *messageInfo.Key.ID),
444 Event: config.EventMsgDelete,
445 Text: config.EventMsgDelete,
446 Channel: *messageInfo.Key.RemoteJID,
447 }
448
449 b.Log.Debugf("<= Sending message from %s to gateway", b.Account)
450 b.Log.Debugf("<= Message is %#v", rmsg)
451 b.Remote <- rmsg
452}
453