Merge emoji and discord_file tables

Also fix duplicate reaction when reacting with custom emoji from Matrix
This commit is contained in:
Tulir Asokan
2023-01-30 01:35:17 +02:00
parent e183f5cffa
commit 466139164c
9 changed files with 114 additions and 235 deletions

View File

@@ -16,6 +16,7 @@ import (
"maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/crypto/attachment" "maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-discord/database" "go.mau.fi/mautrix-discord/database"
) )
@@ -62,7 +63,7 @@ func uploadDiscordAttachment(url string, data []byte) error {
return nil return nil
} }
func (portal *Portal) downloadMatrixAttachment(content *event.MessageEventContent) ([]byte, error) { func downloadMatrixAttachment(intent *appservice.IntentAPI, content *event.MessageEventContent) ([]byte, error) {
var file *event.EncryptedFileInfo var file *event.EncryptedFileInfo
rawMXC := content.URL rawMXC := content.URL
@@ -76,7 +77,7 @@ func (portal *Portal) downloadMatrixAttachment(content *event.MessageEventConten
return nil, err return nil, err
} }
data, err := portal.MainIntent().DownloadBytes(mxc) data, err := intent.DownloadBytes(mxc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -91,23 +92,24 @@ func (portal *Portal) downloadMatrixAttachment(content *event.MessageEventConten
return data, nil return data, nil
} }
func (br *DiscordBridge) uploadMatrixAttachment(intent *appservice.IntentAPI, data []byte, url string, encrypt bool, attachmentID, mime string) (*database.File, error) { func (br *DiscordBridge) uploadMatrixAttachment(intent *appservice.IntentAPI, data []byte, url string, encrypt bool, meta AttachmentMeta) (*database.File, error) {
dbFile := br.DB.File.New() dbFile := br.DB.File.New()
dbFile.Timestamp = time.Now() dbFile.Timestamp = time.Now()
dbFile.URL = url dbFile.URL = url
dbFile.ID = attachmentID dbFile.ID = meta.AttachmentID
dbFile.EmojiName = meta.EmojiName
dbFile.Size = len(data) dbFile.Size = len(data)
dbFile.MimeType = mimetype.Detect(data).String() dbFile.MimeType = mimetype.Detect(data).String()
if mime == "" { if meta.MimeType == "" {
mime = dbFile.MimeType meta.MimeType = dbFile.MimeType
} }
if strings.HasPrefix(mime, "image/") { if strings.HasPrefix(meta.MimeType, "image/") {
cfg, _, _ := image.DecodeConfig(bytes.NewReader(data)) cfg, _, _ := image.DecodeConfig(bytes.NewReader(data))
dbFile.Width = cfg.Width dbFile.Width = cfg.Width
dbFile.Height = cfg.Height dbFile.Height = cfg.Height
} }
uploadMime := mime uploadMime := meta.MimeType
if encrypt { if encrypt {
dbFile.Encrypted = true dbFile.Encrypted = true
dbFile.DecryptionInfo = attachment.NewEncryptedFile() dbFile.DecryptionInfo = attachment.NewEncryptedFile()
@@ -140,14 +142,16 @@ func (br *DiscordBridge) uploadMatrixAttachment(intent *appservice.IntentAPI, da
} }
dbFile.MXC = uploaded.ContentURI dbFile.MXC = uploaded.ContentURI
} }
// TODO add option to cache encrypted files too?
if !dbFile.Encrypted {
dbFile.Insert(nil)
}
return dbFile, nil return dbFile, nil
} }
func (br *DiscordBridge) copyAttachmentToMatrix(intent *appservice.IntentAPI, url string, encrypt bool, attachmentID, mime string) (*database.File, error) { type AttachmentMeta struct {
AttachmentID string
MimeType string
EmojiName string
}
func (br *DiscordBridge) copyAttachmentToMatrix(intent *appservice.IntentAPI, url string, encrypt bool, meta *AttachmentMeta) (*database.File, error) {
dbFile := br.DB.File.Get(url, encrypt) dbFile := br.DB.File.Get(url, encrypt)
if dbFile == nil { if dbFile == nil {
data, err := downloadDiscordAttachment(url) data, err := downloadDiscordAttachment(url)
@@ -155,10 +159,38 @@ func (br *DiscordBridge) copyAttachmentToMatrix(intent *appservice.IntentAPI, ur
return nil, err return nil, err
} }
dbFile, err = br.uploadMatrixAttachment(intent, data, url, encrypt, attachmentID, mime) if meta == nil {
meta = &AttachmentMeta{}
}
dbFile, err = br.uploadMatrixAttachment(intent, data, url, encrypt, *meta)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO add option to cache encrypted files too?
if !dbFile.Encrypted {
dbFile.Insert(nil)
}
} }
return dbFile, nil return dbFile, nil
} }
func (portal *Portal) getEmojiMXCByDiscordID(emojiID, name string, animated bool) id.ContentURI {
var url, mimeType string
if animated {
url = discordgo.EndpointEmojiAnimated(emojiID)
mimeType = "image/gif"
} else {
url = discordgo.EndpointEmoji(emojiID)
mimeType = "image/png"
}
dbFile, err := portal.bridge.copyAttachmentToMatrix(portal.MainIntent(), url, false, &AttachmentMeta{
AttachmentID: emojiID,
MimeType: mimeType,
EmojiName: name,
})
if err != nil {
portal.log.Warnfln("Failed to download emoji %s from discord: %v", emojiID, err)
return id.ContentURI{}
}
return dbFile.MXC
}

View File

@@ -21,7 +21,6 @@ type Database struct {
Message *MessageQuery Message *MessageQuery
Thread *ThreadQuery Thread *ThreadQuery
Reaction *ReactionQuery Reaction *ReactionQuery
Emoji *EmojiQuery
Guild *GuildQuery Guild *GuildQuery
Role *RoleQuery Role *RoleQuery
File *FileQuery File *FileQuery
@@ -54,10 +53,6 @@ func New(baseDB *dbutil.Database, log maulogger.Logger) *Database {
db: db, db: db,
log: log.Sub("Reaction"), log: log.Sub("Reaction"),
} }
db.Emoji = &EmojiQuery{
db: db,
log: log.Sub("Emoji"),
}
db.Guild = &GuildQuery{ db.Guild = &GuildQuery{
db: db, db: db,
log: log.Sub("Guild"), log: log.Sub("Guild"),

View File

@@ -1,99 +0,0 @@
package database
import (
"database/sql"
"errors"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/util/dbutil"
)
type EmojiQuery struct {
db *Database
log log.Logger
}
const (
emojiSelect = "SELECT discord_id, discord_name, matrix_url FROM emoji"
)
func (eq *EmojiQuery) New() *Emoji {
return &Emoji{
db: eq.db,
log: eq.log,
}
}
func (eq *EmojiQuery) GetByDiscordID(discordID string) *Emoji {
query := emojiSelect + " WHERE discord_id=$1"
return eq.get(query, discordID)
}
func (eq *EmojiQuery) GetByMatrixURL(matrixURL id.ContentURI) *Emoji {
query := emojiSelect + " WHERE matrix_url=$1"
return eq.get(query, matrixURL.String())
}
func (eq *EmojiQuery) get(query string, args ...interface{}) *Emoji {
return eq.New().Scan(eq.db.QueryRow(query, args...))
}
type Emoji struct {
db *Database
log log.Logger
DiscordID string
DiscordName string
MatrixURL id.ContentURI
}
func (e *Emoji) Scan(row dbutil.Scannable) *Emoji {
var matrixURL sql.NullString
err := row.Scan(&e.DiscordID, &e.DiscordName, &matrixURL)
if err != nil {
if !errors.Is(err, sql.ErrNoRows) {
e.log.Errorln("Database scan failed:", err)
panic(err)
}
return nil
}
e.MatrixURL, _ = id.ParseContentURI(matrixURL.String)
return e
}
func (e *Emoji) Insert() {
query := "INSERT INTO emoji" +
" (discord_id, discord_name, matrix_url)" +
" VALUES ($1, $2, $3);"
_, err := e.db.Exec(query, e.DiscordID, e.DiscordName, e.MatrixURL.String())
if err != nil {
e.log.Warnfln("Failed to insert emoji %s: %v", e.DiscordID, err)
panic(err)
}
}
func (e *Emoji) Delete() {
query := "DELETE FROM emoji WHERE discord_id=$1"
_, err := e.db.Exec(query, e.DiscordID)
if err != nil {
e.log.Warnfln("Failed to delete emoji %s: %v", e.DiscordID, err)
panic(err)
}
}
func (e *Emoji) APIName() string {
if e.DiscordID != "" && e.DiscordName != "" {
return e.DiscordName + ":" + e.DiscordID
} else if e.DiscordName != "" {
return e.DiscordName
}
return e.DiscordID
}

View File

@@ -20,10 +20,10 @@ type FileQuery struct {
// language=postgresql // language=postgresql
const ( const (
fileSelect = "SELECT url, encrypted, id, mxc, size, width, height, mime_type, decryption_info, timestamp FROM discord_file" fileSelect = "SELECT url, encrypted, mxc, id, emoji_name, size, width, height, mime_type, decryption_info, timestamp FROM discord_file"
fileInsert = ` fileInsert = `
INSERT INTO discord_file (url, encrypted, id, mxc, size, width, height, mime_type, decryption_info, timestamp) INSERT INTO discord_file (url, encrypted, mxc, id, emoji_name, size, width, height, mime_type, decryption_info, timestamp)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
` `
) )
@@ -39,15 +39,21 @@ func (fq *FileQuery) Get(url string, encrypted bool) *File {
return fq.New().Scan(fq.db.QueryRow(query, url, encrypted)) return fq.New().Scan(fq.db.QueryRow(query, url, encrypted))
} }
func (fq *FileQuery) GetByMXC(mxc id.ContentURI) *File {
query := fileSelect + " WHERE mxc=$1"
return fq.New().Scan(fq.db.QueryRow(query, mxc.String()))
}
type File struct { type File struct {
db *Database db *Database
log log.Logger log log.Logger
URL string URL string
Encrypted bool Encrypted bool
MXC id.ContentURI
ID string ID string
MXC id.ContentURI EmojiName string
Size int Size int
Width int Width int
@@ -55,16 +61,15 @@ type File struct {
MimeType string MimeType string
DecryptionInfo *attachment.EncryptedFile DecryptionInfo *attachment.EncryptedFile
Timestamp time.Time
Timestamp time.Time
} }
func (f *File) Scan(row dbutil.Scannable) *File { func (f *File) Scan(row dbutil.Scannable) *File {
var fileID, decryptionInfo sql.NullString var fileID, emojiName, decryptionInfo sql.NullString
var width, height sql.NullInt32 var width, height sql.NullInt32
var timestamp int64 var timestamp int64
var mxc string var mxc string
err := row.Scan(&f.URL, &f.Encrypted, &fileID, &mxc, &f.Size, &width, &height, &f.MimeType, &decryptionInfo, &timestamp) err := row.Scan(&f.URL, &f.Encrypted, &mxc, &fileID, &emojiName, &f.Size, &width, &height, &f.MimeType, &decryptionInfo, &timestamp)
if err != nil { if err != nil {
if !errors.Is(err, sql.ErrNoRows) { if !errors.Is(err, sql.ErrNoRows) {
f.log.Errorln("Database scan failed:", err) f.log.Errorln("Database scan failed:", err)
@@ -73,6 +78,7 @@ func (f *File) Scan(row dbutil.Scannable) *File {
return nil return nil
} }
f.ID = fileID.String f.ID = fileID.String
f.EmojiName = emojiName.String
f.Timestamp = time.UnixMilli(timestamp) f.Timestamp = time.UnixMilli(timestamp)
f.Width = int(width.Int32) f.Width = int(width.Int32)
f.Height = int(height.Int32) f.Height = int(height.Int32)
@@ -114,7 +120,7 @@ func (f *File) Insert(txn dbutil.Execable) {
decryptionInfoStr.String = string(decryptionInfo) decryptionInfoStr.String = string(decryptionInfo)
} }
_, err := txn.Exec(fileInsert, _, err := txn.Exec(fileInsert,
f.URL, f.Encrypted, strPtr(f.ID), f.MXC.String(), f.Size, f.URL, f.Encrypted, f.MXC.String(), strPtr(f.ID), strPtr(f.EmojiName), f.Size,
positiveIntToNullInt32(f.Width), positiveIntToNullInt32(f.Height), f.MimeType, positiveIntToNullInt32(f.Width), positiveIntToNullInt32(f.Height), f.MimeType,
decryptionInfoStr, f.Timestamp.UnixMilli(), decryptionInfoStr, f.Timestamp.UnixMilli(),
) )

View File

@@ -1,4 +1,4 @@
-- v0 -> v12: Latest revision -- v0 -> v13: Latest revision
CREATE TABLE guild ( CREATE TABLE guild (
dcid TEXT PRIMARY KEY, dcid TEXT PRIMARY KEY,
@@ -126,12 +126,6 @@ CREATE TABLE reaction (
CONSTRAINT reaction_message_fkey FOREIGN KEY (dc_msg_id, dc_first_attachment_id, _dc_first_edit_index, dc_chan_id, dc_chan_receiver) REFERENCES message (dcid, dc_attachment_id, dc_edit_index, dc_chan_id, dc_chan_receiver) ON DELETE CASCADE CONSTRAINT reaction_message_fkey FOREIGN KEY (dc_msg_id, dc_first_attachment_id, _dc_first_edit_index, dc_chan_id, dc_chan_receiver) REFERENCES message (dcid, dc_attachment_id, dc_edit_index, dc_chan_id, dc_chan_receiver) ON DELETE CASCADE
); );
CREATE TABLE emoji (
discord_id TEXT PRIMARY KEY,
discord_name TEXT,
matrix_url TEXT
);
CREATE TABLE role ( CREATE TABLE role (
dc_guild_id TEXT, dc_guild_id TEXT,
dcid TEXT, dcid TEXT,
@@ -151,21 +145,20 @@ CREATE TABLE role (
CONSTRAINT role_guild_fkey FOREIGN KEY (dc_guild_id) REFERENCES guild (dcid) ON DELETE CASCADE CONSTRAINT role_guild_fkey FOREIGN KEY (dc_guild_id) REFERENCES guild (dcid) ON DELETE CASCADE
); );
CREATE TABLE discord_file ( CREATE TABLE new_discord_file (
url TEXT, url TEXT,
encrypted BOOLEAN, encrypted BOOLEAN,
mxc TEXT NOT NULL UNIQUE,
id TEXT, id TEXT,
mxc TEXT NOT NULL, emoji_name TEXT,
size BIGINT NOT NULL,
width INTEGER,
height INTEGER,
mime_type TEXT NOT NULL,
size BIGINT NOT NULL,
width INTEGER,
height INTEGER,
mime_type TEXT NOT NULL,
decryption_info jsonb, decryption_info jsonb,
timestamp BIGINT NOT NULL,
timestamp BIGINT NOT NULL,
PRIMARY KEY (url, encrypted) PRIMARY KEY (url, encrypted)
); );

View File

@@ -0,0 +1,4 @@
-- v13: Merge tables used for cached custom emojis and attachments
ALTER TABLE discord_file ADD CONSTRAINT mxc_unique UNIQUE (mxc);
ALTER TABLE discord_file ADD COLUMN emoji_name TEXT;
DROP TABLE emoji;

View File

@@ -0,0 +1,24 @@
-- v13: Merge tables used for cached custom emojis and attachments
CREATE TABLE new_discord_file (
url TEXT,
encrypted BOOLEAN,
mxc TEXT NOT NULL UNIQUE,
id TEXT,
emoji_name TEXT,
size BIGINT NOT NULL,
width INTEGER,
height INTEGER,
mime_type TEXT NOT NULL,
decryption_info jsonb,
timestamp BIGINT NOT NULL,
PRIMARY KEY (url, encrypted)
);
INSERT INTO new_discord_file (url, encrypted, id, mxc, size, width, height, mime_type, decryption_info, timestamp)
SELECT url, encrypted, id, mxc, size, width, height, mime_type, decryption_info, timestamp FROM discord_file;
DROP TABLE discord_file;
ALTER TABLE new_discord_file RENAME TO discord_file;

View File

@@ -1,79 +0,0 @@
package main
import (
"io/ioutil"
"net/http"
"github.com/bwmarrin/discordgo"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/id"
)
func (portal *Portal) getEmojiMXCByDiscordID(emojiID, name string, animated bool) id.ContentURI {
dbEmoji := portal.bridge.DB.Emoji.GetByDiscordID(emojiID)
if dbEmoji == nil {
data, mimeType, err := portal.downloadDiscordEmoji(emojiID, animated)
if err != nil {
portal.log.Warnfln("Failed to download emoji %s from discord: %v", emojiID, err)
return id.ContentURI{}
}
uri, err := portal.uploadMatrixEmoji(portal.MainIntent(), data, mimeType)
if err != nil {
portal.log.Warnfln("Failed to upload discord emoji %s to homeserver: %v", emojiID, err)
return id.ContentURI{}
}
dbEmoji = portal.bridge.DB.Emoji.New()
dbEmoji.DiscordID = emojiID
dbEmoji.DiscordName = name
dbEmoji.MatrixURL = uri
dbEmoji.Insert()
}
return dbEmoji.MatrixURL
}
func (portal *Portal) downloadDiscordEmoji(id string, animated bool) ([]byte, string, error) {
var url string
var mimeType string
if animated {
// This url requests a gif, so that's what we set the mimetype to.
url = discordgo.EndpointEmojiAnimated(id)
mimeType = "image/gif"
} else {
// This url requests a png, so that's what we set the mimetype to.
url = discordgo.EndpointEmoji(id)
mimeType = "image/png"
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, mimeType, err
}
req.Header.Set("User-Agent", discordgo.DroidBrowserUserAgent)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, mimeType, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
return data, mimeType, err
}
func (portal *Portal) uploadMatrixEmoji(intent *appservice.IntentAPI, data []byte, mimeType string) (id.ContentURI, error) {
uploaded, err := intent.UploadBytes(data, mimeType)
if err != nil {
return id.ContentURI{}, err
}
return uploaded.ContentURI, nil
}

View File

@@ -555,7 +555,7 @@ func (portal *Portal) sendMediaFailedMessage(intent *appservice.IntentAPI, bridg
const DiscordStickerSize = 160 const DiscordStickerSize = 160
func (portal *Portal) handleDiscordFile(typeName string, intent *appservice.IntentAPI, id, url string, content *event.MessageEventContent, ts time.Time, threadRelation *event.RelatesTo) *database.MessagePart { func (portal *Portal) handleDiscordFile(typeName string, intent *appservice.IntentAPI, id, url string, content *event.MessageEventContent, ts time.Time, threadRelation *event.RelatesTo) *database.MessagePart {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, url, portal.Encrypted, id, content.Info.MimeType) dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, url, portal.Encrypted, &AttachmentMeta{AttachmentID: id, MimeType: content.Info.MimeType})
if err != nil { if err != nil {
errorEventID := portal.sendMediaFailedMessage(intent, err) errorEventID := portal.sendMediaFailedMessage(intent, err)
if errorEventID != "" { if errorEventID != "" {
@@ -675,7 +675,7 @@ type ConvertedMessage struct {
} }
func (portal *Portal) convertDiscordVideoEmbed(intent *appservice.IntentAPI, embed *discordgo.MessageEmbed) *ConvertedMessage { func (portal *Portal) convertDiscordVideoEmbed(intent *appservice.IntentAPI, embed *discordgo.MessageEmbed) *ConvertedMessage {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Video.ProxyURL, portal.Encrypted, "", "") dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Video.ProxyURL, portal.Encrypted, nil)
if err != nil { if err != nil {
return &ConvertedMessage{Content: portal.createMediaFailedMessage(err)} return &ConvertedMessage{Content: portal.createMediaFailedMessage(err)}
} }
@@ -768,7 +768,7 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe
} }
authorHTML = fmt.Sprintf(embedHTMLAuthorPlain, authorNameHTML) authorHTML = fmt.Sprintf(embedHTMLAuthorPlain, authorNameHTML)
if embed.Author.ProxyIconURL != "" { if embed.Author.ProxyIconURL != "" {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Author.ProxyIconURL, false, "", "") dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Author.ProxyIconURL, false, nil)
if err != nil { if err != nil {
portal.log.Warnfln("Failed to reupload author icon in embed #%d of message %s: %v", index+1, msgID, err) portal.log.Warnfln("Failed to reupload author icon in embed #%d of message %s: %v", index+1, msgID, err)
} else { } else {
@@ -818,7 +818,7 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe
} }
} }
if embed.Image != nil { if embed.Image != nil {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Image.ProxyURL, false, "", "") dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Image.ProxyURL, false, nil)
if err != nil { if err != nil {
portal.log.Warnfln("Failed to reupload image in embed #%d of message %s: %v", index+1, msgID, err) portal.log.Warnfln("Failed to reupload image in embed #%d of message %s: %v", index+1, msgID, err)
} else { } else {
@@ -844,7 +844,7 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe
} }
footerHTML = fmt.Sprintf(embedHTMLFooterPlain, html.EscapeString(embed.Footer.Text), datePart) footerHTML = fmt.Sprintf(embedHTMLFooterPlain, html.EscapeString(embed.Footer.Text), datePart)
if embed.Footer.ProxyIconURL != "" { if embed.Footer.ProxyIconURL != "" {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Footer.ProxyIconURL, false, "", "") dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, embed.Footer.ProxyIconURL, false, nil)
if err != nil { if err != nil {
portal.log.Warnfln("Failed to reupload footer icon in embed #%d of message %s: %v", index+1, msgID, err) portal.log.Warnfln("Failed to reupload footer icon in embed #%d of message %s: %v", index+1, msgID, err)
} else { } else {
@@ -876,7 +876,7 @@ type BeeperLinkPreview struct {
} }
func (portal *Portal) convertDiscordLinkEmbedImage(intent *appservice.IntentAPI, url string, width, height int, preview *BeeperLinkPreview) { func (portal *Portal) convertDiscordLinkEmbedImage(intent *appservice.IntentAPI, url string, width, height int, preview *BeeperLinkPreview) {
dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, url, portal.Encrypted, "", "") dbFile, err := portal.bridge.copyAttachmentToMatrix(intent, url, portal.Encrypted, nil)
if err != nil { if err != nil {
portal.log.Warnfln("Failed to copy image in URL preview: %v", err) portal.log.Warnfln("Failed to copy image in URL preview: %v", err)
} else { } else {
@@ -1576,7 +1576,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
} }
sendReq.Content = portal.parseMatrixHTML(sender, content) sendReq.Content = portal.parseMatrixHTML(sender, content)
case event.MsgAudio, event.MsgFile, event.MsgImage, event.MsgVideo: case event.MsgAudio, event.MsgFile, event.MsgImage, event.MsgVideo:
data, err := portal.downloadMatrixAttachment(content) data, err := downloadMatrixAttachment(portal.MainIntent(), content)
if err != nil { if err != nil {
go portal.sendMessageMetrics(evt, err, "Error downloading media in") go portal.sendMessageMetrics(evt, err, "Error downloading media in")
return return
@@ -1819,17 +1819,20 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) {
// Figure out if this is a custom emoji or not. // Figure out if this is a custom emoji or not.
emojiID := reaction.RelatesTo.Key emojiID := reaction.RelatesTo.Key
requestEmojiID := emojiID
if strings.HasPrefix(emojiID, "mxc://") { if strings.HasPrefix(emojiID, "mxc://") {
uri, _ := id.ParseContentURI(emojiID) uri, _ := id.ParseContentURI(emojiID)
emoji := portal.bridge.DB.Emoji.GetByMatrixURL(uri) emojiFile := portal.bridge.DB.File.GetByMXC(uri)
if emoji == nil { if emojiFile == nil || emojiFile.ID == "" || emojiFile.EmojiName == "" {
go portal.sendMessageMetrics(evt, fmt.Errorf("%w %s", errUnknownEmoji, emojiID), "Ignoring") go portal.sendMessageMetrics(evt, fmt.Errorf("%w %s", errUnknownEmoji, emojiID), "Ignoring")
return return
} }
emojiID = emoji.APIName() emojiID = emojiFile.ID
requestEmojiID = fmt.Sprintf("%s:%s", emojiFile.EmojiName, emojiFile.ID)
} else { } else {
emojiID = variationselector.Remove(emojiID) emojiID = variationselector.Remove(emojiID)
requestEmojiID = emojiID
} }
existing := portal.bridge.DB.Reaction.GetByDiscordID(portal.Key, msg.DiscordID, sender.DiscordID, emojiID) existing := portal.bridge.DB.Reaction.GetByDiscordID(portal.Key, msg.DiscordID, sender.DiscordID, emojiID)
@@ -1839,7 +1842,7 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) {
return return
} }
err := sender.Session.MessageReactionAdd(msg.DiscordProtoChannelID(), msg.DiscordID, emojiID) err := sender.Session.MessageReactionAdd(msg.DiscordProtoChannelID(), msg.DiscordID, requestEmojiID)
go portal.sendMessageMetrics(evt, err, "Error sending") go portal.sendMessageMetrics(evt, err, "Error sending")
if err == nil { if err == nil {
dbReaction := portal.bridge.DB.Reaction.New() dbReaction := portal.bridge.DB.Reaction.New()