Back to Daily Stock Analysis

Bot Integration Guide

docs/bot-command_EN.md

3.15.09.1 KB
Original Source

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

CommandDescriptionExample
/analyzeAnalyze a specific stock/analyze AAPL or /analyze 600519
/askSingle-turn question about a stock or the market/ask what is RSI for AAPL
/batchBatch-analyze your configured watchlist/batch
/chatMulti-turn strategy chat (maintains conversation context)/chat
/marketMarket review (A-shares / US stocks)/market
/helpShow help text/help
/statusShow 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. /status and LLM configuration diagnostics

Configuration precedence for readiness in /status

  • The AI availability displayed by /status follows runtime precedence:
    • LITELLM_CONFIG (LiteLLM YAML)
    • LLM_CHANNELS
    • legacy provider keys (GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / DEEPSEEK_API_KEY)
  • If the primary model (LITELLM_MODEL or AGENT_LITELLM_MODEL) has no configured source in the active layer, /status shows AI 服务未配置 and keeps the explicit reason line.
  • Runtime dependency range in this repository is litellm>=1.80.10,<1.82.7; current status semantics are aligned with this window.
  • This diagnostic follows the same readiness rules as GET /api/v1/system/config/setup/status for LLM checks: channels/yaml are active higher priority than legacy keys, and no silent migration is performed when toggling modes.

Fallback and migration boundary

  • When LITELLM_CONFIG or LLM_CHANNELS is active, lower-priority legacy provider keys are ignored as the active source for that run (no silent downgrade).
  • This change only improves diagnosis and does not perform automatic migration: legacy configuration values are not deleted or rewritten during startup or status collection.

Official compatibility references (for triage)

6. 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.

RouteMethodStatusNotes
/bot/dingtalkPOSTReadyDingtalkPlatform is registered in ALL_PLATFORMS
/bot/feishuPOSTStream onlyUse feishu_stream.py; no Webhook adapter in ALL_PLATFORMS
/bot/wecomPOSTNot implementedHandler exists but no platform adapter
/bot/telegramPOSTNot implementedHandler 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)

7. 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.