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>
This commit is contained in:
Siavash Sameni
2026-03-29 09:52:12 +04:00
parent 8603087afb
commit 76cac77259
14 changed files with 288 additions and 74 deletions

View File

@@ -236,6 +236,8 @@ struct RegisterBotRequest {
e2e: Option<bool>, // true = E2E bot, false/None = plaintext bot
#[serde(default)]
owner: Option<String>, // fingerprint of the bot creator
#[serde(default)]
botfather_token: Option<String>,
}
/// Register a bot and receive a token.
@@ -249,12 +251,25 @@ async fn register_bot(
State(state): State<AppState>,
Json(req): Json<RegisterBotRequest>,
) -> AppResult<Json<serde_json::Value>> {
// TODO: In production, only @botfather should be able to register bots.
// For v1, direct registration is allowed for development.
if !state.bots_enabled {
return Ok(Json(serde_json::json!({"ok": false, "description": "Bot API is disabled on this server. Use a server with --enable-bots"})));
}
// Only BotFather can register bots
// Require botfather_token field matching the stored BotFather token
if let Some(ref bf_token) = req.botfather_token {
let botfather_fp = "0000000000000000botfather00000000";
let bf_key = format!("bot_fp:{}", botfather_fp);
let stored_token = state.db.tokens.get(bf_key.as_bytes())
.ok().flatten()
.map(|v| String::from_utf8_lossy(&v).to_string());
if stored_token.as_deref() != Some(bf_token.as_str()) {
return Ok(Json(serde_json::json!({"ok": false, "description": "invalid BotFather token"})));
}
} else {
return Ok(Json(serde_json::json!({"ok": false, "description": "bot registration requires BotFather authorization. Message @botfather to create a bot."})));
}
let fp = req
.fingerprint
.chars()