Files
Cyrene_v2/plugins/channels/qq_obv11/ws_client.go
T
AskaEth 0f9251b983 fix(channel): 渠道适配器与出站工具的上游响应体/带凭据 URL 不再进日志与错误(M41)
接 M38 的 18 条真泄漏里「渠道适配器 + 出站工具」这一族(#10 #11 #12 #13 #15 #16)。
只动日志与 error 文字:发送/接收、解析口径、重试次数一个都没改。

- httpclient.do:非 2xx 不再把 512B 响应体原文拼进 error,改 trace.UpstreamError
  (状态/体积/sha256 前缀)+ 信封 code/message(RedactText + 200 rune);error 里的
  target 改 trace.HostOf,并剥掉 *url.Error 外壳(它带整条地址)
- telegram doJSON:4xx 只解 error_code/description(清洗 + 截断),体其余字段不拼;
  传输错误只留来源 —— Bot API 把 token 写在地址路径里
- weixin_ilink doJSON:4096B 体 ⇒ trace.UpstreamError,rawURL ⇒ mediafetch.HostOf;
  uploadOnce 剥掉预签名上传地址(encrypted_query_param),重试日志补来源主机
- qq_obv11 connectClient 成功 INFO:改走同包已有的 maskAccessTokenQuery
  (OneBot 惯例把 ?access_token= 写进 remote_url)
- plugins/tools/io http 拦截日志:改记 trace.HostOf + 原因码,不记原 URL、也不记错误
  原文(解析失败时它包着 *url.Error)

每条都有能变异打红的用例:QQ/iLink 预签名地址的哨兵、三处 4xx 体哨兵、
user:pass@ 与 query 凭据;八处变异逐条验证变红。
2026-10-26 13:47:56 +08:00

84 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package qq_obv11
// 本文件实现四个端里的 **ws + client**(裁决 1.4):连接由 Cyrene 发起
// (Cyrene→OneBot),主动拨号 OneBot 实现的「正向 WS」端点(remote_url),建立后
// 收发都走这条长连接。access_token 同时以 Authorization: Bearer 头与
// ?access_token= 查询参数发送,兼容只认其中一种的 OneBot 实现。
//
// 首连失败**不致命**:对面(NapCat 等)可能比 Cyrene 后启动,失败只记 WARN,重试交给
// readLoop 的同一条重连路径(指数退避 + 抖动 + 上限,见 reconnect.go)。
import (
"context"
"errors"
"fmt"
"net/url"
"strings"
)
// errClientConfig 是 client 模式配置缺失(remote_url 为空)的错误哨兵:重试治不好
// 配置错,必须让 Connect 直接失败(渠道桥接器的凭据门同样要求 remoteURL 非空,见
// adapter.go 的 configRemoteURL)。
var errClientConfig = errors.New("qq-obv11: client mode requires remoteURL")
// connectFirstClient 做一次 ws+client 首连(Connect 同步调用)。
//
// 为什么失败不致命:OneBot 实现(NapCat 等)可能比 Cyrene 后启动,或者正在重启——
// 若把首连失败当作渠道起不来,部署顺序就成了硬约束。因此可重试的失败只记一条 WARN
// 就返回 nil,真正的重试由 readLoop 复用同一条重连路径(指数退避 + 抖动 + 上限)
// 在后台进行。只有配置缺失(errClientConfig)才致命:重试治不好配置错,渠道桥接器
// 的凭据门也要求 remote_url 非空。
func (a *Adapter) connectFirstClient(ctx context.Context) error {
// 新一轮启动:退避从初值起步,不继承上一轮会话残留的失败计数。
a.reconnect.reset()
err := a.connectClient(ctx)
switch {
case err == nil:
return nil
case errors.Is(err, errClientConfig):
return err
default:
a.noteConnectFailure(err)
return nil
}
}
// 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 errClientConfig
}
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)
}
// 拨号期间可能已被 Disconnect / ctx 取消:此时绝不把连接挂成「已连接」,
// 否则状态撒谎,而且这条连接没有读循环会去消费它。
if cerr := ctx.Err(); cerr != nil {
_ = conn.Close()
return fmt.Errorf("qq-obv11: dial onebot ws %s: %w", a.cfg.RemoteURL, cerr)
}
a.setConn(conn)
// remote_url 按 OneBot 惯例可以直接写着 `?access_token=…`,原样 INFO 就是把凭据
// 写进日志。这里复用同包已有的第二层脱敏(connect_outcome.go 的
// maskAccessTokenQuery)——不另写一套,也不用 sanitizeConnectReason 的 80 rune
// 上限,否则地址的路径会被切掉、失去「连到了哪」的诊断价值。
a.log.Info("connected to onebot ws (client mode)", "url", maskAccessTokenQuery(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)
}