fix(telegram): surface verification codes in mail previews - #1034
fix(telegram): surface verification codes in mail previews#1034liut-coder wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthrough此 PR 为 Telegram 邮件渲染添加 HTML→纯文本回退、可搜索邮件文本构建和启发式 4–8 位验证码提取,并在 parseMail 中优先显示提取出的验证码;包含单元测试和变更日志及 Pages 项目名更新。 ChangesTelegram验证码提取功能
Sequence Diagram(s)(该变更为库内部工具与 parseMail 集成,交互较局部且涉及不到 3 个独立外部组件;此处省略序列图。) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 分钟 Suggested labels
Suggested reviewers
总体概览这个PR为Telegram的邮件内容处理添加了智能验证码提取功能。新增验证码提取工具集包括HTML实体解码、纯文本转换和基于多重启发式的验证码评分选择。 变更详情Telegram验证码提取功能
可能相关的PR
建议标签
建议审阅者
代码审查工作量估计🎯 4 (Complex) | ⏱️ ~45 分钟
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
781fdca to
5a005ee
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
pages/wrangler.toml (1)
1-1: ⚡ Quick win同步
pages/目录下的配置文件项目名仓库中
pages/wrangler.toml的项目名为cloudflare-temp-email-pages,但pages/package.json中仍为temp-email-pages。尽管生产部署时 CI/CD 通过 GitHub secrets 动态注入wrangler.toml(减低部署风险),但仓库内的配置不一致会影响本地开发体验(如wrangler pages dev等本地命令)。建议将两个配置文件的项目名统一,或在文档中说明两者为何不同。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pages/wrangler.toml` at line 1, 仓库中 pages/wrangler.toml 的 name 字段值 "cloudflare-temp-email-pages" 与 pages/package.json 的 name ("temp-email-pages") 不一致;请在 pages/wrangler.toml 的 name 字段或 pages/package.json 的 name 字段中统一为同一个项目名(例如将 wrangler.toml 的 name 改为 "temp-email-pages" 或反向统一到 "cloudflare-temp-email-pages"),并在 README 中补充说明若 CI/CD 使用 secrets 在部署时覆盖 wrangler.toml 的场景,以免影响本地命令(如 wrangler pages dev)。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@worker/src/telegram_api/telegram.ts`:
- Around line 400-402: The content concatenation hardcodes the Chinese prefix
"验证码:" which breaks i18n; update the logic that builds content (the const named
content that uses verificationCode and parsedText) to use a new LocaleMessages
key (e.g., TgVerificationCodeLabel) from msgs instead of the hardcoded string,
then add TgVerificationCodeLabel to the i18n entries for both languages (Chinese
"验证码" and English "Verification code") so content becomes
`${msgs.TgVerificationCodeLabel}:${verificationCode}` (keeping the parsedText
fallback behavior).
In `@worker/src/telegram_api/verification_code.ts`:
- Around line 17-30: The decodeHtmlEntities function can call
String.fromCodePoint with out-of-range values (e.g., parseInt("999999999")),
causing a RangeError; update its numeric-entity handling (the branches that
parse hex and decimal using parseInt) to validate the parsed charCode is an
integer within [0, 0x10FFFF] before calling String.fromCodePoint and otherwise
fall back to returning the original entity; ensure both the "#x" and "#"
branches perform this check so decodeHtmlEntities no longer throws for oversized
code points.
---
Nitpick comments:
In `@pages/wrangler.toml`:
- Line 1: 仓库中 pages/wrangler.toml 的 name 字段值 "cloudflare-temp-email-pages" 与
pages/package.json 的 name ("temp-email-pages") 不一致;请在 pages/wrangler.toml 的 name
字段或 pages/package.json 的 name 字段中统一为同一个项目名(例如将 wrangler.toml 的 name 改为
"temp-email-pages" 或反向统一到 "cloudflare-temp-email-pages"),并在 README 中补充说明若 CI/CD
使用 secrets 在部署时覆盖 wrangler.toml 的场景,以免影响本地命令(如 wrangler pages dev)。
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ee0a05e3-4c06-4020-b691-8f9f61843113
📒 Files selected for processing (6)
CHANGELOG.mdCHANGELOG_EN.mdpages/wrangler.tomlworker/src/telegram_api/telegram.tsworker/src/telegram_api/verification_code.test.tsworker/src/telegram_api/verification_code.ts
| const content = verificationCode | ||
| ? `验证码:${verificationCode}` + (parsedText ? `\n\n${parsedText}` : "") | ||
| : (parsedText || msgs.TgParseFailedViewInAppMsg); |
There was a problem hiding this comment.
验证码标签硬编码中文,破坏多语言一致性。
文件中所有面向用户的文案(TgMsgTooLongMsg / TgParseFailedViewInAppMsg / TgNoSenderMsg 等)都走 msgs: LocaleMessages,且 Bot 已通过 /lang 支持中英文切换。此处直接拼接 验证码:,英文用户也会看到中文前缀,与现有 i18n 约定不一致。建议在 LocaleMessages 中新增对应 key(如 TgVerificationCodeLabel),中文 验证码、英文 Verification code。
♻️ 建议改为走 i18n
const content = verificationCode
- ? `验证码:${verificationCode}` + (parsedText ? `\n\n${parsedText}` : "")
+ ? `${msgs.TgVerificationCodeLabel}: ${verificationCode}` + (parsedText ? `\n\n${parsedText}` : "")
: (parsedText || msgs.TgParseFailedViewInAppMsg);并在 worker/src/i18n 中为中英文各加一条 TgVerificationCodeLabel(如 验证码 / Verification code)。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@worker/src/telegram_api/telegram.ts` around lines 400 - 402, The content
concatenation hardcodes the Chinese prefix "验证码:" which breaks i18n; update the
logic that builds content (the const named content that uses verificationCode
and parsedText) to use a new LocaleMessages key (e.g., TgVerificationCodeLabel)
from msgs instead of the hardcoded string, then add TgVerificationCodeLabel to
the i18n entries for both languages (Chinese "验证码" and English "Verification
code") so content becomes `${msgs.TgVerificationCodeLabel}:${verificationCode}`
(keeping the parsedText fallback behavior).
| export const decodeHtmlEntities = (value: string): string => { | ||
| return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/g, (entity, code: string) => { | ||
| const lowerCode = code.toLowerCase(); | ||
| if (lowerCode.startsWith("#x")) { | ||
| const charCode = parseInt(lowerCode.slice(2), 16); | ||
| return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity; | ||
| } | ||
| if (lowerCode.startsWith("#")) { | ||
| const charCode = parseInt(lowerCode.slice(1), 10); | ||
| return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity; | ||
| } | ||
| return htmlEntityMap[lowerCode] ?? entity; | ||
| }); | ||
| }; |
There was a problem hiding this comment.
String.fromCodePoint 可能因越界码点抛出 RangeError。
Number.isFinite 只能拦截 NaN,但对于 � 这类超过 0x10FFFF 的数值实体,parseInt 会返回合法的有限数,String.fromCodePoint 会抛出 RangeError。虽然 parseMail 有 try/catch 兜底,但会导致验证码提取整段失败,建议显式校验码点范围后再调用。
🛡️ 建议加入码点范围校验
export const decodeHtmlEntities = (value: string): string => {
return value.replace(/&(`#x`?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/g, (entity, code: string) => {
const lowerCode = code.toLowerCase();
if (lowerCode.startsWith("#x")) {
const charCode = parseInt(lowerCode.slice(2), 16);
- return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity;
+ return Number.isFinite(charCode) && charCode >= 0 && charCode <= 0x10FFFF
+ ? String.fromCodePoint(charCode) : entity;
}
if (lowerCode.startsWith("#")) {
const charCode = parseInt(lowerCode.slice(1), 10);
- return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity;
+ return Number.isFinite(charCode) && charCode >= 0 && charCode <= 0x10FFFF
+ ? String.fromCodePoint(charCode) : entity;
}
return htmlEntityMap[lowerCode] ?? entity;
});
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const decodeHtmlEntities = (value: string): string => { | |
| return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/g, (entity, code: string) => { | |
| const lowerCode = code.toLowerCase(); | |
| if (lowerCode.startsWith("#x")) { | |
| const charCode = parseInt(lowerCode.slice(2), 16); | |
| return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity; | |
| } | |
| if (lowerCode.startsWith("#")) { | |
| const charCode = parseInt(lowerCode.slice(1), 10); | |
| return Number.isFinite(charCode) ? String.fromCodePoint(charCode) : entity; | |
| } | |
| return htmlEntityMap[lowerCode] ?? entity; | |
| }); | |
| }; | |
| export const decodeHtmlEntities = (value: string): string => { | |
| return value.replace(/&(`#x`?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]+);/g, (entity, code: string) => { | |
| const lowerCode = code.toLowerCase(); | |
| if (lowerCode.startsWith("#x")) { | |
| const charCode = parseInt(lowerCode.slice(2), 16); | |
| return Number.isFinite(charCode) && charCode >= 0 && charCode <= 0x10FFFF | |
| ? String.fromCodePoint(charCode) : entity; | |
| } | |
| if (lowerCode.startsWith("#")) { | |
| const charCode = parseInt(lowerCode.slice(1), 10); | |
| return Number.isFinite(charCode) && charCode >= 0 && charCode <= 0x10FFFF | |
| ? String.fromCodePoint(charCode) : entity; | |
| } | |
| return htmlEntityMap[lowerCode] ?? entity; | |
| }); | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@worker/src/telegram_api/verification_code.ts` around lines 17 - 30, The
decodeHtmlEntities function can call String.fromCodePoint with out-of-range
values (e.g., parseInt("999999999")), causing a RangeError; update its
numeric-entity handling (the branches that parse hex and decimal using parseInt)
to validate the parsed charCode is an integer within [0, 0x10FFFF] before
calling String.fromCodePoint and otherwise fall back to returning the original
entity; ensure both the "#x" and "#" branches perform this check so
decodeHtmlEntities no longer throws for oversized code points.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
worker/src/telegram_api/verification_code.ts (1)
17-30:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
String.fromCodePoint仍可能因越界码点抛出RangeError。
Number.isFinite不会拦截�这类超出0x10FFFF的合法有限数值实体,建议显式约束码点范围到[0, 0x10FFFF]再调用String.fromCodePoint,否则会触发整段验证码提取失败(仅由parseMail的 try/catch 兜底)。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@worker/src/telegram_api/verification_code.ts` around lines 17 - 30, The decodeHtmlEntities function can call String.fromCodePoint with out-of-range values (e.g., from parseInt on large numeric entities) which throws RangeError; update the numeric-entity branches in decodeHtmlEntities to validate the parsed charCode is an integer within 0..0x10FFFF (and non-negative) before calling String.fromCodePoint, and otherwise fall back to returning the original entity; leave the named-entity path using htmlEntityMap as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@worker/src/telegram_api/verification_code.ts`:
- Around line 55-66: The colon+two-digits check in isLikelyDateOrTime falsely
tags 4-digit OTPs when the surrounding context has labels like "code: 1234";
change the logic so you only treat a 4-digit string as time when the context
contains the code itself split by a colon (e.g. "12:34" or "12:34") rather than
any colon followed by two digits anywhere: replace the /^\d{4}$/.test(code) &&
/[::]\s*\d{2}/.test(context) branch with a check that builds and searches for
the code split by a colon (e.g. new
RegExp(`${code.slice(0,2)}[::]\\s*${code.slice(2)}`)) or otherwise verifies the
matched HH:MM substring includes the code characters, so labels like "code:" or
"验证码:" do not trigger a time match; update isLikelyDateOrTime accordingly (and
any callers such as scoreCandidate) to avoid the -100 penalty for legitimate
OTPs.
---
Duplicate comments:
In `@worker/src/telegram_api/verification_code.ts`:
- Around line 17-30: The decodeHtmlEntities function can call
String.fromCodePoint with out-of-range values (e.g., from parseInt on large
numeric entities) which throws RangeError; update the numeric-entity branches in
decodeHtmlEntities to validate the parsed charCode is an integer within
0..0x10FFFF (and non-negative) before calling String.fromCodePoint, and
otherwise fall back to returning the original entity; leave the named-entity
path using htmlEntityMap as-is.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ae1dc80c-c2d4-4b82-8438-f8067749c2bc
📒 Files selected for processing (6)
CHANGELOG.mdCHANGELOG_EN.mdpages/wrangler.tomlworker/src/telegram_api/telegram.tsworker/src/telegram_api/verification_code.test.tsworker/src/telegram_api/verification_code.ts
✅ Files skipped from review due to trivial changes (4)
- pages/wrangler.toml
- CHANGELOG_EN.md
- worker/src/telegram_api/verification_code.test.ts
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
- worker/src/telegram_api/telegram.ts
| const isLikelyDateOrTime = (code: string, context: string): boolean => { | ||
| if (/^(19|20)\d{2}$/.test(code)) { | ||
| return true; | ||
| } | ||
| if (/^\d{8}$/.test(code) && /(?:date|日期|时间|time|expires?|过期|有效期)/i.test(context)) { | ||
| return true; | ||
| } | ||
| if (/^\d{4}$/.test(code) && /[::]\s*\d{2}/.test(context)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| }; |
There was a problem hiding this comment.
4 位验证码会因相邻的“code: ”等上下文被误判为时间,导致 -100 惩罚后被丢弃。
第 62 行使用 /[::]\s*\d{2}/.test(context) 在 ±120 字符上下文里搜索“冒号+两位数字”。对于常见提示语 Your code: 1234 或 验证码:1234,上下文中的 : 12/:12 同样会命中该模式,从而把合法的 4 位 OTP 标记为时间,叠加 scoreCandidate 中 -100 的扣分,几乎一定使其得分变负而被过滤。
建议只在数字未紧邻 code 本身时判定为时间,例如把整段时间形如 HH:MM 的判断改为仅匹配 4 位代码自身被冒号一分为二的情况,或者在匹配时排除 code 自身所在区间。
🛠️ 建议修正
- if (/^\d{4}$/.test(code) && /[::]\s*\d{2}/.test(context)) {
- return true;
- }
+ // 仅当 4 位代码本身呈现为 "HH:MM" 形式时才视为时间
+ if (/^\d{4}$/.test(code) && new RegExp(`${code[0]}${code[1]}[::]${code[2]}${code[3]}`).test(context)) {
+ return true;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const isLikelyDateOrTime = (code: string, context: string): boolean => { | |
| if (/^(19|20)\d{2}$/.test(code)) { | |
| return true; | |
| } | |
| if (/^\d{8}$/.test(code) && /(?:date|日期|时间|time|expires?|过期|有效期)/i.test(context)) { | |
| return true; | |
| } | |
| if (/^\d{4}$/.test(code) && /[::]\s*\d{2}/.test(context)) { | |
| return true; | |
| } | |
| return false; | |
| }; | |
| const isLikelyDateOrTime = (code: string, context: string): boolean => { | |
| if (/^(19|20)\d{2}$/.test(code)) { | |
| return true; | |
| } | |
| if (/^\d{8}$/.test(code) && /(?:date|日期|时间|time|expires?|过期|有效期)/i.test(context)) { | |
| return true; | |
| } | |
| // 仅当 4 位代码本身呈现为 "HH:MM" 形式时才视为时间 | |
| if (/^\d{4}$/.test(code) && new RegExp(`${code[0]}${code[1]}[::]${code[2]}${code[3]}`).test(context)) { | |
| return true; | |
| } | |
| return false; | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@worker/src/telegram_api/verification_code.ts` around lines 55 - 66, The
colon+two-digits check in isLikelyDateOrTime falsely tags 4-digit OTPs when the
surrounding context has labels like "code: 1234"; change the logic so you only
treat a 4-digit string as time when the context contains the code itself split
by a colon (e.g. "12:34" or "12:34") rather than any colon followed by two
digits anywhere: replace the /^\d{4}$/.test(code) &&
/[::]\s*\d{2}/.test(context) branch with a check that builds and searches for
the code split by a colon (e.g. new
RegExp(`${code.slice(0,2)}[::]\\s*${code.slice(2)}`)) or otherwise verifies the
matched HH:MM substring includes the code characters, so labels like "code:" or
"验证码:" do not trigger a time match; update isLikelyDateOrTime accordingly (and
any callers such as scoreCandidate) to avoid the -100 penalty for legitimate
OTPs.
|
感谢贡献,需要适配下多语言 |
Summary
Test Plan
node --test src/telegram_api/verification_code.test.tscorepack pnpm lintcorepack pnpm buildSummary by CodeRabbit
发布说明
Bug 修复
测试