Files
Cyrene_v2/plugins/channels/kook/message_test.go
T
Aska 764d592859
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(channel): KOOK 渠道适配器 + 框架兼容增强(wsutil/httpclient)
- plugins/channels/kook: config/adapter/gateway/websocket/send/message/errors — 实现 PlatformAdapter 13方法
  - WS 有状态网关: 取gateway地址→连WS→信令0/1/2/3/4/6 + 30s心跳/6s超时 + resume(拼接sn/session_id) + 指数退避重连
  - 收事件: 信令0 → UnifiedMessage(word注 channel_type/type/target_id/extra.author→字段)
  - 发消息: /message/create(频道) + /direct-message/create(私聊,修正正确端点,非user-chat) + Bearer鉴权
  - 注册 channels.go + CHANNELS_JSON 多账号 + channelbridge.configured 加 kook(bot_token非空才连)
- 框架增强: internal/channel/adapter/wsutil(连接管理/心跳/重连/resume骨架,复用gorilla,KOOK用上) + httpclient(Bearer+envelope归一,KOOK用)
- 全仓 go build/vet/test 绿, 零新依赖(复用餐gorilla), 未动telegram/qq/weixin/webhook契约
- §4.6 隔离/黑白名单/第三方边界未破坏

注: KOOK WS/WH 双模式待补(ReceiveMode), 实测需主人提供 CHANNEL_KOOK_TOKEN
2026-09-07 12:01:35 +08:00

163 lines
5.9 KiB
Go

package kook
import (
"encoding/json"
"testing"
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
)
func TestToUnified_GroupText(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
data := json.RawMessage(`{
"channel_type":"GROUP","type":1,"target_id":"chan-1","author_id":"u-1",
"content":"hello world","msg_id":"m-1","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":1,"guild_id":"g-1","channel_name":"test","mention":[],"author":{"id":"u-1","username":"alice","nickname":"Alice"}}
}`)
msg, ok := a.toUnifiedMessage(data, []byte(`{"s":0,"d":{}}`))
if !ok {
t.Fatal("expected a deliverable message")
}
if msg.Platform != "kook" {
t.Errorf("Platform = %q, want kook", msg.Platform)
}
if msg.ChannelType != adapter.ChannelGroup {
t.Errorf("ChannelType = %q, want group", msg.ChannelType)
}
if msg.ChannelID != "chan-1" {
t.Errorf("ChannelID = %q, want chan-1", msg.ChannelID)
}
if msg.GroupID != "g-1" {
t.Errorf("GroupID = %q, want g-1", msg.GroupID)
}
if msg.GroupName != "test" {
t.Errorf("GroupName = %q, want test", msg.GroupName)
}
if msg.SenderID != "u-1" || msg.SenderName != "Alice" {
t.Errorf("Sender = %q/%q, want u-1/Alice", msg.SenderID, msg.SenderName)
}
if msg.Sender.DisplayName != "Alice" || msg.Sender.UserID != "u-1" {
t.Errorf("Actor = %+v, want DisplayName Alice/UserID u-1", msg.Sender)
}
if msg.Content != "hello world" || msg.ContentType != adapter.ContentText {
t.Errorf("Content = %q/%q, want hello world/text", msg.Content, msg.ContentType)
}
if msg.MessageID != "m-1" || msg.Timestamp.UnixMilli() != 1700000000000 {
t.Errorf("MessageID/Timestamp = %q/%d", msg.MessageID, msg.Timestamp.UnixMilli())
}
}
func TestToUnified_PrivateText(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
data := json.RawMessage(`{
"channel_type":"PERSON","type":1,"target_id":"user-9","author_id":"user-9",
"content":"hi","msg_id":"m-2","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":1,"author":{"id":"user-9","username":"bob","nickname":"Bob"}}
}`)
msg, ok := a.toUnifiedMessage(data, nil)
if !ok {
t.Fatal("expected a deliverable message")
}
if msg.ChannelType != adapter.ChannelDirect {
t.Errorf("ChannelType = %q, want direct", msg.ChannelType)
}
if msg.ChannelID != "user-9" {
t.Errorf("ChannelID = %q, want user-9 (peer id)", msg.ChannelID)
}
if msg.GroupID != "" {
t.Errorf("GroupID should be empty for private, got %q", msg.GroupID)
}
if msg.SenderName != "Bob" {
t.Errorf("SenderName = %q, want Bob", msg.SenderName)
}
}
func TestToUnified_KMarkdown(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
data := json.RawMessage(`{
"channel_type":"GROUP","type":9,"target_id":"chan-2","author_id":"u-2",
"content":"*bold*","msg_id":"m-3","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":9,"guild_id":"g-2","channel_name":"c","author":{"id":"u-2","username":"u","nickname":"N"}}
}`)
msg, ok := a.toUnifiedMessage(data, nil)
if !ok {
t.Fatal("expected a deliverable message")
}
if msg.ContentType != adapter.ContentText || msg.Content != "*bold*" {
t.Errorf("kmarkdown should map to text, got %q/%q", msg.Content, msg.ContentType)
}
if msg.GroupID != "g-2" {
t.Errorf("GroupID = %q, want g-2", msg.GroupID)
}
}
func TestToUnified_Image(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
data := json.RawMessage(`{
"channel_type":"GROUP","type":2,"target_id":"chan-3","author_id":"u-3",
"content":"https://img/photo.jpg","msg_id":"m-4","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":2,"guild_id":"g-3","author":{"id":"u-3","username":"u","nickname":"N"}}
}`)
msg, ok := a.toUnifiedMessage(data, nil)
if !ok {
t.Fatal("expected a deliverable message")
}
if msg.ContentType != adapter.ContentImage {
t.Errorf("ContentType = %q, want image", msg.ContentType)
}
if len(msg.Media) != 1 || msg.Media[0].Kind != adapter.MediaImage || msg.Media[0].URL != "https://img/photo.jpg" {
t.Errorf("Media = %+v, want image URL https://img/photo.jpg", msg.Media)
}
}
func TestToUnified_VideoMapsToFile(t *testing.T) {
t.Parallel()
data := json.RawMessage(`{
"channel_type":"GROUP","type":3,"target_id":"c","author_id":"u",
"content":"https://img/a.mp4","msg_id":"m","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":3,"guild_id":"g","author":{"id":"u","username":"u","nickname":"N"}}
}`)
a := New(Config{BotToken: "x"})
msg, ok := a.toUnifiedMessage(data, nil)
if !ok {
t.Fatal("expected a deliverable message")
}
if len(msg.Media) != 1 || msg.Media[0].Kind != adapter.MediaVideo {
t.Fatalf("Media = %+v, want video", msg.Media)
}
}
func TestToUnified_SystemSkipped(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
// type 255 system message → not a chat deliverable.
if _, ok := a.toUnifiedMessage(json.RawMessage(`{"channel_type":"GROUP","type":255,"target_id":"c","author_id":"1","content":"{}","msg_id":"m","msg_timestamp":1,"extra":{}}`), nil); ok {
t.Error("system message should be skipped")
}
// author_id 1 = system → skipped even for a normal type.
if _, ok := a.toUnifiedMessage(json.RawMessage(`{"channel_type":"GROUP","type":1,"target_id":"c","author_id":"1","content":"x","msg_id":"m","msg_timestamp":1,"extra":{}}`), nil); ok {
t.Error("author_id 1 (system) should be skipped")
}
}
func TestMentions(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
data := json.RawMessage(`{
"channel_type":"GROUP","type":1,"target_id":"c","author_id":"u",
"content":"hello","msg_id":"m","msg_timestamp":1700000000000,"nonce":"",
"extra":{"type":1,"guild_id":"g","mention":["u-1","u-2"],"author":{"id":"u","username":"u","nickname":"N"}}
}`)
msg, ok := a.toUnifiedMessage(data, nil)
if !ok {
t.Fatal("expected a deliverable message")
}
if len(msg.Mentions) != 2 || msg.Mentions[0].UserID != "u-1" || msg.Mentions[1].UserID != "u-2" {
t.Errorf("Mentions = %+v, want u-1/u-2", msg.Mentions)
}
}