chipa_webhooks/platform/
telegram.rs1use serde_json::{Map, Value, json};
2
3use super::Platform;
4
5pub struct Telegram {
6 token: String,
7 chat_id: i64,
8 parse_mode: ParseMode,
9}
10
11#[derive(Clone, Copy, Default)]
12pub enum ParseMode {
13 #[default]
14 MarkdownV2,
15 Html,
16 Plain,
17}
18
19impl ParseMode {
20 fn as_str(&self) -> Option<&'static str> {
21 match self {
22 Self::MarkdownV2 => Some("MarkdownV2"),
23 Self::Html => Some("HTML"),
24 Self::Plain => None,
25 }
26 }
27}
28
29impl Telegram {
30 pub fn new(token: impl Into<String>, chat_id: i64) -> Self {
31 Self {
32 token: token.into(),
33 chat_id,
34 parse_mode: ParseMode::default(),
35 }
36 }
37
38 pub fn with_parse_mode(mut self, parse_mode: ParseMode) -> Self {
39 self.parse_mode = parse_mode;
40 self
41 }
42}
43
44fn escape_markdown_v2(text: &str) -> String {
47 const RESERVED: &[char] = &[
48 '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!',
49 ];
50
51 let mut out = String::with_capacity(text.len());
52 for ch in text.chars() {
53 if RESERVED.contains(&ch) {
54 out.push('\\');
55 }
56 out.push(ch);
57 }
58 out
59}
60
61impl Platform for Telegram {
62 fn build_payload(&self, rendered: &str, hints: &Map<String, Value>) -> Value {
63 let text = match self.parse_mode {
64 ParseMode::MarkdownV2 => escape_markdown_v2(rendered),
65 _ => rendered.to_owned(),
66 };
67
68 let mut payload = json!({
69 "chat_id": self.chat_id,
70 "text": text,
71 });
72
73 if let Some(mode) = self.parse_mode.as_str() {
74 payload["parse_mode"] = Value::String(mode.to_owned());
75 }
76
77 if hints
78 .get("__tg_silent")
79 .and_then(Value::as_bool)
80 .unwrap_or(false)
81 {
82 payload["disable_notification"] = Value::Bool(true);
83 }
84
85 if hints
86 .get("__tg_disable_preview")
87 .and_then(Value::as_bool)
88 .unwrap_or(false)
89 {
90 payload["disable_web_page_preview"] = Value::Bool(true);
91 }
92
93 payload
94 }
95
96 fn endpoint(&self) -> &str {
97 Box::leak(
102 format!("https://api.telegram.org/bot{}/sendMessage", self.token).into_boxed_str(),
103 )
104 }
105}