Files
featherChat/warzone/docs/LLM_BOT_DEV.md
Siavash Sameni 76cac77259 v0.0.28: BotFather-only registration, per-instance bot toggle, docs update
Security:
- Bot registration restricted to BotFather (requires botfather_token)
- Direct POST /v1/bot/register without BotFather auth → rejected

Deploy:
- systemd service reads /home/warzone/server.env for EXTRA_ARGS
- deploy/warzone-server.env.mequ: no bots (default)
- deploy/warzone-server.env.kh3rad3ree: --enable-bots
- setup.sh copies per-hostname env file

Docs updated:
- LLM_HELP.md: BotFather flow, plaintext bot messaging, E2E option, bridge
- LLM_BOT_DEV.md: botfather_token requirement, E2E mode, bridge section
- BOT_API.md: full BotFather flow, ownership, numeric IDs, webhook delivery
- SERVER.md: --enable-bots flag, per-instance config, bot system section
- USAGE.md: bot messaging, BotFather, bridge tool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:52:12 +04:00

8.1 KiB

featherChat Bot Development Reference

Setup

Server: http://HOST:7700 All bot endpoints: /v1/bot/<TOKEN>/METHOD

Prerequisites: The server must be started with --enable-bots to activate bot functionality.

BotFather Registration

Bots can only be created through @botfather. On first server start, BotFather is auto-created and its token is printed in the server logs.

To create a bot:

  1. Message @botfather in the chat client (or use the BotFather token from server logs for programmatic access).
  2. BotFather calls /v1/bot/register with your request, including a botfather_token field for authorization.

Registration request (sent by BotFather internally):

POST /v1/bot/register
{"name":"MyBot","fingerprint":"any_32_hex_chars","botfather_token":"<bf_token>"}
→ {"ok":true,"result":{"token":"TOKEN","alias":"@mybot_bot","owner":"<creator_fp>"}}

Bot names must end with Bot/bot/_bot. Token format: <fp_prefix>:<random_hex>.

E2E Bot Mode

Bots can optionally participate in E2E encryption. Pass additional fields during registration:

{
  "name": "SecureBot",
  "fingerprint": "...",
  "botfather_token": "...",
  "e2e": true,
  "bundle": { "identity_key": "...", "signed_prekey": "...", "signature": "...", "one_time_prekeys": ["..."] },
  "eth_address": "0x..."
}

An E2E bot registers a full prekey bundle and can establish X3DH sessions with users, receiving decryptable messages instead of raw_encrypted blobs.

Endpoints

getMe

GET /v1/bot/TOKEN/getMe
→ {"ok":true,"result":{"id":"fp","is_bot":true,"first_name":"MyBot","username":"MyBot"}}

getUpdates (long-poll)

POST /v1/bot/TOKEN/getUpdates
{"offset":LAST_UPDATE_ID+1,"timeout":30,"limit":100}
→ {"ok":true,"result":[{"update_id":N,"message":{...}}]}

offset: skip updates with id < offset (acknowledge processed) timeout: long-poll seconds (max 30) limit: max updates to return (default 100)

sendMessage

POST /v1/bot/TOKEN/sendMessage
{
  "chat_id": "FINGERPRINT",
  "text": "Hello!",
  "parse_mode": "HTML",                    // optional
  "reply_to_message_id": "MSG_ID",         // optional
  "reply_markup": {                         // optional, inline keyboard
    "inline_keyboard": [
      [{"text":"Yes","callback_data":"yes"},{"text":"No","callback_data":"no"}]
    ]
  }
}
→ {"ok":true,"result":{"message_id":"UUID","delivered":true}}

answerCallbackQuery

POST /v1/bot/TOKEN/answerCallbackQuery
{"callback_query_id":"ID","text":"Done!","show_alert":false}
→ {"ok":true,"result":true}

editMessageText

POST /v1/bot/TOKEN/editMessageText
{"chat_id":"FP","message_id":"MSG_ID","text":"Updated text","reply_markup":{...}}

sendDocument

POST /v1/bot/TOKEN/sendDocument
{"chat_id":"FP","document":"filename_or_url","caption":"optional"}

setWebhook / deleteWebhook / getWebhookInfo

POST /v1/bot/TOKEN/setWebhook {"url":"https://mybot.example.com/webhook"}
POST /v1/bot/TOKEN/deleteWebhook
GET  /v1/bot/TOKEN/getWebhookInfo

Update Types

Messages from users arrive in getUpdates as:

Plaintext (from other bots):

{"update_id":1,"message":{"message_id":"id","from":{"id":"fp","is_bot":true},"chat":{"id":"fp","type":"private"},"text":"Hello"}}

Encrypted (from users with E2E sessions):

{"update_id":2,"message":{"message_id":"id","from":{"id":"fp","is_bot":false},"chat":{"id":"fp"},"text":null,"raw_encrypted":"base64..."}}

Note: v1 bots cannot decrypt E2E messages. They see text=null + raw_encrypted blob.

Call signal:

{"update_id":3,"message":{"text":"/call_Offer","call_signal":{"type":"Offer","payload":"..."}}}

File:

{"update_id":4,"message":{"document":{"file_name":"report.pdf","file_size":1234}}}

Python Examples

Echo Bot

import requests, time

TOKEN = "YOUR_TOKEN"
API = f"http://localhost:7700/v1/bot/{TOKEN}"
offset = 0

while True:
    resp = requests.post(f"{API}/getUpdates", json={"offset": offset, "timeout": 30}).json()
    for update in resp.get("result", []):
        offset = update["update_id"] + 1
        msg = update.get("message", {})
        chat_id = msg.get("chat", {}).get("id", "")
        text = msg.get("text")
        if text and chat_id:
            requests.post(f"{API}/sendMessage", json={"chat_id": chat_id, "text": f"Echo: {text}"})

Inline Keyboard Bot

import requests

TOKEN = "YOUR_TOKEN"
API = f"http://localhost:7700/v1/bot/{TOKEN}"
offset = 0

def send_menu(chat_id):
    requests.post(f"{API}/sendMessage", json={
        "chat_id": chat_id,
        "text": "Choose an option:",
        "reply_markup": {
            "inline_keyboard": [
                [{"text": "Option A", "callback_data": "a"}, {"text": "Option B", "callback_data": "b"}],
                [{"text": "Help", "callback_data": "help"}]
            ]
        }
    })

while True:
    resp = requests.post(f"{API}/getUpdates", json={"offset": offset, "timeout": 30}).json()
    for update in resp.get("result", []):
        offset = update["update_id"] + 1
        msg = update.get("message", {})
        text = msg.get("text", "")
        chat_id = msg.get("chat", {}).get("id", "")
        if text == "/start":
            send_menu(chat_id)
        elif text:
            requests.post(f"{API}/sendMessage", json={"chat_id": chat_id, "text": f"You said: {text}"})

Node.js Echo Bot

const API = `http://localhost:7700/v1/bot/${process.env.BOT_TOKEN}`;
let offset = 0;

async function poll() {
  while (true) {
    try {
      const res = await fetch(`${API}/getUpdates`, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({offset, timeout: 30})
      });
      const data = await res.json();
      for (const update of data.result || []) {
        offset = update.update_id + 1;
        const msg = update.message;
        if (msg?.text && msg?.chat?.id) {
          await fetch(`${API}/sendMessage`, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({chat_id: msg.chat.id, text: `Echo: ${msg.text}`})
          });
        }
      }
    } catch (e) { console.error(e); await new Promise(r => setTimeout(r, 3000)); }
  }
}
poll();

Differences from Telegram

Feature Telegram featherChat
chat_id numeric hex fingerprint string OR numeric (both accepted)
getUpdates timeout up to 50s up to 50s
User messages plaintext E2E encrypted (text=null unless E2E bot)
Bot messages plaintext plaintext (no E2E) unless E2E bot mode
File upload multipart form JSON reference (v1)
Inline keyboards full support stored + delivered, no popup
Callback queries full popup acknowledged, no popup
Webhooks full HTTPS URL stored, updates delivered live (POST to webhook URL)
Media groups supported not yet
parse_mode renders HTML/MD HTML rendered (, , , )

Key Patterns

Always use offset — without it, the same messages are returned every poll.

chat_id is the sender's fingerprint — use msg.chat.id or msg.from.id. Note: from.id is now a numeric integer for TG compatibility; use from.id_str for the hex fingerprint.

Bot alias — users message bots via @mybot_bot which resolves to the bot's fingerprint.

Error handling — all responses have {"ok": bool}. Check ok before accessing result.

Rate limits — 200 concurrent server requests, no per-bot limit (be reasonable).

Bot Bridge (tools/bot-bridge.py)

The bot bridge provides a compatibility layer for existing Telegram bot libraries. It translates between featherChat's Bot API and standard TG libraries.

Supported libraries:

  • python-telegram-bot — set base_url to http://your-server:7700/v1/bot/
  • aiogram — configure the bot session with the featherChat server URL
  • Telegraf (Node.js) — set telegram.apiRoot to http://your-server:7700/v1/bot

Usage:

python tools/bot-bridge.py --token YOUR_BOT_TOKEN --server http://localhost:7700

The bridge handles differences like fingerprint-based chat_id, numeric ID translation, and webhook forwarding.