Differences From Artifact [5758349194]:
- File src/telegram.rs — part of check-in [512369f93e] at 2026-08-01 11:20:12 on branch trunk — bump, add some docstrings, switch from just-getopt to clap (user: arcade, size: 3864) [annotate] [blame] [check-ins using]
To Artifact [4f9a819f27]:
- File src/telegram.rs — part of check-in [1723b63d69] at 2026-08-01 13:46:06 on branch trunk — doc corrections/optimizations (user: arcade, size: 4189) [annotate] [blame] [check-ins using]
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 | /// /// # Arguments /// * `api_key` - Telegram Bot API token. /// * `recipients` - Mapping of email addresses to Telegram chat IDs. /// * `settings` - Additional configuration (API gateway, default chat). /// /// # Errors | > | | 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 | ).await.stack() } /// Sends a message with attachments to a specified chat. /// /// # Arguments /// * `to` - Target chat ID. | | | 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(())
}
}
|