From 98b6495741601772d47c8d8939283b529b3b44e4 Mon Sep 17 00:00:00 2001 From: Gloridust Date: Wed, 29 Jul 2026 00:37:34 +0800 Subject: [PATCH] =?UTF-8?q?docs(tg-bot):=20=E6=9B=B4=E6=AD=A3=E7=A7=81?= =?UTF-8?q?=E8=81=8A=E7=AA=97=E5=8F=A3=E4=B8=BA=205=20=E5=88=86=E9=92=9F?= =?UTF-8?q?=E2=80=94=E2=80=94=E8=BD=AE=E8=AF=A2=E6=A8=A1=E5=BC=8F=E4=B8=8B?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E9=80=81=E4=B8=8D=E8=BE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一个提交里我写「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 / 自托管长轮询) 验证逻辑本身无需改动,换传输层即可复用。 --- .github/scripts/telegram-bot.mjs | 24 ++++++++++++++++++++---- .github/workflows/telegram-bot.yml | 12 +++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/scripts/telegram-bot.mjs b/.github/scripts/telegram-bot.mjs index f6df765..20f1fd4 100644 --- a/.github/scripts/telegram-bot.mjs +++ b/.github/scripts/telegram-bot.mjs @@ -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) 改 webhook(Cloudflare 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 里手动批准/拒绝。`, + ); } } diff --git a/.github/workflows/telegram-bot.yml b/.github/workflows/telegram-bot.yml index d9a9e2f..eb9d425 100644 --- a/.github/workflows/telegram-bot.yml +++ b/.github/workflows/telegram-bot.yml @@ -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 里手动批准。 +# 要让验证码真正送达,需改为近实时接收更新:webhook(Cloudflare Workers 等免费常驻) +# 或自托管长轮询(NAS 上跑个小容器,无需公网端点)。脚本里的验证逻辑可直接复用。 +# +# 局限:cron 最小 5 分钟且可能再延后 → 命令非实时; # GitHub 会在仓库 60 天无活动时暂停定时任务。 # 想立即处理一次:Actions → telegram-bot → Run workflow(workflow_dispatch)。