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