msgconv: clean up reuploading attachments to Matrix
This commit is contained in:
@@ -1,104 +0,0 @@
|
||||
// mautrix-discord - A Matrix-Discord puppeting bridge.
|
||||
// Copyright (C) 2026 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package connector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
|
||||
"go.mau.fi/mautrix-discord/pkg/attachment"
|
||||
)
|
||||
|
||||
func downloadDiscordAttachment(cli *http.Client, url string, maxSize int64) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, value := range discordgo.DroidDownloadHeaders {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode > 300 {
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("unexpected status %d downloading %s: %s", resp.StatusCode, url, data)
|
||||
}
|
||||
if resp.Header.Get("Content-Length") != "" {
|
||||
length, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse content length: %w", err)
|
||||
} else if length > maxSize {
|
||||
return nil, fmt.Errorf("attachment too large (%d > %d)", length, maxSize)
|
||||
}
|
||||
return io.ReadAll(resp.Body)
|
||||
} else {
|
||||
var mbe *http.MaxBytesError
|
||||
data, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxSize))
|
||||
if err != nil && errors.As(err, &mbe) {
|
||||
return nil, fmt.Errorf("attachment too large (over %d)", maxSize)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscordConnector) ReuploadMedia(ctx context.Context, intent bridgev2.MatrixAPI, portal *bridgev2.Portal, upload attachment.AttachmentReupload) (*attachment.ReuploadedAttachment, error) {
|
||||
// TODO(skip): Do we need to check if we've already downloaded this media before?
|
||||
// TODO(skip): Read a maximum size from the config.
|
||||
data, err := downloadDiscordAttachment(http.DefaultClient, upload.DownloadingURL, 1_024*1_024*50)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't download attachment for reupload: %w", err)
|
||||
}
|
||||
|
||||
if upload.FileName == "" {
|
||||
url, err := url.Parse(upload.DownloadingURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse URL to download for media reupload: %w", err)
|
||||
}
|
||||
fileName := path.Base(url.Path)
|
||||
upload.FileName = fileName
|
||||
}
|
||||
|
||||
if upload.MimeType == "" {
|
||||
mime := http.DetectContentType(data)
|
||||
upload.MimeType = mime
|
||||
}
|
||||
|
||||
mxc, file, err := intent.UploadMedia(ctx, portal.MXID, data, upload.FileName, upload.MimeType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &attachment.ReuploadedAttachment{
|
||||
AttachmentReupload: upload,
|
||||
DownloadedSize: len(data),
|
||||
MXC: mxc,
|
||||
EncryptedFile: file,
|
||||
}, nil
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (dc *DiscordClient) FetchMessages(ctx context.Context, fetchParams bridgev2
|
||||
|
||||
converted = append(converted, &bridgev2.BackfillMessage{
|
||||
ID: networkid.MessageID(msg.ID),
|
||||
ConvertedMessage: dc.connector.MsgConv.ToMatrix(ctx, fetchParams.Portal, intent, dc.UserLogin, msg),
|
||||
ConvertedMessage: dc.connector.MsgConv.ToMatrix(ctx, fetchParams.Portal, intent, dc.UserLogin, dc.Session, msg),
|
||||
Sender: sender,
|
||||
Timestamp: ts,
|
||||
StreamOrder: streamOrder,
|
||||
|
||||
@@ -30,14 +30,21 @@ type DiscordConnector struct {
|
||||
MsgConv *msgconv.MessageConverter
|
||||
}
|
||||
|
||||
var _ bridgev2.NetworkConnector = (*DiscordConnector)(nil)
|
||||
var (
|
||||
_ bridgev2.NetworkConnector = (*DiscordConnector)(nil)
|
||||
_ bridgev2.MaxFileSizeingNetwork = (*DiscordConnector)(nil)
|
||||
)
|
||||
|
||||
func (d *DiscordConnector) Init(bridge *bridgev2.Bridge) {
|
||||
d.Bridge = bridge
|
||||
d.MsgConv = msgconv.NewMessageConverter(bridge, d.ReuploadMedia)
|
||||
d.MsgConv = msgconv.NewMessageConverter(bridge)
|
||||
d.setUpProvisioningAPIs()
|
||||
}
|
||||
|
||||
func (d *DiscordConnector) SetMaxFileSize(maxSize int64) {
|
||||
d.MsgConv.MaxFileSize = maxSize
|
||||
}
|
||||
|
||||
func (d *DiscordConnector) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ var (
|
||||
)
|
||||
|
||||
func (m *DiscordMessage) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
|
||||
return m.Client.connector.MsgConv.ToMatrix(ctx, portal, intent, m.Client.UserLogin, m.Data), nil
|
||||
return m.Client.connector.MsgConv.ToMatrix(ctx, portal, intent, m.Client.UserLogin, m.Client.Session, m.Data), nil
|
||||
}
|
||||
|
||||
func (m *DiscordMessage) GetID() networkid.MessageID {
|
||||
|
||||
Reference in New Issue
Block a user