0f47e23e21 2026-01-12 1: use crate::utils::Attachment;
f5ed284f8c 2025-06-21 2:
f5ed284f8c 2025-06-21 3: use std::{
f5ed284f8c 2025-06-21 4: collections::HashMap,
f5ed284f8c 2025-06-21 5: fmt::Debug,
f5ed284f8c 2025-06-21 6: };
f5ed284f8c 2025-06-21 7:
a044f68fa7 2025-08-23 8: use stacked_errors::{
f5ed284f8c 2025-06-21 9: Result,
a044f68fa7 2025-08-23 10: StackableErr,
f5ed284f8c 2025-06-21 11: };
f5ed284f8c 2025-06-21 12: use tgbot::{
f5ed284f8c 2025-06-21 13: api::Client,
f5ed284f8c 2025-06-21 14: types::{
f5ed284f8c 2025-06-21 15: ChatPeerId,
f5ed284f8c 2025-06-21 16: InputFile,
f5ed284f8c 2025-06-21 17: InputFileReader,
f5ed284f8c 2025-06-21 18: InputMediaDocument,
f5ed284f8c 2025-06-21 19: MediaGroup,
f5ed284f8c 2025-06-21 20: MediaGroupItem,
f5ed284f8c 2025-06-21 21: Message,
0f47e23e21 2026-01-12 22: ParseMode::Html,
f5ed284f8c 2025-06-21 23: SendMediaGroup,
f5ed284f8c 2025-06-21 24: SendMessage,
f5ed284f8c 2025-06-21 25: SendDocument,
f5ed284f8c 2025-06-21 26: },
f5ed284f8c 2025-06-21 27: };
f5ed284f8c 2025-06-21 28:
f5ed284f8c 2025-06-21 29: #[derive(Debug)]
f5ed284f8c 2025-06-21 30: pub struct TelegramTransport {
f5ed284f8c 2025-06-21 31: tg: Client,
f5ed284f8c 2025-06-21 32: recipients: HashMap<String, ChatPeerId>,
f5ed284f8c 2025-06-21 33: pub default: ChatPeerId,
f5ed284f8c 2025-06-21 34: }
f5ed284f8c 2025-06-21 35:
f5ed284f8c 2025-06-21 36: impl TelegramTransport {
0f47e23e21 2026-01-12 37: /// Creates new TelegramTransport object.
14ef340959 2026-01-01 38: pub fn new (api_key: String, recipients: HashMap<String, i64>, settings: &config::Config) -> Result<TelegramTransport> {
14ef340959 2026-01-01 39: let default = settings.get_int("default")
14ef340959 2026-01-01 40: .context("[smtp2tg.toml] missing \"default\" recipient.\n")?;
14ef340959 2026-01-01 41: let api_gateway = settings.get_string("api_gateway")
14ef340959 2026-01-01 42: .context("[smtp2tg.toml] missing \"api_gateway\" destination.\n")?;
0f47e23e21 2026-01-12 43:
f5ed284f8c 2025-06-21 44: let tg = Client::new(api_key)
14ef340959 2026-01-01 45: .context("Failed to create API.\n")?
14ef340959 2026-01-01 46: .with_host(api_gateway);
f5ed284f8c 2025-06-21 47: let recipients = recipients.into_iter()
f5ed284f8c 2025-06-21 48: .map(|(a, b)| (a, ChatPeerId::from(b))).collect();
f5ed284f8c 2025-06-21 49: let default = ChatPeerId::from(default);
f5ed284f8c 2025-06-21 50:
f5ed284f8c 2025-06-21 51: Ok(TelegramTransport {
f5ed284f8c 2025-06-21 52: tg,
f5ed284f8c 2025-06-21 53: recipients,
f5ed284f8c 2025-06-21 54: default,
f5ed284f8c 2025-06-21 55: })
f5ed284f8c 2025-06-21 56: }
f5ed284f8c 2025-06-21 57:
f5ed284f8c 2025-06-21 58: /// Send message to default user, used for debug/log/info purposes
f5ed284f8c 2025-06-21 59: pub async fn debug (&self, msg: &str) -> Result<Message> {
0f47e23e21 2026-01-12 60: self.send(&self.default, format!("<pre>{msg}</pre>")).await
f5ed284f8c 2025-06-21 61: }
f5ed284f8c 2025-06-21 62:
f5ed284f8c 2025-06-21 63: /// Get recipient by address
f5ed284f8c 2025-06-21 64: pub fn get (&self, name: &str) -> Result<&ChatPeerId> {
f5ed284f8c 2025-06-21 65: self.recipients.get(name)
f5ed284f8c 2025-06-21 66: .with_context(|| format!("Recipient \"{name}\" not found in configuration"))
f5ed284f8c 2025-06-21 67: }
f5ed284f8c 2025-06-21 68:
f5ed284f8c 2025-06-21 69: /// Send message to specified user
f5ed284f8c 2025-06-21 70: pub async fn send <S> (&self, to: &ChatPeerId, msg: S) -> Result<Message>
f5ed284f8c 2025-06-21 71: where S: Into<String> + Debug{
a044f68fa7 2025-08-23 72: self.tg.execute(
f5ed284f8c 2025-06-21 73: SendMessage::new(*to, msg)
0f47e23e21 2026-01-12 74: .with_parse_mode(Html)
a044f68fa7 2025-08-23 75: ).await.stack()
f5ed284f8c 2025-06-21 76: }
f5ed284f8c 2025-06-21 77:
f5ed284f8c 2025-06-21 78: /// Send media to specified user
f5ed284f8c 2025-06-21 79: pub async fn sendgroup (&self, to: &ChatPeerId, media: Vec<Attachment>, msg: &str) -> Result<()> {
f5ed284f8c 2025-06-21 80: if media.len() > 1 {
f5ed284f8c 2025-06-21 81: let mut attach = vec![];
f5ed284f8c 2025-06-21 82: let mut pos = media.len();
f5ed284f8c 2025-06-21 83: for file in media {
f5ed284f8c 2025-06-21 84: let mut caption = InputMediaDocument::default();
f5ed284f8c 2025-06-21 85: if pos == 1 {
f5ed284f8c 2025-06-21 86: caption = caption.with_caption(msg)
0f47e23e21 2026-01-12 87: .with_caption_parse_mode(Html);
f5ed284f8c 2025-06-21 88: }
f5ed284f8c 2025-06-21 89: pos -= 1;
f5ed284f8c 2025-06-21 90: attach.push(
f5ed284f8c 2025-06-21 91: MediaGroupItem::for_document(
f5ed284f8c 2025-06-21 92: InputFile::from(
f5ed284f8c 2025-06-21 93: InputFileReader::from(file.data)
f5ed284f8c 2025-06-21 94: .with_file_name(file.name)
f5ed284f8c 2025-06-21 95: ),
f5ed284f8c 2025-06-21 96: caption
f5ed284f8c 2025-06-21 97: )
f5ed284f8c 2025-06-21 98: );
f5ed284f8c 2025-06-21 99: }
a044f68fa7 2025-08-23 100: self.tg.execute(SendMediaGroup::new(*to, MediaGroup::new(attach).stack()?)).await.stack()?;
f5ed284f8c 2025-06-21 101: } else {
f5ed284f8c 2025-06-21 102: self.tg.execute(
f5ed284f8c 2025-06-21 103: SendDocument::new(
f5ed284f8c 2025-06-21 104: *to,
f5ed284f8c 2025-06-21 105: InputFileReader::from(media[0].data.clone())
f5ed284f8c 2025-06-21 106: .with_file_name(media[0].name.clone())
f5ed284f8c 2025-06-21 107: ).with_caption(msg)
0f47e23e21 2026-01-12 108: .with_caption_parse_mode(Html)
a044f68fa7 2025-08-23 109: ).await.stack()?;
f5ed284f8c 2025-06-21 110: }
f5ed284f8c 2025-06-21 111: Ok(())
f5ed284f8c 2025-06-21 112: }
f5ed284f8c 2025-06-21 113: }