feat(channel): KOOK Webhook(WH) 接收模式——与 WS 配置切换(ReceiveMode)
- kook.Config 加 ReceiveMode("ws"默认/"webhook") + WebhookPath(默认/webhook/kook) + WebhookVerifyToken + WebhookEncryptKey(AES-256-CBC)
- 按权威文档(Reference/kaiheila webhook.md)实现: 无X-Signature HMAC, 真实校验=d.verify_token比对(不匹配403)+可选AES解密+zlib解压+challenge回显
- webhook.go: InboundHandler+WebhookPath, POST→zlib解压→AES解密→verify_token校验→challenge回显→归一UnifiedMessage→SetMessageHandler; WS模式Connect不拨网关仅标记connected
- channelbridge/webhookAdapter 接口改用 WebhookPath(); WebhookHandlers 收KOOK WH(ws模式跳过); 既有webhook通道补WebhookPath照常挂载
- namedAdapter 转发InboundHandler/WebhookPath; kook env兜底 +4个CHANNEL_KOOK_*键
- 测试: verify_token 403/challenge/zlib/AES往返/WebhookPath按模式/WH不拨gateway/WebhookHandlers收KOOK; 全仓build/vet/test绿
- 待真token端到端实测(需主人提供CHANNEL_KOOK_TOKEN+开发者后台切WebHook配回调URL)
This commit is contained in:
1 parent
1ebc82fffd
commit
617157262b
11 files changed
+981
-32
No files matched your search
@@ -113,3 +113,70 @@ func TestSendMessage_NotConnected(t *testing.T) {
|
||||
t.Fatalf("SendProactive err = %v, want ErrNotConnected", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedReceiveMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := (Config{}).normalizedReceiveMode(); got != modeWS {
|
||||
t.Fatalf("empty ReceiveMode = %q, want %q", got, modeWS)
|
||||
}
|
||||
if got := (Config{ReceiveMode: modeWebhook}).normalizedReceiveMode(); got != modeWebhook {
|
||||
t.Fatalf("webhook ReceiveMode = %q, want %q", got, modeWebhook)
|
||||
}
|
||||
if got := (Config{ReceiveMode: " ws "}).normalizedReceiveMode(); got != modeWS {
|
||||
t.Fatalf("trimmed ReceiveMode = %q, want %q", got, modeWS)
|
||||
}
|
||||
if (Config{}).IsWebhook() {
|
||||
t.Error("empty ReceiveMode should not be webhook")
|
||||
}
|
||||
if !(Config{ReceiveMode: modeWebhook}).IsWebhook() {
|
||||
t.Error("webhook ReceiveMode should be webhook")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigReceiveModeSnapshotAndRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := New(Config{
|
||||
BotToken: "abc123",
|
||||
ReceiveMode: modeWebhook,
|
||||
WebhookPath: "/cb",
|
||||
WebhookVerifyToken: "vt-1",
|
||||
WebhookEncryptKey: "ek-1",
|
||||
})
|
||||
got := a.Config()
|
||||
if got["receive_mode"] != modeWebhook {
|
||||
t.Errorf("receive_mode = %v, want %q", got["receive_mode"], modeWebhook)
|
||||
}
|
||||
if got["webhook_path"] != "/cb" {
|
||||
t.Errorf("webhook_path = %v, want /cb", got["webhook_path"])
|
||||
}
|
||||
if got["webhook_verify_token"] != "vt-1" || got["webhook_encrypt_key"] != "ek-1" {
|
||||
t.Errorf("webhook secrets = %v/%v, want vt-1/ek-1", got["webhook_verify_token"], got["webhook_encrypt_key"])
|
||||
}
|
||||
|
||||
var round Config
|
||||
if err := adapter.DecodeConfig(got, &round); err != nil {
|
||||
t.Fatalf("DecodeConfig: %v", err)
|
||||
}
|
||||
if round.ReceiveMode != modeWebhook || round.WebhookPath != "/cb" ||
|
||||
round.WebhookVerifyToken != "vt-1" || round.WebhookEncryptKey != "ek-1" {
|
||||
t.Fatalf("round trip mismatch: %+v", round)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDefaultReceiveModeSnapshot(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := New(Config{BotToken: "x"})
|
||||
if got := a.Config()["receive_mode"]; got != modeWS {
|
||||
t.Errorf("default receive_mode = %v, want %q", got, modeWS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequiresTokenBothModes(t *testing.T) {
|
||||
t.Parallel()
|
||||
if err := (Config{BotToken: "t", ReceiveMode: modeWebhook}).Validate(); err != nil {
|
||||
t.Fatalf("webhook mode with token Validate = %v, want nil", err)
|
||||
}
|
||||
if err := (Config{ReceiveMode: modeWebhook}).Validate(); !errors.Is(err, ErrMissingToken) {
|
||||
t.Fatalf("webhook mode without token Validate = %v, want ErrMissingToken", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user