Add initial support for relay mode with webhooks

This commit is contained in:
Tulir Asokan 2023-02-28 00:40:53 +02:00
parent 6365db46cc
commit 17d4b79554
10 changed files with 254 additions and 53 deletions

View file

@ -55,6 +55,7 @@ func (br *DiscordBridge) RegisterCommands() {
cmdLogout,
cmdReconnect,
cmdDisconnect,
cmdSetRelay,
cmdGuilds,
cmdRejoinSpace,
cmdDeleteAllPortals,
@ -352,6 +353,91 @@ func fnRejoinSpace(ce *WrappedCommandEvent) {
}
}
var cmdSetRelay = &commands.FullHandler{
Func: wrapCommand(fnSetRelay),
Name: "set-relay",
Help: commands.HelpMeta{
Section: commands.HelpSectionUnclassified,
Description: "Create or set a relay webhook for a portal",
Args: "[room ID] <--url URL> OR <--create [name]>",
},
RequiresLogin: true,
}
const webhookURLFormat = "https://discord.com/api/webhooks/%d/%s"
const selectRelayHelp = "Usage: `$cmdprefix [room ID] <--url URL> OR <--create [name]>`"
func fnSetRelay(ce *WrappedCommandEvent) {
portal := ce.Portal
if len(ce.Args) > 0 && strings.HasPrefix(ce.Args[0], "!") {
portal = ce.Bridge.GetPortalByMXID(id.RoomID(ce.Args[0]))
if portal == nil {
ce.Reply("Portal with room ID %s not found", ce.Args[0])
return
}
ce.Args = ce.Args[1:]
} else if portal == nil {
ce.Reply("You must either run the command in a portal, or specify an internal room ID as the first parameter")
return
}
if len(ce.Args) == 0 {
ce.Reply(selectRelayHelp)
return
}
log := ce.ZLog.With().Str("channel_id", portal.Key.ChannelID).Logger()
createType := strings.ToLower(strings.TrimLeft(ce.Args[0], "-"))
var webhookID int64
var webhookSecret string
switch createType {
case "url":
if len(ce.Args) < 2 {
ce.Reply("Usage: `$cmdprefix [room ID] --url <URL>")
return
}
ce.Redact()
_, err := fmt.Sscanf(ce.Args[1], webhookURLFormat, &webhookID, &webhookSecret)
if err != nil {
log.Warn().Str("webhook_url", ce.Args[1]).Err(err).Msg("Failed to parse provided webhook URL")
ce.Reply("Invalid webhook URL")
return
}
case "create":
perms, err := ce.User.Session.UserChannelPermissions(ce.User.DiscordID, portal.Key.ChannelID)
if err != nil {
log.Warn().Err(err).Msg("Failed to check user permissions")
ce.Reply("Failed to check if you have permission to create webhooks")
return
} else if perms&discordgo.PermissionManageWebhooks == 0 {
log.Debug().Int64("perms", perms).Msg("User doesn't have permissions to manage webhooks in channel")
ce.Reply("You don't have permission to manage webhooks in that channel")
return
}
name := "mautrix"
if len(ce.Args) > 1 {
name = strings.Join(ce.Args[1:], " ")
}
log.Debug().Str("webhook_name", name).Msg("Creating webhook")
webhook, err := ce.User.Session.WebhookCreate(portal.Key.ChannelID, name, "")
if err != nil {
log.Warn().Err(err).Msg("Failed to create webhook")
ce.Reply("Failed to create webhook: %v", err)
return
}
webhookID, _ = strconv.ParseInt(webhook.ID, 10, 64)
ce.Reply("Created webhook %s", webhook.Name)
webhookSecret = webhook.Token
default:
ce.Reply(selectRelayHelp)
return
}
log.Debug().Int64("webhook_id", webhookID).Msg("Setting portal relay webhook")
portal.RelayWebhookID = strconv.FormatInt(webhookID, 10)
portal.RelayWebhookSecret = webhookSecret
portal.Update()
ce.Reply("Saved webhook ID %s as portal relay webhook", portal.RelayWebhookID)
}
var cmdGuilds = &commands.FullHandler{
Func: wrapCommand(fnGuilds),
Name: "guilds",

View file

@ -25,6 +25,8 @@ import (
func DoUpgrade(helper *up.Helper) {
bridgeconfig.Upgrader.DoUpgrade(helper)
helper.Copy(up.Str|up.Null, "homeserver", "public_address")
helper.Copy(up.Str, "bridge", "username_template")
helper.Copy(up.Str, "bridge", "displayname_template")
helper.Copy(up.Str, "bridge", "channel_name_template")

View file

@ -16,7 +16,7 @@ const (
portalSelect = `
SELECT dcid, receiver, type, other_user_id, dc_guild_id, dc_parent_id, mxid,
plain_name, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
encrypted, in_space, first_event_id
encrypted, in_space, first_event_id, relay_webhook_id, relay_webhook_secret
FROM portal
`
)
@ -121,16 +121,19 @@ type Portal struct {
InSpace id.RoomID
FirstEventID id.EventID
RelayWebhookID string
RelayWebhookSecret string
}
func (p *Portal) Scan(row dbutil.Scannable) *Portal {
var otherUserID, guildID, parentID, mxid, firstEventID sql.NullString
var otherUserID, guildID, parentID, mxid, firstEventID, relayWebhookID, relayWebhookSecret sql.NullString
var chanType int32
var avatarURL string
err := row.Scan(&p.Key.ChannelID, &p.Key.Receiver, &chanType, &otherUserID, &guildID, &parentID,
&mxid, &p.PlainName, &p.Name, &p.NameSet, &p.Topic, &p.TopicSet, &p.Avatar, &avatarURL, &p.AvatarSet,
&p.Encrypted, &p.InSpace, &firstEventID)
&p.Encrypted, &p.InSpace, &firstEventID, &relayWebhookID, &relayWebhookSecret)
if err != nil {
if err != sql.ErrNoRows {
@ -148,6 +151,8 @@ func (p *Portal) Scan(row dbutil.Scannable) *Portal {
p.Type = discordgo.ChannelType(chanType)
p.FirstEventID = id.EventID(firstEventID.String)
p.AvatarURL, _ = id.ParseContentURI(avatarURL)
p.RelayWebhookID = relayWebhookID.String
p.RelayWebhookSecret = relayWebhookSecret.String
return p
}
@ -156,13 +161,13 @@ func (p *Portal) Insert() {
query := `
INSERT INTO portal (dcid, receiver, type, other_user_id, dc_guild_id, dc_parent_id, mxid,
plain_name, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
encrypted, in_space, first_event_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
encrypted, in_space, first_event_id, relay_webhook_id, relay_webhook_secret)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
`
_, err := p.db.Exec(query, p.Key.ChannelID, p.Key.Receiver, p.Type,
strPtr(p.OtherUserID), strPtr(p.GuildID), strPtr(p.ParentID), strPtr(string(p.MXID)),
p.PlainName, p.Name, p.NameSet, p.Topic, p.TopicSet, p.Avatar, p.AvatarURL.String(), p.AvatarSet,
p.Encrypted, p.InSpace, p.FirstEventID.String())
p.Encrypted, p.InSpace, p.FirstEventID.String(), strPtr(p.RelayWebhookID), strPtr(p.RelayWebhookSecret))
if err != nil {
p.log.Warnfln("Failed to insert %s: %v", p.Key, err)
@ -175,13 +180,13 @@ func (p *Portal) Update() {
UPDATE portal
SET type=$1, other_user_id=$2, dc_guild_id=$3, dc_parent_id=$4, mxid=$5,
plain_name=$6, name=$7, name_set=$8, topic=$9, topic_set=$10, avatar=$11, avatar_url=$12, avatar_set=$13,
encrypted=$14, in_space=$15, first_event_id=$16
WHERE dcid=$17 AND receiver=$18
encrypted=$14, in_space=$15, first_event_id=$16, relay_webhook_id=$17, relay_webhook_secret=$18
WHERE dcid=$19 AND receiver=$20
`
_, err := p.db.Exec(query,
p.Type, strPtr(p.OtherUserID), strPtr(p.GuildID), strPtr(p.ParentID), strPtr(string(p.MXID)),
p.PlainName, p.Name, p.NameSet, p.Topic, p.TopicSet, p.Avatar, p.AvatarURL.String(), p.AvatarSet,
p.Encrypted, p.InSpace, p.FirstEventID.String(),
p.Encrypted, p.InSpace, p.FirstEventID.String(), strPtr(p.RelayWebhookID), strPtr(p.RelayWebhookSecret),
p.Key.ChannelID, p.Key.Receiver)
if err != nil {

View file

@ -1,4 +1,4 @@
-- v0 -> v14: Latest revision
-- v0 -> v15: Latest revision
CREATE TABLE guild (
dcid TEXT PRIMARY KEY,
@ -39,6 +39,9 @@ CREATE TABLE portal (
first_event_id TEXT NOT NULL,
relay_webhook_id TEXT,
relay_webhook_secret TEXT,
PRIMARY KEY (dcid, receiver),
CONSTRAINT portal_parent_fkey FOREIGN KEY (dc_parent_id, dc_parent_receiver) REFERENCES portal (dcid, receiver) ON DELETE CASCADE,
CONSTRAINT portal_guild_fkey FOREIGN KEY (dc_guild_id) REFERENCES guild(dcid) ON DELETE CASCADE

View file

@ -0,0 +1,3 @@
-- v15: Store relay webhook URL for portals
ALTER TABLE portal ADD COLUMN relay_webhook_id TEXT;
ALTER TABLE portal ADD COLUMN relay_webhook_secret TEXT;

View file

@ -2,6 +2,9 @@
homeserver:
# The address that this appservice can use to connect to the homeserver.
address: https://matrix.example.com
# Publicly accessible base URL for media, used for avatars in relay mode.
# If not set, the connection address above will be used.
public_address: null
# The domain of the homeserver (also known as server_name, used for MXIDs, etc).
domain: example.com

View file

@ -21,6 +21,7 @@ import (
"regexp"
"strings"
"github.com/bwmarrin/discordgo"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
@ -91,6 +92,16 @@ func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string, allowInlineLink
}
const formatterContextPortalKey = "fi.mau.discord.portal"
const formatterContextAllowedMentionsKey = "fi.mau.discord.allowed_mentions"
func appendIfNotContains(arr []string, newItem string) []string {
for _, item := range arr {
if item == newItem {
return arr
}
}
return append(arr, newItem)
}
func (br *DiscordBridge) pillConverter(displayname, mxid, eventID string, ctx format.Context) string {
if len(mxid) == 0 {
@ -124,12 +135,15 @@ func (br *DiscordBridge) pillConverter(displayname, mxid, eventID string, ctx fo
}
}
} else if mxid[0] == '@' {
mentions := ctx.ReturnData[formatterContextAllowedMentionsKey].(*discordgo.MessageAllowedMentions)
parsedID, ok := br.ParsePuppetMXID(id.UserID(mxid))
if ok {
mentions.Users = appendIfNotContains(mentions.Users, parsedID)
return fmt.Sprintf("<@%s>", parsedID)
}
mentionedUser := br.GetUserByMXID(id.UserID(mxid))
if mentionedUser != nil && mentionedUser.DiscordID != "" {
mentions.Users = appendIfNotContains(mentions.Users, mentionedUser.DiscordID)
return fmt.Sprintf("<@%s>", mentionedUser.DiscordID)
}
}
@ -195,12 +209,18 @@ var matrixHTMLParser = &format.HTMLParser{
},
}
func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent) string {
func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent) (string, *discordgo.MessageAllowedMentions) {
allowedMentions := &discordgo.MessageAllowedMentions{
Parse: []discordgo.AllowedMentionType{},
Users: []string{},
RepliedUser: true,
}
if content.Format == event.FormatHTML && len(content.FormattedBody) > 0 {
ctx := format.NewContext()
ctx.ReturnData[formatterContextPortalKey] = portal
return variationselector.FullyQualify(matrixHTMLParser.Parse(content.FormattedBody, ctx))
ctx.ReturnData[formatterContextAllowedMentionsKey] = allowedMentions
return variationselector.FullyQualify(matrixHTMLParser.Parse(content.FormattedBody, ctx)), allowedMentions
} else {
return variationselector.FullyQualify(escapeDiscordMarkdown(content.Body))
return variationselector.FullyQualify(escapeDiscordMarkdown(content.Body)), allowedMentions
}
}

4
go.mod
View file

@ -15,7 +15,7 @@ require (
github.com/stretchr/testify v1.8.1
github.com/yuin/goldmark v1.5.4
maunium.net/go/maulogger/v2 v2.4.1
maunium.net/go/mautrix v0.15.0-beta.1.0.20230226232632-00f40652f33d
maunium.net/go/mautrix v0.15.0-beta.1.0.20230227211640-c8b3566fb7ba
)
require (
@ -37,4 +37,4 @@ require (
maunium.net/go/mauflag v1.0.0 // indirect
)
replace github.com/bwmarrin/discordgo => github.com/beeper/discordgo v0.0.0-20230226184350-ef6bcfe94f07
replace github.com/bwmarrin/discordgo => github.com/beeper/discordgo v0.0.0-20230227224009-daaee0136f88

8
go.sum
View file

@ -1,6 +1,6 @@
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/beeper/discordgo v0.0.0-20230226184350-ef6bcfe94f07 h1:YajAt8iJkBn4aavUuftybeXUaeN4p0DPCE3a4wxE2Oc=
github.com/beeper/discordgo v0.0.0-20230226184350-ef6bcfe94f07/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/beeper/discordgo v0.0.0-20230227224009-daaee0136f88 h1:sZUZP+ClkQk1uC0KB6dYJ+v6Ygao3RaPKp/3leRjYik=
github.com/beeper/discordgo v0.0.0-20230227224009-daaee0136f88/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -82,5 +82,5 @@ maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.15.0-beta.1.0.20230226232632-00f40652f33d h1:16Q4co5TusYEovGLiHSkT6FY6fFn5tNNLCR3FvGCLFk=
maunium.net/go/mautrix v0.15.0-beta.1.0.20230226232632-00f40652f33d/go.mod h1:AE3TCX9q4W7fYfrL/1RsuOell9rTUBO27XUULuwArH4=
maunium.net/go/mautrix v0.15.0-beta.1.0.20230227211640-c8b3566fb7ba h1:OS+zjLTyeqxzMcgnbBbXlZSr0B2yfalCo2lNhC2wP5A=
maunium.net/go/mautrix v0.15.0-beta.1.0.20230227211640-c8b3566fb7ba/go.mod h1:AE3TCX9q4W7fYfrL/1RsuOell9rTUBO27XUULuwArH4=

147
portal.go
View file

@ -1,6 +1,7 @@
package main
import (
"bytes"
"errors"
"fmt"
"reflect"
@ -41,6 +42,8 @@ type portalMatrixMessage struct {
user *User
}
var relayClient, _ = discordgo.New("")
type Portal struct {
*database.Portal
@ -85,7 +88,7 @@ func (portal *Portal) MarkEncrypted() {
}
func (portal *Portal) ReceiveMatrixEvent(user bridge.User, evt *event.Event) {
if user.GetPermissionLevel() >= bridgeconfig.PermissionLevelUser /*|| portal.HasRelaybot()*/ {
if user.GetPermissionLevel() >= bridgeconfig.PermissionLevelUser || portal.RelayWebhookID != "" {
portal.matrixMessages <- portalMatrixMessage{user: user.(*User), evt: evt}
}
}
@ -971,6 +974,7 @@ var (
errUnknownRelationType = errors.New("unknown relation type")
errTargetNotFound = errors.New("target event not found")
errUnknownEmoji = errors.New("unknown emoji")
errCantStartThread = errors.New("can't create thread without being logged into Discord")
)
func errorToStatusReason(err error) (reason event.MessageStatusReason, status event.MessageStatus, isCertain, sendNotice bool, humanMessage string) {
@ -981,7 +985,8 @@ func errorToStatusReason(err error) (reason event.MessageStatusReason, status ev
errors.Is(err, errUnknownEmoji),
errors.Is(err, id.InvalidContentURI),
errors.Is(err, attachment.UnsupportedVersion),
errors.Is(err, attachment.UnsupportedAlgorithm):
errors.Is(err, attachment.UnsupportedAlgorithm),
errors.Is(err, errCantStartThread):
return event.MessageStatusUnsupported, event.MessageStatusFail, true, true, ""
case errors.Is(err, attachment.HashMismatch),
errors.Is(err, attachment.InvalidKey),
@ -1065,6 +1070,22 @@ func (portal *Portal) sendMessageMetrics(evt *event.Event, err error, part strin
}
}
func (portal *Portal) getRelayUserMeta(sender *User) (name, avatarURL string) {
member := portal.bridge.StateStore.GetMember(portal.MXID, sender.MXID)
name = member.Displayname
if name == "" {
name = sender.MXID.String()
}
mxc := member.AvatarURL.ParseOrIgnore()
if !mxc.IsEmpty() {
avatarURL = mautrix.BuildURL(
portal.bridge.PublicHSAddress,
"_matrix", "media", "v3", "download", mxc.Homeserver, mxc.FileID,
).String()
}
return
}
func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if portal.IsPrivateChat() && sender.DiscordID != portal.Key.Receiver {
go portal.sendMessageMetrics(evt, errUserNotReceiver, "Ignoring")
@ -1078,14 +1099,23 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
}
channelID := portal.Key.ChannelID
sess := sender.Session
var threadID string
if editMXID := content.GetRelatesTo().GetReplaceID(); editMXID != "" && content.NewContent != nil {
edits := portal.bridge.DB.Message.GetByMXID(portal.Key, editMXID)
if edits != nil {
discordContent := portal.parseMatrixHTML(content.NewContent)
// TODO save edit in message table
_, err := sender.Session.ChannelMessageEdit(edits.DiscordProtoChannelID(), edits.DiscordID, discordContent)
discordContent, allowedMentions := portal.parseMatrixHTML(content.NewContent)
var err error
if sess != nil {
// TODO save edit in message table
_, err = sess.ChannelMessageEdit(edits.DiscordProtoChannelID(), edits.DiscordID, discordContent)
} else {
_, err = relayClient.WebhookMessageEdit(portal.RelayWebhookID, portal.RelayWebhookSecret, edits.DiscordID, &discordgo.WebhookEdit{
Content: &discordContent,
AllowedMentions: allowedMentions,
})
}
go portal.sendMessageMetrics(evt, err, "Failed to edit")
} else {
go portal.sendMessageMetrics(evt, fmt.Errorf("%w %s", errUnknownEditTarget, editMXID), "Ignoring")
@ -1096,6 +1126,11 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if existingThread != nil {
threadID = existingThread.ID
} else {
if sess == nil {
// TODO start thread with bot?
go portal.sendMessageMetrics(evt, errCantStartThread, "Dropping")
return
}
var err error
threadID, err = portal.startThreadFromMatrix(sender, threadRoot)
if err != nil {
@ -1129,48 +1164,85 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
}
}
}
sendReq.Content = portal.parseMatrixHTML(content)
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content)
case event.MsgAudio, event.MsgFile, event.MsgImage, event.MsgVideo:
data, err := downloadMatrixAttachment(portal.MainIntent(), content)
if err != nil {
go portal.sendMessageMetrics(evt, err, "Error downloading media in")
return
}
att := &discordgo.MessageAttachment{
ID: "0",
Filename: content.Body,
Description: description,
}
sendReq.Attachments = []*discordgo.MessageAttachment{att}
filename := content.Body
if content.FileName != "" && content.FileName != content.Body {
att.Filename = content.FileName
sendReq.Content = portal.parseMatrixHTML(content)
filename = content.FileName
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content)
}
prep, err := sender.Session.ChannelAttachmentCreate(channelID, &discordgo.ReqPrepareAttachments{
Files: []*discordgo.FilePrepare{{
Size: len(data),
Name: att.Filename,
ID: sender.NextDiscordUploadID(),
}},
})
if err != nil {
go portal.sendMessageMetrics(evt, err, "Error preparing to reupload media in")
return
}
prepared := prep.Attachments[0]
att.UploadedFilename = prepared.UploadFilename
err = uploadDiscordAttachment(prepared.UploadURL, data)
if err != nil {
go portal.sendMessageMetrics(evt, err, "Error reuploading media in")
return
if sess != nil && sess.IsUser {
att := &discordgo.MessageAttachment{
ID: "0",
Filename: filename,
Description: description,
}
sendReq.Attachments = []*discordgo.MessageAttachment{att}
prep, err := sender.Session.ChannelAttachmentCreate(channelID, &discordgo.ReqPrepareAttachments{
Files: []*discordgo.FilePrepare{{
Size: len(data),
Name: att.Filename,
ID: sender.NextDiscordUploadID(),
}},
})
if err != nil {
go portal.sendMessageMetrics(evt, err, "Error preparing to reupload media in")
return
}
prepared := prep.Attachments[0]
att.UploadedFilename = prepared.UploadFilename
err = uploadDiscordAttachment(prepared.UploadURL, data)
if err != nil {
go portal.sendMessageMetrics(evt, err, "Error reuploading media in")
return
}
} else {
sendReq.Files = []*discordgo.File{{
Name: filename,
ContentType: content.Info.MimeType,
Reader: bytes.NewReader(data),
}}
}
default:
go portal.sendMessageMetrics(evt, fmt.Errorf("%w %q", errUnknownMsgType, content.MsgType), "Ignoring")
return
}
if sess != nil {
// AllowedMentions must not be set for real users, and it's also not that useful for personal bots.
// It's only important for relaying, where the webhook may have higher permissions than the user on Matrix.
sendReq.AllowedMentions = nil
} else if strings.Contains(sendReq.Content, "@everyone") || strings.Contains(sendReq.Content, "@here") {
powerLevels, err := portal.MainIntent().PowerLevels(portal.MXID)
if err != nil {
portal.log.Warnfln("Failed to get power levels in %s to check if %s can @everyone: %v", portal.MXID, sender.MXID, err)
} else if powerLevels.GetUserLevel(sender.MXID) >= powerLevels.Notifications.Room() {
sendReq.AllowedMentions.Parse = append(sendReq.AllowedMentions.Parse, discordgo.AllowedMentionTypeEveryone)
}
}
sendReq.Nonce = generateNonce()
msg, err := sender.Session.ChannelMessageSendComplex(channelID, &sendReq)
var msg *discordgo.Message
var err error
if sess != nil {
msg, err = sess.ChannelMessageSendComplex(channelID, &sendReq)
} else {
username, avatarURL := portal.getRelayUserMeta(sender)
msg, err = relayClient.WebhookThreadExecute(portal.RelayWebhookID, portal.RelayWebhookSecret, true, threadID, &discordgo.WebhookParams{
Content: sendReq.Content,
Username: username,
AvatarURL: avatarURL,
TTS: sendReq.TTS,
Files: sendReq.Files,
Components: sendReq.Components,
Embeds: sendReq.Embeds,
AllowedMentions: sendReq.AllowedMentions,
})
}
go portal.sendMessageMetrics(evt, err, "Error sending")
if msg != nil {
dbMsg := portal.bridge.DB.Message.New()
@ -1180,7 +1252,11 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
dbMsg.AttachmentID = msg.Attachments[0].ID
}
dbMsg.MXID = evt.ID
dbMsg.SenderID = sender.DiscordID
if sess != nil {
dbMsg.SenderID = sender.DiscordID
} else {
dbMsg.SenderID = portal.RelayWebhookID
}
dbMsg.Timestamp, _ = discordgo.SnowflakeTimestamp(msg.ID)
dbMsg.ThreadID = threadID
dbMsg.Insert()
@ -1333,6 +1409,9 @@ func (portal *Portal) handleMatrixReaction(sender *User, evt *event.Event) {
if portal.IsPrivateChat() && sender.DiscordID != portal.Key.Receiver {
go portal.sendMessageMetrics(evt, errUserNotReceiver, "Ignoring")
return
} else if !sender.IsLoggedIn() {
//go portal.sendMessageMetrics(evt, errReactionUserNotLoggedIn, "Ignoring")
return
}
reaction := evt.Content.AsReaction()