feat(channel/qq): optimize message sending, seq generation, and message parsing

1. Define `kindType` as constants.
2. Set `seq` generation to global incremental, simplifying `seq` handling. (QQ platform validation rule is `chatID + replyMsgID + seq`, and `seq` does not affect display order.)
3. Increase API timeout from 5 seconds to 20 seconds to reduce send timeouts.
4. Prioritize sending messages using Markdown; fall back to plain text if Markdown sending fails. Also remove the `QQConfig.SendMarkdown` configuration option.
5. Restrict `InputNotify` to be sent only for direct messages.
6. Adjust message parsing to support emoji parsing and utilize the platform-provided ASR (Automatic Speech Recognition) content.
This commit is contained in:
aishannon 2026-04-03 00:31:40 +08:00
parent 0efe8bb444
commit 4ce07362b0
2 changed files with 11 additions and 12 deletions

View file

@ -643,7 +643,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
}
if !c.IsAllowedSender(sender) {
logger.Infof("qq", "Received group message from unauthorized sender", map[string]any{
logger.InfoCF("qq", "Received group message from unauthorized sender", map[string]any{
"sender": sender,
})
return nil
@ -1027,6 +1027,9 @@ func sanitizeURLs(text string) string {
}
func getVoiceInfo(event *dto.WSPayload) (string, string) {
if event == nil {
return "", ""
}
_raw, err := json.Marshal(event.Data)
if err != nil {
logger.ErrorCF("qq", "Failed to marshal event data", map[string]any{

View file

@ -3,13 +3,11 @@ package qq
import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"os"
"strings"
"sync/atomic"
"testing"
"time"
@ -207,7 +205,6 @@ func TestSendMedia_UploadsLocalFileAsBase64(t *testing.T) {
ch.SetMediaStore(store)
ch.chatType.Store("group-1", "group")
ch.lastMsgID.Store("group-1", "msg-1")
ch.msgSeqCounters.Store("group-1", new(atomic.Uint64))
_, err = ch.SendMedia(context.Background(), bus.OutboundMediaMessage{
ChatID: "group-1",
@ -234,9 +231,8 @@ func TestSendMedia_UploadsLocalFileAsBase64(t *testing.T) {
if upload.body.URL != "" {
t.Fatalf("upload URL = %q, want empty", upload.body.URL)
}
wantBase64 := base64.StdEncoding.EncodeToString(content)
if upload.body.FileData != wantBase64 {
t.Fatalf("upload file_data = %q, want %q", upload.body.FileData, wantBase64)
if !bytes.Equal(upload.body.FileData, content) {
t.Fatalf("upload file_data = %q, want %q", upload.body.FileData, content)
}
if upload.body.FileType != 1 {
t.Fatalf("upload file_type = %d, want 1", upload.body.FileType)
@ -415,7 +411,7 @@ func TestSendMedia_UsesRemoteURLUploadForC2C(t *testing.T) {
ctx: context.Background(),
}
ch.SetRunning(true)
ch.chatType.Store("user-1", "direct")
ch.chatType.Store("user-1", kindDirect)
_, err := ch.SendMedia(context.Background(), bus.OutboundMediaMessage{
ChatID: "user-1",
@ -438,7 +434,7 @@ func TestSendMedia_UsesRemoteURLUploadForC2C(t *testing.T) {
if upload.body.URL != "https://cdn.example.com/report.pdf" {
t.Fatalf("upload URL = %q", upload.body.URL)
}
if upload.body.FileData != "" {
if len(upload.body.FileData) > 0 {
t.Fatalf("upload file_data = %q, want empty", upload.body.FileData)
}
if upload.body.FileType != 4 {
@ -511,7 +507,7 @@ func TestSendMedia_LocalFileUploadIncludesStoredFilename(t *testing.T) {
if upload.body.FileName != "report.pdf" {
t.Fatalf("upload file_name = %q, want report.pdf", upload.body.FileName)
}
if upload.body.FileData == "" {
if len(upload.body.FileData) == 0 {
t.Fatal("upload file_data = empty, want base64 payload")
}
}
@ -606,7 +602,7 @@ type fakeQQAPI struct {
type fakeTransportCall struct {
method string
url string
body qqMediaUpload
body RichMediaMessage
}
func (f *fakeQQAPI) WS(
@ -638,7 +634,7 @@ func (f *fakeQQAPI) PostC2CMessage(
}
func (f *fakeQQAPI) Transport(_ context.Context, method, url string, body any) ([]byte, error) {
upload, ok := body.(*qqMediaUpload)
upload, ok := body.(*RichMediaMessage)
if !ok {
return nil, errors.New("unexpected transport body type")
}