v0.0.34: fix bot sendMessage — store per-bot numeric ID reverse mapping
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:<numeric_id> → fingerprint in tokens tree when generating updates. resolve_chat_id checks this mapping first. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
warzone/Cargo.lock
generated
10
warzone/Cargo.lock
generated
@@ -2956,7 +2956,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-client"
|
name = "warzone-client"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
@@ -2989,7 +2989,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-mule"
|
name = "warzone-mule"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -2998,7 +2998,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-protocol"
|
name = "warzone-protocol"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bincode",
|
"bincode",
|
||||||
@@ -3023,7 +3023,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-server"
|
name = "warzone-server"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3053,7 +3053,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "warzone-wasm"
|
name = "warzone-wasm"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.75"
|
rust-version = "1.75"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "warzone-protocol"
|
name = "warzone-protocol"
|
||||||
version = "0.0.33"
|
version = "0.0.34"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "Core crypto & wire protocol for featherChat (Warzone messenger)"
|
description = "Core crypto & wire protocol for featherChat (Warzone messenger)"
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ fn resolve_chat_id(state: &AppState, chat_id: &serde_json::Value) -> Option<Stri
|
|||||||
}
|
}
|
||||||
serde_json::Value::Number(n) => {
|
serde_json::Value::Number(n) => {
|
||||||
let num = n.as_i64().unwrap_or(0);
|
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() {
|
for item in state.db.keys.iter().flatten() {
|
||||||
let key_str = String::from_utf8_lossy(&item.0).to_string();
|
let key_str = String::from_utf8_lossy(&item.0).to_string();
|
||||||
if !key_str.contains(':') && key_str.len() == 32 {
|
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) =
|
let update = if let Ok(wire) =
|
||||||
bincode::deserialize::<warzone_protocol::message::WireMessage>(message)
|
bincode::deserialize::<warzone_protocol::message::WireMessage>(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::<serde_json::Value>(message) {
|
} else if let Ok(bot_msg) = serde_json::from_slice::<serde_json::Value>(message) {
|
||||||
bot_json_to_update(&bot_msg, &token)
|
bot_json_to_update(state, &bot_msg, &token)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -515,9 +521,9 @@ fn migrate_raw_queue(state: &AppState, bot_fp: &str, bot_token: &str) {
|
|||||||
let update = if let Ok(wire) =
|
let update = if let Ok(wire) =
|
||||||
bincode::deserialize::<warzone_protocol::message::WireMessage>(&value)
|
bincode::deserialize::<warzone_protocol::message::WireMessage>(&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::<serde_json::Value>(&value) {
|
} else if let Ok(bot_msg) = serde_json::from_slice::<serde_json::Value>(&value) {
|
||||||
bot_json_to_update(&bot_msg, bot_token)
|
bot_json_to_update(state, &bot_msg, bot_token)
|
||||||
} else {
|
} else {
|
||||||
None
|
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).
|
/// Convert a `WireMessage` into a Telegram-style update JSON (without update_id).
|
||||||
fn wire_message_to_update(
|
fn wire_message_to_update(
|
||||||
|
state: &AppState,
|
||||||
wire: &warzone_protocol::message::WireMessage,
|
wire: &warzone_protocol::message::WireMessage,
|
||||||
raw_bytes: &[u8],
|
raw_bytes: &[u8],
|
||||||
bot_token: &str,
|
bot_token: &str,
|
||||||
@@ -546,7 +559,7 @@ fn wire_message_to_update(
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let raw_b64 = base64::engine::general_purpose::STANDARD.encode(raw_bytes);
|
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!({
|
Some(serde_json::json!({
|
||||||
"message": {
|
"message": {
|
||||||
"message_id": id,
|
"message_id": id,
|
||||||
@@ -571,7 +584,7 @@ fn wire_message_to_update(
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let raw_b64 = base64::engine::general_purpose::STANDARD.encode(raw_bytes);
|
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!({
|
Some(serde_json::json!({
|
||||||
"message": {
|
"message": {
|
||||||
"message_id": id,
|
"message_id": id,
|
||||||
@@ -597,7 +610,7 @@ fn wire_message_to_update(
|
|||||||
payload,
|
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!({
|
Some(serde_json::json!({
|
||||||
"message": {
|
"message": {
|
||||||
"message_id": id,
|
"message_id": id,
|
||||||
@@ -626,7 +639,7 @@ fn wire_message_to_update(
|
|||||||
file_size,
|
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!({
|
Some(serde_json::json!({
|
||||||
"message": {
|
"message": {
|
||||||
"message_id": id,
|
"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).
|
/// 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<serde_json::Value> {
|
fn bot_json_to_update(state: &AppState, bot_msg: &serde_json::Value, bot_token: &str) -> Option<serde_json::Value> {
|
||||||
let msg_type = bot_msg.get("type").and_then(|v| v.as_str())?;
|
let msg_type = bot_msg.get("type").and_then(|v| v.as_str())?;
|
||||||
match msg_type {
|
match msg_type {
|
||||||
"bot_message" => {
|
"bot_message" => {
|
||||||
let from_fp = bot_msg.get("from").and_then(|v| v.as_str()).unwrap_or("");
|
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!({
|
Some(serde_json::json!({
|
||||||
"message": {
|
"message": {
|
||||||
"message_id": bot_msg.get("id").and_then(|v| v.as_str()).unwrap_or(""),
|
"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<se
|
|||||||
}
|
}
|
||||||
"callback_query" => {
|
"callback_query" => {
|
||||||
let from_fp = bot_msg.get("from").and_then(|v| v.as_str()).unwrap_or("");
|
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!({
|
Some(serde_json::json!({
|
||||||
"callback_query": {
|
"callback_query": {
|
||||||
"id": bot_msg.get("id").and_then(|v| v.as_str()).unwrap_or(""),
|
"id": bot_msg.get("id").and_then(|v| v.as_str()).unwrap_or(""),
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ async fn pwa_manifest() -> impl IntoResponse {
|
|||||||
|
|
||||||
async fn service_worker() -> impl IntoResponse {
|
async fn service_worker() -> impl IntoResponse {
|
||||||
([(header::CONTENT_TYPE, "application/javascript")], r##"
|
([(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'];
|
const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json'];
|
||||||
|
|
||||||
self.addEventListener('install', e => {
|
self.addEventListener('install', e => {
|
||||||
@@ -251,7 +251,7 @@ let pollTimer = null;
|
|||||||
let ws = null; // WebSocket connection
|
let ws = null; // WebSocket connection
|
||||||
let wasmReady = false;
|
let wasmReady = false;
|
||||||
|
|
||||||
const VERSION = '0.0.33';
|
const VERSION = '0.0.34';
|
||||||
let DEBUG = true; // toggle with /debug command
|
let DEBUG = true; // toggle with /debug command
|
||||||
|
|
||||||
// ── Receipt tracking ──
|
// ── Receipt tracking ──
|
||||||
|
|||||||
Reference in New Issue
Block a user