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