From 8380c4b1b0e1b332ff2dd2074a1e545aea929d3b Mon Sep 17 00:00:00 2001 From: Gary Kramlich Date: Tue, 8 Feb 2022 03:51:29 -0600 Subject: [PATCH] Finish up reaction removals from both sides Also implement redactions on the matrix side --- bridge/matrix.go | 12 +++ bridge/portal.go | 138 ++++++++++++++++++++--------- database/message.go | 13 +++ database/migrations/01-initial.sql | 4 +- database/reaction.go | 31 +++---- database/reactionquery.go | 15 +--- 6 files changed, 135 insertions(+), 78 deletions(-) diff --git a/bridge/matrix.go b/bridge/matrix.go index 6c09bba..bbc5a45 100644 --- a/bridge/matrix.go +++ b/bridge/matrix.go @@ -30,6 +30,7 @@ func (b *Bridge) setupEvents() { b.eventProcessor.On(event.EventMessage, b.matrixHandler.handleMessage) b.eventProcessor.On(event.EventReaction, b.matrixHandler.handleReaction) + b.eventProcessor.On(event.EventRedaction, b.matrixHandler.handleRedaction) b.eventProcessor.On(event.StateMember, b.matrixHandler.handleMembership) } @@ -242,3 +243,14 @@ func (mh *matrixHandler) handleReaction(evt *event.Event) { portal.handleMatrixReaction(evt) } } + +func (mh *matrixHandler) handleRedaction(evt *event.Event) { + if mh.ignoreEvent(evt) { + return + } + + portal := mh.bridge.GetPortalByMXID(evt.RoomID) + if portal != nil { + portal.handleMatrixRedaction(evt) + } +} diff --git a/bridge/portal.go b/bridge/portal.go index ec5062c..da2876d 100644 --- a/bridge/portal.go +++ b/bridge/portal.go @@ -526,6 +526,17 @@ func (p *Portal) handleMatrixKick(sender *User, target *Puppet) { } func (p *Portal) handleMatrixReaction(evt *event.Event) { + user := p.bridge.GetUserByMXID(evt.Sender) + if user == nil { + p.log.Errorf("failed to find user for %s", evt.Sender) + + return + } + + if user.ID != p.Key.Receiver { + return + } + reaction := evt.Content.AsReaction() if reaction.RelatesTo.Type != event.RelAnnotation { p.log.Errorfln("Ignoring reaction %s due to unknown m.relates_to data", evt.ID) @@ -540,10 +551,22 @@ func (p *Portal) handleMatrixReaction(evt *event.Event) { return } - user := p.bridge.GetUserByMXID(evt.Sender) - if user != nil { - user.Session.MessageReactionAdd(p.Key.ChannelID, msg.DiscordID, reaction.RelatesTo.Key) + err := user.Session.MessageReactionAdd(p.Key.ChannelID, msg.DiscordID, reaction.RelatesTo.Key) + if err != nil { + p.log.Debugf("Failed to send reaction %s@%s: %v", p.Key, msg.DiscordID, err) + + return } + + dbReaction := p.bridge.db.Reaction.New() + dbReaction.Channel.ChannelID = p.Key.ChannelID + dbReaction.Channel.Receiver = p.Key.Receiver + dbReaction.MatrixEventID = evt.ID + dbReaction.DiscordMessageID = msg.DiscordID + dbReaction.AuthorID = user.ID + dbReaction.MatrixName = reaction.RelatesTo.Key + dbReaction.DiscordID = reaction.RelatesTo.Key + dbReaction.Insert() } func (p *Portal) handleDiscordReaction(user *User, reaction *discordgo.MessageReaction, add bool) { @@ -551,17 +574,18 @@ func (p *Portal) handleDiscordReaction(user *User, reaction *discordgo.MessageRe return } - // Emoji.ID is only set if it's a custom emote, otherwise Emoji.Name is - // used. - customEmote := (reaction.Emoji.ID != "") - // This is temporary until we add support for custom emoji. - if customEmote { + if reaction.Emoji.ID != "" { p.log.Debugln("ignoring non-unicode reaction") return } + emoteID := reaction.Emoji.ID + if reaction.Emoji.Name != "" { + emoteID = reaction.Emoji.Name + } + // Find the message that we're working with. message := p.bridge.db.Message.GetByDiscordID(p.Key, reaction.MessageID) if message == nil { @@ -570,24 +594,18 @@ func (p *Portal) handleDiscordReaction(user *User, reaction *discordgo.MessageRe return } - // Lookup an existing reaction - var existing *database.Reaction - - if customEmote { - existing = p.bridge.db.Reaction.GetByDiscordID(p.Key, message.DiscordID, reaction.Emoji.ID) - } else { - existing = p.bridge.db.Reaction.GetByDiscordName(p.Key, message.DiscordID, reaction.Emoji.Name) - } - - if !add && existing == nil { - p.log.Debugln("Failed to remove emote for unknown message", reaction.MessageID) - - return - } - intent := p.bridge.GetPuppetByID(reaction.UserID).IntentFor(p) + // Lookup an existing reaction + existing := p.bridge.db.Reaction.GetByDiscordID(p.Key, message.DiscordID, emoteID) + if !add { + if existing == nil { + p.log.Debugln("Failed to remove reaction for unknown message", reaction.MessageID) + + return + } + _, err := intent.RedactEvent(p.MXID, existing.MatrixEventID) if err != nil { p.log.Warnfln("Failed to remove reaction from %s: %v", p.MXID, err) @@ -606,29 +624,63 @@ func (p *Portal) handleDiscordReaction(user *User, reaction *discordgo.MessageRe }, }} - if add { - resp, err := intent.Client.SendMessageEvent(p.MXID, event.EventReaction, &content) - if err != nil { - p.log.Errorfln("failed to send reaction from %s: %v", reaction.MessageID, err) + resp, err := intent.Client.SendMessageEvent(p.MXID, event.EventReaction, &content) + if err != nil { + p.log.Errorfln("failed to send reaction from %s: %v", reaction.MessageID, err) - return - } + return + } - if existing == nil { - dbReaction := p.bridge.db.Reaction.New() - dbReaction.Channel = p.Key - dbReaction.DiscordMessageID = message.DiscordID - dbReaction.MatrixEventID = resp.EventID - dbReaction.AuthorID = reaction.UserID + if existing == nil { + dbReaction := p.bridge.db.Reaction.New() + dbReaction.Channel = p.Key + dbReaction.DiscordMessageID = message.DiscordID + dbReaction.MatrixEventID = resp.EventID + dbReaction.AuthorID = reaction.UserID - if customEmote { - // TODO: - } else { - dbReaction.MatrixName = reaction.Emoji.Name - dbReaction.DiscordName = reaction.Emoji.Name - } + dbReaction.MatrixName = reaction.Emoji.Name + dbReaction.DiscordID = emoteID - dbReaction.Insert() - } + dbReaction.Insert() } } + +func (p *Portal) handleMatrixRedaction(evt *event.Event) { + user := p.bridge.GetUserByMXID(evt.Sender) + + if user.ID != p.Key.Receiver { + return + } + + // First look if we're redacting a message + message := p.bridge.db.Message.GetByMatrixID(p.Key, evt.Redacts) + if message != nil { + if message.DiscordID != "" { + err := user.Session.ChannelMessageDelete(p.Key.ChannelID, message.DiscordID) + if err != nil { + p.log.Debugfln("Failed to delete discord message %s: %v", message.DiscordID, err) + } else { + message.Delete() + } + } + + return + } + + // Now check if it's a reaction. + reaction := p.bridge.db.Reaction.GetByMatrixID(p.Key, evt.Redacts) + if reaction != nil { + if reaction.DiscordID != "" { + err := user.Session.MessageReactionRemove(p.Key.ChannelID, reaction.DiscordMessageID, reaction.DiscordID, reaction.AuthorID) + if err != nil { + p.log.Debugfln("Failed to delete reaction %s for message %s: %v", reaction.DiscordID, reaction.DiscordMessageID, err) + } else { + reaction.Delete() + } + } + + return + } + + p.log.Warnfln("Failed to redact %s@%s: no event found", p.Key, evt.Redacts) +} diff --git a/database/message.go b/database/message.go index 703120d..044b555 100644 --- a/database/message.go +++ b/database/message.go @@ -54,6 +54,19 @@ func (m *Message) Insert() { } } +func (m *Message) Delete() { + query := "DELETE FROM message" + + " WHERE channel_id=$1 AND receiver=$2 AND discord_message_id=$3 AND" + + " matrix_message_id=$4" + + _, err := m.db.Exec(query, m.Channel.ChannelID, m.Channel.Receiver, + m.DiscordID, m.MatrixID) + + if err != nil { + m.log.Warnfln("Failed to delete %s@%s: %v", m.Channel, m.DiscordID, err) + } +} + func (m *Message) UpdateMatrixID(mxid id.EventID) { query := "UPDATE message SET matrix_message_id=$1 WHERE channel_id=$2" + "AND receiver=$3 AND discord_message_id=$4" diff --git a/database/migrations/01-initial.sql b/database/migrations/01-initial.sql index bbebc7b..ccb1329 100644 --- a/database/migrations/01-initial.sql +++ b/database/migrations/01-initial.sql @@ -59,11 +59,9 @@ CREATE TABLE reaction ( matrix_name TEXT, matrix_url TEXT, - discord_name TEXT, discord_id TEXT, - CHECK ((discord_name IS NULL AND discord_id IS NOT NULL) OR (discord_name IS NOT NULL AND discord_id IS NULL)), - UNIQUE (discord_name, discord_id, author_id, discord_message_id, channel_id, receiver), + UNIQUE (discord_id, author_id, discord_message_id, channel_id, receiver), FOREIGN KEY(channel_id, receiver) REFERENCES portal(channel_id, receiver) ON DELETE CASCADE ); diff --git a/database/reaction.go b/database/reaction.go index 8e634f3..617eedc 100644 --- a/database/reaction.go +++ b/database/reaction.go @@ -23,19 +23,18 @@ type Reaction struct { MatrixName string MatrixURL string // Used for custom emoji - DiscordName string // Used for unicode emoji - DiscordID string // Used for custom emoji + DiscordID string // The id or unicode of the emoji for discord } func (r *Reaction) Scan(row Scannable) *Reaction { - var discordName, discordID sql.NullString + var discordID sql.NullString err := row.Scan( &r.Channel.ChannelID, &r.Channel.Receiver, &r.DiscordMessageID, &r.MatrixEventID, &r.AuthorID, &r.MatrixName, &r.MatrixURL, - &discordName, &discordID) + &discordID) if err != nil { if !errors.Is(err, sql.ErrNoRows) { @@ -45,7 +44,6 @@ func (r *Reaction) Scan(row Scannable) *Reaction { return nil } - r.DiscordName = discordName.String r.DiscordID = discordID.String return r @@ -54,14 +52,10 @@ func (r *Reaction) Scan(row Scannable) *Reaction { func (r *Reaction) Insert() { query := "INSERT INTO reaction" + " (channel_id, receiver, discord_message_id, matrix_event_id," + - " author_id, matrix_name, matrix_url, discord_name, discord_id)" + - " VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9);" + " author_id, matrix_name, matrix_url, discord_id)" + + " VALUES($1, $2, $3, $4, $5, $6, $7, $8);" - var discordName, discordID sql.NullString - - if r.DiscordName != "" { - discordName = sql.NullString{r.DiscordName, true} - } + var discordID sql.NullString if r.DiscordID != "" { discordID = sql.NullString{r.DiscordID, true} @@ -73,7 +67,7 @@ func (r *Reaction) Insert() { r.DiscordMessageID, r.MatrixEventID, r.AuthorID, r.MatrixName, r.MatrixURL, - discordName, discordID, + discordID, ) if err != nil { @@ -90,14 +84,9 @@ func (r *Reaction) Update() { func (r *Reaction) Delete() { query := "DELETE FROM reaction WHERE" + " channel_id=$1 AND receiver=$2 AND discord_message_id=$3 AND" + - " author_id=$4 AND discord_name=$5 AND discord_id=$6" - - var discordName, discordID sql.NullString - - if r.DiscordName != "" { - discordName = sql.NullString{r.DiscordName, true} - } + " author_id=$4 AND discord_id=$5" + var discordID sql.NullString if r.DiscordID != "" { discordID = sql.NullString{r.DiscordID, true} } @@ -106,7 +95,7 @@ func (r *Reaction) Delete() { query, r.Channel.ChannelID, r.Channel.Receiver, r.DiscordMessageID, r.AuthorID, - discordName, discordID, + discordID, ) if err != nil { diff --git a/database/reactionquery.go b/database/reactionquery.go index 6a5eaa9..d0bc0af 100644 --- a/database/reactionquery.go +++ b/database/reactionquery.go @@ -13,7 +13,7 @@ type ReactionQuery struct { const ( reactionSelect = "SELECT channel_id, receiver, discord_message_id," + " matrix_event_id, author_id, matrix_name, matrix_url, " + - " discord_name, discord_id FROM reaction" + " discord_id FROM reaction" ) func (rq *ReactionQuery) New() *Reaction { @@ -51,13 +51,6 @@ func (rq *ReactionQuery) getAll(query string, args ...interface{}) []*Reaction { return reactions } -func (rq *ReactionQuery) GetByDiscordName(key PortalKey, discordMessageID, discordName string) *Reaction { - query := reactionSelect + " WHERE channel_id=$1 AND receiver=$2" + - " AND discord_message_id=$3 AND discord_name=$4" - - return rq.get(query, key.ChannelID, key.Receiver, discordMessageID, discordName) -} - func (rq *ReactionQuery) GetByDiscordID(key PortalKey, discordMessageID, discordID string) *Reaction { query := reactionSelect + " WHERE channel_id=$1 AND receiver=$2" + " AND discord_message_id=$3 AND discord_id=$4" @@ -65,11 +58,11 @@ func (rq *ReactionQuery) GetByDiscordID(key PortalKey, discordMessageID, discord return rq.get(query, key.ChannelID, key.Receiver, discordMessageID, discordID) } -func (rq *ReactionQuery) GetByMatrixName(key PortalKey, matrixEventID id.EventID, matrixName string) *Reaction { +func (rq *ReactionQuery) GetByMatrixID(key PortalKey, matrixEventID id.EventID) *Reaction { query := reactionSelect + " WHERE channel_id=$1 AND receiver=$2" + - " AND matrix_event_id=$3 AND matrix_name=$4" + " AND matrix_event_id=$3" - return rq.get(query, key.ChannelID, key.Receiver, matrixEventID, matrixName) + return rq.get(query, key.ChannelID, key.Receiver, matrixEventID) } func (rq *ReactionQuery) get(query string, args ...interface{}) *Reaction {