功能介绍
核心概念
hodor 把「用户私聊 bot」与「超级群组的话题(topic)」一一映射:一个人,一个 topic。
绑定长期复用;只有话题被物理删除(/deluser,或 Telegram 侧手动删除)后,该用户下次联系才会换新话题——/archive 归档、原生关闭话题都不改变绑定,用户回访仍回到原话题。
本页只描述用户与管理员可见的行为;消息管线、验证状态机等内部实现见原理与架构。
用户 A ──私聊──▶ Bot ──Webhook──▶ hodor ──转发──▶ 群组 · Topic A
用户 B ──私聊──▶ Bot ──Webhook──▶ hodor ──转发──▶ 群组 · Topic B
用户 A ◀─私聊── Bot ◀──Webhook── hodor ◀─发言── 群组 · Topic A(管理员)- 用户侧:像和普通客服 bot 聊天一样直接发消息
- 管理员侧:在群组中该用户的 topic 里直接说话,消息送达该用户
- 每个用户只能看到自己的消息;管理员可以看到所有对话
对话流程
用户侧
- 首次给 bot 发消息(无论是否输入
/start,文本或媒体均可)都会触发:建立用户档案 → 发送欢迎语 → 紧随其后发送验证挑战(默认数学题,模式可切换,见人机验证) /start有频控(每用户每分钟最多响应 1 次),防止反复触发欢迎语刷接口;/start本身是入口命令,不转发到客服话题、不进消息账本- 完成验证后,直接发消息即可与客服对话,无需任何命令;验证通过前的消息直接丢弃(不积压补发)
欢迎语文案:
你好,欢迎使用 hodor 私聊机器人!👋
直接发送消息即可与客服对话,无需任何命令;客服的回复也会在这里显示。
文案三要素齐备:项目名称(hodor 私聊机器人)、使用方式(直接发送消息即可)、项目地址。文案集中存放在单一模块中,fork 后可整体改写(含多语言)。
欢迎语可通过环境变量 WELCOME_TEXT 自定义(支持换行,字面 \n 会解释为换行),未配置时使用默认文案。
管理员侧
- 只有
ADMIN_IDS中的管理员在 topic 里的发言会被中继给用户,其他群成员发言静默忽略 - 在 topic 里直接说话即可,无需回复某条特定消息
- 以
/开头的消息优先按管理命令处理(见下方命令表);全部 18 个命令已注册到客服群的命令菜单(输入框点/可直接选择,客户端会自动附加@bot名后缀,均按去后缀解析)。菜单恒全量注册、不随验证开关变化(帮助文本才是动态面);已部署实例更新版本后需重跑一次 setwebhook 刷新菜单 - 非管理员发送
/命令 → 收到「该命令仅客服管理员可用。」提示;非管理员普通文本仍静默忽略
查看模式建议
Telegram 超级群组有两种查看方式,客服场景建议使用话题模式:
- 话题模式(推荐):话题面板就是「收件箱」——有新消息的话题带未读圆点、按活跃度上浮,点开即达对应用户;中继消息在话题内干净呈现,身份即话题标题(用户昵称)
- 消息模式(整群单条时间线):所有中继消息都显示为 bot 发送,无法直接分辨来自哪位用户;如需逐条处理用户消息,请切换到话题模式
消息能力
- 文本 + 7 类媒体原样直传(Telegram file_id 引用,不下载、不落盘、零存储成本):图片、视频、语音(语音条)、音乐(音频文件)、文件、贴纸、动图;支持集之外的消息类型(视频留言、位置、联系人、投票等)受控静默忽略,不做部分转发
- topic 名称展示用户昵称(
first_name;缺失时回退@username,再回退用户 ID),建 topic 时确定 - 每个 topic 首次建立时置顶一条用户信息(每 topic 恰一条,不重复),用户改昵称后自动刷新:
昵称:张三(@zhangsan)
用户 ID:123456789
首次聊天:2026-09-29 18:00 (UTC)
验证状态:✅ 已验证「高危」行仅在该用户被 /risk 标记后显示,「备注」行仅在 /note 写入备注后显示;验证行三态随 /verifyon / /verifyoff 与用户验证状态变化(✅ 已验证 / ❌ 未验证 / 未启用)。
本页与实现的关系
本页所述全部行为(核心概念 / 对话流程 / 消息能力 / 人机验证 / 频率限制 / 拦截行为汇总与命令表)为已发布 v1 的当前行为(2026-10-09 完整发布,版本历史见仓库 CHANGELOG)。
多个独立 Bot 可按多实例部署运行;新实例的资料与话题从头开始。
人机验证
防止恶意刷消息消耗你的 Cloudflare 请求配额。建议保持开启(/verifyoff 仅在受信任的小范围场景临时使用)。
触发场景
| 场景 | 行为 |
|---|---|
| 首次发消息 | 建档并发送验证码 |
/archive 之后 | 软归档保留历史;用户重新联系后复用/重建 topic 并重新验证 |
| 分钟限频超限 | 立即标记未验证并发送新的验证挑战(形态随当前模式) |
VERIFY_TTL_HOURS 过期 | 下一条消息触发重新验证 |
验证模式
| 模式 | 说明 | 防护强度 |
|---|---|---|
| 数学题(默认) | a ± b 的题目配 4 个答案按钮(1 对 3 错),正确答案只存数据库 | 中 |
| 纯按钮 | 点击单个按钮即通过;bot 可直接调 API「点击」,仅作便利选项 | 低 |
| Turnstile(可选) | 私聊内点「打开验证页面」按钮,在 Telegram 打开的网页中完成 Cloudflare Turnstile 人机验证;需自行申请配置(见部署指南) | 高 |
/verifymode无参数只查看当前模式,不改变任何设置;/verifymode_math//verifymode_button//verifymode_turnstile各自切换到对应模式(兼容别名/verifymode math|button|turnstile保留);非法参数直接拒绝- 验证配置命令(
/verifyon、/verifyoff与/verifymode系列)仅在客服群 General 中生效:在其他话题发送会收到引导提示,不会改动任何设置;含新命令的版本上线后需重跑一次setwebhook刷新 Telegram 客户端的命令菜单 - 切换为 Turnstile 时若两把密钥未配齐,切换被拒绝并点名缺失项,当前题目与待验证用户不受影响
- 同一模式重复设置是幂等的,不会作废进行中的验证
- 答题错误(数学题 / 纯按钮):编辑原消息提示错误,并自动重新出一题
- 验证未通过期间发送的消息直接丢弃(不积压补发),并按每分钟最多 1 次的频率重发验证码引导
Turnstile 模式细则
- 入口仅限 Bot 私聊里的「打开验证页面」按钮(Telegram Mini App);在普通浏览器直接打开链接无法确认 Telegram 身份,页面只提示回到 Bot
- 完成者必须是发起验证的同一 Telegram 账号,换账号打开无法通过
- 完成挑战后页面提示返回聊天窗口重新发送消息;验证前被拦截的消息不补发
- 网页提交有防重复的节流,提示「稍候几秒再试」属正常现象
时间边界:
| 时限 | 时长 | 到期后的做法 |
|---|---|---|
| 验证链接 | 自创建起 10 分钟 | 回 Bot 发送任意消息重新获取 |
| 页面身份 | 页面打开超过 5 分钟 | 关闭页面,从 Bot 按钮重新打开(仅刷新挑战组件无效) |
| Turnstile 挑战结果 | 5 分钟且一次性 | 在页面内重新完成挑战即可,不影响链接与已验证状态 |
已验证用户的有效期仍由 VERIFY_TTL_HOURS 控制,与上表三个时限互不相干。
以下情况会让未完成的验证请求失效、需重新发起;已验证状态不受影响,解封或切回原模式也不会让旧请求复活:
- 验证模式或开关发生变化(含切走再切回)
- 用户被封禁(
/ban)、软归档(/archive)或物理删除(/deluser) - 链接被使用或超过 10 分钟
有效期与开关
VERIFY_TTL_HOURS控制有效期,默认0= 永久有效(重验只发生在上方四种场景)/verifyoff关闭验证期间不清除验证记录;/verifyon重新开启后,此前已验证的用户仍然有效,只有从未验证过的用户需要验证- 验证开关与模式存数据库(settings 表),命令切换即时生效,无需重新部署
频率限制
MAX_MESSAGES_PER_MINUTE(默认 20):每用户 60 秒固定窗口内最多转发的消息数- 超限处理:本条丢弃 + 标记未验证 + 发送新验证码。提示与题面合并为一条消息,文案带限制数字(如「发送过快,每分钟最多 20 条消息,本条未送达。请重新完成验证:…」),即触发重新验证,不是封禁
- 提示类回复(重发验证码、禁言提示、欢迎语)每用户每分钟最多 1 次,防止对方反过来刷你的请求额度
管理命令
普通管理命令在对应 topic 内执行,仅 ADMIN_IDS 生效;/deluser、/archive 与 /purgemsg 需要话题当前可写。/help 无需绑定、任意话题可用;验证配置命令(/verifyon、/verifyoff 与 /verifymode 系列)仅限客服群 General 执行,在其他话题会收到引导提示且不产生任何改动。
Telegram 原生关闭的话题不能接收新消息,管理员需先在客户端重开再执行这些命令;/archive 完成后的结果反馈会发到客服群 General。
| 命令 | 作用 | 备注 |
|---|---|---|
/help | 查看命令帮助 | 按当前验证开关状态动态展示对应命令 |
/ban | 禁言该 topic 的用户 | 用户发消息会收到「你已被禁言」提示(限频) |
/unban | 解除禁言 | |
/risk | 标记高危用户 | 正常聊天不受影响;置顶信息加 ⚠️ 标注,并在 topic 内醒目提示一次(24 小时不重复) |
/unrisk | 取消高危标记 | 更新置顶信息 |
/archive | 软归档当前用户 | 关闭 topic、清除验证状态;保留用户、绑定、账本、历史与备注;用户重新联系后自动恢复并重新验证 |
/deluser | 物理删除当前用户 | 不可恢复,二次确认;删除 Telegram topic 及其中群内消息,并删除对应 Hodor 用户、绑定与账本;不删除私聊窗口历史 |
/purgemsg | 清理本 topic 可追踪的群内聊天消息 | 含管理员发言;按账本逐条删除并报告部分失败;重置用户信息置顶,不删除 topic 或私聊消息 |
/broadcast | 向全部用户群发一条公告 | 仅限客服群 General 发起;预览确认后发送;详见下方「/broadcast 全员广播」章节 |
/wipealldata | 一键清空 Hodor 数据库并删除群内话题 | 危险操作;全局命令,任意可写 topic 内可执行;确认后先删除客服群内全部话题(General 除外)及其中消息,全部成功后清空 users/topics/messages 记录(settings、幂等台账、bot 身份保留);两步确认;不删除双方私聊历史 |
/note <内容> | 添加备注 | 标注该用户 / 对话的用途(如「仅咨询退款」);写入并更新置顶信息 |
/unnote | 清除备注 | 更新置顶信息 |
/verifyon /verifyoff | 开 / 关人机验证 | 两个命令按当前状态展示对应的那个;仅限客服群 General 执行 |
/verifymode | 查看验证模式 | 无参数仅查看当前模式;兼容别名 `/verifymode math |
/verifymode_math | 切换到数学题验证 | 与别名 /verifymode math 等效;仅限客服群 General 执行 |
/verifymode_button | 切换到纯按钮验证 | 防护较弱,确认文案会再次提示;与别名 /verifymode button 等效 |
/verifymode_turnstile | 切换到 Turnstile 验证 | 需先配好两把密钥,缺失时拒绝切换;仅限客服群 General 执行 |
/wipealldata 的两步确认
/wipealldata 用于一键清空全部数据并删除群内话题,因影响全局且不可恢复,执行前强制两步确认:
- 首次发送 → 回复危险警告并附「确认清理 / 取消」内联按钮
- 点击「确认清理」后才真正执行;点「取消」或 60 秒超时未确认则放弃(超时提示会直接改写在警告消息上),需重新发起
执行顺序:先逐一删除客服群内全部话题(General 除外)及其中消息,全部成功后才清空数据库。
若有话题删除失败(如权限问题),数据不会清空,按钮提示会报出失败数量;修复权限后重新发起 /wipealldata 即可继续(已删话题自动跳过)。
警告文案明确影响范围:删除客服群内全部话题及其中群内消息、全部 users/topics/messages 记录,不可恢复;settings(验证开关与模式)、processed_updates 幂等台账和 bots 身份保留;双方私聊窗口历史不在删除范围。
完成后按钮处以 toast 反馈结果(警告消息所在话题也已被删除)。
/broadcast 全员广播
/broadcast 用于把一条公告一次性发送给所有符合资格的历史用户私聊,避免逐个话题重复回复。仅 ADMIN_IDS 中的管理员在客服群 General(不带话题的消息)发起;在用户话题内发送只会收到「请到 General 发起」的提示,绝不中继给该用户。
输入形态(一条完整消息,第一行命令后为标题,其余为正文,二者均必填):
/broadcast 系统维护通知
今晚 23:00–23:30 进行系统维护,期间客服回复可能延迟。
你仍然可以留言,我们会在恢复后尽快处理。公告统一格式:📣 加粗标题、空行、分段正文、空行、斜体落款(Bot 显示名称)。正文按普通文字处理——*、_、<、& 等符号原样展示,不解释 Markdown / HTML,不保留输入中的 Telegram 富文本。全文(含标题与落款)超过 4096 字符将被拒绝,需精简后重发;不自动拆分。
收件人资格:当前 Bot 下仍有话题绑定、用户档案存在且未被禁言的用户——包括已软归档(/archive)和话题已关闭的用户;不按验证状态或有效期筛选。物理删除(/deluser)、无绑定与孤儿映射的用户不在其列。广播不恢复归档、不重开话题、不改变任何用户状态。
流程四步:
- 输入:General 发送完整命令 → Bot 立即在 General 回发与用户所见完全一致的公告预览,并回复一条「广播确认(未发送)」控制消息,显示预计收件人数与「确认发送 / 取消」按钮
- 确认:仅发起管理员本人在 5 分钟内点击有效;其他人(包括其他管理员)、过期或已取消的按钮一律拒绝。确认时冻结收件人名单——之后新增的用户不加入本次广播
- 发送:确认后在同一请求内顺序逐位私聊发送(建议规模控制在数百以内);单个用户失败(含拉黑 bot)不影响其余用户;Telegram 限流时系统至多延迟重试一次
- 统计:完成后控制消息原地改为「广播完成:成功 N,失败 M」。成功仅表示 Telegram 已接收,不代表用户已读;失败不自动补发
每位收件人至少消耗一次 Worker 出站子请求。Cloudflare 免费套餐每次调用默认仅有 50 个普通子请求,因此 100–300 人广播需要套餐与额度足以覆盖整批及控制调用。实际容量取决于 Cloudflare 套餐与配额。
取消只在确认前提供;确认后不提供停止。General 中保留公告全文与最终统计,作为广播记录回溯——未确认的预览可通过其关联控制消息(待确认 / 已取消 / 已过期)与真实广播区分。同一时间只有一个广播在发送,发送中的广播会拒绝新广播的确认并提示稍后再试。
若发送中途 Worker 重启或断连,广播会停在半路:控制消息标记「广播中断,结果未知;未自动补发」,不提供恢复操作(详见运维手册)。
/wipealldata 清库会将进行中的广播标为结果未知;发送循环在下一位收件人前检查任务状态并停止。已进入 Telegram 的单条发送请求无法撤回,可能仍会送达。
/archive 与历史保留
/archive 是软归档:清除验证与 pending 题目、标记用户软删除并关闭现有 topic,但保留 users 行、绑定、群内消息历史、账本与 /note 备注。
用户重新私聊时会自动重开原 topic 并重新验证;若旧 topic 已被原生删除,Hodor 会新建替代 topic。
确认反馈发在客服群 General,因为 Telegram 官方语义规定 closed topic 不能再接收消息。
/deluser 是物理删除:二次确认后删除 Telegram topic 及其中所有群内消息,并清理对应用户、topic 绑定和 Hodor 消息账本。
不删除双方 Telegram 私聊窗口中的历史;D1 账本只含成功中继消息,不能代表完整私聊记录。
话题已原生删除时,Hodor 仍会清理对应数据库行;其他删除失败则保留数据库数据并反馈。
Telegram 原生「关闭 / 删除话题」与 Hodor 的关系
- 原生关闭话题:保留 topic 及历史,只更改 open/closed 状态。Telegram Core API 描述 closed topic 为“不能再向该话题发送消息”;官方 API 文档未说明 bot/管理员存在发言例外。Hodor 接收
forum_topic_closed/forum_topic_reopened服务消息同步数据库 topic 状态,不清验证、不归档用户。用户之后私聊 bot 时,Hodor 会显式调用reopenForumTopic自动恢复原话题并继续原验证状态——这是 Hodor 的恢复策略,不是 Telegram 原生自动重开。 - 原生删除话题:删除 topic 与其中所有群内消息,但 Bot API 没有可依赖的删除事件。用户普通消息转发到已删除 thread 时,Hodor 识别 topic-gone 错误、重建替代 topic 并尽量在当条续传;closed 行在 reopen 时报 topic-gone 时也会在新 topic 继续。只发
/start时没有只读 topic 查询接口,Hodor 不会发探测消息;此时可能暂不发现开放绑定指向已删除 topic,需用户下一条普通消息触发恢复。旧绑定里的 note 随绑定回收丢失,历史账本记录保留。 - 命令边界:不提供 Hodor
/close命令,原生 Telegram 客户端负责 close/reopen;/archive才执行软归档;/deluser执行物理删除;/purgemsg只清可追踪群消息;/wipealldata删除全部群内话题并清空 Hodor 数据库(General 除外,私聊历史不动)。
拦截行为汇总
| 用户状态 | 发消息后的行为 |
|---|---|
| 未通过验证 | 消息丢弃,每分钟最多重发 1 次验证码 |
| 已被 ban | 消息丢弃,每分钟最多回复 1 次「你已被禁言」 |
| 限频超限 | 本条丢弃,触发重新验证(提示带限制数字) |
路线图
多实例已可用于独立运营及换 Bot / 换群组,操作与数据边界见部署多个实例。Turnstile 人机验证已原生交付(不经第三方服务,见部署指南)。