docs(tg-bot): 更正私聊窗口为 5 分钟——轮询模式下验证码送不达

上一个提交里我写「user_chat_id 允许 24 小时内私聊,故 Actions 轮询方案成立」,
这是错的。核对官方文档原文:

  "The bot can use this identifier for 5 minutes to send messages
   until the join request is processed"

是 5 分钟。全文中所有 24 hours 均与入群申请无关——我多半是把
"Incoming updates ... not be kept longer than 24 hours"(更新在服务器的保留时长)
错当成了私聊窗口,两者是两回事。

影响:cron 最小 5 分钟且常延后 10~15 分钟 → 窗口多已过期 → 验证码大概率发不出去。
安全性不受影响(待批准用户进不了群、发不了消息),但会退化为人工审批。

- 注释更正为 5 分钟,并写明混淆点,避免以后再踩
- 私聊失败的日志改为说明真实成因与后续动作(仍不自动拒绝,避免误伤真人)
- workflow 注释加显著提示,并给出两条可行的近实时方案(webhook / 自托管长轮询)

验证逻辑本身无需改动,换传输层即可复用。
This commit is contained in:
Gloridust
2026-07-29 00:37:34 +08:00
parent 91c591f1dd
commit 98b6495741
2 changed files with 29 additions and 7 deletions

View File

@@ -15,10 +15,21 @@
// 也就不需要"进群后删广告 / 禁言 / 移除"那一整套事后补救。
//
// 流程chat_join_request → 私聊出一道加法题(选择题按钮)→ 答对 approve、连错 3 次 decline。
// 关键前提已查官方文档确认ChatJoinRequest.user_chat_id 允许机器人在【24 小时】内私聊该用户,
// 远大于 cron 的 5~15 分钟延迟,所以本方案在 Actions 上成立。
// 无状态:正确答案与已答错次数全部编码进按钮的 callback_data不需要任何持久化存储。
//
// ⚠️ 已知限制(官方文档原文核对过,别再想当然):
// ChatJoinRequest.user_chat_id —— "The bot can use this identifier for 5 minutes to send
// messages until the join request is processed",即【只有 5 分钟】能私聊该用户。
// (更新本身在服务器保留 24h那是另一回事别混淆——我就混过一次。
// 而 GitHub cron 最小 5 分钟且常再延后 10~15 分钟 → 轮询模式下验证码【大概率发不出去】。
//
// 安全性不受影响:发不出去时用户仍卡在待批准,进不了群也发不了消息,只是退化成人工审批。
// 故私聊失败时【绝不自动拒绝】,留给管理员人工处理,避免误伤真人。
//
// 要让验证码真正送达,必须让机器人近实时地收到更新,两条路:
// a) 改 webhookCloudflare Workers / Deno Deploy 等,免费且常驻,本文件逻辑可直接复用)
// b) 自托管长轮询getUpdates timeout=50 常驻进程NAS 上跑个小容器即可,无需公网端点)
//
// 需要:机器人是群管理员且有 can_invite_users 权限(否则收不到 chat_join_request
const TG = process.env.TG_TOKEN;
@@ -164,8 +175,13 @@ async function onJoinRequest(r) {
const { q, markup } = captchaKeyboard(chatId, userId, 0);
const res = await tg('sendMessage', { chat_id: dm, text: captchaText(q, 0), reply_markup: markup });
if (!res.ok) {
// 私聊发不出去(用户屏蔽了机器人 / 超窗口)——不自动拒绝,留给管理员人工处理,避免误伤
console.log(`captcha DM 失败 user=${userId}: ${res.description}`);
// 最常见原因:距离入群申请已超过 5 分钟的私聊窗口(轮询模式下这是常态,不是偶发)。
// 也可能是用户屏蔽了机器人。一律【不自动拒绝】,留给管理员人工处理,避免误伤真人。
console.log(
`⚠️ 验证码私聊失败 user=${userId}: ${res.description}\n` +
` 多半是超过了 user_chat_id 的 5 分钟窗口cron 延迟所致)。该用户仍处于待批准状态,` +
`进不了群,需要管理员在 Telegram 里手动批准/拒绝。`,
);
}
}

View File

@@ -13,10 +13,16 @@ name: telegram-bot
# 启用入群验证还需(缺一不可):
# a) 群组设为「新成员需管理员批准」(群设置 → 邀请链接勾选 Request Admin Approval
# b) 机器人是群管理员且有 can_invite_users 权限 —— 否则收不到 chat_join_request 更新。
# 为什么这样就不怕 cron 有延迟:待批准的用户看不到群、也发不了消息,晚几分钟处理没有风险;
# 而 ChatJoinRequest.user_chat_id 允许机器人在 24 小时内私聊该用户,远大于 cron 延迟。
#
# 局限cron 最小 5 分钟且可能再延后 → 命令与验证码送达非实时(安全性不受影响,只影响体验);
# ⚠️ 轮询模式下验证码大概率发不出去,务必知悉:
# Telegram 只允许机器人在入群申请后【5 分钟】内私聊该用户(官方文档 ChatJoinRequest.user_chat_id
# 而本工作流的 cron 最小 5 分钟且常再延后 10~15 分钟,多数情况下窗口已过。
# —— 安全性不受影响(待批准用户进不了群、发不了消息),但会退化成「人工审批」,
# 发不出验证码时不会自动拒绝,需要管理员在 Telegram 里手动批准。
# 要让验证码真正送达需改为近实时接收更新webhookCloudflare Workers 等免费常驻)
# 或自托管长轮询NAS 上跑个小容器,无需公网端点)。脚本里的验证逻辑可直接复用。
#
# 局限cron 最小 5 分钟且可能再延后 → 命令非实时;
# GitHub 会在仓库 60 天无活动时暂停定时任务。
# 想立即处理一次Actions → telegram-bot → Run workflowworkflow_dispatch