Skip to content

Commit 00b4a95

Browse files
committed
fix
1 parent d15baab commit 00b4a95

File tree

2 files changed

+148
-27
lines changed

2 files changed

+148
-27
lines changed

matrix/client.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,40 @@ func (mc *MatrixClient) ListJoinedRooms(ctx context.Context, userID id.UserID) (
210210
return resp.JoinedRooms, nil
211211
}
212212

213+
// UploadMedia uploads media to the Matrix content repository and returns the MXC URI.
214+
// This follows the Matrix spec: https://spec.matrix.org/v1.2/client-server-api/#content-repository
215+
// The returned URI can be used in message events via the `url` field.
216+
func (mc *MatrixClient) UploadMedia(ctx context.Context, userID id.UserID, contentType string, data []byte) (id.ContentURIString, error) {
217+
mc.mu.Lock()
218+
defer mc.mu.Unlock()
219+
220+
logger.Debug().
221+
Str("user_id", string(userID)).
222+
Str("content_type", contentType).
223+
Int("size", len(data)).
224+
Msg("matrix: uploading media to content repository")
225+
226+
mc.cli.UserID = userID
227+
228+
resp, err := mc.cli.UploadBytes(ctx, data, contentType)
229+
if err != nil {
230+
logger.Error().
231+
Str("user_id", string(userID)).
232+
Str("content_type", contentType).
233+
Err(err).
234+
Msg("matrix: failed to upload media")
235+
return "", fmt.Errorf("upload media: %w", err)
236+
}
237+
238+
contentURI := resp.ContentURI.CUString()
239+
logger.Debug().
240+
Str("user_id", string(userID)).
241+
Str("content_uri", string(contentURI)).
242+
Msg("matrix: media uploaded successfully")
243+
244+
return contentURI, nil
245+
}
246+
213247
// SetPusher registers or updates a push gateway for the specified user.
214248
// This is used to configure Matrix to send push notifications to the proxy's /_matrix/push/v1/notify endpoint.
215249
func (mc *MatrixClient) SetPusher(ctx context.Context, userID id.UserID, req *models.SetPusherRequest) error {

service/messages.go

Lines changed: 114 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"io"
9+
"net/http"
810
"os"
911
"strconv"
1012
"strings"
@@ -216,51 +218,98 @@ func (s *MessageService) SendMessage(ctx context.Context, req *models.SendMessag
216218
Body: ftMsg.Body,
217219
}
218220
} else {
219-
// Convert to Matrix event content
221+
// Download attachments from Acrobits and upload to Matrix content repository
220222
msgType, rawContent, err := models.FileTransferToMatrixEventContent(ftMsg)
221223
if err != nil {
222224
logger.Warn().Err(err).Msg("failed to convert file transfer to Matrix format")
223225
return nil, fmt.Errorf("failed to convert file transfer: %w", err)
224226
}
225227

226-
// Build the Matrix event content
227-
content = &event.MessageEventContent{
228-
MsgType: event.MessageType(msgType),
229-
Body: rawContent["body"].(string),
228+
// Download and upload the main attachment
229+
acrobitsURL := ""
230+
if url, ok := rawContent["url"].(string); ok {
231+
acrobitsURL = url
230232
}
231233

232-
// Set the URL for media messages
233-
if url, ok := rawContent["url"].(string); ok {
234-
content.URL = id.ContentURIString(url)
234+
mimetype := ""
235+
if info, ok := rawContent["info"].(map[string]interface{}); ok {
236+
if mt, ok := info["mimetype"].(string); ok {
237+
mimetype = mt
238+
}
235239
}
236240

237-
// Set the filename if present
238-
if filename, ok := rawContent["filename"].(string); ok {
239-
content.FileName = filename
241+
matrixURL := ""
242+
uploadSuccess := true
243+
if acrobitsURL != "" {
244+
logger.Debug().Str("content_url", acrobitsURL).Msg("downloading attachment from Acrobits")
245+
fileData, err := s.downloadFile(ctx, acrobitsURL)
246+
if err != nil {
247+
logger.Warn().Err(err).Str("content_url", acrobitsURL).Msg("failed to download attachment, falling back to text message")
248+
// Fallback: send as text message only
249+
content = &event.MessageEventContent{
250+
MsgType: event.MsgText,
251+
Body: ftMsg.Body,
252+
}
253+
uploadSuccess = false
254+
} else {
255+
// Upload to Matrix content repository
256+
logger.Debug().Str("content_url", acrobitsURL).Int("size", len(fileData)).Msg("uploading attachment to Matrix content repository")
257+
uploadedURL, err := s.matrixClient.UploadMedia(ctx, senderMatrix, mimetype, fileData)
258+
if err != nil {
259+
logger.Warn().Err(err).Str("content_url", acrobitsURL).Msg("failed to upload attachment to Matrix, falling back to text message")
260+
// Fallback: send as text message only
261+
content = &event.MessageEventContent{
262+
MsgType: event.MsgText,
263+
Body: ftMsg.Body,
264+
}
265+
uploadSuccess = false
266+
} else {
267+
matrixURL = string(uploadedURL)
268+
logger.Debug().Str("matrix_url", matrixURL).Str("content_url", acrobitsURL).Msg("attachment uploaded to Matrix content repository")
269+
}
270+
}
240271
}
241272

242-
// Set info block if present
243-
if info, ok := rawContent["info"].(map[string]interface{}); ok {
244-
content.Info = &event.FileInfo{}
245-
if mimetype, ok := info["mimetype"].(string); ok {
246-
content.Info.MimeType = mimetype
273+
if uploadSuccess {
274+
// Build the Matrix event content
275+
content = &event.MessageEventContent{
276+
MsgType: event.MessageType(msgType),
277+
Body: rawContent["body"].(string),
247278
}
248-
if size, ok := info["size"].(int64); ok {
249-
content.Info.Size = int(size)
279+
280+
// Set the URL for media messages (use uploaded Matrix URL)
281+
if matrixURL != "" {
282+
content.URL = id.ContentURIString(matrixURL)
250283
}
251-
// Handle thumbnail info
252-
if thumbnailURL, ok := info["thumbnail_url"].(string); ok {
253-
content.Info.ThumbnailURL = id.ContentURIString(thumbnailURL)
284+
285+
// Set the filename if present
286+
if filename, ok := rawContent["filename"].(string); ok {
287+
content.FileName = filename
254288
}
255-
if thumbnailInfo, ok := info["thumbnail_info"].(map[string]interface{}); ok {
256-
content.Info.ThumbnailInfo = &event.FileInfo{}
257-
if tm, ok := thumbnailInfo["mimetype"].(string); ok {
258-
content.Info.ThumbnailInfo.MimeType = tm
289+
290+
// Set info block if present
291+
if info, ok := rawContent["info"].(map[string]interface{}); ok {
292+
content.Info = &event.FileInfo{}
293+
if mimetype, ok := info["mimetype"].(string); ok {
294+
content.Info.MimeType = mimetype
295+
}
296+
if size, ok := info["size"].(int64); ok {
297+
content.Info.Size = int(size)
298+
}
299+
// Handle thumbnail info
300+
if thumbnailURL, ok := info["thumbnail_url"].(string); ok {
301+
content.Info.ThumbnailURL = id.ContentURIString(thumbnailURL)
302+
}
303+
if thumbnailInfo, ok := info["thumbnail_info"].(map[string]interface{}); ok {
304+
content.Info.ThumbnailInfo = &event.FileInfo{}
305+
if tm, ok := thumbnailInfo["mimetype"].(string); ok {
306+
content.Info.ThumbnailInfo.MimeType = tm
307+
}
259308
}
260309
}
261-
}
262310

263-
logger.Debug().Str("msg_type", msgType).Str("url", string(content.URL)).Msg("converted file transfer to Matrix media message")
311+
logger.Debug().Str("msg_type", msgType).Str("matrix_url", matrixURL).Msg("converted file transfer to Matrix media message")
312+
}
264313
}
265314
} else {
266315
// Regular text message
@@ -887,3 +936,41 @@ func (s *MessageService) clearBatchToken(userID string) {
887936
defer s.mu.Unlock()
888937
delete(s.batchTokens, userID)
889938
}
939+
940+
// downloadFile downloads a file from the given URL with a context timeout.
941+
// Returns the file contents as bytes, or an error if the download fails.
942+
func (s *MessageService) downloadFile(ctx context.Context, url string) ([]byte, error) {
943+
// Create a new request with context
944+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
945+
if err != nil {
946+
return nil, fmt.Errorf("failed to create download request: %w", err)
947+
}
948+
949+
// Execute the request
950+
client := &http.Client{Timeout: 30 * time.Second}
951+
resp, err := client.Do(req)
952+
if err != nil {
953+
return nil, fmt.Errorf("failed to download file: %w", err)
954+
}
955+
defer resp.Body.Close()
956+
957+
// Check response status
958+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
959+
return nil, fmt.Errorf("download failed with status %d", resp.StatusCode)
960+
}
961+
962+
// Read the response body with a reasonable size limit (100MB)
963+
const maxSize = 100 * 1024 * 1024 // 100MB
964+
limitedReader := io.LimitReader(resp.Body, maxSize+1)
965+
data, err := io.ReadAll(limitedReader)
966+
if err != nil {
967+
return nil, fmt.Errorf("failed to read file: %w", err)
968+
}
969+
970+
if len(data) > maxSize {
971+
return nil, fmt.Errorf("file too large: %d bytes exceeds limit of %d bytes", len(data), maxSize)
972+
}
973+
974+
logger.Debug().Str("url", url).Int("size", len(data)).Int("status", resp.StatusCode).Msg("file downloaded successfully")
975+
return data, nil
976+
}

0 commit comments

Comments
 (0)