docs(comments): 注释中文化 · plugins/channels/weixin_ilink(20 文件,纯替换)
- 只翻注释/文档字符串:剥掉行尾注释后,每条「-」都有逐字节相同的「+」对应行(8 组,代码零改动) - 保留协议/线上标识符原文:errcode、bot_token、context_token、getupdates、binded_redirect、PKCS#7、ECB 等 - 术语对齐既有译法:owner→主人、peer→对端、long poll→长轮询、backoff→退避、sentinel error→哨兵错误 - 已知既有欠债(未顺手修):login_reuse_test.go 在 HEAD 上就未 gofmt 干净;login_test.go 的 TestStartLogin_ScanStatusTransition 91 行 >80(HEAD 同为 91 行)
This commit is contained in:
1 parent
09fba77f87
commit
95f97f5df3
20 files changed
+488
-590
No files matched your search
@@ -1,49 +1,42 @@
|
||||
package weixin_ilink
|
||||
|
||||
// This file holds the adapter's session-rejection state machine (design/08 §4-§5).
|
||||
// 本文件承载适配器的「会话被拒绝」状态机(design/08 §4-§5)。
|
||||
//
|
||||
// iLink answers errcode=-14 when it rejects or invalidates the session credential.
|
||||
// The bot token is a LONG-LIVED credential — a token obtained by scanning a month
|
||||
// ago still works — so -14 is NOT a time-based expiry: it means iLink no longer
|
||||
// accepts this session (unbound, re-logged-in elsewhere, a protocol-level refusal).
|
||||
// It is worth recording that no log has ever actually shown -14; the earlier
|
||||
// reading of it as a time-based expiry was an inference, not an observation.
|
||||
// iLink 在拒绝或作废会话凭据时返回 errcode=-14。bot token 是长期凭据——一个月前
|
||||
// 扫码拿到的 token 依然可用——所以 -14 不是基于时间的过期:它表示 iLink 已不再接受
|
||||
// 这个会话(已解绑、在别处重新登录、或协议层拒绝)。值得记下的是,日志里从未真正
|
||||
// 出现过 -14;早先把 -14 读作「基于时间的过期」只是推断,并非观测。
|
||||
//
|
||||
// Either way it is not a transient failure the read loop can retry away: the same
|
||||
// credential keeps being refused until the owner scans a fresh QR code. The adapter
|
||||
// therefore keeps an EXPLICIT rejected flag instead of silently dropping the
|
||||
// connection, so:
|
||||
// 无论如何,它都不是读循环能靠重试消解的瞬时故障:同一份凭据会一直被拒,直到主人
|
||||
// 重新扫码。因此适配器保留一个**显式**的 rejected 标志,而不是静默丢弃连接,使得:
|
||||
//
|
||||
// - the read loop cannot exit and leave the adapter quietly mute while still
|
||||
// reporting "connected" (the "credential rejected → silently mute" defect);
|
||||
// - Connect/HealthCheck/send surface ErrSessionRejected — "re-scan required" —
|
||||
// rather than a fake OK or a generic "not connected";
|
||||
// - the admin panel can read RescanRequired() and show 需要重新扫码 (see
|
||||
// internal/services/gateway/admin.go).
|
||||
// - 读循环不会退出后让适配器悄悄变哑,却仍上报「connected」(即「凭据被拒 →
|
||||
// 静默变哑」缺陷);
|
||||
// - Connect/HealthCheck/发送路径上报 ErrSessionRejected——「需要重新扫码」——
|
||||
// 而不是假 OK 或笼统的「未连接」;
|
||||
// - 管理面板可以读 RescanRequired() 并显示 需要重新扫码(见
|
||||
// internal/services/gateway/admin.go)。
|
||||
//
|
||||
// Lifecycle (set once, cleared on a usable credential):
|
||||
// 生命周期(一次置位,拿到可用凭据后清除):
|
||||
//
|
||||
// SET markRejected iLink returned errcode=-14 on getupdates,
|
||||
// sendmessage or getuploadurl.
|
||||
// CLEAR clearRescanLocked a scan-code login delivered a new bot_token
|
||||
// (completeLogin), or
|
||||
// rejectedLocked saw a bot_token different from the one
|
||||
// iLink rejected — a config change / hot reload that
|
||||
// supplied a fresh token.
|
||||
// SET markRejected getupdates、sendmessage 或 getuploadurl
|
||||
// 返回 errcode=-14。
|
||||
// CLEAR clearRescanLocked 扫码登录交付了新的 bot_token(completeLogin),或
|
||||
// rejectedLocked 发现 bot_token 已不同于 iLink 拒绝的
|
||||
// 那一个——配置变更 / 热重载提供了新 token。
|
||||
|
||||
// Rescan reasons recorded on the rejected state. They are short, human-readable
|
||||
// and carry no credential: the bot token itself never leaves cfg.
|
||||
// 记录在「被拒绝」状态上的重扫原因。它们简短、人类可读,且不携带任何凭据:
|
||||
// bot token 本身从不离开 cfg。
|
||||
const (
|
||||
rescanReasonGetUpdates = "iLink getupdates 返回 errcode=-14(本次会话已被 iLink 拒绝)"
|
||||
rescanReasonSendMessage = "iLink sendmessage 返回 errcode=-14(本次会话已被 iLink 拒绝)"
|
||||
rescanReasonUploadURL = "iLink getuploadurl 返回 errcode=-14(本次会话已被 iLink 拒绝)"
|
||||
)
|
||||
|
||||
// markRejected records that iLink rejected the current session credential
|
||||
// (errcode=-14). It drops the connected flag so IsConnected can never keep claiming
|
||||
// a live session for a rejected credential, and logs the transition at ERROR level
|
||||
// exactly once (a failing poll loop or a repeated send must not flood the log). Only
|
||||
// the non-secret bot_id and the reason are logged.
|
||||
// markRejected 记录 iLink 拒绝了当前会话凭据(errcode=-14)。它清掉 connected
|
||||
// 标志,使 IsConnected 永不为一已被拒绝的凭据继续宣称会话存活;并以 ERROR 级别
|
||||
// 恰好记录一次该状态转移(失败的轮询循环或反复的发送不得刷屏日志)。只记录非敏感的
|
||||
// bot_id 与原因。
|
||||
func (a *Adapter) markRejected(reason string) {
|
||||
a.mu.Lock()
|
||||
first := !a.rejected
|
||||
@@ -60,17 +53,16 @@ func (a *Adapter) markRejected(reason string) {
|
||||
}
|
||||
}
|
||||
|
||||
// clearRescanLocked drops the rejected state. Callers must hold a.mu.
|
||||
// clearRescanLocked 清除被拒绝状态。调用方须持有 a.mu。
|
||||
func (a *Adapter) clearRescanLocked() {
|
||||
a.rejected = false
|
||||
a.rejectedToken = ""
|
||||
a.rescanDetail = ""
|
||||
}
|
||||
|
||||
// rejectedLocked reports whether iLink rejected the session. It first clears the
|
||||
// state when the configured bot_token is no longer the one iLink rejected, so a
|
||||
// fresh token (scan login, operator config change, hot reload) restores the session
|
||||
// without extra bookkeeping. Callers must hold a.mu.
|
||||
// rejectedLocked 报告 iLink 是否拒绝了该会话。当已配置的 bot_token 不再是 iLink
|
||||
// 拒绝的那一个时,它会先清除该状态,于是新 token(扫码登录、运维改配置、热重载)
|
||||
// 无需额外簿记即可恢复会话。调用方须持有 a.mu。
|
||||
func (a *Adapter) rejectedLocked() bool {
|
||||
if a.rejected && a.rejectedToken != a.cfg.BotToken {
|
||||
a.clearRescanLocked()
|
||||
@@ -78,8 +70,8 @@ func (a *Adapter) rejectedLocked() bool {
|
||||
return a.rejected
|
||||
}
|
||||
|
||||
// rejectedState reports the rejected flag together with the rescan reason in one
|
||||
// lock acquisition. reason is "" when the session has not been rejected.
|
||||
// rejectedState 在一次加锁内同时返回「被拒绝」标志与重扫原因。会话未被拒绝时
|
||||
// reason 为 ""。
|
||||
func (a *Adapter) rejectedState() (bool, string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@@ -89,11 +81,9 @@ func (a *Adapter) rejectedState() (bool, string) {
|
||||
return true, a.rescanDetail
|
||||
}
|
||||
|
||||
// RescanRequired implements the optional capability the admin panel reads by type
|
||||
// assertion (internal/services/gateway/admin.go): it reports whether this adapter
|
||||
// needs a new scan-code login instead of a plain reconnect, plus the
|
||||
// human-readable reason (never a secret). A healthy adapter reports false with an
|
||||
// empty reason.
|
||||
// RescanRequired 实现管理面板用类型断言读取的可选能力
|
||||
// (internal/services/gateway/admin.go):它报告该适配器是否需要重新扫码登录而非
|
||||
// 简单重连,并给出人类可读的原因(绝不含密钥)。健康的适配器返回 false 与空原因。
|
||||
func (a *Adapter) RescanRequired() (bool, string) {
|
||||
return a.rejectedState()
|
||||
}
|
||||
Reference in new issue
Block a user