重建文字类验证消息文本(含最新的 timeout/tries/failActions)
(state: CaptchaState)
| 724 | function clearCaptchaTimer(state: CaptchaState): void { |
| 725 | if (state.timer) { |
| 726 | clearTimeout(state.timer); |
| 727 | state.timer = null; |
| 728 | } |
| 729 | } |
| 730 | |
| 731 | function removeCaptchaState(userId: number): CaptchaState | undefined { |
| 732 | const state = states.get(userId); |
| 733 | if (!state) return undefined; |
| 734 | clearCaptchaTimer(state); |
| 735 | states.delete(userId); |
| 736 | return state; |
| 737 | } |
| 738 | |
| 739 | function drainCaptchaStates(): void { |
| 740 | for (const state of states.values()) { |
| 741 | clearCaptchaTimer(state); |
| 742 | } |
| 743 | states.clear(); |
| 744 | } |
| 745 | |
| 746 | async function cleanupCaptchaMessages(client: TelegramClient, userId: number, state: CaptchaState) { |
| 747 | if (!isStateCurrent(state)) return; |
| 748 | for (const id of state.msgIds) { |
| 749 | if (!isStateCurrent(state)) return; |
| 750 | try { await client.deleteMessages(peerTarget(userId), [id], { revoke: false }); } catch {} |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | // ─── 消息重建(用于实时刷新)───────────────────────────────────────────────── |
| 755 | |
| 756 | /** 重建文字类验证消息文本(含最新的 timeout/tries/failActions) */ |
| 757 | function rebuildCaptchaText(state: CaptchaState): string { |
| 758 | const timeout = cfg.timeout(); |
| 759 | const maxTries = cfg.maxTries(); |
| 760 | const actions = cfg.failActions(); |
| 761 | const custom = cfg.prompt(); |
| 762 | const actionDesc = actions.length |
| 763 | ? actions.map(a => FAIL_ACTION_LABEL[a] ?? a).join("、") |
| 764 | : "仅归档并静音"; |
| 765 | const remaining = maxTries > 0 ? maxTries - state.tries : 0; |
| 766 | |
| 767 | function buildFooter(): string { |
| 768 | const lines: string[] = []; |
no test coverage detected