v0.0.27: TG-compatible bots — plaintext send, numeric IDs, webhooks, BotFather
Bot compatibility: - Clients send plaintext bot_message to bot aliases (no E2E encryption) - Numeric chat_id: fp_to_numeric_id() deterministic hash, accept string/number - Webhook delivery: POST updates to bot's webhook URL (async, fire-and-forget) - getUpdates timeout raised to 50s (was 30, TG uses 50) - parse_mode HTML rendered in web client - E2E bot registration: optional seed + bundle for encrypted bot sessions BotFather + instance control: - --enable-bots CLI flag (default: disabled) - BotFather auto-created on first start (@botfather alias) - Bot ownership: owner fingerprint stored in bot_info - All bot endpoints return 403 when disabled Bot Bridge: - tools/bot-bridge.py: TG-compatible proxy for unmodified TG bots - Translates chat_id int↔string, proxies getUpdates/sendMessage - README with python-telegram-bot and Telegraf examples Test fixes: - Updated tests for ETH address display in header/messages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -541,6 +541,48 @@ impl App {
|
||||
}
|
||||
};
|
||||
|
||||
// If peer is a bot alias, send plaintext (no E2E)
|
||||
let is_bot_peer = {
|
||||
let url = format!("{}/v1/alias/whois/{}", client.base_url, normfp(&peer));
|
||||
match client.client.get(&url).send().await {
|
||||
Ok(resp) => resp.json::<serde_json::Value>().await.ok()
|
||||
.and_then(|d| d.get("alias").and_then(|a| a.as_str().map(|s| s.ends_with("bot") || s.ends_with("Bot") || s.ends_with("_bot"))))
|
||||
.unwrap_or(false),
|
||||
Err(_) => false,
|
||||
}
|
||||
};
|
||||
|
||||
if is_bot_peer {
|
||||
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||
let bot_msg = serde_json::json!({
|
||||
"type": "bot_message",
|
||||
"id": msg_id,
|
||||
"from": normfp(&self.our_fp),
|
||||
"from_name": if self.our_eth.is_empty() { self.our_fp[..12].to_string() } else { self.our_eth.clone() },
|
||||
"text": text,
|
||||
"timestamp": chrono::Utc::now().timestamp(),
|
||||
});
|
||||
let msg_bytes = serde_json::to_vec(&bot_msg).unwrap_or_default();
|
||||
match client.send_message(&peer, Some(&self.our_fp), &msg_bytes).await {
|
||||
Ok(_) => {
|
||||
self.receipts.lock().unwrap().insert(msg_id.clone(), ReceiptStatus::Sent);
|
||||
let _ = db.touch_contact(&peer, None);
|
||||
let _ = db.store_message(&peer, &self.our_fp, &text, true);
|
||||
self.add_message(ChatLine {
|
||||
sender: if self.our_eth.is_empty() { self.our_fp[..12].to_string() } else { format!("{}...", &self.our_eth[..self.our_eth.len().min(12)]) },
|
||||
text: text.clone(),
|
||||
is_system: false,
|
||||
is_self: true,
|
||||
message_id: Some(msg_id), timestamp: Local::now(),
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
self.add_message(ChatLine { sender: "system".into(), text: format!("Send failed: {}", e), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let msg_id = uuid::Uuid::new_v4().to_string();
|
||||
let our_pub = identity.public_identity();
|
||||
let mut ratchet = db.load_session(&peer_fp).ok().flatten();
|
||||
|
||||
@@ -216,15 +216,16 @@ mod tests {
|
||||
// 2. header_contains_fingerprint
|
||||
// ----------------------------------------------------------------
|
||||
#[test]
|
||||
fn header_contains_fingerprint() {
|
||||
fn header_contains_identity() {
|
||||
let app = make_app();
|
||||
let mut terminal = make_terminal();
|
||||
terminal.draw(|f| app.draw(f)).unwrap();
|
||||
|
||||
let header = row_text(&terminal, 0);
|
||||
// Header shows ETH address (if seed exists) or fingerprint
|
||||
assert!(
|
||||
header.contains("aabbcc"),
|
||||
"header should contain our fingerprint 'aabbcc', got: {header}"
|
||||
header.contains("aabbcc") || header.contains("0x"),
|
||||
"header should contain fingerprint or ETH address, got: {header}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,8 @@ mod tests {
|
||||
let msgs = app.messages.lock().unwrap();
|
||||
assert!(msgs.len() >= 2);
|
||||
assert!(msgs[0].is_system);
|
||||
assert!(msgs[0].text.contains("aabbcc"));
|
||||
// First message shows ETH address (if seed exists) or fingerprint
|
||||
assert!(msgs[0].text.contains("You are"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user