chat_system/messengers/
telegram.rs1use crate::message::MessageType;
4use crate::{Message, Messenger};
5use anyhow::Result;
6use async_trait::async_trait;
7use reqwest::Client;
8use serde_json::Value;
9use tokio::sync::Mutex;
10
11pub struct TelegramMessenger {
12 name: String,
13 api_base_url: String,
14 client: Client,
15 last_update_id: Mutex<Option<i64>>,
16 connected: bool,
17}
18
19impl TelegramMessenger {
20 pub fn new(name: impl Into<String>, token: impl Into<String>) -> Self {
21 let token = token.into();
22 Self {
23 name: name.into(),
24 api_base_url: format!("https://api.telegram.org/bot{token}"),
25 client: Client::new(),
26 last_update_id: Mutex::new(None),
27 connected: false,
28 }
29 }
30
31 pub fn with_api_base_url(mut self, url: impl Into<String>) -> Self {
32 self.api_base_url = url.into();
33 self
34 }
35
36 fn api_url(&self, method: impl AsRef<str>) -> String {
37 format!(
38 "{}/{}",
39 self.api_base_url.trim_end_matches('/'),
40 method.as_ref()
41 )
42 }
43
44 fn get_updates_url(&self, offset: Option<i64>) -> String {
45 match offset {
46 Some(offset) => format!("{}?offset={offset}&timeout=0", self.api_url("getUpdates")),
47 None => format!("{}?timeout=0", self.api_url("getUpdates")),
48 }
49 }
50}
51
52#[async_trait]
53impl Messenger for TelegramMessenger {
54 fn name(&self) -> &str {
55 &self.name
56 }
57
58 fn messenger_type(&self) -> &str {
59 "telegram"
60 }
61
62 async fn initialize(&mut self) -> Result<()> {
63 let resp = self.client.get(self.api_url("getMe")).send().await?;
64
65 let data: Value = resp.json().await?;
66 if data["ok"].as_bool().unwrap_or(false) {
67 self.connected = true;
68 Ok(())
69 } else {
70 anyhow::bail!("Telegram getMe failed: {:?}", data);
71 }
72 }
73
74 async fn send_message(&self, chat_id: &str, text: &str) -> Result<String> {
75 let resp = self
76 .client
77 .post(self.api_url("sendMessage"))
78 .json(&serde_json::json!({
79 "chat_id": chat_id,
80 "text": text,
81 "parse_mode": "HTML",
82 }))
83 .send()
84 .await?;
85
86 let data: Value = resp.json().await?;
87 if data["ok"].as_bool().unwrap_or(false) {
88 let id = data["result"]["message_id"]
89 .as_i64()
90 .map(|i| i.to_string())
91 .unwrap_or_default();
92 Ok(id)
93 } else {
94 anyhow::bail!("Telegram sendMessage failed: {:?}", data);
95 }
96 }
97
98 async fn receive_messages(&self) -> Result<Vec<Message>> {
99 let next_offset = {
100 let last_update_id = self.last_update_id.lock().await;
101 last_update_id.map(|update_id| update_id + 1)
102 };
103 let resp = self
104 .client
105 .get(self.get_updates_url(next_offset))
106 .send()
107 .await?;
108
109 let data: Value = resp.json().await?;
110 let mut messages = Vec::new();
111 let mut max_update_id: Option<i64> = None;
112
113 if let Some(updates) = data["result"].as_array() {
114 for update in updates {
115 if let Some(update_id) = update["update_id"].as_i64() {
116 max_update_id = Some(match max_update_id {
117 Some(current) => current.max(update_id),
118 None => update_id,
119 });
120 }
121
122 if let Some(msg) = update.get("message") {
123 let id = msg["message_id"].as_i64().unwrap_or(0).to_string();
124 let sender = msg["from"]["username"]
125 .as_str()
126 .or_else(|| msg["from"]["first_name"].as_str())
127 .unwrap_or("unknown")
128 .to_string();
129 let content = msg["text"].as_str().unwrap_or("").to_string();
130 let timestamp = msg["date"].as_i64().unwrap_or(0);
131 let chat_id = msg["chat"]["id"].as_i64().map(|i| i.to_string());
132
133 messages.push(Message {
134 id,
135 sender,
136 content,
137 timestamp,
138 channel: chat_id,
139 reply_to: None,
140 thread_id: None,
141 media: None,
142 is_direct: false,
143 message_type: MessageType::Text,
144 edited_timestamp: None,
145 reactions: None,
146 });
147 }
148 }
149 }
150
151 if let Some(max_update_id) = max_update_id {
152 *self.last_update_id.lock().await = Some(max_update_id);
153 }
154
155 Ok(messages)
156 }
157
158 fn is_connected(&self) -> bool {
159 self.connected
160 }
161
162 async fn disconnect(&mut self) -> Result<()> {
163 *self.last_update_id.lock().await = None;
164 self.connected = false;
165 Ok(())
166 }
167}