# featherChat Bot API ## Overview featherChat exposes a **Telegram Bot API-compatible** HTTP interface, allowing developers to build bots that interact with featherChat users using familiar patterns. Bots are created exclusively through **@botfather**, receive a token, and communicate via long-polling or webhooks. The server must be started with `--enable-bots` to activate bot functionality. Key properties: - **BotFather is required** -- only `@botfather` can register bots. It is auto-created on first server start (token printed in server logs). - Bot aliases **must** end with `Bot`, `bot`, or `_bot` (auto-enforced on registration). - Bots receive encrypted user messages as **base64 blobs** (`raw_encrypted` field) unless registered as E2E bots. Plaintext bot-to-bot messages are delivered with a readable `text` field. - Bot-sent messages are **plaintext** (not E2E encrypted) unless the bot is registered in E2E mode. - `chat_id` accepts both hex fingerprints and numeric IDs (Telegram compatibility). Numeric IDs are also returned in `from.id`. - Each bot has an `owner` field linking to the creating user's fingerprint. --- ## Quick Start ``` 1. Message @botfather to create a bot (or use BotFather token from server logs). BotFather registers the bot via: POST /v1/bot/register {"name": "WeatherBot", "fingerprint": "aabbccdd...", "botfather_token": ""} 2. Extract the token from the response. 3. Poll for updates: POST /v1/bot//getUpdates {"timeout": 50} 4. Send a reply: POST /v1/bot//sendMessage {"chat_id": "", "text": "Hello!"} ``` --- ## Endpoints ### 1. Register a Bot ``` POST /v1/bot/register ``` Creates a new bot, stores it in the server database, and auto-registers an alias. **Only @botfather can call this endpoint** -- a valid `botfather_token` is required. **Request:** ```json { "name": "MyBot", "fingerprint": "aabbccdd1122334455667788aabbccdd", "botfather_token": "", "owner": "" } ``` | Field | Type | Description | |--------------------|--------|---------------------------------------------------| | `name` | string | Display name. Alias suffix auto-added if needed. | | `fingerprint` | string | Hex-encoded public key fingerprint for the bot. | | `botfather_token` | string | BotFather authorization token (required). | | `owner` | string | Fingerprint of the user who requested creation. | **E2E bot registration** (optional additional fields): | Field | Type | Description | |---------------|--------|--------------------------------------------------| | `e2e` | bool | Set to `true` to register as an E2E bot. | | `bundle` | object | Full prekey bundle (identity_key, signed_prekey, signature, one_time_prekeys). | | `eth_address` | string | Ethereum address for the bot. | **Response:** ```json { "ok": true, "result": { "token": "aabbccdd11223344:9f8e7d6c5b4a39281706abcdef012345", "name": "MyBot", "fingerprint": "aabbccdd1122334455667788aabbccdd", "alias": "@mybot_bot", "owner": "" } } ``` **Token format:** `:<32-hex-random-bytes>` **Alias rules:** - If the name already ends with `Bot`, `bot`, or `_bot`, the alias is the lowercased name (e.g. `WeatherBot` -> `@weatherbot`). - Otherwise `_bot` is appended (e.g. `weather` -> `@weather_bot`). - The alias is registered in both directions (alias -> fingerprint and fingerprint -> alias). --- ### 2. Get Bot Info ``` GET /v1/bot/:token/getMe ``` Returns information about the bot in a Telegram-compatible shape. **Response (valid token):** ```json { "ok": true, "result": { "id": "aabbccdd1122334455667788aabbccdd", "is_bot": true, "first_name": "MyBot", "username": "MyBot" } } ``` **Response (invalid token):** ```json { "ok": false, "description": "invalid token" } ``` --- ### 3. Get Updates (Long-Poll) ``` POST /v1/bot/:token/getUpdates ``` Returns queued messages for the bot and deletes them from the queue. **Request:** ```json { "timeout": 5 } ``` | Field | Type | Description | |-----------|------|------------------------------------------------------| | `timeout` | u64 | Optional. Long-poll wait in seconds. **Capped at 50.** | If the queue is empty and `timeout > 0`, the server waits up to `timeout` seconds (max 50) before returning an empty result, giving new messages a chance to arrive. > **Note:** If a webhook is configured via `setWebhook`, updates are delivered > live to the webhook URL via POST instead of being queued for polling. **Response:** ```json { "ok": true, "result": [ ...updates... ] } ``` #### Update Types **Encrypted message** (from a user — bot must decrypt if it has a session): ```json { "update_id": 1, "message": { "message_id": "uuid", "from": { "id": "sender_fingerprint", "is_bot": false, "first_name": "sender_finge" }, "chat": { "id": "sender_fingerprint", "type": "private" }, "date": 1711670400, "text": null, "raw_encrypted": "base64-encoded-wiremessage..." } } ``` **Key exchange** (X3DH session initiation — same shape as encrypted message): ```json { "update_id": 2, "message": { "message_id": "uuid", "from": { "id": "sender_fp", "is_bot": false, "first_name": "sender_fp..." }, "chat": { "id": "sender_fp", "type": "private" }, "date": 1711670400, "text": null, "raw_encrypted": "base64-encoded-keyexchange..." } } ``` **Call signal:** ```json { "update_id": 3, "message": { "message_id": "uuid", "from": { "id": "sender_fp", "is_bot": false, "first_name": "sender_fp..." }, "chat": { "id": "sender_fp", "type": "private" }, "date": 1711670400, "text": "/call_Offer", "call_signal": { "type": "Offer", "payload": "SDP or ICE data..." } } } ``` **File header:** ```json { "update_id": 4, "message": { "message_id": "uuid", "from": { "id": "sender_fp", "is_bot": false, "first_name": "sender_fp..." }, "chat": { "id": "sender_fp", "type": "private" }, "date": 1711670400, "document": { "file_name": "report.pdf", "file_size": 204800 } } } ``` **Bot message (plaintext, from another bot via `sendMessage`):** ```json { "update_id": 5, "message": { "message_id": "uuid", "from": { "id": "other_bot_fingerprint", "is_bot": true }, "chat": { "id": "other_bot_fingerprint", "type": "private" }, "date": 1711670400, "text": "Hello from the other bot!" } } ``` > **Note:** Receipt and internal wire messages (FileChunk, GroupSenderKey, > SenderKeyDistribution) are silently skipped and never delivered as updates. --- ### 4. Send Message ``` POST /v1/bot/:token/sendMessage ``` Sends a **plaintext** message to a user or another bot. **Request:** ```json { "chat_id": "aabbccdd1122334455667788aabbccdd", "text": "Hello from MyBot!" } ``` | Field | Type | Description | |--------------|--------|-----------------------------------------------------------| | `chat_id` | string/int | Recipient fingerprint (hex), Ethereum address, or numeric ID. | | `text` | string | Message body. | | `parse_mode` | string | Optional. `"HTML"` renders basic tags (, , , ). | `chat_id` accepts hex fingerprint strings, Ethereum addresses, or numeric integer IDs (Telegram compatibility). Non-hex characters in string chat_ids are stripped and the value is lowercased before routing. **Response:** ```json { "ok": true, "result": { "message_id": "550e8400-e29b-41d4-a716-446655440000", "chat": { "id": "aabbccdd1122334455667788aabbccdd", "type": "private" }, "text": "Hello from MyBot!", "date": 1711670400, "delivered": true } } ``` The `delivered` field indicates whether the message was sent over a live WebSocket connection (`true`) or queued for later retrieval (`false`). --- ## Alias Rules | Rule | Detail | |------|--------| | Bot aliases **must** end with `Bot`, `bot`, or `_bot` | Enforced at registration time. | | Non-bot users **cannot** register aliases with these suffixes | Reserved for bots. | | Auto-registered on bot creation | No separate alias step needed. | | Users message bots via alias | e.g. `@mybot_bot`, resolved like any other alias. | --- ## Differences from Telegram Bot API | Feature | Telegram | featherChat | |---------|----------|-------------| | `chat_id` type | Numeric integer | Hex fingerprint string or numeric integer (both accepted) | | `getUpdates` timeout | Up to 50s | Capped at **50s** | | Message content | Always plaintext | Encrypted messages arrive as `raw_encrypted` base64; E2E bots can decrypt | | Bot-sent messages | Plaintext | Plaintext by default; E2E mode available | | `from.id` | Numeric integer | Numeric integer (`from.id_str` has hex fingerprint) | | `parse_mode` | Renders HTML/Markdown | HTML rendered (, , , ) | | Inline keyboards / callback queries | Supported | Stored + delivered, no popup | | Webhooks (`setWebhook`) | Supported | Implemented -- updates delivered live to webhook URL | | Media groups | Supported | Not yet (planned) | | File download (`getFile`) | Supported | Not yet (planned) | --- ## Example: Simple Echo Bot (Python) ```python import requests import time TOKEN = "your_bot_token" API = f"http://localhost:7700/v1/bot/{TOKEN}" while True: resp = requests.post(f"{API}/getUpdates", json={"timeout": 50}).json() for update in resp.get("result", []): msg = update.get("message", {}) text = msg.get("text") or "[encrypted]" chat_id = msg.get("chat", {}).get("id", "") if text and chat_id: requests.post(f"{API}/sendMessage", json={ "chat_id": chat_id, "text": f"Echo: {text}", }) time.sleep(1) ``` ### Example: Registration (curl) ```bash curl -X POST http://localhost:7700/v1/bot/register \ -H "Content-Type: application/json" \ -d '{"name": "EchoBot", "fingerprint": "aabbccdd1122334455667788aabbccdd"}' ``` --- ## Authentication All bot endpoints (except `/register`) are authenticated by the **token** in the URL path. Tokens are generated at registration time and stored server-side. There is no expiration mechanism in v1 -- tokens remain valid until the server database is cleared. The token grants full access to poll and send messages as the bot. **Treat it like a password.** --- ## Internal Details - Bot info is stored in the `tokens` sled tree under key `bot:`. - A reverse lookup `bot_fp:` -> `` is also maintained. - Aliases are stored in the `aliases` sled tree (`a:` -> fingerprint, `fp:` -> alias). - Queued messages live in the `messages` sled tree under prefix `queue::*` and are deleted after `getUpdates` consumes them. - Messages are delivered via `deliver_or_queue` -- live WebSocket if online, otherwise queued. --- ## Bot Bridge (`tools/bot-bridge.py`) A compatibility layer for existing Telegram bot libraries. Translates between featherChat Bot API and standard TG libraries (python-telegram-bot, aiogram, Telegraf). Handles differences like fingerprint-based chat_id, numeric ID translation, and webhook forwarding. ```bash python tools/bot-bridge.py --token YOUR_BOT_TOKEN --server http://localhost:7700 ``` --- ## Future Plans - **File send/receive APIs** -- `sendDocument`, `getFile`. - **Group bot support** -- bots in group chats with sender-key encryption.