# 项目概述 **本文引用的文件** - [package.json](file://package.json) - [src/index.ts](file://src/index.ts) - [src/command/index.ts](file://src/command/index.ts) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts) - [src/command/handlers/start.ts](file://src/command/handlers/start.ts) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts) - [src/command/handlers/events.ts](file://src/command/handlers/events.ts) - [src/command/handlers/eventDetails.ts](file://src/command/handlers/eventDetails.ts) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts) - [src/command/handlers/cancel.ts](file://src/command/handlers/cancel.ts) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts) - [src/scheduler/index.ts](file://src/scheduler/index.ts) - [wrangler.jsonc](file://wrangler.jsonc) - [tsconfig.json](file://tsconfig.json) - [test/index.spec.ts](file://test/index.spec.ts) ## 目录 1. [引言](#引言) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖分析](#依赖分析) 7. [性能考虑](#性能考虑) 8. [故障排除指南](#故障排除指南) 9. [结论](#结论) 10. [附录](#附录) ## 引言 Cosmoe Bot 是一个基于 Cloudflare Workers 的 Telegram 摄影活动预约管理机器人。它通过与 Cosmoe 摄影平台的 API 集成,为用户提供便捷的活动浏览、登录认证、在线预约与取消、预约历史查询等功能,并支持定时任务推送新活动通知。项目采用 TypeScript 开发,结合 grammY 框架与 Cloudflare Workers 平台,具备高扩展性与低运维成本。 ## 项目结构 项目采用按功能模块划分的目录组织方式,核心入口位于 src/index.ts,命令路由与处理器集中在 src/command 下,客户端封装在 src/client 中,定时任务逻辑位于 src/scheduler。Wrangler 作为部署与配置工具,tsconfig 提供类型检查与编译选项,test 目录包含单元与集成测试。 ```mermaid graph TB A["src/index.ts
Worker 入口与 Webhook"] --> B["src/command/index.ts
命令注册与对话插件"] B --> C["src/command/handlers/*.ts
命令处理器"] A --> D["src/scheduler/index.ts
定时任务"] C --> E["src/client/cosmoe.ts
Cosmoe API 客户端"] F["wrangler.jsonc
部署与触发器配置"] --> A G["tsconfig.json
TypeScript 编译配置"] --> A H["test/index.spec.ts
测试用例"] --> A ``` 图表来源 - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L1-L503) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L1-L88) - [wrangler.jsonc](file://wrangler.jsonc#L1-L31) - [tsconfig.json](file://tsconfig.json#L1-L46) - [test/index.spec.ts](file://test/index.spec.ts#L1-L25) 章节来源 - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [wrangler.jsonc](file://wrangler.jsonc#L1-L31) - [tsconfig.json](file://tsconfig.json#L1-L46) ## 核心组件 - Worker 入口与 Webhook:负责初始化 Bot、注册命令菜单、设置 Webhook 回调,并在定时事件触发时执行调度逻辑。 - 命令系统与对话插件:通过 grammY 的 conversations 插件实现多步交互式登录与状态持久化;统一注册各类命令与回调。 - Cosmoe API 客户端:封装与 Cosmoe 平台的交互,包括认证、活动列表、活动详情、预约、历史、取消等接口。 - 定时任务:周期性拉取活动列表,识别新增活动并向已登录用户发送通知。 - 存储层:使用 Cloudflare KV 保存用户凭证与对话状态,确保跨实例一致性与可扩展性。 章节来源 - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L1-L503) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L1-L88) ## 架构总览 下图展示了从 Telegram 用户到 Cloudflare Workers、grammY、KV 存储与外部 Cosmoe API 的整体数据流与控制流。 ```mermaid sequenceDiagram participant U as "Telegram 用户" participant W as "Cloudflare Worker
src/index.ts" participant CMD as "命令系统
src/command/index.ts" participant CONV as "对话插件
conversations" participant KV as "KV 存储
COSMOE_CREDENTIALS/COSMOE_STORAGE" participant API as "Cosmoe API 客户端
src/client/cosmoe.ts" participant S as "定时任务
src/scheduler/index.ts" U->>W : 发送命令/消息 W->>CMD : 路由到对应处理器 CMD->>CONV : 进入对话如登录 CONV->>KV : 读写对话状态 CMD->>API : 调用 Cosmoe API API-->>CMD : 返回结果 CMD-->>U : 发送回复/内联键盘 S->>API : 拉取活动列表 API-->>S : 返回活动数据 S->>KV : 读取/更新最新活动ID S-->>U : 推送新活动通知 ``` 图表来源 - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L1-L503) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L1-L88) ## 详细组件分析 ### 命令系统与对话插件 - 组件职责 - 注册命令菜单与路由:start、login、events、event_{id}、book_{event}_{slot}、history、cancel_{id}、callbackQuery 等。 - 对话插件:基于 @grammyjs/conversations,使用 @grammyjs/storage-cloudflare 将对话状态持久化至 KV。 - 环境绑定:通过 Env 接口注入 KV 命名空间与 Bot 配置。 - 关键流程 - 登录对话:交互式收集用户名与密码,调用 Cosmoe API 获取 token,存入 KV。 - 预约流程:解析 /book_{event}_{slot},校验时段余量,支持优惠券选择与二次确认。 - 取消流程:解析 /cancel_{id},弹出确认内联键盘,二次确认后调用取消接口。 - 错误处理 - 对话读写 KV 失败时记录日志并提示用户重试。 - API 调用异常时捕获并反馈错误信息。 ```mermaid flowchart TD Start(["进入命令处理"]) --> Route{"匹配命令/正则/回调"} Route --> |"/login"| LoginConv["进入登录对话"] Route --> |"/events"| EventsList["获取活动列表并展示"] Route --> |"/event_{id}"| EventDetail["获取活动详情并展示时段"] Route --> |"/book_{event}_{slot}"| BookFlow["校验余量/优惠券并下单"] Route --> |"/history"| History["获取并格式化历史记录"] Route --> |"/cancel_{id}"| CancelFlow["生成确认内联键盘"] Route --> |回调: 确认/优惠券选择| ConfirmOrCoupon["执行确认或优惠券选择"] LoginConv --> End(["结束"]) EventsList --> End EventDetail --> End BookFlow --> End History --> End CancelFlow --> End ConfirmOrCoupon --> End ``` 图表来源 - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L1-L75) - [src/command/handlers/events.ts](file://src/command/handlers/events.ts#L1-L27) - [src/command/handlers/eventDetails.ts](file://src/command/handlers/eventDetails.ts#L1-L61) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L1-L226) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L1-L107) - [src/command/handlers/cancel.ts](file://src/command/handlers/cancel.ts#L1-L132) 章节来源 - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L1-L75) - [src/command/handlers/events.ts](file://src/command/handlers/events.ts#L1-L27) - [src/command/handlers/eventDetails.ts](file://src/command/handlers/eventDetails.ts#L1-L61) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L1-L226) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L1-L107) - [src/command/handlers/cancel.ts](file://src/command/handlers/cancel.ts#L1-L132) ### Cosmoe API 客户端 - 组件职责 - 封装认证、活动查询、详情获取、个人资料、预约、历史、取消、优惠券等 API。 - 支持手动设置凭证与自动读取 KV 凭证。 - 数据模型要点 - 认证响应包含 user_id 与 token。 - 活动详情包含多个时间段与剩余容量。 - 预约返回包含最终价格与订单号等字段。 - 错误处理 - 未认证调用抛出错误;KV 读写失败记录日志并提示用户。 ```mermaid classDiagram class CosmoeClient { +getToken(username, password) +setCredentials(userId, token) +getCredentials() +isAuthenticated() +getEvents() +getEventDetail(eventId) +getProfile() +getMyBookings() +bookEvent(bookingRequest) +getAvailableCoupons(eventId) +cancelBooking(bookingId) +updatePaymentOrder(bookingId, orderId) +updateBookingNote(bookingId, note) +selfReschedule(bookingId, newSlot) +selfTransfer(bookingId, recipient) +changePassword(current, new) +register(key, username, email, password, identity) } ``` 图表来源 - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L1-L503) 章节来源 - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L1-L503) ### 定时任务与新活动通知 - 组件职责 - 每分钟检查一次新活动,对比上次最大活动 ID,向所有已登录用户推送通知。 - 使用 KV 存储“最新活动ID”,保证幂等与去重。 - 关键流程 - 拉取活动列表 → 过滤新活动 → 遍历已登录用户 → 发送通知 → 更新最新活动ID。 ```mermaid flowchart TD Tick["定时触发"] --> LoadLast["读取最新活动ID"] LoadLast --> Fetch["拉取活动列表"] Fetch --> Filter{"过滤新活动"} Filter --> |有| Notify["遍历已登录用户并发送通知"] Filter --> |无| End["结束"] Notify --> Update["更新最新活动ID"] Update --> End ``` 图表来源 - [src/scheduler/index.ts](file://src/scheduler/index.ts#L1-L88) - [wrangler.jsonc](file://wrangler.jsonc#L13-L17) 章节来源 - [src/scheduler/index.ts](file://src/scheduler/index.ts#L1-L88) - [wrangler.jsonc](file://wrangler.jsonc#L13-L17) ### 存储与环境配置 - KV 命名空间 - COSMOE_CREDENTIALS:存储用户的认证凭证(user_id、token、timestamp)。 - COSMOE_STORAGE:存储调度任务的“最新活动ID”等状态。 - 环境变量 - BOT_INFO、BOT_TOKEN:Bot 基本信息与令牌。 - KV 绑定:通过 wrangler.jsonc 配置。 章节来源 - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) - [src/command/index.ts](file://src/command/index.ts#L20-L52) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L50-L65) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L26-L28) ## 依赖分析 - 运行时依赖 - grammY:Telegram Bot 框架,提供命令路由、回调、内联键盘等能力。 - @grammyjs/conversations:对话插件,配合 KV 实现多步交互。 - @grammyjs/storage-cloudflare:KV 适配器,用于对话状态持久化。 - 开发依赖 - wrangler:Cloudflare Workers 部署与本地开发工具。 - vitest 与 @cloudflare/vitest-pool-workers:Workers 友好的测试框架。 - typescript:类型系统与编译支持。 - 项目脚本 - dev/start:本地开发与调试。 - deploy:部署到 Cloudflare Workers。 - test:运行测试。 - cf-typegen:生成 Cloudflare 类型定义。 ```mermaid graph LR P["package.json"] --> G["grammy"] P --> C["@grammyjs/conversations"] P --> K["@grammyjs/storage-cloudflare"] P --> W["wrangler"] P --> V["vitest"] P --> T["typescript"] ``` 图表来源 - [package.json](file://package.json#L12-L22) 章节来源 - [package.json](file://package.json#L1-L24) ## 性能考虑 - 无服务器冷启动:Cloudflare Workers 在首次访问时可能产生冷启动延迟,建议通过常驻与合理拆分任务降低影响。 - KV 读写:对话状态与用户凭证均使用 KV,注意批量读写与错误重试策略,避免阻塞主线程。 - API 调用:对 Cosmoe API 的调用应尽量减少不必要的请求,例如在预约前先获取活动详情以校验时段。 - 定时任务频率:当前每分钟触发一次,建议根据实际业务量调整 cron 表达式,避免过度轮询。 - 消息长度限制:历史查询与活动详情需遵守 Telegram 的消息长度限制,必要时截断或分条发送。 ## 故障排除指南 - 登录失败 - 检查用户名/密码是否正确,确认 Cosmoe API 返回码与消息。 - 确认 KV 写入是否成功,查看日志输出。 - 预约失败 - 确认用户已登录且凭证有效。 - 检查活动时段余量与优惠券可用性。 - 查看 API 返回的错误信息并提示用户。 - 取消失败 - 确认预约状态允许取消,且未过期。 - 检查回调数据解析与 KV 凭证有效性。 - 新活动通知未送达 - 检查定时任务是否正常触发与 KV 最新活动ID是否更新。 - 确认已登录用户列表是否正确,关注用户拒收或账号异常情况。 - 测试与本地调试 - 使用 vitest 与 @cloudflare/vitest-pool-workers 运行测试。 - 通过 wrangler dev 启动本地服务,结合日志定位问题。 章节来源 - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L67-L74) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L114-L118) - [src/command/handlers/cancel.ts](file://src/command/handlers/cancel.ts#L80-L84) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L81-L84) - [test/index.spec.ts](file://test/index.spec.ts#L1-L25) ## 结论 Cosmoe Bot 通过 Cloudflare Workers 与 grammY 的组合,构建了一个轻量、可扩展、易于维护的摄影活动预约机器人。其清晰的模块划分、完善的错误处理与定时通知机制,使其能够稳定支撑日常运营与用户交互。未来可在前端界面、支付对接与更细粒度的权限控制方面进一步增强用户体验与安全性。 ## 附录 ### 快速开始指南 - 环境准备 - 安装依赖:使用包管理器安装项目依赖。 - 配置 Wrangler:在 wrangler.jsonc 中完善 KV 绑定、Bot 令牌与 Cron 触发器。 - 本地开发 - 启动本地服务:运行开发脚本,访问本地端口进行调试。 - 运行测试:使用测试脚本验证核心流程。 - 部署上线 - 执行部署脚本,将 Worker 部署到 Cloudflare Workers。 - 在 Telegram 中配置 Webhook 与命令菜单,完成机器人上线。 章节来源 - [package.json](file://package.json#L5-L11) - [wrangler.jsonc](file://wrangler.jsonc#L1-L31) - [src/index.ts](file://src/index.ts#L13-L35) - [test/index.spec.ts](file://test/index.spec.ts#L1-L25)