portal.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. package bridge
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. "github.com/bwmarrin/discordgo"
  7. log "maunium.net/go/maulogger/v2"
  8. "maunium.net/go/mautrix"
  9. "maunium.net/go/mautrix/appservice"
  10. "maunium.net/go/mautrix/event"
  11. "maunium.net/go/mautrix/id"
  12. "gitlab.com/beeper/discord/database"
  13. )
  14. type portalDiscordMessage struct {
  15. msg interface{}
  16. user *User
  17. }
  18. type portalMatrixMessage struct {
  19. evt *event.Event
  20. user *User
  21. }
  22. type Portal struct {
  23. *database.Portal
  24. bridge *Bridge
  25. log log.Logger
  26. roomCreateLock sync.Mutex
  27. discordMessages chan portalDiscordMessage
  28. matrixMessages chan portalMatrixMessage
  29. }
  30. var (
  31. portalCreationDummyEvent = event.Type{Type: "fi.mau.dummy.portal_created", Class: event.MessageEventType}
  32. )
  33. func (b *Bridge) loadPortal(dbPortal *database.Portal, key *database.PortalKey) *Portal {
  34. // If we weren't given a portal we'll attempt to create it if a key was
  35. // provided.
  36. if dbPortal == nil {
  37. if key == nil {
  38. return nil
  39. }
  40. dbPortal = b.db.Portal.New()
  41. dbPortal.Key = *key
  42. dbPortal.Insert()
  43. }
  44. portal := b.NewPortal(dbPortal)
  45. // No need to lock, it is assumed that our callers have already acquired
  46. // the lock.
  47. b.portalsByID[portal.Key] = portal
  48. if portal.MXID != "" {
  49. b.portalsByMXID[portal.MXID] = portal
  50. }
  51. return portal
  52. }
  53. func (b *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal {
  54. b.portalsLock.Lock()
  55. defer b.portalsLock.Unlock()
  56. portal, ok := b.portalsByMXID[mxid]
  57. if !ok {
  58. return b.loadPortal(b.db.Portal.GetByMXID(mxid), nil)
  59. }
  60. return portal
  61. }
  62. func (b *Bridge) GetPortalByID(key database.PortalKey) *Portal {
  63. b.portalsLock.Lock()
  64. defer b.portalsLock.Unlock()
  65. portal, ok := b.portalsByID[key]
  66. if !ok {
  67. return b.loadPortal(b.db.Portal.GetByID(key), &key)
  68. }
  69. return portal
  70. }
  71. func (b *Bridge) GetAllPortals() []*Portal {
  72. return b.dbPortalsToPortals(b.db.Portal.GetAll())
  73. }
  74. func (b *Bridge) GetAllPortalsByID(id string) []*Portal {
  75. return b.dbPortalsToPortals(b.db.Portal.GetAllByID(id))
  76. }
  77. func (b *Bridge) dbPortalsToPortals(dbPortals []*database.Portal) []*Portal {
  78. b.portalsLock.Lock()
  79. defer b.portalsLock.Unlock()
  80. output := make([]*Portal, len(dbPortals))
  81. for index, dbPortal := range dbPortals {
  82. if dbPortal == nil {
  83. continue
  84. }
  85. portal, ok := b.portalsByID[dbPortal.Key]
  86. if !ok {
  87. portal = b.loadPortal(dbPortal, nil)
  88. }
  89. output[index] = portal
  90. }
  91. return output
  92. }
  93. func (b *Bridge) NewPortal(dbPortal *database.Portal) *Portal {
  94. portal := &Portal{
  95. Portal: dbPortal,
  96. bridge: b,
  97. log: b.log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
  98. discordMessages: make(chan portalDiscordMessage, b.Config.Bridge.PortalMessageBuffer),
  99. matrixMessages: make(chan portalMatrixMessage, b.Config.Bridge.PortalMessageBuffer),
  100. }
  101. go portal.messageLoop()
  102. return portal
  103. }
  104. func (p *Portal) handleMatrixInvite(sender *User, evt *event.Event) {
  105. // Look up an existing puppet or create a new one.
  106. puppet := p.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
  107. if puppet != nil {
  108. p.log.Infoln("no puppet for %v", sender)
  109. // Open a conversation on the discord side?
  110. }
  111. p.log.Infoln("puppet:", puppet)
  112. }
  113. func (p *Portal) messageLoop() {
  114. for {
  115. select {
  116. case msg := <-p.matrixMessages:
  117. p.handleMatrixMessages(msg)
  118. case msg := <-p.discordMessages:
  119. p.handleDiscordMessages(msg)
  120. }
  121. }
  122. }
  123. func (p *Portal) IsPrivateChat() bool {
  124. return p.Type == discordgo.ChannelTypeDM
  125. }
  126. func (p *Portal) MainIntent() *appservice.IntentAPI {
  127. if p.IsPrivateChat() && p.DMUser != "" {
  128. return p.bridge.GetPuppetByID(p.DMUser).DefaultIntent()
  129. }
  130. return p.bridge.bot
  131. }
  132. func (p *Portal) createMatrixRoom(user *User, channel *discordgo.Channel) error {
  133. p.Type = channel.Type
  134. if p.Type == discordgo.ChannelTypeDM {
  135. p.DMUser = channel.Recipients[0].ID
  136. }
  137. p.roomCreateLock.Lock()
  138. defer p.roomCreateLock.Unlock()
  139. // If we have a matrix id the room should exist so we have nothing to do.
  140. if p.MXID != "" {
  141. return nil
  142. }
  143. intent := p.MainIntent()
  144. if err := intent.EnsureRegistered(); err != nil {
  145. return err
  146. }
  147. // if p.IsPrivateChat() {
  148. p.Name = channel.Name
  149. p.Topic = channel.Topic
  150. // TODO: get avatars figured out
  151. // p.Avatar = puppet.Avatar
  152. // p.AvatarURL = puppet.AvatarURL
  153. // }
  154. p.log.Infoln("Creating Matrix room for channel:", p.Portal.Key.ChannelID)
  155. initialState := []*event.Event{}
  156. creationContent := make(map[string]interface{})
  157. // if !portal.bridge.Config.Bridge.FederateRooms {
  158. creationContent["m.federate"] = false
  159. // }
  160. var invite []id.UserID
  161. if p.IsPrivateChat() {
  162. invite = append(invite, p.bridge.bot.UserID)
  163. }
  164. resp, err := intent.CreateRoom(&mautrix.ReqCreateRoom{
  165. Visibility: "private",
  166. Name: p.Name,
  167. Topic: p.Topic,
  168. Preset: "private_chat",
  169. IsDirect: p.IsPrivateChat(),
  170. InitialState: initialState,
  171. CreationContent: creationContent,
  172. })
  173. if err != nil {
  174. return err
  175. }
  176. p.MXID = resp.RoomID
  177. p.Update()
  178. p.bridge.portalsLock.Lock()
  179. p.bridge.portalsByMXID[p.MXID] = p
  180. p.bridge.portalsLock.Unlock()
  181. p.log.Debugln("inviting user", user)
  182. p.ensureUserInvited(user)
  183. if p.IsPrivateChat() {
  184. p.syncParticipants(user, channel.Recipients)
  185. }
  186. firstEventResp, err := p.MainIntent().SendMessageEvent(p.MXID, portalCreationDummyEvent, struct{}{})
  187. if err != nil {
  188. p.log.Errorln("Failed to send dummy event to mark portal creation:", err)
  189. } else {
  190. p.FirstEventID = firstEventResp.EventID
  191. p.Update()
  192. }
  193. return nil
  194. }
  195. func (p *Portal) handleDiscordMessages(msg portalDiscordMessage) {
  196. if p.MXID == "" {
  197. p.log.Debugln("Creating Matrix room from incoming message")
  198. discordMsg := msg.msg.(*discordgo.MessageCreate)
  199. channel, err := msg.user.Session.Channel(discordMsg.ChannelID)
  200. if err != nil {
  201. p.log.Errorln("Failed to find channel for message:", err)
  202. return
  203. }
  204. if err := p.createMatrixRoom(msg.user, channel); err != nil {
  205. p.log.Errorln("Failed to create portal room:", err)
  206. return
  207. }
  208. }
  209. switch msg.msg.(type) {
  210. case *discordgo.MessageCreate:
  211. p.handleDiscordMessageCreate(msg.user, msg.msg.(*discordgo.MessageCreate).Message)
  212. case *discordgo.MessageDelete:
  213. p.handleDiscordMessageDelete(msg.user, msg.msg.(*discordgo.MessageDelete).Message)
  214. case *discordgo.MessageReactionAdd:
  215. p.handleDiscordReaction(msg.user, msg.msg.(*discordgo.MessageReactionAdd).MessageReaction, true)
  216. case *discordgo.MessageReactionRemove:
  217. p.handleDiscordReaction(msg.user, msg.msg.(*discordgo.MessageReactionRemove).MessageReaction, false)
  218. default:
  219. p.log.Warnln("unknown message type")
  220. }
  221. }
  222. func (p *Portal) ensureUserInvited(user *User) bool {
  223. return user.ensureInvited(p.MainIntent(), p.MXID, p.IsPrivateChat())
  224. }
  225. func (p *Portal) markMessageHandled(msg *database.Message, discordID string, mxid id.EventID, authorID string, timestamp time.Time) *database.Message {
  226. if msg == nil {
  227. msg := p.bridge.db.Message.New()
  228. msg.Channel = p.Key
  229. msg.DiscordID = discordID
  230. msg.MatrixID = mxid
  231. msg.AuthorID = authorID
  232. msg.Timestamp = timestamp
  233. msg.Insert()
  234. } else {
  235. msg.UpdateMatrixID(mxid)
  236. }
  237. return msg
  238. }
  239. func (p *Portal) handleDiscordMessageCreate(user *User, msg *discordgo.Message) {
  240. if user.ID == msg.Author.ID {
  241. return
  242. }
  243. if p.MXID == "" {
  244. p.log.Warnln("handle message called without a valid portal")
  245. return
  246. }
  247. existing := p.bridge.db.Message.GetByDiscordID(p.Key, msg.ID)
  248. if existing != nil {
  249. p.log.Debugln("not handling duplicate message", msg.ID)
  250. return
  251. }
  252. content := &event.MessageEventContent{
  253. Body: msg.Content,
  254. MsgType: event.MsgText,
  255. }
  256. intent := p.bridge.GetPuppetByID(msg.Author.ID).IntentFor(p)
  257. resp, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
  258. if err != nil {
  259. p.log.Warnfln("failed to send message %q to matrix: %v", msg.ID, err)
  260. return
  261. }
  262. ts, _ := msg.Timestamp.Parse()
  263. p.markMessageHandled(nil, msg.ID, resp.EventID, msg.Author.ID, ts)
  264. }
  265. func (p *Portal) handleDiscordMessageDelete(user *User, msg *discordgo.Message) {
  266. // The discord delete message object is pretty empty and doesn't include
  267. // the author so we have to use the DMUser from the portal that was added
  268. // at creation time if we're a DM. We'll might have similar issues when we
  269. // add guild message support, but we'll cross that bridge when we get
  270. // there.
  271. // Find the message that we're working with.
  272. existing := p.bridge.db.Message.GetByDiscordID(p.Key, msg.ID)
  273. if existing == nil {
  274. p.log.Debugfln("failed to find message", msg.ID)
  275. return
  276. }
  277. var intent *appservice.IntentAPI
  278. if p.Type == discordgo.ChannelTypeDM {
  279. intent = p.bridge.GetPuppetByID(p.DMUser).IntentFor(p)
  280. } else {
  281. p.log.Errorfln("no guilds yet...")
  282. }
  283. _, err := intent.RedactEvent(p.MXID, existing.MatrixID)
  284. if err != nil {
  285. p.log.Warnfln("Failed to remove message %s: %v", existing.MatrixID, err)
  286. }
  287. existing.Delete()
  288. }
  289. func (p *Portal) syncParticipants(source *User, participants []*discordgo.User) {
  290. for _, participant := range participants {
  291. puppet := p.bridge.GetPuppetByID(participant.ID)
  292. puppet.SyncContact(source)
  293. user := p.bridge.GetUserByID(participant.ID)
  294. if user != nil {
  295. p.ensureUserInvited(user)
  296. }
  297. if user == nil || !puppet.IntentFor(p).IsCustomPuppet {
  298. if err := puppet.IntentFor(p).EnsureJoined(p.MXID); err != nil {
  299. p.log.Warnfln("Failed to make puppet of %s join %s: %v", participant.ID, p.MXID, err)
  300. }
  301. }
  302. }
  303. }
  304. func (p *Portal) handleMatrixMessages(msg portalMatrixMessage) {
  305. switch msg.evt.Type {
  306. case event.EventMessage:
  307. p.handleMatrixMessage(msg.user, msg.evt)
  308. default:
  309. p.log.Debugln("unknown event type", msg.evt.Type)
  310. }
  311. }
  312. func (p *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
  313. if p.IsPrivateChat() && sender.ID != p.Key.Receiver {
  314. return
  315. }
  316. existing := p.bridge.db.Message.GetByMatrixID(p.Key, evt.ID)
  317. if existing != nil {
  318. p.log.Debugln("not handling duplicate message", evt.ID)
  319. return
  320. }
  321. content, ok := evt.Content.Parsed.(*event.MessageEventContent)
  322. if !ok {
  323. p.log.Debugfln("Failed to handle event %s: unexpected parsed content type %T", evt.ID, evt.Content.Parsed)
  324. return
  325. }
  326. if content.RelatesTo != nil && content.RelatesTo.Type == event.RelReplace {
  327. existing := p.bridge.db.Message.GetByMatrixID(p.Key, content.RelatesTo.EventID)
  328. if existing != nil && existing.DiscordID != "" {
  329. // we don't have anything to save for the update message right now
  330. // as we're not tracking edited timestamps.
  331. _, err := sender.Session.ChannelMessageEdit(p.Key.ChannelID,
  332. existing.DiscordID, content.NewContent.Body)
  333. if err != nil {
  334. p.log.Errorln("Failed to update message %s: %v", existing.DiscordID, err)
  335. return
  336. }
  337. }
  338. } else {
  339. msg, err := sender.Session.ChannelMessageSend(p.Key.ChannelID, content.Body)
  340. if err != nil {
  341. p.log.Errorfln("Failed to send message: %v", err)
  342. return
  343. }
  344. dbMsg := p.bridge.db.Message.New()
  345. dbMsg.Channel = p.Key
  346. dbMsg.DiscordID = msg.ID
  347. dbMsg.MatrixID = evt.ID
  348. dbMsg.AuthorID = sender.ID
  349. dbMsg.Timestamp = time.Now()
  350. dbMsg.Insert()
  351. }
  352. }
  353. func (p *Portal) handleMatrixLeave(sender *User) {
  354. if p.IsPrivateChat() {
  355. p.log.Debugln("User left private chat portal, cleaning up and deleting...")
  356. p.delete()
  357. p.cleanup(false)
  358. return
  359. }
  360. // TODO: figure out how to close a dm from the API.
  361. p.cleanupIfEmpty()
  362. }
  363. func (p *Portal) delete() {
  364. p.Portal.Delete()
  365. p.bridge.portalsLock.Lock()
  366. delete(p.bridge.portalsByID, p.Key)
  367. if p.MXID != "" {
  368. delete(p.bridge.portalsByMXID, p.MXID)
  369. }
  370. p.bridge.portalsLock.Unlock()
  371. }
  372. func (p *Portal) cleanupIfEmpty() {
  373. users, err := p.getMatrixUsers()
  374. if err != nil {
  375. p.log.Errorfln("Failed to get Matrix user list to determine if portal needs to be cleaned up: %v", err)
  376. return
  377. }
  378. if len(users) == 0 {
  379. p.log.Infoln("Room seems to be empty, cleaning up...")
  380. p.delete()
  381. p.cleanup(false)
  382. }
  383. }
  384. func (p *Portal) cleanup(puppetsOnly bool) {
  385. if p.MXID != "" {
  386. return
  387. }
  388. if p.IsPrivateChat() {
  389. _, err := p.MainIntent().LeaveRoom(p.MXID)
  390. if err != nil {
  391. p.log.Warnln("Failed to leave private chat portal with main intent:", err)
  392. }
  393. return
  394. }
  395. intent := p.MainIntent()
  396. members, err := intent.JoinedMembers(p.MXID)
  397. if err != nil {
  398. p.log.Errorln("Failed to get portal members for cleanup:", err)
  399. return
  400. }
  401. for member := range members.Joined {
  402. if member == intent.UserID {
  403. continue
  404. }
  405. puppet := p.bridge.GetPuppetByMXID(member)
  406. if p != nil {
  407. _, err = puppet.DefaultIntent().LeaveRoom(p.MXID)
  408. if err != nil {
  409. p.log.Errorln("Error leaving as puppet while cleaning up portal:", err)
  410. }
  411. } else if !puppetsOnly {
  412. _, err = intent.KickUser(p.MXID, &mautrix.ReqKickUser{UserID: member, Reason: "Deleting portal"})
  413. if err != nil {
  414. p.log.Errorln("Error kicking user while cleaning up portal:", err)
  415. }
  416. }
  417. }
  418. _, err = intent.LeaveRoom(p.MXID)
  419. if err != nil {
  420. p.log.Errorln("Error leaving with main intent while cleaning up portal:", err)
  421. }
  422. }
  423. func (p *Portal) getMatrixUsers() ([]id.UserID, error) {
  424. members, err := p.MainIntent().JoinedMembers(p.MXID)
  425. if err != nil {
  426. return nil, fmt.Errorf("failed to get member list: %w", err)
  427. }
  428. var users []id.UserID
  429. for userID := range members.Joined {
  430. _, isPuppet := p.bridge.ParsePuppetMXID(userID)
  431. if !isPuppet && userID != p.bridge.bot.UserID {
  432. users = append(users, userID)
  433. }
  434. }
  435. return users, nil
  436. }
  437. func (p *Portal) handleMatrixKick(sender *User, target *Puppet) {
  438. // TODO: need to learn how to make this happen as discordgo proper doesn't
  439. // support group dms and it looks like it's a binary blob.
  440. }
  441. func (p *Portal) handleMatrixReaction(evt *event.Event) {
  442. user := p.bridge.GetUserByMXID(evt.Sender)
  443. if user == nil {
  444. p.log.Errorf("failed to find user for %s", evt.Sender)
  445. return
  446. }
  447. if user.ID != p.Key.Receiver {
  448. return
  449. }
  450. reaction := evt.Content.AsReaction()
  451. if reaction.RelatesTo.Type != event.RelAnnotation {
  452. p.log.Errorfln("Ignoring reaction %s due to unknown m.relates_to data", evt.ID)
  453. return
  454. }
  455. msg := p.bridge.db.Message.GetByMatrixID(p.Key, reaction.RelatesTo.EventID)
  456. if msg.DiscordID == "" {
  457. p.log.Debugf("Message %s has not yet been sent to discord", reaction.RelatesTo.EventID)
  458. return
  459. }
  460. err := user.Session.MessageReactionAdd(p.Key.ChannelID, msg.DiscordID, reaction.RelatesTo.Key)
  461. if err != nil {
  462. p.log.Debugf("Failed to send reaction %s@%s: %v", p.Key, msg.DiscordID, err)
  463. return
  464. }
  465. dbReaction := p.bridge.db.Reaction.New()
  466. dbReaction.Channel.ChannelID = p.Key.ChannelID
  467. dbReaction.Channel.Receiver = p.Key.Receiver
  468. dbReaction.MatrixEventID = evt.ID
  469. dbReaction.DiscordMessageID = msg.DiscordID
  470. dbReaction.AuthorID = user.ID
  471. dbReaction.MatrixName = reaction.RelatesTo.Key
  472. dbReaction.DiscordID = reaction.RelatesTo.Key
  473. dbReaction.Insert()
  474. }
  475. func (p *Portal) handleDiscordReaction(user *User, reaction *discordgo.MessageReaction, add bool) {
  476. if user.ID == reaction.UserID {
  477. return
  478. }
  479. // This is temporary until we add support for custom emoji.
  480. if reaction.Emoji.ID != "" {
  481. p.log.Debugln("ignoring non-unicode reaction")
  482. return
  483. }
  484. emoteID := reaction.Emoji.ID
  485. if reaction.Emoji.Name != "" {
  486. emoteID = reaction.Emoji.Name
  487. }
  488. // Find the message that we're working with.
  489. message := p.bridge.db.Message.GetByDiscordID(p.Key, reaction.MessageID)
  490. if message == nil {
  491. p.log.Debugfln("failed to add reaction to message %s: message not found", reaction.MessageID)
  492. return
  493. }
  494. intent := p.bridge.GetPuppetByID(reaction.UserID).IntentFor(p)
  495. // Lookup an existing reaction
  496. existing := p.bridge.db.Reaction.GetByDiscordID(p.Key, message.DiscordID, emoteID)
  497. if !add {
  498. if existing == nil {
  499. p.log.Debugln("Failed to remove reaction for unknown message", reaction.MessageID)
  500. return
  501. }
  502. _, err := intent.RedactEvent(p.MXID, existing.MatrixEventID)
  503. if err != nil {
  504. p.log.Warnfln("Failed to remove reaction from %s: %v", p.MXID, err)
  505. }
  506. existing.Delete()
  507. return
  508. }
  509. content := event.Content{Parsed: &event.ReactionEventContent{
  510. RelatesTo: event.RelatesTo{
  511. EventID: message.MatrixID,
  512. Type: event.RelAnnotation,
  513. Key: reaction.Emoji.Name,
  514. },
  515. }}
  516. resp, err := intent.Client.SendMessageEvent(p.MXID, event.EventReaction, &content)
  517. if err != nil {
  518. p.log.Errorfln("failed to send reaction from %s: %v", reaction.MessageID, err)
  519. return
  520. }
  521. if existing == nil {
  522. dbReaction := p.bridge.db.Reaction.New()
  523. dbReaction.Channel = p.Key
  524. dbReaction.DiscordMessageID = message.DiscordID
  525. dbReaction.MatrixEventID = resp.EventID
  526. dbReaction.AuthorID = reaction.UserID
  527. dbReaction.MatrixName = reaction.Emoji.Name
  528. dbReaction.DiscordID = emoteID
  529. dbReaction.Insert()
  530. }
  531. }
  532. func (p *Portal) handleMatrixRedaction(evt *event.Event) {
  533. user := p.bridge.GetUserByMXID(evt.Sender)
  534. if user.ID != p.Key.Receiver {
  535. return
  536. }
  537. // First look if we're redacting a message
  538. message := p.bridge.db.Message.GetByMatrixID(p.Key, evt.Redacts)
  539. if message != nil {
  540. if message.DiscordID != "" {
  541. err := user.Session.ChannelMessageDelete(p.Key.ChannelID, message.DiscordID)
  542. if err != nil {
  543. p.log.Debugfln("Failed to delete discord message %s: %v", message.DiscordID, err)
  544. } else {
  545. message.Delete()
  546. }
  547. }
  548. return
  549. }
  550. // Now check if it's a reaction.
  551. reaction := p.bridge.db.Reaction.GetByMatrixID(p.Key, evt.Redacts)
  552. if reaction != nil {
  553. if reaction.DiscordID != "" {
  554. err := user.Session.MessageReactionRemove(p.Key.ChannelID, reaction.DiscordMessageID, reaction.DiscordID, reaction.AuthorID)
  555. if err != nil {
  556. p.log.Debugfln("Failed to delete reaction %s for message %s: %v", reaction.DiscordID, reaction.DiscordMessageID, err)
  557. } else {
  558. reaction.Delete()
  559. }
  560. }
  561. return
  562. }
  563. p.log.Warnfln("Failed to redact %s@%s: no event found", p.Key, evt.Redacts)
  564. }