diff --git a/pkg/channels/qq/qq.go b/pkg/channels/qq/qq.go index cd66964dd..4ea71f6df 100644 --- a/pkg/channels/qq/qq.go +++ b/pkg/channels/qq/qq.go @@ -357,6 +357,7 @@ type qqMediaUpload struct { FileType uint64 `json:"file_type"` URL string `json:"url,omitempty"` FileData string `json:"file_data,omitempty"` + FileName string `json:"file_name,omitempty"` SrvSendMsg bool `json:"srv_send_msg,omitempty"` } @@ -393,6 +394,7 @@ func (c *QQChannel) buildMediaUpload(part bus.MediaPart) (*qqMediaUpload, error) if isHTTPURL(mediaRef) { payload.FileType = qqFileType(c.outboundMediaType(part, "")) payload.URL = mediaRef + payload.FileName = qqUploadFilename(part, mediaRef, payload.FileType) return payload, nil } @@ -415,9 +417,11 @@ func (c *QQChannel) buildMediaUpload(part bus.MediaPart) (*qqMediaUpload, error) if isHTTPURL(resolved) { payload.FileType = qqFileType(c.outboundMediaType(part, "")) payload.URL = resolved + payload.FileName = qqUploadFilename(part, resolved, payload.FileType) return payload, nil } payload.FileType = qqFileType(c.outboundMediaType(part, resolved)) + payload.FileName = qqUploadFilename(part, resolved, payload.FileType) if limitBytes := c.maxBase64FileSizeBytes(); limitBytes > 0 { info, statErr := os.Stat(resolved) @@ -444,6 +448,28 @@ func (c *QQChannel) buildMediaUpload(part bus.MediaPart) (*qqMediaUpload, error) return payload, nil } +func qqUploadFilename(part bus.MediaPart, resolved string, fileType uint64) string { + if fileType != qqFileType("file") { + return "" + } + if part.Filename != "" { + return part.Filename + } + if isHTTPURL(resolved) { + if parsed, err := url.Parse(resolved); err == nil { + if base := path.Base(parsed.Path); base != "" && base != "." && base != "/" { + return base + } + } + return "" + } + + if base := filepath.Base(resolved); base != "" && base != "." { + return base + } + return "" +} + func (c *QQChannel) outboundMediaType(part bus.MediaPart, localPath string) string { if part.Type != "audio" { return part.Type diff --git a/pkg/channels/qq/qq_test.go b/pkg/channels/qq/qq_test.go index 108965c00..7ed736827 100644 --- a/pkg/channels/qq/qq_test.go +++ b/pkg/channels/qq/qq_test.go @@ -444,6 +444,9 @@ func TestSendMedia_UsesRemoteURLUploadForC2C(t *testing.T) { if upload.body.FileType != 4 { t.Fatalf("upload file_type = %d, want 4", upload.body.FileType) } + if upload.body.FileName != "report.pdf" { + t.Fatalf("upload file_name = %q, want report.pdf", upload.body.FileName) + } if len(api.c2cMessages) != 1 { t.Fatalf("c2cMessages = %d, want 1", len(api.c2cMessages)) @@ -460,6 +463,59 @@ func TestSendMedia_UsesRemoteURLUploadForC2C(t *testing.T) { } } +func TestSendMedia_LocalFileUploadIncludesStoredFilename(t *testing.T) { + messageBus := bus.NewMessageBus() + store := media.NewFileMediaStore() + + localPath := writeTempFile(t, t.TempDir(), "report.pdf", []byte("fake-pdf")) + ref, err := store.Store(localPath, media.MediaMeta{ + Filename: "report.pdf", + ContentType: "application/pdf", + }, "qq:test") + if err != nil { + t.Fatalf("Store() error = %v", err) + } + + api := &fakeQQAPI{ + transportResp: mustJSON(t, dto.Message{FileInfo: []byte("local-file-info")}), + } + ch := &QQChannel{ + BaseChannel: channels.NewBaseChannel("qq", nil, messageBus, nil), + api: api, + dedup: make(map[string]time.Time), + done: make(chan struct{}), + ctx: context.Background(), + } + ch.SetRunning(true) + ch.SetMediaStore(store) + ch.chatType.Store("user-1", "direct") + + err = ch.SendMedia(context.Background(), bus.OutboundMediaMessage{ + ChatID: "user-1", + Parts: []bus.MediaPart{{ + Type: "file", + Ref: ref, + }}, + }) + if err != nil { + t.Fatalf("SendMedia() error = %v", err) + } + + if len(api.transportCalls) != 1 { + t.Fatalf("transportCalls = %d, want 1", len(api.transportCalls)) + } + upload := api.transportCalls[0] + if upload.body.FileType != 4 { + t.Fatalf("upload file_type = %d, want 4", upload.body.FileType) + } + if upload.body.FileName != "report.pdf" { + t.Fatalf("upload file_name = %q, want report.pdf", upload.body.FileName) + } + if upload.body.FileData == "" { + t.Fatal("upload file_data = empty, want base64 payload") + } +} + func TestSendMedia_ReturnsSendFailedWithoutMediaStore(t *testing.T) { messageBus := bus.NewMessageBus() ch := &QQChannel{