feat(channels): QQ 账号 get_status 主动探活(复用既有 action 通道 / 状态变化才记日志 / AccountStatus 与 WS 连通分开)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 12:10:34 +08:00
1 parent f44114301e
commit a79fe9684e
6 files changed
+577 -18

No files matched your search

+23 -10
View File
@@ -96,7 +96,8 @@ func (c *obHTTPClient) Close() {
// send 把一条已构造好的 action 请求(payload 已含 action/params/echo)POST 给 OneBot
// 并等待结果。它替代 WS 端的「写一帧就返回」:HTTP 有明确响应,成败以 retcode 为准。
func (c *obHTTPClient) send(ctx context.Context, payload []byte) error {
return c.post(ctx, payload)
_, err := c.post(ctx, payload)
return err
}
// probe 用最轻量的标准 action `get_login_info` 探活 OneBot 实现,供 HealthCheck 使用。
@@ -106,20 +107,32 @@ func (c *obHTTPClient) probe(ctx context.Context) error {
if err != nil {
return err
}
_, err = c.post(ctx, payload)
return err
}
// call 编码一条通用 action 请求并取回**响应信封**(含 data)。get_status 之类需要读
// 返回值的主动 action 走它——它复用与 send_msg 完全相同的出站客户端与校验路径,绝不
// 新建第二条请求通道。
func (c *obHTTPClient) call(ctx context.Context, action string, params any) (*obAPIResponse, error) {
payload, err := buildActionReq(action, params, "")
if err != nil {
return nil, err
}
return c.post(ctx, payload)
}
// post 发送一条 action 请求并解析响应。任何失败都返回带上下文(action、retcode)的
// wrapped error;错误信息与日志都不含 token。
func (c *obHTTPClient) post(ctx context.Context, payload []byte) error {
func (c *obHTTPClient) post(ctx context.Context, payload []byte) (*obAPIResponse, error) {
if c == nil {
return errors.New("qq-obv11: http api client not configured")
return nil, errors.New("qq-obv11: http api client not configured")
}
action := actionOf(payload)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/"+action, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("qq-obv11: build http %s request: %w", action, err)
return nil, fmt.Errorf("qq-obv11: build http %s request: %w", action, err)
}
req.Header.Set("Content-Type", "application/json")
if c.token != "" {
@@ -129,26 +142,26 @@ func (c *obHTTPClient) post(ctx context.Context, payload []byte) error {
resp, err := c.http.Do(req)
if err != nil {
return fmt.Errorf("qq-obv11: http %s request: %w", action, err)
return nil, fmt.Errorf("qq-obv11: http %s request: %w", action, err)
}
defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(io.LimitReader(resp.Body, maxHTTPRespBytes))
if err != nil {
return fmt.Errorf("qq-obv11: read http %s response: %w", action, err)
return nil, fmt.Errorf("qq-obv11: read http %s response: %w", action, err)
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("qq-obv11: http %s status %d", action, resp.StatusCode)
return nil, fmt.Errorf("qq-obv11: http %s status %d", action, resp.StatusCode)
}
var env obAPIResponse
if err := json.Unmarshal(body, &env); err != nil {
return fmt.Errorf("qq-obv11: decode http %s response: %w", action, err)
return nil, fmt.Errorf("qq-obv11: decode http %s response: %w", action, err)
}
if env.RetCode != 0 || (env.Status != "" && !strings.EqualFold(env.Status, "ok")) {
return fmt.Errorf("qq-obv11: http %s failed: retcode=%d status=%q msg=%q",
return nil, fmt.Errorf("qq-obv11: http %s failed: retcode=%d status=%q msg=%q",
action, env.RetCode, env.Status, env.Msg)
}
return nil
return &env, nil
}
// actionOf 从已编码的 action 请求里取出 action 名,仅用于日志与错误上下文。