docs: add EN doc index, contributing guide, bot guide; bilingual issue/PR templates (#713)

* docs: add EN doc index, contributing guide, bot guide; bilingual issue/PR templates

- Add docs/INDEX_EN.md: full English docs index with China-market glossary
- Add docs/CONTRIBUTING_EN.md: English contributing guide (setup, CI, commit conventions)
- Add docs/bot-command_EN.md: English bot integration guide (commands, webhooks, config)
- Bilingualize .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md
- Update .github/ISSUE_TEMPLATE/config.yml with English Docs Index link
- Bilingualize .github/PULL_REQUEST_TEMPLATE.md checklist and field labels
- Add CONTRIBUTING_EN and INDEX_EN links to docs/README_EN.md nav bar

Refs #711

* docs: fix review feedback on bot-command_EN and CONTRIBUTING_EN

- Correct bot/platforms/ directory tree to match actual files
  (feishu_stream.py+discord.py present; feishu.py/wecom.py/telegram.py absent)
- Add missing commands: /ask, /chat, /batch to commands table
- Fix BotCommand.execute() signature: sync def, not async
- Clarify webhook routes as planned/not-yet-registered in FastAPI;
  point to bot/handler.py as the actual implementation location
- Fix backend-gate description in CI table to include ./test.sh code
  and ./test.sh yfinance steps from ci_gate.sh

* docs: fix format_response signature and webhook route status in bot-command_EN

- format_response: correct signature to (response, message) -> WebhookResponse
  to match bot/platforms/base.py abstract method
- Webhook route table: clarify that only dingtalk is in ALL_PLATFORMS (webhook
  mode ready); feishu is stream-only; wecom/telegram not yet implemented
- Add concrete example for mounting dingtalk webhook in FastAPI

* docs: fix remaining review feedback in EN docs

- bot-command_EN: stop claiming bot env keys are in .env.example
- bot-command_EN: mount webhook routes in api/app.py instead of api/v1/router.py
- CONTRIBUTING_EN: keep PR CI table limited to actual pull-request checks
- CONTRIBUTING_EN: clarify network-smoke is schedule/workflow_dispatch only

* docs: clarify EN issue links and bot env guidance
This commit is contained in:
mumu
2026-03-16 23:51:10 +08:00
committed by GitHub
parent 1ea9db3e53
commit f99f07d716
8 changed files with 534 additions and 67 deletions

View File

@@ -1,47 +1,65 @@
---
name: Bug 报告
about: 报告一个问题帮助我们改进
name: Bug 报告 / Bug Report
about: 报告一个问题帮助我们改进 / Report a problem to help us improve
title: '[Bug] '
labels: bug
assignees: ''
---
## ⚠️ 提交前必读
请确认已更新到最新版本后再提交 Issue避免重复报告已修复的问题
<!--
中文用户请直接填写下方表单
English users: fill in the English fields below (marked with 🌐).
-->
## 版本确认(必填)
- [ ] 我已同步最新代码Fork 用户请先 Sync fork然后重新运行 Actions
- 代码版本:
- 本地运行:执行 `git rev-parse --short HEAD` 的输出______
- GitHub Actions查看 workflow 运行日志开头的 commit hash______
## ⚠️ 提交前必读 / Before You Submit
## 问题描述
简明扼要地描述遇到的问题。
请确认已更新到最新版本后再提交 Issue避免重复报告已修复的问题。
Please confirm you are on the latest version before filing, to avoid duplicate reports of already-fixed issues.
## 复现步骤
1. 执行命令 '...'
2. 配置 '...'
3. 查看 '...'
4. 出现错误
## 版本确认 / Version Check必填 / Required
- [ ] 我已同步最新代码 / I am on the latest commit (Fork users: Sync fork first, then re-run Actions)
- 代码版本 / Commit hash:
- 本地 / Local: `git rev-parse --short HEAD`______
- GitHub Actions: commit hash shown at the top of the workflow log → ______
## 期望行为
描述你期望发生的情况。
## 问题描述 / Problem Description
## 实际行为
描述实际发生的情况。
简明扼要地描述遇到的问题。
Briefly describe the problem.
## 复现步骤 / Reproduction Steps
1. 执行命令 / Run command '...'
2. 配置 / Config '...'
3. 查看 / View '...'
4. 出现错误 / Error occurs
## 期望行为 / Expected Behavior
描述你期望发生的情况。
Describe what you expected to happen.
## 实际行为 / Actual Behavior
描述实际发生的情况。
Describe what actually happened.
## 错误日志 / Error Logs
## 错误日志
```
粘贴相关错误日志
Paste relevant error logs here
```
## 环境信息
- 操作系统: [例如 Ubuntu 22.04]
- Python 版本: [例如 3.11]
- 运行方式: [本地/Docker/GitHub Actions]
- 相关配置:
- GEMINI_MODEL:
- 数据源:
## 环境信息 / Environment
## 其他信息
添加任何其他有关问题的信息或截图。
- 操作系统 / OS: [e.g. Ubuntu 22.04 / Windows 11 / macOS 14]
- Python 版本 / Python version: [e.g. 3.11]
- 运行方式 / Run mode: [Local / Docker / GitHub Actions]
- 相关配置 / Relevant config:
- GEMINI_MODEL / AI model:
- 数据源 / Data source:
## 其他信息 / Additional Context
添加任何其他有关问题的信息或截图。
Add any other context or screenshots about the problem.

View File

@@ -1,8 +1,11 @@
blank_issues_enabled: true
contact_links:
- name: 💬 讨论区
- name: 💬 讨论区 / Discussions
url: https://github.com/ZhuLinsen/daily_stock_analysis/discussions
about: 有问题想讨论?欢迎来讨论区交流
- name: 📖 使用文档
about: 有问题想讨论?欢迎来讨论区交流 / Want to chat? Join the discussion board
- name: 📖 使用文档 / Documentation
url: https://github.com/ZhuLinsen/daily_stock_analysis#-快速开始
about: 查看 README 获取使用帮助
about: 查看中文 README 获取使用帮助 / View the Chinese README for setup help
- name: 📋 English Docs Index
url: https://github.com/ZhuLinsen/daily_stock_analysis/blob/main/docs/INDEX_EN.md
about: All English-language documentation in one place

View File

@@ -1,24 +1,38 @@
---
name: 功能建议
about: 提出新功能或改进建议
name: 功能建议 / Feature Request
about: 提出新功能或改进建议 / Suggest a new feature or improvement
title: '[Feature] '
labels: enhancement
assignees: ''
---
## 功能描述
简明扼要地描述你希望增加的功能
<!--
中文用户请直接填写下方表单
English users: fill in the English fields below.
-->
## 使用场景
描述在什么情况下需要这个功能。
## 功能描述 / Feature Description
## 期望实现
描述你期望这个功能如何工作。
简明扼要地描述你希望增加的功能。
Briefly describe the feature you would like added.
## 备选方案
描述你考虑过的其他替代方案。
## 使用场景 / Use Case
## 相关信息
- 是否愿意贡献代码实现: [是/否]
- 参考链接/文档:
- 其他说明:
描述在什么情况下需要这个功能。
Describe the situation where this feature would be useful.
## 期望实现 / Proposed Solution
描述你期望这个功能如何工作。
Describe how you would expect this feature to work.
## 备选方案 / Alternatives Considered
描述你考虑过的其他替代方案。
Describe any alternative solutions you have considered.
## 相关信息 / Additional Context
- 是否愿意贡献代码实现 / Willing to implement: [Yes / No]
- 参考链接 / Reference links:
- 其他说明 / Other notes:

View File

@@ -1,3 +1,8 @@
<!--
For Chinese contributors: 请直接用中文填写。
For English contributors: please fill in English. All fields marked (EN) accept English.
-->
## PR Type
- [ ] fix
@@ -9,22 +14,25 @@
## Background And Problem
请描述当前问题、影响范围与触发场景。
请描述当前问题、影响范围与触发场景。
*(EN) Describe the problem, its impact, and what triggers it.*
## Scope Of Change
请列出本 PR 修改的模块和文件范围。
请列出本 PR 修改的模块和文件范围。
*(EN) List the modules and files changed in this PR.*
## Issue Link
必须填写以下之一
必须填写以下之一 / Fill in one of:
- `Fixes #<issue_number>`
- `Refs #<issue_number>`
- 无 Issue 时说明原因与验收标准
- 无 Issue 时说明原因与验收标准 / If no issue, explain the motivation and acceptance criteria
## Verification Commands And Results
请填写你实际执行过的命令和关键结果(不要只写已测试”):
请填写你实际执行过的命令和关键结果(不要只写"已测试")。
*(EN) Paste the commands you actually ran and their key output (don't just write "tested"):*
```bash
# example
@@ -32,33 +40,36 @@
python -m pytest -m "not network"
```
关键输出/结论
关键输出/结论 / Key output & conclusion:
## Compatibility And Risk
请说明兼容性影响、潜在风险(如无请写 `None`)。
请说明兼容性影响、潜在风险(如无请写 `None`)。
*(EN) Describe compatibility impact and potential risks (write `None` if not applicable).*
## Rollback Plan
请至少写一句可执行的回滚方案(必填)。
请至少写一句可执行的回滚方案(必填)。
*(EN) Provide at least one actionable rollback step (required).*
## EXTRACT_PROMPT 变更(如适用)
## EXTRACT_PROMPT Change (if applicable)
若本 PR 修改了 `src/services/image_stock_extractor.py` 中的 `EXTRACT_PROMPT`,请在此处粘贴完整变更后的 prompt,便于审查者评估指令效果:
若本 PR 修改了 `src/services/image_stock_extractor.py` 中的 `EXTRACT_PROMPT`,请在此处粘贴完整变更后的 prompt
*If this PR changes `EXTRACT_PROMPT` in `src/services/image_stock_extractor.py`, paste the full updated prompt here:*
<details>
<summary>点击展开:完整 EXTRACT_PROMPT</summary>
<summary>展开 / Expand: Full EXTRACT_PROMPT</summary>
```
(在此粘贴完整 prompt 内容)
(paste full prompt here)
```
</details>
## Checklist
- [ ] 我已确认本 PR 有明确动机和业务价值
- [ ] 已提供可复现的验证命令与结果
- [ ] 已评估兼容性与风险
- [ ] 已提供回滚方案
- [ ] 若涉及用户可见变更,已同步更新 `README.md``docs/CHANGELOG.md`
- [ ] 本 PR 有明确动机和业务价值 / This PR has a clear motivation and value
- [ ] 已提供可复现的验证命令与结果 / Reproducible verification commands and results are included
- [ ] 已评估兼容性与风险 / Compatibility and risk have been assessed
- [ ] 已提供回滚方案 / A rollback plan is provided
- [ ] 若涉及用户可见变更,已同步更新 `README.md``docs/CHANGELOG.md` / If user-visible changes are included, `README.md` and `docs/CHANGELOG.md` are updated

122
docs/CONTRIBUTING_EN.md Normal file
View File

@@ -0,0 +1,122 @@
# Contributing Guide
Thank you for your interest in contributing! All kinds of contributions are welcome.
## 🐛 Reporting Bugs
1. Search [Issues](https://github.com/ZhuLinsen/daily_stock_analysis/issues) first to check if it has already been reported.
2. Create a new Issue using the **Bug Report** template.
3. Provide detailed reproduction steps and environment information.
## 💡 Suggesting Features
1. Search Issues to make sure the suggestion hasn't already been raised.
2. Create a new Issue using the **Feature Request** template.
3. Describe your use case and expected behavior in detail.
## 🔧 Submitting Code
### Setting Up the Development Environment
```bash
# Clone the repository
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git
cd daily_stock_analysis
# Create a virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Configure environment variables
cp .env.example .env
# Edit .env and fill in the required API keys
```
### Contribution Workflow
1. Fork this repository.
2. Create a feature branch: `git checkout -b feature/your-feature`
3. Commit your changes: `git commit -m 'feat: add some feature'`
4. Push the branch: `git push origin feature/your-feature`
5. Open a Pull Request against `main`.
### Commit Message Convention
This project follows [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: New feature
fix: Bug fix
docs: Documentation update
style: Code formatting (no logic change)
refactor: Code refactoring
perf: Performance improvement
test: Test-related changes
chore: Build / tooling changes
```
Examples:
```
feat: add DingTalk bot support
fix: handle 429 rate-limit with retry backoff
docs: update README deployment section
```
### Code Style
- Python code follows PEP 8 (line length: 120).
- Add docstrings to functions and classes.
- Add comments for non-obvious logic.
- Update relevant documentation when adding new features.
### CI Checks
After opening a PR, CI will automatically run the following PR checks:
| Check | Description | Required |
|-------|-------------|:--------:|
| `backend-gate` | `scripts/ci_gate.sh` — py_compile + flake8 critical errors + `./test.sh code` + `./test.sh yfinance` + offline pytest | ✅ |
| `docker-build` | Docker image build and key module import smoke test | ✅ |
| `web-gate` | `npm run lint` + `npm run build` (triggered when `apps/dsa-web/` changes) | ✅ (when triggered) |
Separately, the repository also has a non-blocking `network-smoke` workflow in `.github/workflows/network-smoke.yml`, but it is only triggered by `schedule` and `workflow_dispatch`, not by pull requests.
**Running checks locally:**
```bash
# Backend gate (recommended)
pip install -r requirements.txt
pip install flake8 pytest
./scripts/ci_gate.sh
# Frontend gate (only if you changed apps/dsa-web/)
cd apps/dsa-web
npm ci
npm run lint
npm run build
```
### Documentation Sync Rule
When modifying a Chinese-language core document (e.g., `docs/full-guide.md`), your PR description **must state** whether the corresponding English document has been updated. If not updated, explain why.
## 📋 Priority Areas for Contribution
- 🔔 New notification channels (e.g., Slack, Matrix)
- 🤖 New AI model integrations
- 📊 New data source adapters
- 🐛 Bug fixes and performance improvements
- 📖 Documentation improvements and translations
## ❓ Questions
Feel free to:
- Open an Issue for discussion.
- Browse existing Issues and Discussions.
Thank you for contributing! 🎉

64
docs/INDEX_EN.md Normal file
View File

@@ -0,0 +1,64 @@
# English Documentation Index
> All English-language documentation for the AI Stock Analysis System.
>
> For Chinese docs, see the [main README](../README.md).
---
## 🚀 Getting Started
| Document | Description |
|----------|-------------|
| [README (EN)](README_EN.md) | Project overview, quick start, features, sample output |
| [Full Guide (EN)](full-guide_EN.md) | Complete setup and usage walkthrough |
| [Deploy Guide (EN)](DEPLOY_EN.md) | Server deployment (Docker, systemd, Supervisor) |
| [LLM Config Guide (EN)](LLM_CONFIG_GUIDE_EN.md) | AI model configuration (Gemini, OpenAI-compatible, DeepSeek, Ollama, etc.) |
## ❓ Help & Troubleshooting
| Document | Description |
|----------|-------------|
| [FAQ (EN)](FAQ_EN.md) | Frequently asked questions and common errors |
## 🤖 Bot Integration
| Document | Description |
|----------|-------------|
| [Bot Commands (EN)](bot-command_EN.md) | Bot architecture, commands, webhook routes, Feishu / DingTalk integration |
## 🤝 Contributing
| Document | Description |
|----------|-------------|
| [Contributing Guide (EN)](CONTRIBUTING_EN.md) | How to report bugs, request features, and submit pull requests |
## 📋 Reference
| Document | Description |
|----------|-------------|
| [Changelog](CHANGELOG.md) | Version history and release notes (maintained in Chinese with English summaries) |
---
## Glossary of China-market Terms
Some terms in this project are specific to Chinese financial markets. Here is a quick reference:
| Term | Meaning |
|------|---------|
| **A-shares** | Stocks listed on the Shanghai (SSE) or Shenzhen (SZSE) stock exchanges, denominated in CNY |
| **Northbound capital flow** (北向资金) | Net buy/sell flow from foreign investors via the Stock Connect programs (Shanghai/ShenzhenHong Kong Connect) |
| **Dragon-Tiger List** (龙虎榜) | Daily SSE/SZSE disclosure of the top 5 institutional seats by turnover for heavily traded stocks |
| **Chip distribution** (筹码分布) | Cost-basis distribution of all outstanding shares, used to estimate support/resistance levels |
| **三板块涨跌榜** (boards / sectors) | Intraday sector rotation ranking published by SSE/SZSE |
| **Tushare** | A popular Chinese financial data API; requires a token (free tier available) |
| **AkShare** | An open-source Python library for Chinese/HK/US market data; no key required for most endpoints |
| **Baostock** | A free Python SDK for historical A-share data |
| **WeChat Work** (企业微信) | Tencent's enterprise messaging platform; supports webhook-based notifications |
| **Feishu** (飞书) | ByteDance's enterprise collaboration platform (similar to Slack); also supports webhooks |
| **PushPlus / ServerChan** | Chinese mobile push notification services |
---
*Last updated: see [CHANGELOG.md](CHANGELOG.md)*

View File

@@ -15,7 +15,7 @@ Analyze your watchlist daily → generate a decision dashboard → push to multi
**Zero-cost deployment** · Runs on GitHub Actions · No server required
[**Quick Start**](#-quick-start) · [**Key Features**](#-key-features) · [**Sample Output**](#-sample-output) · [**Full Guide**](full-guide_EN.md) · [**FAQ**](FAQ_EN.md) · [**Changelog**](CHANGELOG.md)
[**Quick Start**](#-quick-start) · [**Key Features**](#-key-features) · [**Sample Output**](#-sample-output) · [**Full Guide**](full-guide_EN.md) · [**FAQ**](FAQ_EN.md) · [**Contributing**](CONTRIBUTING_EN.md) · [**All Docs**](INDEX_EN.md)
English | [简体中文](../README.md) | [繁體中文](README_CHT.md)

235
docs/bot-command_EN.md Normal file
View File

@@ -0,0 +1,235 @@
# Bot Integration Guide
This document covers the bot module architecture, supported commands, webhook routes, and how to configure platform integrations.
> **Glossary:** "Enterprise bot" in this context means a chatbot that receives commands via webhook from a messaging platform (Feishu / DingTalk / WeChat Work / Telegram) and calls the analysis pipeline to reply inline.
---
## 1. Architecture Overview
```mermaid
flowchart TB
subgraph Platforms [Messaging Platforms]
FS[Feishu]
DT[DingTalk]
WC[WeChat Work]
TG[Telegram]
More[More platforms...]
end
subgraph BotModule [bot/ module]
WH[Webhook Server]
Adapters[Platform Adapters]
Dispatcher[Command Dispatcher]
Commands[Command Handlers]
end
subgraph Core [Core Modules]
AS[AnalysisService]
MA[MarketAnalyzer]
NS[NotificationService]
end
FS -->|POST /bot/feishu| WH
DT -->|POST /bot/dingtalk| WH
WC -->|POST /bot/wecom| WH
TG -->|POST /bot/telegram| WH
WH --> Adapters
Adapters -->|Unified message format| Dispatcher
Dispatcher --> Commands
Commands --> AS
Commands --> MA
Commands --> NS
```
---
## 2. Directory Structure
```
bot/
├── __init__.py # Module entry, exports main classes
├── models.py # Unified message/response models
├── dispatcher.py # Command dispatcher (core)
├── handler.py # Webhook handler functions (one per platform)
├── commands/ # Command handlers
│ ├── __init__.py
│ ├── base.py # Abstract base class for commands
│ ├── analyze.py # /analyze — stock analysis
│ ├── ask.py # /ask — single-turn question
│ ├── batch.py # /batch — batch watchlist analysis
│ ├── chat.py # /chat — multi-turn strategy chat
│ ├── market.py # /market — market review
│ ├── help.py # /help — help text
│ └── status.py # /status — system status
└── platforms/ # Platform adapters
├── __init__.py
├── base.py # Abstract base class for platforms
├── dingtalk.py # DingTalk bot
├── dingtalk_stream.py # DingTalk Stream bot
└── feishu_stream.py # Feishu (Lark) Stream bot
```
---
## 3. Core Abstractions
### 3.1 Unified Message Model (`bot/models.py`)
```python
@dataclass
class BotMessage:
platform: str # Platform ID: feishu / dingtalk / wecom / telegram
user_id: str # Sender ID
user_name: str # Sender display name
chat_id: str # Conversation ID (group or DM)
chat_type: str # Conversation type: group / private
content: str # Message text
raw_data: Dict # Raw request data (platform-specific)
timestamp: datetime
mentioned: bool = False # Whether the bot was @-mentioned
@dataclass
class BotResponse:
text: str
markdown: bool = False # Whether the response is Markdown
at_user: bool = True # Whether to @-mention the sender
```
### 3.2 Platform Adapter Base (`bot/platforms/base.py`)
```python
class BotPlatform(ABC):
@property
@abstractmethod
def platform_name(self) -> str: ...
@abstractmethod
def verify_request(self, headers: Dict, body: bytes) -> bool:
"""Verify request signature (security check)"""
...
@abstractmethod
def parse_message(self, data: Dict) -> Optional[BotMessage]:
"""Parse platform message into unified format"""
...
@abstractmethod
def format_response(self, response: BotResponse, message: BotMessage) -> WebhookResponse:
"""Convert unified response to platform format"""
...
```
### 3.3 Command Base Class (`bot/commands/base.py`)
```python
class BotCommand(ABC):
@property
@abstractmethod
def name(self) -> str: ... # e.g. 'analyze'
@property
@abstractmethod
def aliases(self) -> List[str]: ... # e.g. ['a', 'analyse']
@property
@abstractmethod
def description(self) -> str: ...
@property
@abstractmethod
def usage(self) -> str: ...
@abstractmethod
def execute(self, message: BotMessage, args: List[str]) -> BotResponse: ...
```
---
## 4. Supported Commands
| Command | Description | Example |
|---------|-------------|---------|
| `/analyze` | Analyze a specific stock | `/analyze AAPL` or `/analyze 600519` |
| `/ask` | Single-turn question about a stock or the market | `/ask what is RSI for AAPL` |
| `/batch` | Batch-analyze your configured watchlist | `/batch` |
| `/chat` | Multi-turn strategy chat (maintains conversation context) | `/chat` |
| `/market` | Market review (A-shares / US stocks) | `/market` |
| `/help` | Show help text | `/help` |
| `/status` | Show system status | `/status` |
> **Stock code formats:** A-shares use 6-digit codes (e.g. `600519`); HK stocks prefix `hk` (e.g. `hk00700`); US stocks use ticker symbols (e.g. `AAPL`, `TSLA`).
---
## 5. Webhook Routes
Handler functions for each platform live in `bot/handler.py`.
These routes are **not yet wired** into the FastAPI application — you must mount them manually.
| Route | Method | Status | Notes |
|-------|--------|--------|-------|
| `/bot/dingtalk` | POST | **Ready** | `DingtalkPlatform` is registered in `ALL_PLATFORMS` |
| `/bot/feishu` | POST | Stream only | Use `feishu_stream.py`; no Webhook adapter in `ALL_PLATFORMS` |
| `/bot/wecom` | POST | Not implemented | Handler exists but no platform adapter |
| `/bot/telegram` | POST | Not implemented | Handler exists but no platform adapter |
To mount the DingTalk webhook in your FastAPI app:
```python
from bot.handler import handle_dingtalk_webhook
@app.post("/bot/dingtalk")
async def dingtalk_webhook(request: Request):
headers = dict(request.headers)
body = await request.body()
return handle_dingtalk_webhook(headers, body)
```
---
## 6. Configuration
Add the following to your `.env`. Some of these bot-specific keys are already listed in `.env.example` (for example the DingTalk and Feishu app credentials), while others are not, so treat this section as a consolidated reference for bot setup:
```dotenv
# --- Bot general ---
BOT_ENABLED=false
BOT_COMMAND_PREFIX=/
# --- Feishu (Lark) bot ---
FEISHU_APP_ID=
FEISHU_APP_SECRET=
FEISHU_VERIFICATION_TOKEN= # Event verification token
FEISHU_ENCRYPT_KEY= # Encryption key (optional)
# --- DingTalk bot ---
DINGTALK_APP_KEY=
DINGTALK_APP_SECRET=
# --- WeChat Work bot (in development) ---
WECOM_TOKEN=
WECOM_ENCODING_AES_KEY=
# --- Telegram bot ---
TELEGRAM_BOT_TOKEN= # Get from @BotFather
TELEGRAM_WEBHOOK_SECRET= # Webhook secret token
```
---
## 7. Extending the Bot
### Adding a new platform adapter
1. Create a new file in `bot/platforms/`.
2. Subclass `BotPlatform` and implement `verify_request`, `parse_message`, `format_response`.
3. Mount the webhook route directly in your FastAPI app (for example in `api/app.py`) instead of `api/v1/router.py`, so the callback path stays `/bot/<platform>` rather than `/api/v1/bot/<platform>`.
### Adding a new command
1. Create a new file in `bot/commands/`.
2. Subclass `BotCommand` and implement the `execute` method.
3. Register the command in the dispatcher startup code.