命令系统
本文引用的文件
- src/index.ts
- src/command/index.ts
- src/command/handlers/start.ts
- src/command/handlers/login.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
- src/command/handlers/logout.ts
- src/client/cosmoe.ts
- src/scheduler/index.ts
- package.json
目录
- 简介
- 项目结构
- 核心组件
- 架构总览
- 详细组件分析
- 依赖关系分析
- 性能考量
- 故障排查指南
- 结论
- 附录
简介
本文件系统性梳理该 Telegram 机器人的命令系统,覆盖命令路由机制(command() 与 hears())、正则表达式匹配、交互式对话(conversations)与回调查询处理、以及命令处理器的实现模式与错误处理策略。重点解释 /start、/login、/events、/history、/logout 等命令的处理流程,并给出命令注册最佳实践与性能优化建议。
项目结构
图表来源
- src/index.ts
- src/command/index.ts
- src/client/cosmoe.ts
- src/scheduler/index.ts
- package.json
章节来源
- src/index.ts
- src/command/index.ts
- package.json
核心组件
- Bot 初始化与 Webhook:入口文件创建 Bot 实例,设置命令菜单并通过 Cloudflare 的 webhook 回调处理请求;同时注册定时任务。
- 命令注册与路由:
- 使用 bot.command() 注册标准命令(如 /start、/login、/events、/history、/logout)。
- 使用 bot.hears() 注册基于正则的消息监听(如 /event{id}、/book{eventid}{slotid}、/cancel{booking_id})。
- 使用 bot.callbackQuery() 注册回调查询处理(如确认取消、优惠券选择)。
- 对话系统:通过 @grammyjs/conversations 插件启用交互式对话,结合 KV 存储持久化会话状态。
- 客户端封装:统一的 CosmoeClient 提供认证、事件、预约、历史、取消等 API 封装,支持错误码与数据结构校验。
章节来源
- src/index.ts
- src/command/index.ts
- src/client/cosmoe.ts
架构总览
整体采用“入口初始化 + 命令注册中心 + 处理器 + 客户端 + 调度”的分层架构。命令注册中心集中管理路由与对话,处理器专注于业务逻辑,客户端抽象外部 API,调度模块负责定时任务。
graph TB
subgraph "运行时"
W["Worker 入口<br/>src/index.ts"]
S["调度任务<br/>src/scheduler/index.ts"]
end
subgraph "命令系统"
R["命令注册中心<br/>src/command/index.ts"]
H1["start 处理器<br/>handlers/start.ts"]
H2["login 处理器<br/>handlers/login.ts"]
H3["events 处理器<br/>handlers/events.ts"]
H4["eventDetails 处理器<br/>handlers/eventDetails.ts"]
H5["bookEvent 处理器<br/>handlers/bookEvent.ts"]
H6["history 处理器<br/>handlers/history.ts"]
H7["cancel 处理器<br/>handlers/cancel.ts"]
H8["logout 处理器<br/>handlers/logout.ts"]
end
subgraph "外部服务"
C["Cosmoe API 客户端<br/>src/client/cosmoe.ts"]
end
W --> R
R --> H1
R --> H2
R --> H3
R --> H4
R --> H5
R --> H6
R --> H7
R --> H8
H2 --> C
H3 --> C
H4 --> C
H5 --> C
H6 --> C
H7 --> C
H8 --> C
W --> S
图表来源
- src/index.ts
- src/command/index.ts
- src/command/handlers/start.ts
- src/command/handlers/login.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
- src/command/handlers/logout.ts
- src/client/cosmoe.ts
- src/scheduler/index.ts
详细组件分析
命令路由机制:command() 与 hears()
- 标准命令路由
- /start:直接回复欢迎与可用命令列表。
- /login:进入交互式登录对话。
- /events:拉取最新活动列表,生成带链接的消息。
- /history:根据用户凭证拉取预约历史并格式化展示。
- /logout:删除用户凭证。
- 正则消息监听
- /event_{id}:解析活动 ID,获取详情并列出时间段与预约链接。
- /book_{eventid}{slot_index}:解析活动与时间段索引,检查配额与优惠券,发起预约。
- /cancel_{booking_id}:解析预约 ID,生成确认键盘。
- 回调查询处理
- confirmcancel{id}:执行取消操作。
- selectcoupon{eventid}{slotindex}{coupon_code}:处理优惠券选择并继续预约流程。
章节来源
- src/command/index.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
正则表达式匹配与参数解析
- /event{id}:使用 /^\/event(.+)$/ 匹配并提取活动 ID,随后调用事件详情接口。
- /book_{eventid}{slotindex}:使用 /^\/book(\d+)_(\d+)$/ 捕获两个数字参数,分别对应活动 ID 与时间段索引。
- /cancel_{bookingid}:使用 /^\/cancel(\d+)$/ 捕获预约 ID。
- 回调动作解析:使用字符串分割解析 selectcoupon{eventid}{slotindex}{coupon_code} 与 confirmcancel{id}。
章节来源
- src/command/index.ts
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/cancel.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/cancel.ts
交互式对话与回调处理
- 登录对话:通过 createConversation 创建交互式对话,逐步收集用户名与密码,调用 CosmoeClient 获取 token 并存入 KV。
- 优惠券选择:当活动存在多个可用优惠券时,生成内联键盘供用户选择;回调解析后继续预约流程。
- 取消确认:发送内联键盘,用户点击确认或取消,分别执行取消或提示已取消。
章节来源
- src/command/index.ts
- src/command/handlers/login.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/cancel.ts
- src/command/handlers/cancel.ts
命令处理函数标准化模板
- 输入输出约定
- 输入:Context 或带 Context 的组合(如带 Conversation 的上下文)。
- 输出:Promise,统一通过 ctx.reply()/editMessageText()/answerCallbackQuery() 进行响应。
- 错误处理策略
- try/catch 包裹关键逻辑,捕获异常后向用户反馈通用错误信息。
- 对外部 API 调用进行结果码校验,区分成功与失败分支。
- 对 KV 读写进行异常捕获与日志记录。
- 参数解析与校验
- 使用正则捕获组提取参数,对空值与越界情况进行保护。
- 对用户输入进行基本校验(如用户名、密码非空)。
- Markdown 渲染与长度限制
- 对长文本进行长度截断,避免 Telegram API 限制。
- 合理使用 Markdown 格式提升可读性。
章节来源
- src/command/handlers/start.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
- src/command/handlers/logout.ts
命令注册最佳实践
- 集中式注册:将所有命令与监听规则集中在命令注册中心,便于维护与审计。
- 分离关注点:命令注册只做路由与转发,具体业务逻辑放入独立处理器。
- 统一环境注入:通过 Env 接口注入 KV 命名空间与 Bot 配置,保证处理器可测试性。
- 回调与正则命名规范:为回调动作与正则表达式建立清晰的命名约定,便于解析与扩展。
- 会话持久化:使用 KV Adapter 为对话提供持久化存储,确保多轮交互状态一致。
章节来源
- src/command/index.ts
- src/command/index.ts
- src/command/index.ts
关键命令处理流程
/start 流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant H as "start 处理器"
U->>B : "/start"
B->>H : 调用 handleStartCommand(ctx)
H-->>U : 回复欢迎与可用命令列表
图表来源
- src/command/index.ts
- src/command/handlers/start.ts
章节来源
- src/command/index.ts
- src/command/handlers/start.ts
/login 交互式登录流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant C as "conversations"
participant L as "login 处理器"
participant API as "CosmoeClient"
U->>B : "/login"
B->>C : 进入 "login" 会话
C->>L : 传入 conversation, ctx, env
L->>U : 请求用户名
U-->>L : 发送用户名
L->>U : 请求密码
U-->>L : 发送密码
L->>API : getToken(username, password)
API-->>L : 返回 token
L->>U : 登录成功并保存凭证
图表来源
- src/command/index.ts
- src/command/index.ts
- src/command/handlers/login.ts
- src/client/cosmoe.ts
章节来源
- src/command/index.ts
- src/command/handlers/login.ts
- src/client/cosmoe.ts
/events 列表与 /event_{id} 详情流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant E as "events 处理器"
participant ED as "eventDetails 处理器"
participant API as "CosmoeClient"
U->>B : "/events"
B->>E : 调用 handleEventsCommand(ctx)
E->>API : getEvents()
API-->>E : 返回事件列表
E-->>U : 发送活动列表含 /event_{id} 链接
U->>B : "/event_{id}"
B->>ED : 调用 handleEventDetails(ctx)
ED->>API : getEventDetail(id)
API-->>ED : 返回活动详情
ED-->>U : 发送活动详情与时间段及 /book_{event_id}_{slot_id} 链接
图表来源
- src/command/index.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/client/cosmoe.ts
章节来源
- src/command/index.ts
- src/command/handlers/events.ts
- src/command/handlers/eventDetails.ts
- src/client/cosmoe.ts
/book_{eventid}{slot_index} 预约流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant BE as "bookEvent 处理器"
participant API as "CosmoeClient"
U->>B : "/book_{event_id}_{slot_index}"
B->>BE : 调用 handleBookEvent(ctx, env)
BE->>API : getEventDetail(event_id)
API-->>BE : 返回活动详情与时间段
BE->>BE : 校验 slotIndex 与剩余容量
alt 多个优惠券
BE-->>U : 展示优惠券选择键盘
U->>B : 回调 select_coupon_{event_id}_{slot_index}_{coupon_code}
B->>BE : 调用 handleCouponSelection(ctx, env, action)
else 单个或无优惠券
BE->>API : bookEvent({event_id, time_slot, coupon_code?})
API-->>BE : 返回预约结果
end
BE-->>U : 成功/失败消息
图表来源
- src/command/index.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/bookEvent.ts
- src/client/cosmoe.ts
- src/client/cosmoe.ts
- src/client/cosmoe.ts
章节来源
- src/command/index.ts
- src/command/handlers/bookEvent.ts
- src/client/cosmoe.ts
- src/client/cosmoe.ts
/history 预约历史流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant H as "history 处理器"
participant API as "CosmoeClient"
U->>B : "/history"
B->>H : 调用 handleHistoryCommand(ctx, env)
H->>H : 读取 KV 凭证并校验
H->>API : getMyBookings()
API-->>H : 返回预约历史
H->>H : 格式化消息并添加取消链接
H-->>U : 发送历史消息
图表来源
- src/command/index.ts
- src/command/handlers/history.ts
- src/client/cosmoe.ts
章节来源
- src/command/index.ts
- src/command/handlers/history.ts
- src/client/cosmoe.ts
/cancel_{booking_id} 取消流程
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant CA as "cancel 处理器"
participant API as "CosmoeClient"
U->>B : "/cancel_{booking_id}"
B->>CA : 调用 handleCancelCommand(ctx, env)
CA->>API : getMyBookings()
API-->>CA : 返回预约列表
CA-->>U : 发送确认键盘
U->>B : 回调 confirm_cancel_{id}
B->>CA : 调用 handleCancelConfirmation(ctx, env, action)
CA->>API : cancelBooking(booking_id)
API-->>CA : 返回取消结果
CA-->>U : 成功/失败消息
图表来源
- src/command/index.ts
- src/command/handlers/cancel.ts
- src/command/handlers/cancel.ts
- src/client/cosmoe.ts
章节来源
- src/command/index.ts
- src/command/handlers/cancel.ts
- src/client/cosmoe.ts
/logout 登出流程
flowchart TD
Start(["开始"]) --> GetUserID["获取用户ID"]
GetUserID --> HasCreds{"KV中是否存在凭证?"}
HasCreds --> |否| ReplyNotLoggedIn["提示先登录"]
HasCreds --> |是| DeleteCreds["删除KV中的凭证"]
DeleteCreds --> ReplySuccess["回复登出成功"]
ReplyNotLoggedIn --> End(["结束"])
ReplySuccess --> End
图表来源
- src/command/index.ts
- src/command/handlers/logout.ts
章节来源
- src/command/index.ts
- src/command/handlers/logout.ts
依赖关系分析
- 运行时依赖:grammy、@grammyjs/conversations、@grammyjs/storage-cloudflare。
- 入口与命令注册:入口文件负责初始化 Bot、设置命令菜单、挂载命令与调度。
- 命令处理器:依赖 CosmoeClient 进行外部 API 调用,依赖 KV 命名空间进行凭证与会话存储。
调度模块:依赖 Bot API 向已注册用户推送新活动通知。
graph LR
P["package.json"] --> G["grammy"]
P --> C["@grammyjs/conversations"]
P --> K["@grammyjs/storage-cloudflare"]
IDX["src/index.ts"] --> CMD["src/command/index.ts"]
CMD --> HSTART["handlers/start.ts"]
CMD --> HLOGIN["handlers/login.ts"]
CMD --> HEVENTS["handlers/events.ts"]
CMD --> HEVENTD["handlers/eventDetails.ts"]
CMD --> HBOOK["handlers/bookEvent.ts"]
CMD --> HHIST["handlers/history.ts"]
CMD --> HCANCEL["handlers/cancel.ts"]
CMD --> HLOGOUT["handlers/logout.ts"]
HLOGIN --> CC["src/client/cosmoe.ts"]
HEVENTS --> CC
HEVENTD --> CC
HBOOK --> CC
HHIST --> CC
HCANCEL --> CC
HLOGOUT --> CC
IDX --> SCH["src/scheduler/index.ts"]
SCH --> CC
图表来源
- package.json
- src/index.ts
- src/command/index.ts
- src/command/handlers/*.ts
- src/client/cosmoe.ts
- src/scheduler/index.ts
章节来源
- package.json
- src/index.ts
- src/command/index.ts
性能考量
- KV 存储访问:对话与凭证均使用 KV,注意读写次数与延迟,建议:
- 对频繁读取的数据进行本地缓存(如会话状态),减少 KV 访问。
- 批量操作时合并请求,避免多次往返。
- API 调用节流:对外部 API 的调用应进行幂等与去重,避免重复请求。
- 消息长度控制:历史与详情消息可能较长,需在发送前截断,防止 Telegram API 限制导致失败。
- 正则匹配优化:正则表达式尽量简单明确,避免回溯开销;必要时预编译正则对象。
- 会话持久化:使用 KV Adapter 时,确保序列化/反序列化稳定,避免大对象频繁写入。
故障排查指南
- 登录失败
- 检查用户名与密码是否正确,确认 CosmoeClient.getToken 返回码。
- 检查 KV 写入是否成功,确认凭证字段完整。
- 预约失败
- 检查活动 ID 与时间段索引是否有效,确认时间段剩余容量。
- 检查用户凭证是否过期或被删除。
- 历史为空
- 确认用户已登录且凭证有效。
- 检查外部 API 返回码与数据结构。
- 取消失败
- 确认预约 ID 存在且状态允许取消。
- 检查回调解析是否正确。
- 回调无响应
- 确认回调查询已正确注册,answerCallbackQuery 是否被调用。
- 调度通知未送达
- 检查 KV 中用户列表是否为空,确认消息发送返回值。
章节来源
- src/command/handlers/login.ts
- src/command/handlers/bookEvent.ts
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
- src/scheduler/index.ts
结论
该命令系统通过集中式路由与处理器分离的设计,实现了清晰的职责划分与良好的可维护性。借助 @grammyjs/conversations 与 KV 存储,系统支持交互式对话与状态持久化;通过正则表达式与回调查询,实现了灵活的消息与交互处理。建议在生产环境中进一步完善缓存策略、错误监控与日志追踪,以提升稳定性与可观测性。
附录
- 命令菜单设置:入口文件在启动时设置命令菜单,便于用户发现可用命令。
- 定时任务:调度模块定期扫描新活动并向已注册用户推送通知。
章节来源
- src/index.ts
- src/scheduler/index.ts