(client: TelegramClient, userId: number, input: string, incomingMsgId: number)
| 1042 | } catch {} |
| 1043 | if (!isStateCurrent(state)) return; |
| 1044 | await runFailActions(client, userId); |
| 1045 | }, { label: `pmcaptcha-timeout:${userId}` }).catch((error) => { |
| 1046 | log(LogLevel.ERROR, `Captcha timeout task failed: ${userId}`, error); |
| 1047 | }); |
| 1048 | }, timeout * 1000, { label: `pmcaptcha-timer:${userId}` }); |
| 1049 | } |
| 1050 | |
| 1051 | states.set(userId, state); |
| 1052 | log(LogLevel.INFO, `Captcha sent (${mode}, timeout=${timeout}s) → user ${userId}`); |
| 1053 | |
| 1054 | } catch (e) { |
| 1055 | log(LogLevel.ERROR, `Failed to send captcha to ${userId}`, e); |
| 1056 | } |
| 1057 | } |
| 1058 | |
| 1059 | // ─── 处理验证回复 ───────────────────────────────────────────────────────────── |
| 1060 | |
| 1061 | function levenshtein(a: string, b: string): number { |
| 1062 | const m = a.length, n = b.length; |
| 1063 | const dp: number[][] = Array.from({ length: m + 1 }, (_, i) => [i, ...new Array(n).fill(0)]); |
| 1064 | for (let j = 0; j <= n; j++) dp[0][j] = j; |
| 1065 | for (let i = 1; i <= m; i++) { |
| 1066 | for (let j = 1; j <= n; j++) { |
| 1067 | dp[i][j] = a[i - 1] === b[j - 1] |
| 1068 | ? dp[i - 1][j - 1] |
| 1069 | : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]); |
| 1070 | } |
| 1071 | } |
| 1072 | return dp[m][n]; |
| 1073 | } |
| 1074 | |
| 1075 | async function handleReply(client: TelegramClient, userId: number, input: string, incomingMsgId: number): Promise<void> { |
| 1076 | const state = states.get(userId); |
| 1077 | if (!state || !isStateCurrent(state)) return; |
| 1078 | |
| 1079 | if (!input.trim()) return; |
| 1080 | |
| 1081 | if (incomingMsgId) state.msgIds.push(incomingMsgId); |
| 1082 | |
| 1083 | if (!state.answer) { |
| 1084 | log(LogLevel.WARN, `handleReply: empty answer for user ${userId}`); |
| 1085 | removeCaptchaState(userId); |
| 1086 | await cleanupCaptchaMessages(client, userId, state); |
| 1087 | try { await client.sendMessage(peerTarget(userId), { message: "❌ 验证状态异常,请联系对方重置。", parseMode: "html" }); } catch {} |
| 1088 | return; |
| 1089 | } |
| 1090 | |
| 1091 | const isImgMode = state.mode === CaptchaMode.IMG_DIGIT || state.mode === CaptchaMode.IMG_MIXED; |
| 1092 | const inputNorm = input.trim().toUpperCase(); |
| 1093 | const answerNorm = state.answer.trim().toUpperCase(); |
| 1094 | |
| 1095 | const correct = isImgMode |
| 1096 | ? levenshtein(inputNorm, answerNorm) <= 1 |
| 1097 | : inputNorm === answerNorm; |
| 1098 | |
| 1099 | if (correct) { |
| 1100 | removeCaptchaState(userId); |
| 1101 | await cleanupCaptchaMessages(client, userId, state); |
no test coverage detected