v0.0.32: system bots config — persist across data wipes, welcome screen

Server:
- --bots-config <path> loads JSON array of system bots on startup
- Bots auto-created if missing, aliases restored on every start
- Bot list stored in DB for welcome screen (system:bot_list key)
- GET /v1/bot/list returns system bots (public, no auth)

Welcome screen:
- Web + TUI show available bots on first login
- "Available bots: @helpbot — featherChat help, @codebot — Coding..."
- Clickable in web (via address detection)

Config: bots.example.json with 10 suggested bots
Usage: warzone-server --enable-bots --bots-config bots.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-29 14:07:34 +04:00
parent f04c24187d
commit 13f2227bf0
8 changed files with 145 additions and 9 deletions

View File

@@ -31,6 +31,7 @@ use crate::state::AppState;
/// Build the bot API routes (nested under `/v1`).
pub fn routes() -> Router<AppState> {
Router::new()
.route("/bot/list", get(list_system_bots))
.route("/bot/register", post(register_bot))
.route("/bot/:token/getMe", get(get_me))
.route("/bot/:token/getUpdates", post(get_updates))
@@ -43,6 +44,22 @@ pub fn routes() -> Router<AppState> {
.route("/bot/:token/sendDocument", post(send_document))
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// System bot list (public, no auth needed)
// ---------------------------------------------------------------------------
/// GET /v1/bot/list — returns system bots for welcome screen.
async fn list_system_bots(
State(state): State<AppState>,
) -> Json<serde_json::Value> {
let bots = state.db.tokens.get(b"system:bot_list")
.ok().flatten()
.and_then(|v| serde_json::from_slice::<Vec<serde_json::Value>>(&v).ok())
.unwrap_or_default();
Json(serde_json::json!({ "ok": true, "bots": bots }))
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

View File

@@ -50,7 +50,7 @@ async fn pwa_manifest() -> impl IntoResponse {
async fn service_worker() -> impl IntoResponse {
([(header::CONTENT_TYPE, "application/javascript")], r##"
const CACHE = 'wz-v13';
const CACHE = 'wz-v14';
const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json'];
self.addEventListener('install', e => {
@@ -251,7 +251,7 @@ let pollTimer = null;
let ws = null; // WebSocket connection
let wasmReady = false;
const VERSION = '0.0.31';
const VERSION = '0.0.32';
let DEBUG = true; // toggle with /debug command
// ── Receipt tracking ──
@@ -924,6 +924,20 @@ async function enterChat() {
addSys('v' + VERSION + ' | DM: paste peer fingerprint or @alias above');
addSys('/alias · /g · /gleave · /gkick · /gmembers · /glist · /friend · /file · /info');
// Show system bots if available
try {
const botResp = await fetch(SERVER + '/v1/bot/list');
const botData = await botResp.json();
if (botData.ok && botData.bots && botData.bots.length > 0) {
addSys('');
addSys('Available bots:');
for (const b of botData.bots) {
addSys(' @' + b.name + ' — ' + b.description);
}
addSys('Message a bot: /peer @botname');
}
} catch(e) {}
const savedPeer = localStorage.getItem('wz-peer');
if (savedPeer) {
$peerInput.value = savedPeer;