refactor(weixin-ilink): 措辞纠正 —「会话过期」→「会话被 iLink 拒绝」(零逻辑改动)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

事实(主人 2026-10-03 确认):iLink 的 bot token 是长期凭据(一个月前扫码的仍可用);errcode=-14 是 iLink 侧拒绝/作废该会话,不是时间性过期;且至今未观测到真实 -14(当日全部日志 0 次)。此前代码把这些写成「过期」,属沿用的旧命名,现纠正。
- 纯改名/文案:ErrSessionExpired→ErrSessionRejected、errSessionExpired→errcodeSessionRejected、expired/expiredToken→rejected/rejectedToken、markExpired/expiredLocked/expiredState→markRejected/rejectedLocked/rejectedState;409 错误码 session_expired→session_rejected
- 行为一字未改:显式标记 / Connect 不假 OK / 面板 needsRescan+lastError / 扫码或换新 token 即清除 全部保留
- 微信协议自身的「二维码过期」与 auth 的 JWT/refresh 过期一律未动(那些是真·时间性过期)
- Docs/api-client v0.39 补事实说明;顺手收尾 3 处残留注释(admin.go / channelbridge/inbound.go / readloop_test.go)
This commit is contained in:
AskaEth committed 2026-10-03 18:26:27 +08:00
1 parent cdc370dc82
commit a51aadeef4
14 files changed
+205 -192

No files matched your search

+16 -17
View File
@@ -73,13 +73,12 @@ func (a *Adapter) SendProactive(ctx context.Context, msg adapter.UnifiedMessage)
return a.sendToOne(ctx, peer, msg)
}
// sendGuard reports the error a send must surface before touching the network: an
// expired session (iLink already refused this token with errcode=-14) reads as
// ErrSessionExpired — the owner must re-scan — while any other down session stays
// ErrNotConnected.
// sendGuard reports the error a send must surface before touching the network: a
// session iLink already rejected (errcode=-14) reads as ErrSessionRejected — the
// owner must re-scan — while any other down session stays ErrNotConnected.
func (a *Adapter) sendGuard() error {
if expired, reason := a.expiredState(); expired {
return fmt.Errorf("weixin_ilink: send: %w: %s", ErrSessionExpired, reason)
if rejected, reason := a.rejectedState(); rejected {
return fmt.Errorf("weixin_ilink: send: %w: %s", ErrSessionRejected, reason)
}
if !a.IsConnected() {
return adapter.ErrNotConnected
@@ -165,12 +164,12 @@ func (a *Adapter) sendNative(ctx context.Context, peer, ctxToken string, items [
bytes.NewReader(body), &out); err != nil {
return fmt.Errorf("weixin_ilink: sendmessage: %w", err)
}
if out.Errcode == errSessionExpired {
// The send path sees the same dead token as the poll path: record the
// expired state so the adapter stops claiming a live session and the panel
// can ask for a re-scan.
a.markExpired(rescanReasonSendMessage)
return ErrSessionExpired
if out.Errcode == errcodeSessionRejected {
// The send path sees the same rejected credential as the poll path: record
// the rejected state so the adapter stops claiming a live session and the
// panel can ask for a re-scan.
a.markRejected(rescanReasonSendMessage)
return ErrSessionRejected
}
if out.Ret != 0 {
return fmt.Errorf("weixin_ilink: sendmessage ret=%d", out.Ret)
@@ -190,11 +189,11 @@ func (a *Adapter) getUploadURL(ctx context.Context) (string, error) {
bytes.NewReader(body), &raw); err != nil {
return "", fmt.Errorf("weixin_ilink: getuploadurl: %w", err)
}
if intOf(raw["errcode"]) == errSessionExpired {
// Media upload is part of the send path, so the same dead token is recorded
// here too rather than degrading into "no url returned".
a.markExpired(rescanReasonUploadURL)
return "", ErrSessionExpired
if intOf(raw["errcode"]) == errcodeSessionRejected {
// Media upload is part of the send path, so the same rejected credential is
// recorded here too rather than degrading into "no url returned".
a.markRejected(rescanReasonUploadURL)
return "", ErrSessionRejected
}
u := firstString(raw, "upload_url", "url", "uploadUrl", "cdn_url")
if u == "" {