handlematrix: bridge outgoing message attachments
This commit is contained in:
@@ -17,12 +17,16 @@
|
||||
package msgconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rs/zerolog"
|
||||
"go.mau.fi/util/variationselector"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/event"
|
||||
@@ -59,14 +63,43 @@ func parseAllowedLinkPreviews(raw map[string]any) []string {
|
||||
return allowedLinkPreviews
|
||||
}
|
||||
|
||||
func uploadDiscordAttachment(cli *http.Client, url string, data []byte) error {
|
||||
req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, value := range discordgo.DroidBaseHeaders {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Referer", "https://discord.com/")
|
||||
req.Header.Set("Sec-Fetch-Dest", "empty")
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
req.Header.Set("Sec-Fetch-Site", "cross-site")
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode > 300 {
|
||||
respData, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("unexpected status %d: %s", resp.StatusCode, respData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToDiscord converts a Matrix message into a discordgo.MessageSend that is appropriate
|
||||
// for bridging the message to Discord.
|
||||
func (mc *MessageConverter) ToDiscord(
|
||||
ctx context.Context,
|
||||
session *discordgo.Session,
|
||||
msg *bridgev2.MatrixMessage,
|
||||
) (discordgo.MessageSend, error) {
|
||||
) (*discordgo.MessageSend, error) {
|
||||
var req discordgo.MessageSend
|
||||
req.Nonce = generateMessageNonce()
|
||||
log := zerolog.Ctx(ctx)
|
||||
|
||||
if msg.ReplyTo != nil {
|
||||
req.Reference = &discordgo.MessageReference{
|
||||
@@ -75,18 +108,73 @@ func (mc *MessageConverter) ToDiscord(
|
||||
}
|
||||
}
|
||||
|
||||
switch msg.Content.MsgType {
|
||||
case event.MsgText, event.MsgEmote, event.MsgNotice:
|
||||
req.Content, req.AllowedMentions = mc.convertMatrixMessageContent(ctx, msg.Portal, msg.Content, parseAllowedLinkPreviews(msg.Event.Content.Raw))
|
||||
if msg.Content.MsgType == event.MsgEmote {
|
||||
portal := msg.Portal
|
||||
channelID := string(portal.ID)
|
||||
content := msg.Content
|
||||
|
||||
convertMatrix := func() {
|
||||
req.Content, req.AllowedMentions = mc.convertMatrixMessageContent(ctx, msg.Portal, content, parseAllowedLinkPreviews(msg.Event.Content.Raw))
|
||||
if content.MsgType == event.MsgEmote {
|
||||
req.Content = fmt.Sprintf("_%s_", req.Content)
|
||||
}
|
||||
// TODO: Handle attachments.
|
||||
}
|
||||
|
||||
switch content.MsgType {
|
||||
case event.MsgText, event.MsgEmote, event.MsgNotice:
|
||||
convertMatrix()
|
||||
case event.MsgAudio, event.MsgFile, event.MsgVideo:
|
||||
mediaData, err := mc.Bridge.Bot.DownloadMedia(ctx, content.URL, content.File)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to download Matrix attachment for bridging")
|
||||
return nil, bridgev2.ErrMediaDownloadFailed
|
||||
}
|
||||
|
||||
filename := content.Body
|
||||
if content.FileName != "" && content.FileName != content.Body {
|
||||
filename = content.FileName
|
||||
convertMatrix()
|
||||
}
|
||||
if msg.Event.Content.Raw["page.codeberg.everypizza.msc4193.spoiler"] == true {
|
||||
filename = "SPOILER_" + filename
|
||||
}
|
||||
|
||||
// TODO: Support attachments for relay/webhook. (A branch was removed here.)
|
||||
att := &discordgo.MessageAttachment{
|
||||
ID: "0",
|
||||
Filename: filename,
|
||||
}
|
||||
|
||||
upload_id := mc.NextDiscordUploadID()
|
||||
log.Debug().Str("upload_id", upload_id).Msg("Preparing attachment")
|
||||
prep, err := session.ChannelAttachmentCreate(channelID, &discordgo.ReqPrepareAttachments{
|
||||
Files: []*discordgo.FilePrepare{{
|
||||
Size: len(mediaData),
|
||||
Name: att.Filename,
|
||||
ID: mc.NextDiscordUploadID(),
|
||||
}},
|
||||
// TODO: Populate with guild ID. Support threads.
|
||||
}, discordgo.WithChannelReferer("", channelID))
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to create attachment in preparation for attachment reupload")
|
||||
return nil, bridgev2.ErrMediaReuploadFailed
|
||||
}
|
||||
|
||||
prepared := prep.Attachments[0]
|
||||
att.UploadedFilename = prepared.UploadFilename
|
||||
|
||||
err = uploadDiscordAttachment(session.Client, prepared.UploadURL, mediaData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to reupload Discord attachment after preparing")
|
||||
return nil, bridgev2.ErrMediaReuploadFailed
|
||||
}
|
||||
|
||||
req.Attachments = append(req.Attachments, att)
|
||||
}
|
||||
|
||||
// TODO: Handle (silent) replies and allowed mentions.
|
||||
|
||||
return req, nil
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func (mc *MessageConverter) convertMatrixMessageContent(ctx context.Context, portal *bridgev2.Portal, content *event.MessageEventContent, allowedLinkPreviews []string) (string, *discordgo.MessageAllowedMentions) {
|
||||
|
||||
Reference in New Issue
Block a user