新命令开发
本文档引用的文件
- 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
目录
- 简介
- 项目结构
- 核心组件
- 架构总览
- 详细组件分析
- 依赖分析
- 性能考虑
- 故障排除指南
- 结论
- 附录:新命令开发完整流程
简介
本指南面向希望为现有命令系统添加新命令的开发者,涵盖命令注册流程、参数解析方法、业务逻辑实现步骤、命令路由机制(command() 与 hears() 的使用场景与区别)、错误处理策略以及用户交互最佳实践。文档以项目现有实现为蓝本,提供可直接参考的扩展示例,帮助快速完成新命令的开发与集成。
项目结构
该项目基于 grammY 框架与 Cloudflare Workers 运行时,采用模块化组织方式:
图表来源
- src/index.ts
- src/command/index.ts
章节来源
- src/index.ts
- src/command/index.ts
核心组件
- Bot 初始化与 Webhook 回调:入口文件创建 Bot 实例,配置命令菜单,并通过 Cloudflare Worker 的 webhookCallback 接收外部请求。
- 命令注册中心:命令模块集中导入各处理器,使用 bot.command() 与 bot.hears() 注册命令与模式匹配事件,并安装对话插件支持多步交互。
- 命令处理器:每个处理器负责单一职责,处理参数、调用客户端 API、构建响应消息并进行错误处理。
- 客户端封装:CosmoeClient 提供认证、活动查询、预约、取消等 API 调用,统一返回格式与错误处理。
章节来源
- src/index.ts
- src/command/index.ts
- src/client/cosmoe.ts
架构总览
下图展示了从用户触发命令到业务处理与响应的完整流程,以及命令路由机制的差异。
sequenceDiagram
participant U as "用户"
participant W as "Cloudflare Webhook"
participant B as "Bot 实例"
participant R as "命令路由"
participant H as "命令处理器"
participant C as "CosmoeClient"
participant API as "第三方 API"
U->>W : "发送消息/命令"
W->>B : "webhookCallback 分发"
B->>R : "根据路由规则匹配"
alt "命令路由 : bot.command()"
R->>H : "调用对应处理器"
else "模式路由 : bot.hears()"
R->>H : "正则匹配后调用处理器"
else "回调路由 : bot.callbackQuery()"
R->>H : "回调数据解析后调用处理器"
end
H->>C : "调用客户端 API"
C->>API : "HTTP 请求"
API-->>C : "返回结果"
C-->>H : "标准化结果"
H-->>U : "发送响应消息"
图表来源
- src/index.ts
- src/command/index.ts
- src/client/cosmoe.ts
详细组件分析
命令注册与路由机制
- bot.command("命令名", 处理器):用于标准命令触发,例如 /start、/login、/events、/history、/logout。
- bot.hears(正则表达式, 处理器):用于模式匹配的消息触发,例如 /event{id}、/book{eventid}{slotid}、/cancel{booking_id}。
bot.callbackQuery(正则表达式, 处理器):用于内联键盘回调,例如确认取消、选择优惠券等。
flowchart TD
Start(["收到消息"]) --> Type{"消息类型"}
Type --> |命令| Cmd["bot.command() 匹配"]
Type --> |文本/正则| Hears["bot.hears() 匹配"]
Type --> |回调| CB["bot.callbackQuery() 匹配"]
Cmd --> Handler["调用对应处理器"]
Hears --> Handler
CB --> Handler
Handler --> End(["发送响应"])
图表来源
章节来源
参数解析与业务逻辑实现步骤
- 解析上下文参数:从 ctx.from、ctx.message、ctx.match、ctx.callbackQuery.data 等提取必要信息。
- 数据校验:检查用户身份、登录状态、参数有效性、业务约束(如时间范围、库存)。
- 调用客户端 API:通过 CosmoeClient 执行业务操作,统一处理返回码与错误信息。
- 构建响应消息:使用 Markdown 或纯文本格式,注意长度限制与 Telegram API 限制。
- 错误处理:捕获异常、记录日志、向用户反馈错误信息。
章节来源
- 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/login.ts
- src/command/handlers/logout.ts
命令处理器示例分析
启动命令处理器
- 功能:向用户展示可用命令列表。
- 关键点:使用 ctx.reply 发送文本;适合简单无状态命令。
章节来源
- src/command/handlers/start.ts
事件列表命令处理器
- 功能:拉取最新活动并生成列表链接。
- 关键点:调用 CosmoeClient.getEvents;限制数量;Markdown 格式化;错误兜底。
章节来源
- src/command/handlers/events.ts
事件详情与预约命令处理器
- 功能:解析 /event{id} 与 /book{eventid}{slot_id},展示时间槽并支持优惠券选择。
关键点:正则匹配参数;KV 存储用户凭证;调用 getEventDetail 与 getAvailableCoupons;内联键盘与回调处理;Markdown 格式化;库存与时间判断。
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant ED as "事件详情处理器"
participant BE as "预约处理器"
participant C as "CosmoeClient"
participant API as "第三方 API"
U->>B : "/event_123"
B->>ED : "匹配 /event_{id}"
ED->>C : "getEventDetail(id)"
C->>API : "请求"
API-->>C : "返回活动详情"
C-->>ED : "返回数据"
ED-->>U : "发送活动详情与可预约链接"
U->>B : "/book_123_0"
B->>BE : "匹配 /book_{event_id}_{slot_id}"
BE->>C : "getEventDetail(eventId)"
C-->>BE : "返回数据"
BE->>BE : "检查库存与优惠券"
alt "多个优惠券"
BE-->>U : "显示优惠券选择键盘"
U->>B : "回调 : select_coupon_123_0_xxx"
B->>BE : "解析回调"
BE->>C : "bookEvent(couponCode)"
else "单个/无优惠券"
BE->>C : "bookEvent()"
end
C-->>BE : "返回结果"
BE-->>U : "发送预约结果"
图表来源
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
- src/client/cosmoe.ts
章节来源
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
预约历史与取消命令处理器
- 功能:读取用户凭证,查询预约历史,生成 Markdown 列表,并提供取消链接与确认回调。
关键点:KV 读写凭证;认证校验;排序与截断;贝时拼接与比较;内联键盘确认;回调解析与状态更新。
sequenceDiagram
participant U as "用户"
participant B as "Bot"
participant HC as "历史处理器"
participant CC as "取消处理器"
participant C as "CosmoeClient"
participant API as "第三方 API"
U->>B : "/history"
B->>HC : "调用历史处理器"
HC->>HC : "KV 读取凭证"
HC->>C : "getMyBookings()"
C-->>HC : "返回历史数据"
HC-->>U : "发送历史列表(含取消链接)"
U->>B : "/cancel_456"
B->>CC : "匹配 /cancel_{booking_id}"
CC->>C : "getMyBookings() 确认"
CC-->>U : "发送确认键盘"
U->>B : "回调 : confirm_cancel_456"
B->>CC : "解析回调"
CC->>C : "cancelBooking(456)"
C-->>CC : "返回结果"
CC-->>U : "编辑消息为取消结果"
图表来源
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
- src/client/cosmoe.ts
章节来源
- src/command/handlers/history.ts
- src/command/handlers/cancel.ts
登录与登出命令处理器
- 功能:登录采用对话式多步交互,登出清理 KV 中的凭证。
- 关键点:对话插件与 KV 适配器;凭证持久化;错误处理与提示。
章节来源
- src/command/handlers/login.ts
- src/command/handlers/logout.ts
客户端 API 封装
CosmoeClient 统一封装了认证、活动、预约、取消、转账、改密等 API,提供一致的返回格式与错误处理,便于命令处理器复用。
章节来源
依赖分析
- grammY:核心框架,提供 Bot、Context、InlineKeyboard、callbackQuery 等能力。
- @grammyjs/conversations:对话插件,支持多步交互与状态存储。
- @grammyjs/storage-cloudflare:KV 存储适配器,用于会话状态持久化。
Cloudflare Workers:运行时环境,提供 KVNamespace、Webhook 回调等。
graph TB
Pkg["package.json 依赖"] --> G["grammy"]
Pkg --> Conv["@grammyjs/conversations"]
Pkg --> KV["@grammyjs/storage-cloudflare"]
G --> Bot["Bot/Context"]
Conv --> Conversations["对话插件"]
KV --> Storage["KV 适配器"]
图表来源
章节来源
性能考虑
- 消息长度控制:历史与详情消息可能较长,需限制长度避免 Telegram API 限制。
- KV 访问优化:尽量减少 KV 读写次数,合并请求或缓存必要数据。
- 异步并发:合理使用异步调用,避免阻塞主线程。
- 错误快速失败:在参数无效或状态不满足时尽早返回,减少无效请求。
故障排除指南
- 命令未生效:检查命令是否在入口文件中设置命令菜单,以及命令注册是否正确导入。
- 参数解析失败:确认正则表达式与 ctx.match 是否匹配预期格式。
- 凭证问题:检查 KV 中是否存在用户凭证,登录状态是否有效。
- API 返回异常:查看客户端封装的错误处理与返回码,确保统一处理。
- 回调无响应:确认回调查询与回调处理器的正则匹配一致,并正确调用 answerCallbackQuery。
章节来源
- src/index.ts
- src/command/index.ts
- src/command/handlers/history.ts
- src/command/handlers/bookEvent.ts
结论
通过统一的命令注册中心与清晰的处理器职责划分,项目实现了可扩展的命令系统。新增命令应遵循现有模式:在命令模块中注册路由,在 handlers 目录中实现处理器,利用 CosmoeClient 封装 API,严格进行参数校验与错误处理,并通过 Markdown 等格式提升用户体验。
附录:新命令开发完整流程
第一步:设计命令与参数
- 明确命令名称与用途(如 /admin_stats)。
- 设计参数格式与可选参数,决定使用 bot.command() 还是 bot.hears()。
- 规划用户交互流程(是否需要内联键盘或对话)。
第二步:创建处理器文件
- 在 src/command/handlers 下新建处理器文件,导出异步处理函数。
- 处理函数签名建议:handleXxxCommand(ctx, env?),其中 env 可选用于访问 KV 等资源。
章节来源
- src/command/handlers/start.ts
- src/command/handlers/history.ts
第三步:实现业务逻辑
- 解析参数:从 ctx.from、ctx.message、ctx.match、ctx.callbackQuery.data 获取必要信息。
- 校验输入:检查用户身份、登录状态、参数合法性。
- 调用客户端:通过 CosmoeClient 执行业务操作。
- 构建响应:使用 Markdown 或纯文本,注意长度限制与格式规范。
章节来源
- src/command/handlers/eventDetails.ts
- src/command/handlers/bookEvent.ts
第四步:注册命令路由
- 在命令模块中导入新处理器。
- 使用 bot.command() 或 bot.hears() 注册路由,必要时使用 bot.callbackQuery() 注册回调。
- 如需对话交互,使用 conversations 插件与 createConversation 包裹处理器。
章节来源
- src/command/index.ts
- src/command/index.ts
- src/command/index.ts
- src/command/index.ts
第五步:设置命令菜单
- 在入口文件中调用 bot.api.setMyCommands 设置命令菜单,确保用户可见。
章节来源
第六步:错误处理与测试
- 添加 try-catch 捕获异常,记录日志并友好提示用户。
- 编写单元测试覆盖正常与异常路径。
- 在本地与生产环境中验证命令行为。
章节来源
- src/command/handlers/login.ts
- src/command/handlers/history.ts
示例:添加一个“管理员统计”命令
- 命令设计:/admin_stats,仅管理员可用,返回预约统计与用户活跃度。
- 参数:可选日期范围(YYYY-MM-DD),默认最近7天。
- 路由:使用 bot.command() 注册。
- 处理器:解析参数、校验管理员权限、调用客户端统计接口、构建 Markdown 表格并发送。
- 回调:如需分页或筛选,使用内联键盘与回调处理。
章节来源
- src/command/index.ts
- src/command/handlers/history.ts