agentmux_srv/messaging/telegram/
mod.rs1mod poller;
14pub mod rest;
15pub mod types;
16
17use std::sync::{Arc, Mutex, OnceLock};
18
19use tokio::sync::mpsc;
20
21use crate::messaging::{BridgeHealth, OutboundMsg};
22
23#[derive(Debug, Clone)]
26pub struct TelegramConfig {
27 pub token: String,
29 pub allowed_chat_ids: Vec<i64>,
32 pub default_chat_id: Option<i64>,
34 pub target_agent: Option<String>,
37}
38
39pub struct TelegramBridge {
42 outbound_tx: mpsc::UnboundedSender<OutboundMsg>,
43 health: Arc<Mutex<BridgeHealth>>,
44}
45
46static GLOBAL_BRIDGE: OnceLock<TelegramBridge> = OnceLock::new();
47
48impl TelegramBridge {
49 pub fn init_global(config: TelegramConfig, http: reqwest::Client) {
52 let (outbound_tx, outbound_rx) = mpsc::unbounded_channel::<OutboundMsg>();
53 let health = Arc::new(Mutex::new(BridgeHealth::connecting("telegram")));
54
55 let bridge = TelegramBridge {
56 outbound_tx,
57 health: health.clone(),
58 };
59
60 if GLOBAL_BRIDGE.set(bridge).is_err() {
61 return; }
63
64 let allowed_chat_ids = config.allowed_chat_ids.clone();
65 let target_agent = config.target_agent.clone();
66
67 tokio::spawn(async move {
68 poller::run_poll_loop(config, http, outbound_rx, health).await;
69 });
70
71 tracing::info!(
72 "telegram_bridge: initialized (allowed_chats={:?}, target={:?})",
73 allowed_chat_ids,
74 target_agent
75 );
76 }
77
78 pub fn get() -> Option<&'static TelegramBridge> {
79 GLOBAL_BRIDGE.get()
80 }
81
82 pub fn send(&self, msg: OutboundMsg) -> Result<(), String> {
85 self.outbound_tx
86 .send(msg)
87 .map_err(|_| "telegram_bridge: outbound channel closed".to_string())
88 }
89
90 pub fn health(&self) -> BridgeHealth {
91 self.health.lock().unwrap().clone()
92 }
93}
94
95impl crate::messaging::MessagingBridge for TelegramBridge {
96 fn health(&self) -> BridgeHealth {
97 TelegramBridge::health(self)
98 }
99}