forked from mirror/mautrix-discord
Add initial support for relay mode with webhooks
This commit is contained in:
parent
6365db46cc
commit
17d4b79554
10 changed files with 254 additions and 53 deletions
86
commands.go
86
commands.go
|
@ -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",
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
3
database/upgrades/15-portal-relay-webhook.sql
Normal file
3
database/upgrades/15-portal-relay-webhook.sql
Normal 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;
|
|
@ -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
|
||||
|
||||
|
|
26
formatter.go
26
formatter.go
|
@ -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
4
go.mod
|
@ -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
8
go.sum
|
@ -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
147
portal.go
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue