Merge emoji and discord_file tables
Also fix duplicate reaction when reacting with custom emoji from Matrix
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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, ×tamp)
|
err := row.Scan(&f.URL, &f.Encrypted, &mxc, &fileID, &emojiName, &f.Size, &width, &height, &f.MimeType, &decryptionInfo, ×tamp)
|
||||||
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(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
);
|
);
|
||||||
|
|||||||
4
database/upgrades/13-merge-emoji-and-file.postgres.sql
Normal file
4
database/upgrades/13-merge-emoji-and-file.postgres.sql
Normal 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;
|
||||||
24
database/upgrades/13-merge-emoji-and-file.sqlite.sql
Normal file
24
database/upgrades/13-merge-emoji-and-file.sqlite.sql
Normal 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;
|
||||||
79
emoji.go
79
emoji.go
@@ -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
|
|
||||||
}
|
|
||||||
25
portal.go
25
portal.go
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user