- Aliases ending with Bot/bot/_bot reserved for registered bots only - Non-bot users get clear error directing to /v1/bot/register - Bot registration auto-creates alias (@name_bot suffix) - BOT_API.md: full developer guide with endpoints, examples, echo bot - LLM_HELP.md: expanded bot section with update types + Python example Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.5 KiB
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_encryptedfield). Plaintext bot-to-bot messages are delivered with a readabletextfield. - Bot-sent messages are plaintext (not E2E encrypted) in v1.
chat_idvalues 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/<token>/getUpdates
{"timeout": 5}
4. Send a reply:
POST /v1/bot/<token>/sendMessage
{"chat_id": "<sender_fingerprint>", "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:
{
"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:
{
"ok": true,
"result": {
"token": "aabbccdd11223344:9f8e7d6c5b4a39281706abcdef012345",
"name": "MyBot",
"fingerprint": "aabbccdd1122334455667788aabbccdd",
"alias": "@mybot_bot"
}
}
Token format: <first-16-chars-of-fingerprint>:<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
_botis 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):
{
"ok": true,
"result": {
"id": "aabbccdd1122334455667788aabbccdd",
"is_bot": true,
"first_name": "MyBot",
"username": "MyBot"
}
}
Response (invalid token):
{
"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:
{
"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:
{
"ok": true,
"result": [ ...updates... ]
}
Update Types
Encrypted message (from a user — bot must decrypt if it has a session):
{
"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):
{
"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:
{
"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:
{
"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):
{
"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:
{
"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:
{
"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)
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)
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
tokenssled tree under keybot:<token>. - A reverse lookup
bot_fp:<fingerprint>-><token>is also maintained. - Aliases are stored in the
aliasessled tree (a:<alias>-> fingerprint,fp:<fingerprint>-> alias). - Queued messages live in the
messagessled tree under prefixqueue:<bot_fingerprint>:*and are deleted aftergetUpdatesconsumes 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.