# 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 register with the server, receive a token, and communicate via long-polling (webhook support is planned). Key properties of v1: - Bot aliases **must** end with `Bot`, `bot`, or `_bot` (auto-enforced on registration). - Bots receive encrypted user messages as **base64 blobs** (`raw_encrypted` field). Plaintext bot-to-bot messages are delivered with a readable `text` field. - Bot-sent messages are **plaintext** (not E2E encrypted) in v1. - `chat_id` values are hex fingerprints (not numeric Telegram-style IDs). --- ## Quick Start ``` 1. Register your bot: POST /v1/bot/register {"name": "WeatherBot", "fingerprint": "aabbccdd..."} 2. Extract the token from the response. 3. Poll for updates: POST /v1/bot//getUpdates {"timeout": 5} 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. **Request:** ```json { "name": "MyBot", "fingerprint": "aabbccdd1122334455667788aabbccdd" } ``` | Field | Type | Description | |---------------|--------|----------------------------------------------| | `name` | string | Display name. Alias suffix auto-added if needed. | | `fingerprint` | string | Hex-encoded public key fingerprint for the bot. | **Response:** ```json { "ok": true, "result": { "token": "aabbccdd11223344:9f8e7d6c5b4a39281706abcdef012345", "name": "MyBot", "fingerprint": "aabbccdd1122334455667788aabbccdd", "alias": "@mybot_bot" } } ``` **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 5.** | If the queue is empty and `timeout > 0`, the server waits up to `timeout` seconds (max 5) before returning an empty result, giving new messages a chance to arrive. **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 | Recipient fingerprint (hex) or Ethereum address. | | `text` | string | Plaintext message body. | Non-hex characters in `chat_id` 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 | | `getUpdates` timeout | Up to 50s | Capped at **5s** | | Message content | Always plaintext | Encrypted messages arrive as `raw_encrypted` base64; bot must decrypt if it has a session | | Bot-sent messages | Plaintext | Plaintext (not E2E encrypted) in v1 | | Inline keyboards / callback queries | Supported | Not yet (planned) | | Media groups | Supported | Not yet (planned) | | Webhooks (`setWebhook`) | 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": 5}).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. --- ## Future Plans - **Webhook mode** (`setWebhook`) -- push updates to a URL instead of polling. - **Inline keyboards and callback queries** -- interactive message buttons. - **E2E encrypted bot sessions** -- bots participate in X3DH key exchange. - **File send/receive APIs** -- `sendDocument`, `getFile`. - **Group bot support** -- bots in group chats with sender-key encryption.