From 7628ff7a753781cffa23bddeae57bb8ded25ba63 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Sun, 29 Mar 2026 15:35:52 +0400 Subject: [PATCH] =?UTF-8?q?v0.0.34:=20fix=20bot=20sendMessage=20=E2=80=94?= =?UTF-8?q?=20store=20per-bot=20numeric=20ID=20reverse=20mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-bot numeric IDs (privacy feature) broke sendMessage because the reverse lookup couldn't find the fingerprint from the per-bot hash. Fix: store numid: → fingerprint in tokens tree when generating updates. resolve_chat_id checks this mapping first. Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/Cargo.lock | 10 +++--- warzone/Cargo.toml | 2 +- warzone/crates/warzone-protocol/Cargo.toml | 2 +- .../crates/warzone-server/src/routes/bot.rs | 35 +++++++++++++------ .../crates/warzone-server/src/routes/web.rs | 4 +-- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/warzone/Cargo.lock b/warzone/Cargo.lock index 20db4ef..af3744a 100644 --- a/warzone/Cargo.lock +++ b/warzone/Cargo.lock @@ -2956,7 +2956,7 @@ dependencies = [ [[package]] name = "warzone-client" -version = "0.0.33" +version = "0.0.34" dependencies = [ "anyhow", "argon2", @@ -2989,7 +2989,7 @@ dependencies = [ [[package]] name = "warzone-mule" -version = "0.0.33" +version = "0.0.34" dependencies = [ "anyhow", "clap", @@ -2998,7 +2998,7 @@ dependencies = [ [[package]] name = "warzone-protocol" -version = "0.0.33" +version = "0.0.34" dependencies = [ "base64", "bincode", @@ -3023,7 +3023,7 @@ dependencies = [ [[package]] name = "warzone-server" -version = "0.0.33" +version = "0.0.34" dependencies = [ "anyhow", "axum", @@ -3053,7 +3053,7 @@ dependencies = [ [[package]] name = "warzone-wasm" -version = "0.0.33" +version = "0.0.34" dependencies = [ "base64", "bincode", diff --git a/warzone/Cargo.toml b/warzone/Cargo.toml index e9c5a5a..c448f78 100644 --- a/warzone/Cargo.toml +++ b/warzone/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.0.33" +version = "0.0.34" edition = "2021" license = "MIT" rust-version = "1.75" diff --git a/warzone/crates/warzone-protocol/Cargo.toml b/warzone/crates/warzone-protocol/Cargo.toml index 1cfea19..433f9cd 100644 --- a/warzone/crates/warzone-protocol/Cargo.toml +++ b/warzone/crates/warzone-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "warzone-protocol" -version = "0.0.33" +version = "0.0.34" edition = "2021" license = "MIT" description = "Core crypto & wire protocol for featherChat (Warzone messenger)" diff --git a/warzone/crates/warzone-server/src/routes/bot.rs b/warzone/crates/warzone-server/src/routes/bot.rs index 477288f..d5c204d 100644 --- a/warzone/crates/warzone-server/src/routes/bot.rs +++ b/warzone/crates/warzone-server/src/routes/bot.rs @@ -101,6 +101,12 @@ fn resolve_chat_id(state: &AppState, chat_id: &serde_json::Value) -> Option { let num = n.as_i64().unwrap_or(0); + // Look up per-bot numeric ID reverse mapping + let numid_key = format!("numid:{}", num); + if let Some(fp_bytes) = state.db.tokens.get(numid_key.as_bytes()).ok().flatten() { + return Some(String::from_utf8_lossy(&fp_bytes).to_string()); + } + // Fallback: scan all keys with global hash (legacy) for item in state.db.keys.iter().flatten() { let key_str = String::from_utf8_lossy(&item.0).to_string(); if !key_str.contains(':') && key_str.len() == 32 { @@ -188,9 +194,9 @@ pub async fn try_bot_webhook(state: &AppState, to_fp: &str, message: &[u8]) -> b let update = if let Ok(wire) = bincode::deserialize::(message) { - wire_message_to_update(&wire, message, &token) + wire_message_to_update(state, &wire, message, &token) } else if let Ok(bot_msg) = serde_json::from_slice::(message) { - bot_json_to_update(&bot_msg, &token) + bot_json_to_update(state, &bot_msg, &token) } else { None }; @@ -515,9 +521,9 @@ fn migrate_raw_queue(state: &AppState, bot_fp: &str, bot_token: &str) { let update = if let Ok(wire) = bincode::deserialize::(&value) { - wire_message_to_update(&wire, &value, bot_token) + wire_message_to_update(state, &wire, &value, bot_token) } else if let Ok(bot_msg) = serde_json::from_slice::(&value) { - bot_json_to_update(&bot_msg, bot_token) + bot_json_to_update(state, &bot_msg, bot_token) } else { None }; @@ -533,8 +539,15 @@ fn migrate_raw_queue(state: &AppState, bot_fp: &str, bot_token: &str) { } } +/// Store a per-bot numeric ID → fingerprint reverse mapping. +fn store_numid_mapping(state: &AppState, numeric_id: i64, fingerprint: &str) { + let key = format!("numid:{}", numeric_id); + let _ = state.db.tokens.insert(key.as_bytes(), fingerprint.as_bytes()); +} + /// Convert a `WireMessage` into a Telegram-style update JSON (without update_id). fn wire_message_to_update( + state: &AppState, wire: &warzone_protocol::message::WireMessage, raw_bytes: &[u8], bot_token: &str, @@ -546,7 +559,7 @@ fn wire_message_to_update( .. } => { let raw_b64 = base64::engine::general_purpose::STANDARD.encode(raw_bytes); - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); store_numid_mapping(state, numeric, sender_fingerprint); Some(serde_json::json!({ "message": { "message_id": id, @@ -571,7 +584,7 @@ fn wire_message_to_update( .. } => { let raw_b64 = base64::engine::general_purpose::STANDARD.encode(raw_bytes); - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); store_numid_mapping(state, numeric, sender_fingerprint); Some(serde_json::json!({ "message": { "message_id": id, @@ -597,7 +610,7 @@ fn wire_message_to_update( payload, .. } => { - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); store_numid_mapping(state, numeric, sender_fingerprint); Some(serde_json::json!({ "message": { "message_id": id, @@ -626,7 +639,7 @@ fn wire_message_to_update( file_size, .. } => { - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(sender_fingerprint, bot_token); store_numid_mapping(state, numeric, sender_fingerprint); Some(serde_json::json!({ "message": { "message_id": id, @@ -654,12 +667,12 @@ fn wire_message_to_update( } /// Convert a plaintext bot JSON message into a Telegram-style update (without update_id). -fn bot_json_to_update(bot_msg: &serde_json::Value, bot_token: &str) -> Option { +fn bot_json_to_update(state: &AppState, bot_msg: &serde_json::Value, bot_token: &str) -> Option { let msg_type = bot_msg.get("type").and_then(|v| v.as_str())?; match msg_type { "bot_message" => { let from_fp = bot_msg.get("from").and_then(|v| v.as_str()).unwrap_or(""); - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(from_fp, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(from_fp, bot_token); store_numid_mapping(state, numeric, from_fp); Some(serde_json::json!({ "message": { "message_id": bot_msg.get("id").and_then(|v| v.as_str()).unwrap_or(""), @@ -678,7 +691,7 @@ fn bot_json_to_update(bot_msg: &serde_json::Value, bot_token: &str) -> Option { let from_fp = bot_msg.get("from").and_then(|v| v.as_str()).unwrap_or(""); - let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(from_fp, bot_token); + let numeric = crate::routes::resolve::fp_to_numeric_id_for_bot(from_fp, bot_token); store_numid_mapping(state, numeric, from_fp); Some(serde_json::json!({ "callback_query": { "id": bot_msg.get("id").and_then(|v| v.as_str()).unwrap_or(""), diff --git a/warzone/crates/warzone-server/src/routes/web.rs b/warzone/crates/warzone-server/src/routes/web.rs index 2b4420a..8005709 100644 --- a/warzone/crates/warzone-server/src/routes/web.rs +++ b/warzone/crates/warzone-server/src/routes/web.rs @@ -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-v15'; +const CACHE = 'wz-v16'; 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.33'; +const VERSION = '0.0.34'; let DEBUG = true; // toggle with /debug command // ── Receipt tracking ──