flows_connector_dsi/telegram.rs
1use std::collections::HashMap;
2
3use serde::Deserialize;
4use serde_json::Value;
5
6#[derive(Deserialize, Debug)]
7pub struct User {
8 /// Unique identifier for this user or bot.
9 pub id: u64,
10 /// True, if this user is a bot.
11 pub is_bot: bool,
12 /// User's or bot's first name.
13 pub first_name: String,
14 /// User's or bot's username.
15 pub username: Option<String>,
16 /// [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag).
17 pub language_code: Option<String>,
18 /// True, if this user is a Telegram Premium user.
19 #[serde(default = "bool::default")]
20 pub is_premium: bool,
21 /// True, if this user added the bot to the attachment menu.
22 #[serde(default = "bool::default")]
23 pub added_to_attachment_menu: bool,
24}
25
26#[derive(Deserialize, Debug)]
27pub struct Chat {
28 /// Unique identifier for this chat.
29 pub id: i64,
30 /// Type of chat, can be either “private”, “group”, “supergroup” or “channel”.
31 pub r#type: String,
32 /// Title, for supergroups, channels and group chats.
33 pub title: Option<String>,
34 /// Username, for private chats, supergroups and channels if available.
35 pub username: Option<String>,
36 /// First name of the other party in a private chat.
37 pub first_name: Option<String>,
38 /// Last name of the other party in a private chat.
39 pub last_name: Option<String>,
40}
41
42#[derive(Deserialize, Debug)]
43pub struct MessageEntity {
44 /// Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag),
45 /// “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org),
46 /// “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123),
47 /// “bold” (bold text), “italic” (italic text), “underline” (underlined text),
48 /// “strikethrough” (strikethrough text), “spoiler” (spoiler message),
49 /// “code” (monowidth string), “pre” (monowidth block),
50 /// “text_link” (for clickable text URLs), “text_mention” (for users without usernames),
51 /// “custom_emoji” (for inline custom emoji stickers).
52 pub r#type: String,
53 /// Offset in UTF-16 code units to the start of the entity.
54 pub offset: i64,
55 /// Length of the entity in UTF-16 code units.
56 pub length: i64,
57 /// For “text_link” only, URL that will be opened after user taps on the text.
58 pub url: Option<String>,
59 /// For “text_mention” only, the mentioned user.
60 pub user: Option<User>,
61 /// For “pre” only, the programming language of the entity text.
62 pub language: Option<String>,
63 // custom_emoji_id: Option<String>,
64}
65
66#[derive(Deserialize, Debug)]
67pub struct Message {
68 /// Unique message identifier inside this chat.
69 pub message_id: i64,
70 /// Conversation the message belongs to.
71 pub chat: Chat,
72 /// Date the message was sent in Unix time.
73 pub date: u64,
74 /// Sender of the message; empty for messages sent to channels.
75 /// For backward compatibility, the field contains a fake sender user in non-channel chats,
76 /// if the message was sent on behalf of a chat.
77 pub from: Option<User>,
78 /// Sender of the message, sent on behalf of a chat.
79 /// For example, the channel itself for channel posts,
80 /// the supergroup itself for messages from anonymous group administrators,
81 /// the linked channel for messages automatically forwarded to the discussion group.
82 /// For backward compatibility,
83 /// the field from contains a fake sender user in non-channel chats,
84 /// if the message was sent on behalf of a chat.
85 pub sender_chat: Option<Chat>,
86 /// For text messages..
87 #[serde(default = "String::new")]
88 pub text: String,
89 /// For messages forwarded from channels or from anonymous administrators,
90 /// information about the original sender chat.
91 pub forward_from: Option<User>,
92 /// For messages forwarded from channels or from anonymous administrators,
93 /// information about the original sender chat.
94 pub forward_from_chat: Option<Chat>,
95 /// For messages forwarded from channels,
96 /// identifier of the original message in the channel
97 pub forward_from_message_id: Option<i64>,
98 /// For forwarded messages, date the original message was sent in Unix time.
99 pub forward_date: Option<u64>,
100 /// For replies, the original message.
101 /// Note that the Message object in this field will not contain further
102 /// reply_to_message fields even if it itself is a reply.
103 pub reply_to_message: Option<Box<Message>>,
104 /// Bot through which the message was sent.
105 pub via_bot: Option<User>,
106 /// Date the message was last edited in Unix time.
107 pub edit_date: Option<u64>,
108 /// New members that were added to the group or supergroup
109 /// and information about them (the bot itself may be one of these members)
110 #[serde(default = "Vec::new")]
111 pub new_chat_members: Vec<User>,
112 /// A member was removed from the group,
113 /// information about them (this member may be the bot itself)
114 pub left_chat_member: Option<User>,
115 #[serde(default = "Vec::new")]
116 pub entities: Vec<MessageEntity>,
117}
118
119#[derive(Deserialize, Debug)]
120pub struct ChatInviteLink {
121 /// The invite link. If the link was created by another chat administrator,
122 /// then the second part of the link will be replaced with “…”.
123 pub invite_link: String,
124 /// Creator of the link.
125 pub creator: User,
126 /// True, if users joining the chat via the link need
127 /// to be approved by chat administrators.
128 pub creates_join_request: bool,
129 /// True, if the link is primary.
130 pub is_primary: bool,
131 /// True, if the link is revoked.
132 pub is_revoked: bool,
133 /// Optional. Invite link name.
134 pub name: Option<String>,
135 /// Point in time (Unix timestamp) when the link will expire or has been expired.
136 pub expire_date: Option<u64>,
137 /// The maximum number of users that can be members of the chat simultaneously.
138 /// after joining the chat via this invite link; 1-99999.
139 pub member_limit: Option<u64>,
140 /// Number of pending join requests created using this link.
141 pub pending_join_request_count: Option<u64>,
142}
143
144#[derive(Deserialize, Debug)]
145pub struct ChatMember {
146 /// Status of the chat member. it can be “creator” (owner) , "administrator",
147 /// "member", “restricted”, “left” (member who left), "kicked” (member who was banned) .
148 pub status: String,
149 /// Information about the user.
150 pub user: User,
151
152 #[serde(flatten)]
153 pub extra: HashMap<String, Value>,
154}
155
156#[derive(Deserialize, Debug)]
157pub struct ChatMemberUpdated {
158 /// Chat the user belongs to.
159 pub chat: Chat,
160 /// Performer of the action, which resulted in the change.
161 pub from: User,
162 /// Date the change was done in Unix time.
163 pub date: u64,
164 /// Previous information about the chat member.
165 pub old_chat_member: ChatMember,
166 /// New information about the chat member.
167 pub new_chat_member: ChatMember,
168 /// Chat invite link, which was used by the user to join the chat;
169 /// for joining by invite link events only.
170 pub invite_link: Option<ChatInviteLink>,
171}
172
173#[derive(Deserialize, Debug)]
174pub struct ChatJoinRequest {
175 /// Chat to which the request was sent.
176 pub chat: Chat,
177 /// User that sent the join request.
178 pub from: User,
179 /// Date the request was sent in Unix time.
180 pub date: u64,
181 /// Bio of the user.
182 #[serde(default = "String::new")]
183 pub bio: String,
184 /// Chat invite link that was used by the user to send the join request.
185 pub invite_link: Option<ChatInviteLink>,
186}
187
188#[derive(Deserialize, Debug)]
189pub enum InboundData {
190 #[serde(rename = "message")]
191 Message(Message),
192 #[serde(rename = "edited_message")]
193 EditedMessage(Message),
194 #[serde(rename = "channel_post")]
195 ChannelPost(Message),
196 #[serde(rename = "edited_channel_post")]
197 EditedChannelPost(Message),
198 #[serde(rename = "my_chat_member")]
199 MyChatMember(ChatMemberUpdated),
200 #[serde(rename = "chat_member")]
201 ChatMember(ChatMemberUpdated),
202 #[serde(rename = "chat_join_request")]
203 ChatJoinRequest(ChatJoinRequest),
204}
205
206pub fn inbound(s: String) -> Result<InboundData, String> {
207 #[cfg(debug_assertions)]
208 return serde_json::from_str::<InboundData>(&s)
209 .map_err(|e| format!("Parsing Telegram Webhook payload failed: {}", e.to_string()));
210
211 #[cfg(not(debug_assertions))]
212 serde_json::from_str::<InboundData>(&s)
213 .map_err(|_| format!("Parsing Telegram Webhook payload failed: {}", s))
214}
215
216impl InboundData {
217 pub fn as_message(&self) -> Result<&Message, String> {
218 match self {
219 InboundData::Message(ref m) => Ok(m),
220 _ => Err("as_message failed".to_string()),
221 }
222 }
223
224 pub fn as_edited_message(&self) -> Result<&Message, String> {
225 match self {
226 InboundData::EditedMessage(ref m) => Ok(m),
227 _ => Err("as_edited_message failed".to_string()),
228 }
229 }
230
231 pub fn as_channel_post(&self) -> Result<&Message, String> {
232 match self {
233 InboundData::ChannelPost(ref m) => Ok(m),
234 _ => Err("as_channel_post failed".to_string()),
235 }
236 }
237
238 pub fn as_edited_channel_post(&self) -> Result<&Message, String> {
239 match self {
240 InboundData::EditedChannelPost(ref m) => Ok(m),
241 _ => Err("as_edited_channel_post failed".to_string()),
242 }
243 }
244
245 pub fn as_my_chat_member(&self) -> Result<&ChatMemberUpdated, String> {
246 match self {
247 InboundData::MyChatMember(ref c) => Ok(c),
248 _ => Err("as_my_chat_member failed".to_string()),
249 }
250 }
251
252 pub fn as_chat_member(&self) -> Result<&ChatMemberUpdated, String> {
253 match self {
254 InboundData::ChatMember(ref c) => Ok(c),
255 _ => Err("as_chat_member failed".to_string()),
256 }
257 }
258
259 pub fn as_chat_join_request(&self) -> Result<&ChatJoinRequest, String> {
260 match self {
261 InboundData::ChatJoinRequest(ref c) => Ok(c),
262 _ => Err("as_chat_join_request failed".to_string()),
263 }
264 }
265}
266
267pub mod outbound {
268 use std::collections::HashMap;
269
270 use serde::Serialize;
271 use serde_json::{json, Value};
272
273 #[derive(Serialize)]
274 pub struct OutboundData {
275 chat_id: String,
276
277 #[serde(flatten)]
278 extra: HashMap<String, Value>,
279 }
280
281 pub enum ChatId {
282 /// Unique identifier for the target chat
283 Id(i64),
284 /// Username of the target chat (in the format `@channelusername`)
285 Name(String),
286 }
287
288 impl From<i64> for ChatId {
289 fn from(n: i64) -> Self {
290 Self::Id(n)
291 }
292 }
293
294 impl From<String> for ChatId {
295 fn from(n: String) -> Self {
296 Self::Name(n)
297 }
298 }
299
300 impl ToString for ChatId {
301 fn to_string(&self) -> String {
302 match &*self {
303 ChatId::Id(n) => n.to_string(),
304 ChatId::Name(n) => n.clone(),
305 }
306 }
307 }
308
309 #[derive(Serialize)]
310 pub enum ParseMode {
311 Markdown,
312 MarkdownV2,
313 HTML,
314 }
315
316 /// Send a text message.
317 /// Click [here](https://core.telegram.org/bots/api#sendmessage) for other fields.
318 ///
319 /// eg.
320 /// ```rust
321 /// outbound::message(message.chat.id, "__PONG\\!__")
322 /// .reply(message.message_id)
323 /// .parse_mode(ParseMode::MarkdownV2)
324 /// .build()
325 /// ```
326 pub fn message<C: Into<ChatId>, T: Into<String>>(chat_id: C, message: T) -> OutboundData {
327 OutboundData {
328 chat_id: chat_id.into().to_string(),
329 extra: [("text".to_string(), json!(message.into()))]
330 .into_iter()
331 .collect(),
332 }
333 }
334
335 /// Edit a text message.
336 /// Click [here](https://core.telegram.org/bots/api#editmessagetext) for other fields.
337 ///
338 /// eg.
339 /// ```rust
340 /// outbound::edit_message(message.chat.id, message.message_id, "__PONG\\!__")
341 /// .parse_mode(ParseMode::MarkdownV2)
342 /// .build()
343 /// ```
344 pub fn edit_message<C: Into<ChatId>, M: Into<String>, T: Into<String>>(
345 chat_id: C,
346 message_id: M,
347 message: T,
348 ) -> OutboundData {
349 OutboundData {
350 chat_id: chat_id.into().to_string(),
351 extra: [
352 ("message_id".to_string(), json!(message_id.into())),
353 ("text".to_string(), json!(message.into())),
354 ]
355 .into_iter()
356 .collect(),
357 }
358 }
359
360 /// Ban a user in a chat.
361 /// Click [here](https://core.telegram.org/bots/api#banchatmember) for other fields.
362 ///
363 /// eg.
364 /// ```rust
365 /// outbound::ban(message.chat.id, message.from.id)
366 /// .build()
367 /// ```
368 pub fn ban<C: Into<ChatId>, T: Into<String>>(chat_id: C, user_id: T) -> OutboundData {
369 OutboundData {
370 chat_id: chat_id.into().to_string(),
371 extra: [("user_id".to_string(), json!(user_id.into()))]
372 .into_iter()
373 .collect(),
374 }
375 }
376
377 impl OutboundData {
378 /// Build outbound JSON data.
379 pub fn build(self) -> Result<String, String> {
380 if self.extra.is_empty() {
381 return Err("OutboundData build failed: Too few fields".to_string());
382 }
383
384 serde_json::to_string(&self)
385 .map_err(|e| format!("OutboundData build failed: {}", e.to_string()))
386 }
387
388 /// Reply to by original message ID.
389 pub fn reply<M: Into<String>>(mut self, message_id: M) -> Self {
390 self.extra
391 .insert("reply_to_message_id".to_string(), json!(message_id.into()));
392 self
393 }
394
395 /// Mode for parsing entities in the message text.
396 pub fn parse_mode(mut self, mode: ParseMode) -> Self {
397 self.extra.insert("parse_mode".to_string(), json!(mode));
398 self
399 }
400
401 /// Add extra field.
402 pub fn field<K: Into<String>>(mut self, name: K, value: Value) -> Self {
403 self.extra.insert(name.into(), value);
404 self
405 }
406 }
407}