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