mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-22 11:04:21 +00:00
Add /diario
command (#11)
Co-authored-by: Lorenzo Rossi <snowycoder@gmail.com>
This commit is contained in:
parent
15576aa73b
commit
437f3cf565
17 changed files with 245 additions and 73 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -287,6 +287,7 @@ checksum = "62d6dcd069e7b5fe49a302411f759d4cf1cf2c27fe798ef46fb8baefc053dd2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
"diesel_derives",
|
"diesel_derives",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pq-sys",
|
"pq-sys",
|
||||||
|
|
|
@ -66,7 +66,7 @@ features = ["derive"]
|
||||||
|
|
||||||
[dependencies.diesel]
|
[dependencies.diesel]
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
features = ["postgres"]
|
features = ["postgres", "chrono"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.diesel_migrations]
|
[dependencies.diesel_migrations]
|
||||||
|
@ -108,6 +108,7 @@ default = [
|
||||||
interface_database = [
|
interface_database = [
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
|
"chrono",
|
||||||
]
|
]
|
||||||
interface_stratz = [
|
interface_stratz = [
|
||||||
"graphql_client"
|
"graphql_client"
|
||||||
|
@ -116,8 +117,8 @@ service_telegram = [
|
||||||
"interface_database",
|
"interface_database",
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"rand",
|
"rand",
|
||||||
|
"parse_datetime",
|
||||||
"chrono",
|
"chrono",
|
||||||
"parse_datetime"
|
|
||||||
]
|
]
|
||||||
service_brooch = [
|
service_brooch = [
|
||||||
"interface_database",
|
"interface_database",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
[print_schema]
|
[print_schema]
|
||||||
file = "src/database/schema.rs"
|
file = "src/interfaces/database/schema.rs"
|
||||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||||
|
|
||||||
[migrations_directory]
|
[migrations_directory]
|
||||||
|
|
1
migrations/2024-08-05-175443_add_diario/down.sql
Normal file
1
migrations/2024-08-05-175443_add_diario/down.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS diario;
|
13
migrations/2024-08-05-175443_add_diario/up.sql
Normal file
13
migrations/2024-08-05-175443_add_diario/up.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE TABLE diario (
|
||||||
|
id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY(START 10000),
|
||||||
|
|
||||||
|
saver_id INT REFERENCES users (id) DEFAULT null,
|
||||||
|
saved_on TIMESTAMP DEFAULT now(),
|
||||||
|
|
||||||
|
quoted_id INT REFERENCES users (id) DEFAULT null,
|
||||||
|
quoted_name VARCHAR DEFAULT null,
|
||||||
|
|
||||||
|
warning TEXT DEFAULT null,
|
||||||
|
quote TEXT NOT NULL,
|
||||||
|
context TEXT DEFAULT null
|
||||||
|
);
|
|
@ -1,47 +0,0 @@
|
||||||
// @generated automatically by Diesel CLI.
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
brooch_match (id) {
|
|
||||||
id -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
discord (discord_id) {
|
|
||||||
user_id -> Int4,
|
|
||||||
discord_id -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
steam (steam_id) {
|
|
||||||
user_id -> Int4,
|
|
||||||
steam_id -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
telegram (telegram_id) {
|
|
||||||
user_id -> Int4,
|
|
||||||
telegram_id -> Int8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
users (id) {
|
|
||||||
id -> Int4,
|
|
||||||
username -> Varchar,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::joinable!(discord -> users (user_id));
|
|
||||||
diesel::joinable!(steam -> users (user_id));
|
|
||||||
diesel::joinable!(telegram -> users (user_id));
|
|
||||||
|
|
||||||
diesel::allow_tables_to_appear_in_same_query!(
|
|
||||||
brooch_match,
|
|
||||||
discord,
|
|
||||||
steam,
|
|
||||||
telegram,
|
|
||||||
users,
|
|
||||||
);
|
|
|
@ -1,6 +1,6 @@
|
||||||
use diesel::{Identifiable, Insertable, Queryable, Selectable, Associations};
|
use diesel::{Identifiable, Insertable, Queryable, Selectable, Associations};
|
||||||
use diesel::pg::Pg;
|
use diesel::pg::Pg;
|
||||||
use super::schema::{users, telegram, discord, steam, brooch_match};
|
use super::schema::{users, telegram, discord, steam, brooch_match, diario};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Identifiable, Queryable, Selectable, Insertable)]
|
#[derive(Debug, Clone, PartialEq, Identifiable, Queryable, Selectable, Insertable)]
|
||||||
|
@ -51,3 +51,28 @@ pub struct SteamUser {
|
||||||
pub struct BroochMatch {
|
pub struct BroochMatch {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Insertable)]
|
||||||
|
#[diesel(table_name = diario)]
|
||||||
|
#[diesel(check_for_backend(Pg))]
|
||||||
|
pub struct DiarioAddition {
|
||||||
|
pub saver_id: Option<i32>,
|
||||||
|
pub warning: Option<String>,
|
||||||
|
pub quote: String,
|
||||||
|
pub quoted_name: Option<String>,
|
||||||
|
pub context: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Identifiable, Queryable, Selectable, Insertable)]
|
||||||
|
#[diesel(table_name = diario)]
|
||||||
|
#[diesel(check_for_backend(Pg))]
|
||||||
|
pub struct DiarioEntry {
|
||||||
|
pub id: i32,
|
||||||
|
pub saver_id: Option<i32>,
|
||||||
|
pub saved_on: Option<chrono::NaiveDateTime>,
|
||||||
|
pub quoted_id: Option<i32>,
|
||||||
|
pub quoted_name: Option<String>,
|
||||||
|
pub warning: Option<String>,
|
||||||
|
pub quote: String,
|
||||||
|
pub context: Option<String>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,24 @@
|
||||||
// @generated automatically by Diesel CLI.
|
// @generated automatically by Diesel CLI.
|
||||||
|
|
||||||
#[cfg(feature = "service_brooch")]
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
brooch_match (id) {
|
brooch_match (id) {
|
||||||
id -> Int8,
|
id -> Int8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
diario (id) {
|
||||||
|
id -> Int4,
|
||||||
|
saver_id -> Nullable<Int4>,
|
||||||
|
saved_on -> Nullable<Timestamp>,
|
||||||
|
quoted_id -> Nullable<Int4>,
|
||||||
|
quoted_name -> Nullable<Varchar>,
|
||||||
|
warning -> Nullable<Text>,
|
||||||
|
quote -> Text,
|
||||||
|
context -> Nullable<Text>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
discord (discord_id) {
|
discord (discord_id) {
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
|
@ -41,6 +53,7 @@ diesel::joinable!(telegram -> users (user_id));
|
||||||
|
|
||||||
diesel::allow_tables_to_appear_in_same_query!(
|
diesel::allow_tables_to_appear_in_same_query!(
|
||||||
brooch_match,
|
brooch_match,
|
||||||
|
diario,
|
||||||
discord,
|
discord,
|
||||||
steam,
|
steam,
|
||||||
telegram,
|
telegram,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::services::RoyalnetService;
|
||||||
use crate::utils::result::AnyResult;
|
use crate::utils::result::AnyResult;
|
||||||
use crate::interfaces::stratz::{Byte, guild_matches, Long, Short};
|
use crate::interfaces::stratz::{Byte, guild_matches, Long, Short};
|
||||||
use crate::interfaces::stratz::guild_matches::{GameMode, Lane, LobbyType, Match, Player, Role, Steam};
|
use crate::interfaces::stratz::guild_matches::{GameMode, Lane, LobbyType, Match, Player, Role, Steam};
|
||||||
use crate::utils::escape::EscapableInTelegramHTML;
|
use crate::utils::telegramdisplay::TelegramEscape;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BroochService {
|
pub struct BroochService {
|
||||||
|
|
148
src/services/telegram/commands/diario.rs
Normal file
148
src/services/telegram/commands/diario.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
use std::fmt::{Error, Write};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use anyhow::Context;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use teloxide::Bot;
|
||||||
|
use teloxide::payloads::SendMessageSetters;
|
||||||
|
use teloxide::prelude::Requester;
|
||||||
|
use teloxide::types::{Message, ParseMode};
|
||||||
|
use crate::interfaces::database::models::{DiarioAddition, DiarioEntry, RoyalnetUser};
|
||||||
|
use crate::services::telegram::commands::CommandResult;
|
||||||
|
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||||
|
use crate::utils::telegramdisplay::{TelegramEscape, TelegramWrite};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct DiarioArgs {
|
||||||
|
warning: Option<String>,
|
||||||
|
quote: String,
|
||||||
|
quoted: Option<String>,
|
||||||
|
context: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DiarioArgs {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#" *(?:\[(?<warning>.+)])? *"(?<quote>.+)"[, ]*(?:[-–—]+(?<quoted>\w+)(?:, *(?<context>.+))?)?"#).unwrap());
|
||||||
|
|
||||||
|
let captures = REGEX.captures(s)
|
||||||
|
.context("Sintassi del comando incorretta.")?;
|
||||||
|
|
||||||
|
let warning = captures.name("warning")
|
||||||
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
|
let quote = captures.name("quote")
|
||||||
|
.context("Citazione non specificata nel comando.")?
|
||||||
|
.as_str()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let quoted = captures.name("quoted")
|
||||||
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
|
let context = captures.name("context")
|
||||||
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Self { warning, quote, quoted, context }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TelegramWrite for DiarioEntry {
|
||||||
|
fn write_telegram<T>(&self, f: &mut T) -> Result<(), Error>
|
||||||
|
where T: Write
|
||||||
|
{
|
||||||
|
// Diario ID
|
||||||
|
write!(f, "<code>#{}</code>", self.id)?;
|
||||||
|
|
||||||
|
// Optional content warning
|
||||||
|
if let Some(warning) = self.to_owned().warning {
|
||||||
|
write!(f, ", <b>{}</b>", warning.escape_telegram_html())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newline
|
||||||
|
write!(f, "\n")?;
|
||||||
|
|
||||||
|
// Quote optionally covered by a spoiler tag
|
||||||
|
match self.warning.to_owned() {
|
||||||
|
None => write!(f, "<blockquote expandable>{}</blockquote>", self.clone().quote.escape_telegram_html())?,
|
||||||
|
Some(warning) => write!(f, "<blockquote expandable><tg-spoiler>{}</tg-spoiler></blockquote>", warning.escape_telegram_html())?,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newline
|
||||||
|
write!(f, "\n")?;
|
||||||
|
|
||||||
|
// Optional citation with optional context
|
||||||
|
match (self.quoted_name.to_owned(), self.context.to_owned()) {
|
||||||
|
(Some(name), Some(context)) => write!(f, "—{}, <i>{}</i>", name.escape_telegram_html(), context.escape_telegram_html())?,
|
||||||
|
(Some(name), None) => write!(f, "—{}", name.escape_telegram_html())?,
|
||||||
|
(None, Some(context)) => write!(f, "...<i>{}</i>", context.escape_telegram_html())?,
|
||||||
|
(None, None) => write!(f, "")?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler(bot: &Bot, message: &Message, args: DiarioArgs, database: &DatabaseInterface) -> CommandResult {
|
||||||
|
let author = message.from()
|
||||||
|
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||||
|
|
||||||
|
let mut database = database.connect()?;
|
||||||
|
|
||||||
|
let royalnet_user: RoyalnetUser = {
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::{ExpressionMethods, QueryDsl};
|
||||||
|
use crate::interfaces::database::schema::telegram::dsl::*;
|
||||||
|
use crate::interfaces::database::schema::users::dsl::*;
|
||||||
|
use crate::interfaces::database::models::RoyalnetUser;
|
||||||
|
|
||||||
|
telegram
|
||||||
|
.filter(telegram_id.eq::<i64>(
|
||||||
|
author.id.0.try_into()
|
||||||
|
.context("Non è stato possibile processare il tuo ID Telegram per via di un overflow.")?
|
||||||
|
))
|
||||||
|
.inner_join(users)
|
||||||
|
.select(RoyalnetUser::as_select())
|
||||||
|
.get_result(&mut database)
|
||||||
|
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?
|
||||||
|
};
|
||||||
|
|
||||||
|
let addition = DiarioAddition {
|
||||||
|
saver_id: Some(royalnet_user.id),
|
||||||
|
warning: args.warning,
|
||||||
|
quote: args.quote,
|
||||||
|
quoted_name: args.quoted,
|
||||||
|
context: args.context,
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = {
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::dsl::*;
|
||||||
|
use crate::interfaces::database::schema::diario::dsl::*;
|
||||||
|
|
||||||
|
insert_into(diario)
|
||||||
|
.values(&addition)
|
||||||
|
.get_result::<DiarioEntry>(&mut database)
|
||||||
|
.context("Non è stato possibile aggiungere la riga di diario al database RYG.")?
|
||||||
|
};
|
||||||
|
|
||||||
|
let text = format!(
|
||||||
|
"🖋 Riga aggiunta al diario!\n\
|
||||||
|
\n\
|
||||||
|
{}",
|
||||||
|
entry.to_string_telegram(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let _reply = bot
|
||||||
|
.send_message(message.chat.id, text)
|
||||||
|
.parse_mode(ParseMode::Html)
|
||||||
|
.reply_to_message_id(message.id)
|
||||||
|
.await
|
||||||
|
// teloxide does not support blockquotes yet and errors out on parsing the response
|
||||||
|
// .context("Non è stato possibile inviare la risposta.")?
|
||||||
|
;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ mod reminder;
|
||||||
mod dog;
|
mod dog;
|
||||||
mod cat;
|
mod cat;
|
||||||
mod roll;
|
mod roll;
|
||||||
|
mod diario;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, BotCommands)]
|
#[derive(Debug, Clone, PartialEq, Eq, BotCommands)]
|
||||||
#[command(rename_rule = "lowercase")]
|
#[command(rename_rule = "lowercase")]
|
||||||
|
@ -44,6 +45,8 @@ pub enum Command {
|
||||||
Cat,
|
Cat,
|
||||||
#[command(description = "Tira un dado.")]
|
#[command(description = "Tira un dado.")]
|
||||||
Roll(String),
|
Roll(String),
|
||||||
|
#[command(description = "Salva una citazione nel diario RYG.")]
|
||||||
|
Diario(diario::DiarioArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
@ -77,6 +80,7 @@ impl Command {
|
||||||
Command::Dog => dog::handler(&bot, &message).await,
|
Command::Dog => dog::handler(&bot, &message).await,
|
||||||
Command::Cat => cat::handler(&bot, &message).await,
|
Command::Cat => cat::handler(&bot, &message).await,
|
||||||
Command::Roll(roll) => roll::handler(&bot, &message, &roll).await,
|
Command::Roll(roll) => roll::handler(&bot, &message, &roll).await,
|
||||||
|
Command::Diario(args) => diario::handler(&bot, &message, args, &database).await,
|
||||||
};
|
};
|
||||||
|
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
|
@ -118,7 +122,7 @@ async fn error_command(bot: &Bot, chat_id: ChatId, message_id: MessageId, error:
|
||||||
pub async fn unknown_command(bot: Bot, message: Message) -> CommandResult {
|
pub async fn unknown_command(bot: Bot, message: Message) -> CommandResult {
|
||||||
log::debug!("Received an unknown command.");
|
log::debug!("Received an unknown command.");
|
||||||
|
|
||||||
bot.send_message(message.chat.id, "⚠️ Comando sconosciuto.")
|
bot.send_message(message.chat.id, "⚠️ Comando sconosciuto o sintassi non valida.")
|
||||||
.reply_to_message_id(message.id)
|
.reply_to_message_id(message.id)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use teloxide::types::{Message, ParseMode};
|
||||||
use parse_datetime::parse_datetime_at_date;
|
use parse_datetime::parse_datetime_at_date;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use crate::utils::escape::EscapableInTelegramHTML;
|
use crate::utils::telegramdisplay::TelegramEscape;
|
||||||
use super::{CommandResult};
|
use super::{CommandResult};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use teloxide::requests::Requester;
|
||||||
use teloxide::types::{Message, ParseMode};
|
use teloxide::types::{Message, ParseMode};
|
||||||
use crate::interfaces::database::models::{RoyalnetUser};
|
use crate::interfaces::database::models::{RoyalnetUser};
|
||||||
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||||
use crate::utils::escape::EscapableInTelegramHTML;
|
use crate::utils::telegramdisplay::TelegramEscape;
|
||||||
use super::{CommandResult};
|
use super::{CommandResult};
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use teloxide::dptree::entry;
|
||||||
use crate::services::telegram::commands::Command;
|
use crate::services::telegram::commands::Command;
|
||||||
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||||
use crate::utils::result::{AnyError, AnyResult};
|
use crate::utils::result::{AnyError, AnyResult};
|
||||||
use crate::utils::escape::EscapableInTelegramHTML;
|
use crate::utils::telegramdisplay::TelegramEscape;
|
||||||
use super::RoyalnetService;
|
use super::RoyalnetService;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
pub trait EscapableInTelegramHTML {
|
|
||||||
fn escape_telegram_html(self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EscapableInTelegramHTML for T
|
|
||||||
where String: From<T>
|
|
||||||
{
|
|
||||||
fn escape_telegram_html(self) -> String {
|
|
||||||
let s: String = String::from(self);
|
|
||||||
let s = s.replace("<", "<");
|
|
||||||
let s = s.replace(">", ">");
|
|
||||||
let s = s.replace("&", "&");
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod escape;
|
pub mod telegramdisplay;
|
||||||
|
|
28
src/utils/telegramdisplay.rs
Normal file
28
src/utils/telegramdisplay.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub trait TelegramWrite {
|
||||||
|
fn write_telegram<T>(&self, f: &mut T) -> Result<(), std::fmt::Error>
|
||||||
|
where T: Write;
|
||||||
|
|
||||||
|
fn to_string_telegram(&self) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
self.write_telegram(&mut result).unwrap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TelegramEscape {
|
||||||
|
fn escape_telegram_html(self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TelegramEscape for T
|
||||||
|
where String: From<T>
|
||||||
|
{
|
||||||
|
fn escape_telegram_html(self) -> String {
|
||||||
|
let s: String = String::from(self);
|
||||||
|
let s = s.replace("<", "<");
|
||||||
|
let s = s.replace(">", ">");
|
||||||
|
let s = s.replace("&", "&");
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue