TomPlanche/monclub-bot

GitHub: TomPlanche/monclub-bot

一个用于通过反向工程 API 自动化预订和取消 MonClub 课程的 CLI 与 Discord 机器人工具。

Stars: 1 | Forks: 0

# monclub-bot 在 MonClub 驱动的平台上预订并取消课程。 提供终端 CLI 和 Discord 斜杠命令机器人两种使用方式。 MonClub 应用未公开 API。请参考 [REVERSE_ENGINEERING.md](./REVERSE_ENGINEERING.md) 了解如何通过拦截应用 HTTPS 流量发现 API。 ## 二进制文件 | 二进制文件 | 命令 | 描述 | |--------|---------|-------------| | `monclub-bot` | `cargo run --release` | 交互式终端 CLI(默认) | | `monclub-discord` | `cargo run --release --features discord --bin monclub-discord` | Discord 斜杠命令机器人 | `discord` 特性是可选的。不启用时,`poise` 和 `tokio` 不会被编译,从而保持 CLI 构建快速且精简。 ## 运行 ### CLI ``` # 开发 cargo run # 发行版(推荐) cargo run --release # 预编译二进制文件 cargo build --release ./target/release/monclub-bot ``` ### Discord 机器人 ``` # 开发 cargo run --features discord --bin monclub-discord # 发行版(推荐) cargo run --release --features discord --bin monclub-discord # 预编译二进制文件 cargo build --release --features discord ./target/release/monclub-discord ``` ### 一次性构建两者 ``` cargo build --release --features discord ``` ## 配置 ### 核心配置(两个二进制文件均需) | 变量 | 是否必需 | 默认值 | 描述 | |----------|----------|---------|-------------| | `EMAIL` | 是 | | 账户邮箱 | | `PASSWORD` | 是 | | 账户密码 | | `CUSTOM_ID` | 是 | | 俱乐部标识符 | | `BASE_URL` | 是 | | API 基础 URL(不得以斜杠结尾) | | `LATITUDE` | 否 | | 发送至 API 的纬度(影响邻近排序) | | `LONGITUDE` | 否 | | 发送至 API 的经度(影响邻近排序) | | `RETRY_DURATION` | 否 | `300` | 重试总时长(秒),超时后放弃 | | `RETRY_INTERVAL` | 否 | `5` | 重试间隔(秒) | ### Discord 机器人 | 变量 | 是否必需 | 描述 | |----------|----------|-------------| | `DISCORD_TOKEN` | 是 | 来自 [Discord 开发者门户](https://discord.com/developers/applications) 的机器人令牌 | | `DISCORD_OWNER_ID` | 否 | 你的 Discord 用户 ID。设置后,仅该用户可触发命令。 | ### 日志记录 | 变量 | 默认值 | 描述 | |----------|---------|-------------| | `RUST_LOG` | `info` | 日志级别过滤。接受 `error`、`warn`、`info`、`debug`、`trace` 或 crate 特定指令(例如 `monclub_bot=debug`)。 | 日志写入 `logs/.YYYY-MM-DD.log`(每日轮转,保留 30 天)。Discord 机器人也会将日志镜像到标准输出,以便与 systemd 或 Docker 配合使用。 ## 使用 ### CLI ``` cargo run --bin monclub-bot ``` 1. 对 API 进行身份验证 2. 提示选择 **预订** 或 **查看/管理预订** **预订** 1. 获取所有即将开始的课程会话 2. 显示选择提示 3. 请求确认 4. 提交预订,若返回 409 则持续重试,直到时段开放或达到截止时间 **查看/管理预订** 1. 获取用户的即将开始的预订列表 2. 显示选择提示 3. 对选中的预订执行操作: - **查看信息** — 获取并显示完整会话详情(地点、容量、教练、参与者列表等) - **取消预订** — 请求确认后提交取消请求 ### Discord 机器人 ``` cargo run --release --features discord --bin monclub-discord ``` 斜杠命令在首次启动时全局注册(可能需要最多一小时传播,但在你的服务器中通常即时生效)。 #### 设置 1. 在 [discord.com/developers/applications](https://discord.com/developers/applications) 创建应用与机器人 2. 将机器人令牌复制到 `.env` 中的 `DISCORD_TOKEN` 3. 在 Discord 设置中启用 **开发者模式**,然后右键点击你的用户名并选择 **复制用户 ID**;将其设置为 `DISCORD_OWNER_ID` 4. 使用 `bot` 和 `applications.commands` 作用域邀请机器人加入服务器 #### 命令 | 命令 | 描述 | |---------|-------------| | `/list [limit]` | 列出可用会话并按日期排序,每个会话附带其 ID。可选 `limit` 限制返回前 N 条结果。长列表会自动拆分为多条消息。 | | `/book ` | 预订一个会话。`session` 参数支持自动补全——输入筛选或从 `/list` 粘贴 ID。若插槽返回 409(尚未开放),后台任务将持续重试并在确认或截止到达时发送跟进消息。 | | `/booking ` | 显示某条预订的完整详情:会话名称、日期/时间、地点、参与人数、教练、描述及编号参与者列表。`booking` 参数支持自动补全。 | | `/cancel ` | 取消一个即将开始的预订。`booking` 参数支持自动补全。 | | `/bookings` | 列出你的即将开始的预订。 | #### 作为服务运行(systemd 示例) ``` [Unit] Description=monclub Discord bot After=network-online.target [Service] WorkingDirectory=/path/to/monclub-bot ExecStart=/path/to/monclub-bot/target/release/monclub-discord Restart=on-failure RestartSec=10 EnvironmentFile=/path/to/monclub-bot/.env [Install] WantedBy=multi-user.target ``` ## API 端点 所有请求均发送至 `BASE_URL`,并携带以下头部: ``` Content-Type: application/json Accept: application/json Accept-Language: fr User-Agent: okhttp/4.12.0 ``` 认证请求额外携带: ``` authorization: ``` ### POST /users/custom/authenticate/email/v2 邮箱探测——两步认证流程的第一步。 **查询参数** | 参数 | 值 | |-------|-------| | `withCoachAuthentication` | `true` | **请求体** ``` { "email": "your@email.com" } ``` **响应**:不使用,仅检查状态码。 ### POST /users/custom/authenticate/v2 完整认证——返回会话令牌。 **请求体** ``` { "credentials": { "email": "your@email.com", "password": "yourpassword" }, "customId": "", "deviceInfo": { "os": "Android 14", "model": "Phone (2)", "brand": "Nothing", "version": "3.6.0" }, "coachAuthentication": false } ``` **响应**(相关字段) ``` { "token": "", "userId": "" } ``` `userId` 也可能嵌套为 `user._id`。 ### POST /nearfilters/favorite/myclub 列出用户所属俱乐部的所有即将开始的会话。 **查询参数** | 参数 | 值 | |-------|-------| | `customId` | `` | | `userId` | `` | **请求体** ``` { "filters": { "tagName": "myclub", "coordinates": [2.3376446, 48.8704031], "price": null, "discipline": null, "date": null, "time": null, "level": null, "type": null, "category": null, "pinnedSlots": null, "categoryId": null, "group": null }, "coordinates": [2.3376446, 48.8704031] } ``` `coordinates` 为 `[longitude, latitude]`。两个字段均为可选,可为 `null`;提供后会影响邻近排序。 **响应**:JSON 格式的会话对象数组。非数组响应将被视为空列表。 ``` [ { "_id": "", "sessionName": "LOISIR Supplémentaire - Samedi Lacretelle- 17h-19h", "date": "2026-03-28T16:00:00.000Z", "time": "17H00", "type": "free", "discipline": "volley" } ] ``` ### GET /bookings/user/:userId 返回认证用户的即将开始的预订。 **查询参数** | 参数 | 值 | |-------|-------| | `category` | `ondemand` | | `temporality` | `fromToday` | **响应**:JSON 格式的预订对象数组。会话详情嵌套在 `session` 数组中。 ``` [ { "_id": "", "sessionId": "", "session": [ { "_id": "", "sessionName": "Jeu Libre", "date": "2026-03-28T16:00:00.000Z", "time": "17H00" } ] } ] ``` ### POST /sessions/withuser 获取单个会话的完整详情,包括参与者列表。 **请求体** ``` { "sessionId": "", "userId": "" } ``` **响应**(相关字段,包装在 `session` 键下) ``` { "session": { "_id": "", "sessionName": "Session name", "date": "2026-04-04T15:00:00.000Z", "time": "17H00", "endTime": "19H00", "place": { "name": "Venue name", "address": "1 rue Example", "zipCode": "75000", "city": "Paris" }, "totalQuantityFree": 24, "price": 5, "level": "allLevels", "description": "Jeu libre", "info": "Sur inscription préalable", "coachs": [ { "fullName": "Coach Name" } ], "yesParticipants": ["", "..."], "attendees": [ { "userId": "", "fullName": "User Name", "memberNumber": 1001 } ] }, "accessToTrialSession": false } ``` ### POST /sessions/book/licenseeFromClub 为认证用户预订或取消会话。同一端点用于两种操作;`isPresent` 字段用于区分。 #### 预订 **请求体** ``` { "participant": { "userId": "", "isPresent": "yes", "coordinates": null }, "sessionId": "", "customId": "" } ``` **状态码** | 状态码 | 含义 | |------|---------| | `2xx` | 预订确认 | | `409` | 时段尚未开放——机器人将持续重试直到截止时间 | | other | 致命错误——机器人退出 | #### 取消 **请求体** ``` { "participant": { "userId": "", "isPresent": "no", "coordinates": null, "bookingId": "" }, "sessionId": "", "customId": "" } ``` `bookingId` 为 `GET /bookings/user/:userId` 返回的 `_id`。 **状态码** | 状态码 | 含义 | |------|---------| | `2xx` | 取消确认 | | other | 致命错误——机器人退出 |
标签:API 逆向, Cargo, CLI, Discord 机器人, .env, HTTPS 抓包, MonClub, Rust, SEO 机器人, Waymore结果处理, Web 逆向, WiFi技术, 交互式命令行, 会话预订, 反向工程, 取消预订, 可视化界面, 地理坐标, 定时任务, 数字取证, 文档结构分析, 斜杠命令, 环境配置, 终端工具, 网络流量审计, 自动化脚本, 自动化预约, 订阅通知, 通知系统, 重试机制