命令系统.md 22 KB

命令系统

本文引用的文件

  • 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

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件系统性梳理该 Telegram 机器人的命令系统,覆盖命令路由机制(command() 与 hears())、正则表达式匹配、交互式对话(conversations)与回调查询处理、以及命令处理器的实现模式与错误处理策略。重点解释 /start、/login、/events、/history、/logout 等命令的处理流程,并给出命令注册最佳实践与性能优化建议。

项目结构

  • 入口与运行时:Cloudflare Worker 环境下的入口文件负责初始化 Bot、设置命令菜单、挂载命令路由与调度任务。
  • 命令注册中心:集中定义所有命令与消息监听规则,统一管理对话与回调。
  • 命令处理器:按功能拆分到 handlers 目录,职责单一,便于测试与维护。
  • 客户端封装:对第三方 API 的统一封装,提供认证、事件列表、预约、历史等能力。
  • 调度模块:定时任务用于新活动通知推送。

    graph TB
    A["入口: src/index.ts"] --> B["命令注册: src/command/index.ts"]
    B --> C["命令处理器: handlers/*"]
    C --> D["客户端: src/client/cosmoe.ts"]
    A --> E["调度: src/scheduler/index.ts"]
    A --> F["依赖: package.json"]
    

图表来源

  • 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