Index: Cargo.lock ================================================================== --- Cargo.lock +++ Cargo.lock @@ -2098,11 +2098,11 @@ "quick-xml", ] [[package]] name = "rsstg" -version = "0.4.2" +version = "0.4.3" dependencies = [ "anyhow", "async-std", "atom_syndication", "chrono", Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "rsstg" -version = "0.4.2" +version = "0.4.3" authors = ["arcade"] edition = "2021" [dependencies] anyhow = "1.0.86" Index: src/core.rs ================================================================== --- src/core.rs +++ src/core.rs @@ -20,10 +20,12 @@ bail, Result, }; use async_std::task; use chrono::DateTime; +use lazy_static::lazy_static; +use regex::Regex; use tgbot::{ api::Client, handler::UpdateHandler, types::{ Bot, @@ -36,10 +38,19 @@ Update, UpdateType, UserPeerId, }, }; + +lazy_static!{ + pub static ref RE_SPECIAL: Regex = Regex::new(r"([\-_*\[\]()~`>#+|{}\.!])").unwrap(); +} + +/// Encodes special HTML entities to prevent them interfering with Telegram HTML +pub fn encode (text: &str) -> Cow<'_, str> { + RE_SPECIAL.replace_all(text, "\\$1") +} #[derive(Clone)] pub struct Core { owner_chat: ChatPeerId, // max_delay: u16, @@ -210,11 +221,11 @@ Err(err) => format!("Failed to fetch source data:\n{err}"), } }; task::spawn(async move { if let Err(err) = clone.check(source_id, true).await { - if let Err(err) = clone.send(&format!("{source}\nšŸ›‘ {err:?}"), None, None).await { + if let Err(err) = clone.send(&format!("{source}\nšŸ›‘ {}", encode(&err.to_string())), None, Some(ParseMode::MarkdownV2)).await { eprintln!("Check error: {err:?}"); // clone.disable(&source_id, owner).await.unwrap(); }; }; });