Diff
Logged in as anonymous

Differences From Artifact [5758349194]:

To Artifact [4f9a819f27]:


1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
//! Telegram API integration for sending messages and attachments.

use crate::utils::{
	Attachment,
	validate,
};

use std::{
	collections::HashMap,
	fmt::Debug,
};

use stacked_errors::{

	Result,
	StackableErr,
};
use tgbot::{
	api::Client,
	types::{
		ChatPeerId,













>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//! Telegram API integration for sending messages and attachments.

use crate::utils::{
	Attachment,
	validate,
};

use std::{
	collections::HashMap,
	fmt::Debug,
};

use stacked_errors::{
	bail,
	Result,
	StackableErr,
};
use tgbot::{
	api::Client,
	types::{
		ChatPeerId,
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
	///
	/// # Arguments
	/// * `api_key` - Telegram Bot API token.
	/// * `recipients` - Mapping of email addresses to Telegram chat IDs.
	/// * `settings` - Additional configuration (API gateway, default chat).
	///
	/// # Errors

	/// Returns an error if API client creation fails.
	pub fn new (api_key: String, recipients: HashMap<String, i64>, settings: &config::Config) -> Result<TelegramTransport> {
		let default = settings.get_int("default")
			.context("[smtp2tg.toml] missing \"default\" recipient.\n")?;
		let api_gateway = settings.get_string("api_gateway")
			.context("[smtp2tg.toml] missing \"api_gateway\" destination.\n")?;

		let tg = Client::new(api_key)







>
|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	///
	/// # Arguments
	/// * `api_key` - Telegram Bot API token.
	/// * `recipients` - Mapping of email addresses to Telegram chat IDs.
	/// * `settings` - Additional configuration (API gateway, default chat).
	///
	/// # Errors
	/// Returns an error if configuration values cannot be read or if Telegram
	/// API client creation fails.
	pub fn new (api_key: String, recipients: HashMap<String, i64>, settings: &config::Config) -> Result<TelegramTransport> {
		let default = settings.get_int("default")
			.context("[smtp2tg.toml] missing \"default\" recipient.\n")?;
		let api_gateway = settings.get_string("api_gateway")
			.context("[smtp2tg.toml] missing \"api_gateway\" destination.\n")?;

		let tg = Client::new(api_key)
71
72
73
74
75
76
77



78
79
80
81
82
83
84
85
86
87
88



89
90
91
92
93
94
95
	/// Sends a debug message to the default chat.
	///
	/// # Arguments
	/// * `msg` - Message text to send.
	///
	/// # Returns
	/// * `Result<Message>` - Telegram API response.



	pub async fn debug (&self, msg: &str) -> Result<Message> {
		self.send(&self.default, format!("<pre>{}</pre>", validate(msg).stack()?)).await
	}

	/// Retrieves a chat ID by name.
	///
	/// # Arguments
	/// * `name` - Name or email to look up.
	///
	/// # Returns
	/// * `Result<&ChatPeerId>` - Chat ID if found.



	pub fn get (&self, name: &str) -> Result<&ChatPeerId> {
		self.recipients.get(name)
			.with_context(|| format!("Recipient \"{name}\" not found in configuration"))
	}

	/// Sends a text message to a specified chat.
	///







>
>
>











>
>
>







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
	/// Sends a debug message to the default chat.
	///
	/// # Arguments
	/// * `msg` - Message text to send.
	///
	/// # Returns
	/// * `Result<Message>` - Telegram API response.
	///
	/// # Errors
	/// Returns an error if `msg` contains a closing Telegram tag or sending fails.
	pub async fn debug (&self, msg: &str) -> Result<Message> {
		self.send(&self.default, format!("<pre>{}</pre>", validate(msg).stack()?)).await
	}

	/// Retrieves a chat ID by name.
	///
	/// # Arguments
	/// * `name` - Name or email to look up.
	///
	/// # Returns
	/// * `Result<&ChatPeerId>` - Chat ID if found.
	///
	/// # Errors
	/// Returns an error if `name` is not configured.
	pub fn get (&self, name: &str) -> Result<&ChatPeerId> {
		self.recipients.get(name)
			.with_context(|| format!("Recipient \"{name}\" not found in configuration"))
	}

	/// Sends a text message to a specified chat.
	///
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
		).await.stack()
	}

	/// Sends a message with attachments to a specified chat.
	///
	/// # Arguments
	/// * `to` - Target chat ID.
	/// * `media` - List of attachments.
	/// * `msg` - Message text (supports HTML formatting).
	///
	/// # Returns
	/// * `Result<()>` - Success or error.
	pub async fn sendgroup (&self, to: &ChatPeerId, media: Vec<Attachment>, msg: &str) -> Result<()> {
		if media.len() > 1 {
			let mut attach = vec![];







|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
		).await.stack()
	}

	/// Sends a message with attachments to a specified chat.
	///
	/// # Arguments
	/// * `to` - Target chat ID.
	/// * `media` - List of attachments, non-empty.
	/// * `msg` - Message text (supports HTML formatting).
	///
	/// # Returns
	/// * `Result<()>` - Success or error.
	pub async fn sendgroup (&self, to: &ChatPeerId, media: Vec<Attachment>, msg: &str) -> Result<()> {
		if media.len() > 1 {
			let mut attach = vec![];
135
136
137
138
139
140
141



142
143
144
145
146
147
148
149
150
151
152
153
						),
						caption
					)
				);
			}
			self.tg.execute(SendMediaGroup::new(*to, MediaGroup::new(attach).stack()?)).await.stack()?;
		} else {



			self.tg.execute(
				SendDocument::new(
					*to,
					InputFileReader::from(media[0].data.clone())
					.with_file_name(media[0].name.clone())
				).with_caption(msg)
				.with_caption_parse_mode(Html)
			).await.stack()?;
		}
		Ok(())
	}
}







>
>
>












143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
						),
						caption
					)
				);
			}
			self.tg.execute(SendMediaGroup::new(*to, MediaGroup::new(attach).stack()?)).await.stack()?;
		} else {
			if media.is_empty() {
				bail!("At least one attachment is required.");
			}
			self.tg.execute(
				SendDocument::new(
					*to,
					InputFileReader::from(media[0].data.clone())
					.with_file_name(media[0].name.clone())
				).with_caption(msg)
				.with_caption_parse_mode(Html)
			).await.stack()?;
		}
		Ok(())
	}
}