- 四端=ws×client / ws×server(原有,补语义注释)+ **http×client**(新增 http_client.go:
调 OneBot HTTP API、请求-响应、retcode 判成败、token 只走 Authorization 头)+
**http×server**(新增 http_server.go:监听 listen_addr 收上报、fail-closed 鉴权、10MiB 体上限)
- 配置面:适配器新增 transport / api_base_url / report_path(json tag 沿用既有口径);
env 兜底 CHANNEL_QQ_OBV11_{TRANSPORT,API_BASE_URL,REPORT_PATH};**默认值=现状**(ws+server)
- 出站分流:session.go 发送路径按 transport 走 WS 写帧或 HTTP POST(不改它则 HTTP 端无法出站)
- 端口豁免边界:http+server 沿用 AGENTS §9 已登记的同一豁免(design/13 §0 原文即含 HTTP server),
且 listen_addr 为空时报错、**绝不开端口**(与 ws 侧同名校验成对)
- 关键取舍(已上报):OneBot 的 HTTP 是「动作 + 上报」两半,mode 决定**哪一半必需**、
另一半给齐字段就一并启用 ⇒ 单个适配器即可「收上报 + 回消息」,四端仍各自可独立选择与测试
- 复核:build/vet 绿 + qq_obv11/cmd 包测试绿 + `-race` 干净;10 条新用例(HTTP 两端全部用本地
httptest 假 OneBot,未连真实 QQ);变异 3 条(伪造 retcode 成功 / 去掉上报鉴权门 / 默认值翻 http)已还原
- 未接:get_group_list(后续)、无 token 时放行(与既有 ws_server 一致,如需 fail-closed 请裁决)
44 lines
1.4 KiB
Go
44 lines
1.4 KiB
Go
package qq_obv11
|
||
|
||
// 本文件实现四个端里的 **ws + client**(裁决 1.4):连接由 Cyrene 发起
|
||
// (Cyrene→OneBot),主动拨号 OneBot 实现的「正向 WS」端点(remote_url),建立后
|
||
// 收发都走这条长连接。access_token 同时以 Authorization: Bearer 头与
|
||
// ?access_token= 查询参数发送,兼容只认其中一种的 OneBot 实现。
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"net/url"
|
||
"strings"
|
||
)
|
||
|
||
// connectClient dials the configured OneBot WebSocket endpoint and installs the
|
||
// resulting connection as the active session.
|
||
func (a *Adapter) connectClient(ctx context.Context) error {
|
||
if a.cfg.RemoteURL == "" {
|
||
return errors.New("qq-obv11: client mode requires remoteURL")
|
||
}
|
||
target := appendQueryToken(a.cfg.RemoteURL, a.cfg.AccessToken)
|
||
conn, err := dialWS(ctx, a.cfg.DialContext, target, a.cfg.AccessToken)
|
||
if err != nil {
|
||
return fmt.Errorf("qq-obv11: dial onebot ws %s: %w", a.cfg.RemoteURL, err)
|
||
}
|
||
a.setConn(conn)
|
||
a.log.Info("connected to onebot ws (client mode)", "url", a.cfg.RemoteURL)
|
||
return nil
|
||
}
|
||
|
||
// appendQueryToken adds an access_token query parameter to a URL, preserving any
|
||
// existing query string.
|
||
func appendQueryToken(rawURL, token string) string {
|
||
if token == "" {
|
||
return rawURL
|
||
}
|
||
sep := "?"
|
||
if strings.Contains(rawURL, "?") {
|
||
sep = "&"
|
||
}
|
||
return rawURL + sep + "access_token=" + url.QueryEscape(token)
|
||
}
|