feat: bot alias reservation + BOT_API.md documentation
- 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>
This commit is contained in:
@@ -123,6 +123,18 @@ async fn register_alias(
|
|||||||
return Ok(Json(serde_json::json!({ "error": "alias must be 1-32 alphanumeric chars" })));
|
return Ok(Json(serde_json::json!({ "error": "alias must be 1-32 alphanumeric chars" })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reserve *Bot and *_bot suffixes for bots only
|
||||||
|
let is_bot_name = alias.ends_with("bot") || alias.ends_with("_bot");
|
||||||
|
if is_bot_name {
|
||||||
|
// Check if this fingerprint is registered as a bot
|
||||||
|
let bot_key = format!("bot_fp:{}", fp);
|
||||||
|
let is_registered_bot = state.db.tokens.get(bot_key.as_bytes())
|
||||||
|
.ok().flatten().is_some();
|
||||||
|
if !is_registered_bot {
|
||||||
|
return Ok(Json(serde_json::json!({ "error": "aliases ending with 'Bot' or '_bot' are reserved for bots — register via /v1/bot/register first" })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check existing record for this alias
|
// Check existing record for this alias
|
||||||
if let Some(existing) = load_alias_record(&state.db.aliases, &alias) {
|
if let Some(existing) = load_alias_record(&state.db.aliases, &alias) {
|
||||||
if existing.fingerprint == fp {
|
if existing.fingerprint == fp {
|
||||||
|
|||||||
@@ -100,12 +100,25 @@ async fn register_bot(
|
|||||||
&token[..token.len().min(20)]
|
&token[..token.len().min(20)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Auto-register bot alias (name must end with Bot or _bot)
|
||||||
|
let bot_alias = if req.name.ends_with("Bot") || req.name.ends_with("_bot") || req.name.ends_with("bot") {
|
||||||
|
req.name.to_lowercase()
|
||||||
|
} else {
|
||||||
|
format!("{}_bot", req.name.to_lowercase())
|
||||||
|
};
|
||||||
|
let alias_key = format!("a:{}", bot_alias);
|
||||||
|
let _ = state.db.aliases.insert(alias_key.as_bytes(), fp.as_bytes());
|
||||||
|
let fp_key = format!("fp:{}", fp);
|
||||||
|
let _ = state.db.aliases.insert(fp_key.as_bytes(), bot_alias.as_bytes());
|
||||||
|
tracing::info!("Bot alias @{} registered for {}", bot_alias, fp);
|
||||||
|
|
||||||
Ok(Json(serde_json::json!({
|
Ok(Json(serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"result": {
|
"result": {
|
||||||
"token": token,
|
"token": token,
|
||||||
"name": req.name,
|
"name": req.name,
|
||||||
"fingerprint": fp,
|
"fingerprint": fp,
|
||||||
|
"alias": format!("@{}", bot_alias),
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|||||||
401
warzone/docs/BOT_API.md
Normal file
401
warzone/docs/BOT_API.md
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
# 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/<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:**
|
||||||
|
|
||||||
|
```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:** `<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 `_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:<token>`.
|
||||||
|
- A reverse lookup `bot_fp:<fingerprint>` -> `<token>` is also maintained.
|
||||||
|
- Aliases are stored in the `aliases` sled tree (`a:<alias>` -> fingerprint,
|
||||||
|
`fp:<fingerprint>` -> alias).
|
||||||
|
- Queued messages live in the `messages` sled tree under prefix
|
||||||
|
`queue:<bot_fingerprint>:*` 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.
|
||||||
@@ -45,6 +45,8 @@ ETH address | 0x742d35Cc... | derived from same seed, checksum format
|
|||||||
|
|
||||||
All 3 formats work in /peer. Aliases resolve to fp via server. One alias per user. Register with /alias, recover with recovery key.
|
All 3 formats work in /peer. Aliases resolve to fp via server. One alias per user. Register with /alias, recover with recovery key.
|
||||||
|
|
||||||
|
Bot alias reservation: names ending in Bot, bot, or _bot are reserved for the Bot API. Non-bot users cannot register these aliases.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
1. `warzone init` -- generates seed, saves identity.seed, prints 24-word mnemonic. WRITE IT DOWN.
|
1. `warzone init` -- generates seed, saves identity.seed, prints 24-word mnemonic. WRITE IT DOWN.
|
||||||
@@ -127,23 +129,44 @@ Problem | Cause | Fix
|
|||||||
"file too large" | over 10MB | split file manually
|
"file too large" | over 10MB | split file manually
|
||||||
no prekeys available | recipient's one-time prekeys exhausted | they need to re-register or come online
|
no prekeys available | recipient's one-time prekeys exhausted | they need to re-register or come online
|
||||||
|
|
||||||
## Bot API
|
## Bot API (Telegram-compatible)
|
||||||
|
|
||||||
Telegram-compatible REST API. Base: /v1/
|
Register: POST /v1/bot/register {"name":"MyBot","fingerprint":"<fp>"}
|
||||||
|
→ returns token + auto-creates @mybot_bot alias
|
||||||
|
|
||||||
Endpoint | Method | Body | Returns
|
Bot aliases must end with Bot, bot, or _bot. Non-bots cannot use these.
|
||||||
--- | --- | --- | ---
|
|
||||||
/bot/register | POST | {"name":"mybot","fingerprint":"abc..."} | {"token":"...","name":"..."}
|
|Endpoint|Method|Body|
|
||||||
/bot/:token/getMe | GET | -- | bot info
|
|---|---|---|
|
||||||
/bot/:token/getUpdates | POST | {"timeout":5} | array of Update objects
|
|/bot/:token/getMe|GET|—|
|
||||||
/bot/:token/sendMessage | POST | {"chat_id":"<fp>","text":"hello"} | msg confirmation
|
|/bot/:token/getUpdates|POST|{"timeout":5}|
|
||||||
|
|/bot/:token/sendMessage|POST|{"chat_id":"<fp>","text":"Hello"}|
|
||||||
|
|
||||||
- Token format: fp_prefix:random_hex
|
- Token format: fp_prefix:random_hex
|
||||||
- getUpdates: long-poll (max 5s), returns then deletes queued msgs
|
- getUpdates: long-poll (max 5s), returns then deletes queued msgs
|
||||||
- sendMessage: plaintext JSON, NOT E2E encrypted
|
- sendMessage: plaintext JSON, NOT E2E encrypted
|
||||||
- Updates include: messages, key exchanges, call signals, file headers
|
|
||||||
- Bot msgs delivered via same routing (WS push or DB queue)
|
- Bot msgs delivered via same routing (WS push or DB queue)
|
||||||
|
|
||||||
|
Update types in getUpdates:
|
||||||
|
- Encrypted msg: text=null, raw_encrypted=base64
|
||||||
|
- Bot msg (plaintext): text="actual text", from.is_bot=true
|
||||||
|
- Call signal: text="/call_Offer", call_signal={type,payload}
|
||||||
|
- File: document={file_name,file_size}
|
||||||
|
|
||||||
|
v1 limits: sendMessage is plaintext (no E2E), timeout max 5s, no webhooks yet.
|
||||||
|
|
||||||
|
Echo bot (Python):
|
||||||
|
```python
|
||||||
|
import requests, time
|
||||||
|
TOKEN = "your_token"
|
||||||
|
API = f"http://srv:7700/v1/bot/{TOKEN}"
|
||||||
|
while True:
|
||||||
|
for u in requests.post(f"{API}/getUpdates",json={"timeout":5}).json().get("result",[]):
|
||||||
|
m = u["message"]
|
||||||
|
if m.get("text"): requests.post(f"{API}/sendMessage",json={"chat_id":m["chat"]["id"],"text":"Echo: "+m["text"]})
|
||||||
|
time.sleep(1)
|
||||||
|
```
|
||||||
|
|
||||||
## Server API (other endpoints)
|
## Server API (other endpoints)
|
||||||
|
|
||||||
- POST /v1/register -- upload prekey bundle
|
- POST /v1/register -- upload prekey bundle
|
||||||
|
|||||||
Reference in New Issue
Block a user