From 7ec67cb6e4e5202b2e7eef7193df400da40fa401 Mon Sep 17 00:00:00 2001 From: doryan Date: Fri, 31 May 2024 00:37:47 +0400 Subject: [PATCH] reinit git --- .gitignore | 16 ++++ Cargo.toml | 19 +++++ src/handlers/actions/ban.rs | 64 ++++++++++++++ src/handlers/actions/mod.rs | 2 + src/handlers/actions/mute.rs | 80 ++++++++++++++++++ .../commands_handler/endpoints/help.rs | 26 ++++++ .../commands_handler/endpoints/mod.rs | 2 + .../commands_handler/endpoints/unmute.rs | 60 ++++++++++++++ src/handlers/commands_handler/mod.rs | 1 + src/handlers/dice_handler/dice.rs | 50 +++++++++++ src/handlers/dice_handler/mod.rs | 1 + src/handlers/mod.rs | 3 + src/main.rs | 83 +++++++++++++++++++ src/middlewares/admin_check_middleware.rs | 48 +++++++++++ src/middlewares/mod.rs | 1 + src/types/enums/mod.rs | 1 + src/types/enums/time_metrics.rs | 30 +++++++ src/types/mod.rs | 3 + src/types/structs/bot_entity.rs | 8 ++ src/types/structs/countable_time.rs | 37 +++++++++ src/types/structs/message_sender.rs | 43 ++++++++++ src/types/structs/mod.rs | 3 + src/types/traits/countable_interface.rs | 12 +++ src/types/traits/mod.rs | 1 + src/utils/general/mod.rs | 1 + src/utils/general/unrestrict_date.rs | 5 ++ src/utils/mod.rs | 2 + src/utils/telegram/admin_check.rs | 11 +++ src/utils/telegram/ban_member.rs | 5 ++ src/utils/telegram/demote.rs | 20 +++++ src/utils/telegram/get_all_admins.rs | 6 ++ src/utils/telegram/mod.rs | 6 ++ src/utils/telegram/restrict.rs | 32 +++++++ src/utils/telegram/try_do_action.rs | 41 +++++++++ 34 files changed, 723 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/handlers/actions/ban.rs create mode 100644 src/handlers/actions/mod.rs create mode 100644 src/handlers/actions/mute.rs create mode 100644 src/handlers/commands_handler/endpoints/help.rs create mode 100644 src/handlers/commands_handler/endpoints/mod.rs create mode 100644 src/handlers/commands_handler/endpoints/unmute.rs create mode 100644 src/handlers/commands_handler/mod.rs create mode 100644 src/handlers/dice_handler/dice.rs create mode 100644 src/handlers/dice_handler/mod.rs create mode 100644 src/handlers/mod.rs create mode 100644 src/main.rs create mode 100644 src/middlewares/admin_check_middleware.rs create mode 100644 src/middlewares/mod.rs create mode 100644 src/types/enums/mod.rs create mode 100644 src/types/enums/time_metrics.rs create mode 100644 src/types/mod.rs create mode 100644 src/types/structs/bot_entity.rs create mode 100644 src/types/structs/countable_time.rs create mode 100644 src/types/structs/message_sender.rs create mode 100644 src/types/structs/mod.rs create mode 100644 src/types/traits/countable_interface.rs create mode 100644 src/types/traits/mod.rs create mode 100644 src/utils/general/mod.rs create mode 100644 src/utils/general/unrestrict_date.rs create mode 100644 src/utils/mod.rs create mode 100644 src/utils/telegram/admin_check.rs create mode 100644 src/utils/telegram/ban_member.rs create mode 100644 src/utils/telegram/demote.rs create mode 100644 src/utils/telegram/get_all_admins.rs create mode 100644 src/utils/telegram/mod.rs create mode 100644 src/utils/telegram/restrict.rs create mode 100644 src/utils/telegram/try_do_action.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a5fc12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +.env + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b210ce8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ + +[package] +name = "dorya" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.86" +async-trait = "0.1.80" +chrono = "0.4.38" +dashmap = "5.5.3" +dotenvy = "0.15.7" +futures = "0.3.30" +telers = "1.0.0-alpha.18" +tokio = { version="1.37.0", features=["rt-multi-thread"] } +tracing = "0.1.40" +tracing-subscriber = {version="0.3.18", features=["env-filter"]} diff --git a/src/handlers/actions/ban.rs b/src/handlers/actions/ban.rs new file mode 100644 index 0000000..2855ff1 --- /dev/null +++ b/src/handlers/actions/ban.rs @@ -0,0 +1,64 @@ +use telers::{ + event::{telegram::HandlerResult, EventReturn}, + types::{Message, User}, + Bot, +}; + +use tokio::time::{sleep, Duration as DurationSleep}; + +use crate::{ + types::structs::{bot_entity::BotEntity, message_sender::MessageSender}, + utils::telegram::try_do_action::try_restrict, +}; + +use crate::utils::telegram::ban_member::ban_chat_member; + +pub async fn ban(bot_entity: BotEntity, delay: u64) -> HandlerResult { + let (bot, message, sender): (Bot, Message, MessageSender) = ( + bot_entity.bot_instance, + bot_entity.receive_message, + bot_entity.message_sender, + ); + + let reply_to: Message; + let chat_id: i64 = message.chat().id(); + + if let Some(msg) = message.reply_to_message() { + reply_to = msg.clone(); + } else { + sender + .reply_to(message.id()) + .text("Ответьте на сообщение, чтобы забанить.") + .send(&bot) + .await?; + return Ok(EventReturn::Cancel); + } + + let user_data: &User = reply_to.from().unwrap(); + let user_id: i64 = user_data.id; + + sleep(DurationSleep::from_millis(delay)).await; + + let future = || async { ban_chat_member(&bot, user_id, chat_id).await }; + let demote_args: (&Bot, i64, i64) = (&bot, user_id, chat_id); + let failure_sender: MessageSender = sender + .clone() + .text("Невозможно забанить участника чата, демотните и попробуйте снова"); + + if try_restrict(future, demote_args, failure_sender) + .await + .is_err() + { + Ok(EventReturn::Cancel) + } else { + let banned_user_name: String = reply_to.from().unwrap().clone().username.unwrap(); + + sender + .reply_to(message.id()) + .text(format!("Пользователь {} забанен.", banned_user_name)) + .send(&bot) + .await?; + + Ok(EventReturn::Finish) + } +} diff --git a/src/handlers/actions/mod.rs b/src/handlers/actions/mod.rs new file mode 100644 index 0000000..603368e --- /dev/null +++ b/src/handlers/actions/mod.rs @@ -0,0 +1,2 @@ +pub mod ban; +pub mod mute; diff --git a/src/handlers/actions/mute.rs b/src/handlers/actions/mute.rs new file mode 100644 index 0000000..b22ef93 --- /dev/null +++ b/src/handlers/actions/mute.rs @@ -0,0 +1,80 @@ +use futures::stream::futures_unordered; +use telers::{ + event::{telegram::HandlerResult, EventReturn}, + types::{Message, User}, + Bot, +}; + +use tokio::time::{sleep, Duration as DurationSleep}; + +use crate::{ + types::{ + enums::time_metrics::TimeMetrics, + structs::{ + bot_entity::BotEntity, countable_time::CountableTime, message_sender::MessageSender, + }, + traits::countable_interface::ICountable, + }, + utils::telegram::try_do_action::try_restrict, +}; + +use crate::utils::{general::unrestrict_date::unrestrict_date, telegram::restrict::restrict}; + +pub async fn mute(bot_entity: BotEntity, values: (TimeMetrics, u64)) -> HandlerResult { + let (bot, message, sender): (Bot, Message, MessageSender) = ( + bot_entity.bot_instance, + bot_entity.receive_message, + bot_entity.message_sender, + ); + + let reply_to: Message; + let chat_id: i64 = message.chat().id(); + + if let Some(msg) = message.reply_to_message() { + reply_to = msg.clone(); + } else { + sender + .reply_to(message.id()) + .text("Ответьте на сообщение, чтобы замьютить.") + .send(&bot) + .await?; + return Ok(EventReturn::Cancel); + } + + let user_data: &User = reply_to.from().unwrap(); + let user_id: i64 = user_data.id; + + sleep(DurationSleep::from_millis(values.1)).await; + + let time_duration = values.0.extract(); + let unmute_date = unrestrict_date(time_duration); + let postfix = CountableTime::from_value(time_duration) + .get_postfix(values.0) + .unwrap(); + + let future = || async { restrict(&bot, user_id, unmute_date, chat_id).await }; + let demote_args: (&Bot, i64, i64) = (&bot, user_id, chat_id); + let failure_sender: MessageSender = sender + .clone() + .text("Невозможно замьютить участника чата, демотните и попробуйте снова"); + + if try_restrict(future, demote_args, failure_sender) + .await + .is_err() + { + Ok(EventReturn::Cancel) + } else { + let muted_user_name = reply_to.from().unwrap().clone().username.unwrap(); + + sender + .reply_to(message.id()) + .text(format!( + "Пользователь {} замьючен на {:?} {}.", + muted_user_name, time_duration, postfix + )) + .send(&bot) + .await?; + + Ok(EventReturn::Finish) + } +} diff --git a/src/handlers/commands_handler/endpoints/help.rs b/src/handlers/commands_handler/endpoints/help.rs new file mode 100644 index 0000000..e99225d --- /dev/null +++ b/src/handlers/commands_handler/endpoints/help.rs @@ -0,0 +1,26 @@ +use telers::{ + event::{telegram::HandlerResult, EventReturn}, + types::Message, + Bot, +}; + +use crate::types::structs::message_sender::MessageSender; + +const HELP_TEXT: &str = "\ + /help - помощь по боту.\n\ + /unmute - снимает с человека мьют, нужно ответить на сообщение, чтобы команда сработала (\ + только для админов).\n\ + 🎲 - выдаёт мут, для этого нужно отправить ТОЛЬКО эмодзи в ответ на сообщение участника. \ + чата, которого вы хотите замьютить (только для админов).\n\ + 🎰 - выдаёт бан в случае джекпота, напротив, мьют, всё так же кидайте этот эмодзи в ответ \ + на сообщение участника чата, которого вы хотите замьютить/забанить (только для админов)."; + +pub async fn help(bot: Bot, message: Message) -> HandlerResult { + println!("hi"); + MessageSender::new(message.chat().id()) + .text(HELP_TEXT) + .send(&bot) + .await + .unwrap(); + Ok(EventReturn::Finish) +} diff --git a/src/handlers/commands_handler/endpoints/mod.rs b/src/handlers/commands_handler/endpoints/mod.rs new file mode 100644 index 0000000..bbf60e9 --- /dev/null +++ b/src/handlers/commands_handler/endpoints/mod.rs @@ -0,0 +1,2 @@ +pub mod help; +pub mod unmute; diff --git a/src/handlers/commands_handler/endpoints/unmute.rs b/src/handlers/commands_handler/endpoints/unmute.rs new file mode 100644 index 0000000..dae8c4a --- /dev/null +++ b/src/handlers/commands_handler/endpoints/unmute.rs @@ -0,0 +1,60 @@ +use telers::{ + event::{telegram::HandlerResult, EventReturn}, + filters::CommandObject, + methods::RestrictChatMember, + types::User, + types::{ChatPermissions, Message}, + Bot, +}; + +use crate::types::structs::message_sender::MessageSender; + +pub async fn unmute(bot: Bot, message: Message, command: CommandObject) -> HandlerResult { + let sender = MessageSender::new(message.chat().id()); + + let reply_to: Message; + let chat_id: i64 = message.chat().id(); + + if let Some(msg) = message.reply_to_message() { + reply_to = msg.clone(); + } else { + sender + .reply_to(message.id()) + .text("Ответьте на сообщение, чтобы замьютить.") + .send(&bot) + .await?; + return Ok(EventReturn::Cancel); + } + + let user_data: &User = reply_to.from().unwrap(); + let user_id: i64 = user_data.id; + + let default_member_permissions = ChatPermissions::all() + .can_change_info(false) + .can_manage_topics(false) + .can_invite_users(false) + .can_pin_messages(false); + + let bot_action = RestrictChatMember::new(chat_id, user_id, default_member_permissions); + + let username = reply_to.from().unwrap().clone().username.unwrap(); + + if let Err(error) = bot.send(bot_action).await { + sender + .text(format!( + "Невозможно снять мьют с участника чата по причине: {error:?}" + )) + .send(&bot) + .await?; + return Ok(EventReturn::Cancel); + } else { + sender + .reply_to(message.id()) + .text(format!("С пользователя {} был снят мьут.", username)) + .send(&bot) + .await + .unwrap(); + } + + Ok(EventReturn::Finish) +} diff --git a/src/handlers/commands_handler/mod.rs b/src/handlers/commands_handler/mod.rs new file mode 100644 index 0000000..c4b360f --- /dev/null +++ b/src/handlers/commands_handler/mod.rs @@ -0,0 +1 @@ +pub mod endpoints; diff --git a/src/handlers/dice_handler/dice.rs b/src/handlers/dice_handler/dice.rs new file mode 100644 index 0000000..f869cec --- /dev/null +++ b/src/handlers/dice_handler/dice.rs @@ -0,0 +1,50 @@ +use telers::{ + event::{telegram::HandlerResult, EventReturn}, + types::{Dice, Message}, + Bot, +}; + +use crate::{ + handlers::actions::{ban::ban, mute::mute}, + types::{ + enums::time_metrics::TimeMetrics, + structs::{bot_entity::BotEntity, message_sender::MessageSender}, + }, +}; + +const DICE_DELAY_MS: u64 = 4000u64; +const CASINO_DELAY_MS: u64 = 1500u64; + +pub async fn dice_handler(bot: Bot, message: Message) -> HandlerResult { + let (chat_id, dice): (i64, Dice) = (message.chat().id(), message.dice().unwrap().clone()); + + let bot_entity: BotEntity = BotEntity { + bot_instance: bot, + receive_message: message, + message_sender: MessageSender::new(chat_id), + }; + + let (mute_time, emoji): (TimeMetrics, &str) = (TimeMetrics::Days(dice.value), &dice.emoji); + + match emoji { + "🎲" => { + mute(bot_entity, (mute_time, DICE_DELAY_MS)).await?; + } + "🎰" => { + if dice.value == 64 { + ban(bot_entity, CASINO_DELAY_MS).await?; + } else { + mute(bot_entity, (mute_time, CASINO_DELAY_MS)).await?; + } + } + _ => { + bot_entity + .message_sender + .text("Такой эмодзи не имеет привязки к какому либо действию бота.") + .send(&bot_entity.bot_instance) + .await?; + } + } + + Ok(EventReturn::Finish) +} diff --git a/src/handlers/dice_handler/mod.rs b/src/handlers/dice_handler/mod.rs new file mode 100644 index 0000000..99ad3e8 --- /dev/null +++ b/src/handlers/dice_handler/mod.rs @@ -0,0 +1 @@ +pub mod dice; diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs new file mode 100644 index 0000000..13a6c8e --- /dev/null +++ b/src/handlers/mod.rs @@ -0,0 +1,3 @@ +pub mod actions; +pub mod commands_handler; +pub mod dice_handler; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f6c6efa --- /dev/null +++ b/src/main.rs @@ -0,0 +1,83 @@ +use dotenvy::dotenv; +use telers::{ + enums::ContentType, + event::ToServiceProvider, + filters::{content_type::ContentType as CT, Command}, + Bot, Dispatcher, Router, +}; + +use tracing_subscriber::{fmt, layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter}; + +mod middlewares; +mod types; +mod utils; + +use middlewares::admin_check_middleware::AdminCheck; + +mod handlers; +use handlers::{ + commands_handler::endpoints::{help::help, unmute::unmute}, + dice_handler::dice::dice_handler, +}; + +#[tokio::main] +async fn main() { + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_env("RUST_LOG")) + .init(); + + dotenv().ok(); + + let token_result = dotenvy::var("TOKEN"); + match token_result { + Ok(token) => { + let bot = Bot::new(token); + + let mut route = Router::new("main"); + + let mut dice = Router::new("dice"); + + dice.message + .register(dice_handler) + .filter(CT::one(ContentType::Dice)); + + dice.message.inner_middlewares.register(AdminCheck {}); + + let mut command = Router::new("commands"); + let mut admin_commands = Router::new("admin_commands"); + let mut default_commands = Router::new("default_commands"); + + admin_commands + .message + .register(unmute) + .filter(Command::one("unmute")); + + admin_commands + .message + .inner_middlewares + .register(AdminCheck {}); + + default_commands + .message + .register(help) + .filter(Command::one("help")); + + command.include(admin_commands).include(default_commands); + + route.include(dice).include(command); + + let dispatcher = Dispatcher::builder().main_router(route).bot(bot).build(); + + dispatcher + .to_service_provider_default() + .unwrap() + .run_polling() + .await + .unwrap(); + } + Err(error) => { + println!("Error text: {:?}", error); + } + } +} diff --git a/src/middlewares/admin_check_middleware.rs b/src/middlewares/admin_check_middleware.rs new file mode 100644 index 0000000..76caafb --- /dev/null +++ b/src/middlewares/admin_check_middleware.rs @@ -0,0 +1,48 @@ +use async_trait::async_trait; +use telers::{ + errors::{EventErrorKind, HandlerError}, + event::telegram::{HandlerRequest, HandlerResponse}, + middlewares::{InnerMiddleware, Next}, +}; + +use anyhow::Error as Reject; + +use crate::{ + types::structs::message_sender::MessageSender, + utils::telegram::{admin_check::is_admin, get_all_admins::get_all_admins}, +}; + +#[derive(Default)] +pub struct AdminCheck {} + +#[async_trait] +impl InnerMiddleware for AdminCheck { + async fn call( + &self, + request: HandlerRequest, + next: Next, + ) -> Result { + let (bot, message) = (request.clone().bot, request.clone().update); + + let admins_list = get_all_admins(&bot, message.chat().unwrap().id()) + .await + .unwrap(); + + let chat_id: i64 = message.chat_id().unwrap(); + + if is_admin(&admins_list, message.from().unwrap()) { + let response = next(request).await?; + return Ok(response); + } else { + MessageSender::new(chat_id) + .text("У вам нет прав администратора, чтобы использовать эту команду.") + .send(&bot) + .await + .unwrap(); + + return Err(EventErrorKind::Handler(HandlerError::new(Reject::msg( + "User isn't admin.".to_string(), + )))); + } + } +} diff --git a/src/middlewares/mod.rs b/src/middlewares/mod.rs new file mode 100644 index 0000000..a8a9033 --- /dev/null +++ b/src/middlewares/mod.rs @@ -0,0 +1 @@ +pub mod admin_check_middleware; diff --git a/src/types/enums/mod.rs b/src/types/enums/mod.rs new file mode 100644 index 0000000..fbf955b --- /dev/null +++ b/src/types/enums/mod.rs @@ -0,0 +1 @@ +pub mod time_metrics; diff --git a/src/types/enums/time_metrics.rs b/src/types/enums/time_metrics.rs new file mode 100644 index 0000000..02dddbe --- /dev/null +++ b/src/types/enums/time_metrics.rs @@ -0,0 +1,30 @@ +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum TimeMetrics { + Minutes(i64), + Hours(i64), + Days(i64), + Weeks(i64), + Mounths(i64), +} + +impl TimeMetrics { + pub fn extract(self) -> i64 { + match self { + Self::Minutes(min) => min, + Self::Hours(hrs) => hrs, + Self::Days(day) => day, + Self::Weeks(wks) => wks, + Self::Mounths(mon) => mon, + } + } + pub fn get_word_declensions(self) -> (impl Into, impl Into, impl Into) { + match self { + Self::Minutes(_) => ("минута", "минуты", "минут"), + Self::Hours(_) => ("час", "часов", "часов"), + Self::Days(_) => ("день", "дня", "дней"), + Self::Weeks(_) => ("неделя", "недели", "недель"), + Self::Mounths(_) => ("месяц", "месяца", "месяцев"), + } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..dc6734f --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,3 @@ +pub mod enums; +pub mod structs; +pub mod traits; diff --git a/src/types/structs/bot_entity.rs b/src/types/structs/bot_entity.rs new file mode 100644 index 0000000..12dd5bc --- /dev/null +++ b/src/types/structs/bot_entity.rs @@ -0,0 +1,8 @@ +use crate::types::structs::message_sender::MessageSender; +use telers::{types::Message, Bot}; + +pub struct BotEntity { + pub bot_instance: Bot, + pub receive_message: Message, + pub message_sender: MessageSender, +} diff --git a/src/types/structs/countable_time.rs b/src/types/structs/countable_time.rs new file mode 100644 index 0000000..cd19702 --- /dev/null +++ b/src/types/structs/countable_time.rs @@ -0,0 +1,37 @@ +use crate::types::{enums::time_metrics::TimeMetrics, traits::countable_interface::ICountable}; + +pub struct CountableTime(i64, i64); + +impl ICountable<(i64, i64), TimeMetrics> for CountableTime { + fn new() -> Self { + Self(0, 0) + } + + fn from_value(value: i64) -> Self { + Self((value / 10) % 10, value % 10) + } + + fn get_values(&self) -> (i64, i64) { + (self.0, self.1) + } + + fn set_values(&self, value: i64) -> Self { + Self((value / 10) % 10, value % 10) + } + + fn get_postfix(&self, metrics: TimeMetrics) -> Option { + let all_word_declensions = metrics.get_word_declensions(); + let (first, second, third): (String, String, String) = ( + all_word_declensions.0.into(), + all_word_declensions.1.into(), + all_word_declensions.2.into(), + ); + + match self.get_values() { + (0, 1) | (2..=9, 1) => Some(first), + (0, 2..=4) | (2..=9, 2..=4) => Some(second), + (0..=9, 0) | (0, 5..=9) | (1, 0..=9) | (2..=9, 5..=9) => Some(third), + _ => None, + } + } +} diff --git a/src/types/structs/message_sender.rs b/src/types/structs/message_sender.rs new file mode 100644 index 0000000..d227513 --- /dev/null +++ b/src/types/structs/message_sender.rs @@ -0,0 +1,43 @@ +use core::convert::Into; +use telers::{ + errors::session::ErrorKind, + methods::SendMessage, + types::{Message, ReplyParameters}, + Bot, +}; + +#[derive(Clone)] +pub struct MessageSender { + chat_id: i64, + send_message: SendMessage, +} + +impl MessageSender { + pub fn new(group_id: i64) -> Self { + Self { + chat_id: group_id, + send_message: SendMessage::new(group_id, ""), + } + } + pub fn group_id(mut self, val: i64) -> Self { + self.chat_id = val; + self.send_message = self.send_message.clone().chat_id(val); + self + } + pub fn text(mut self, val: impl Into) -> Self { + self.send_message = self.send_message.clone().text(val); + self + } + pub fn disable_notification(mut self, val: bool) -> Self { + self.send_message = self.send_message.clone().disable_notification(val); + self + } + pub fn reply_to(mut self, msg_id: i64) -> Self { + let reply = ReplyParameters::new(msg_id).chat_id(self.chat_id); + self.send_message = self.send_message.clone().reply_parameters(reply); + self + } + pub async fn send(self, bot: &Bot) -> Result { + bot.send(self.send_message).await + } +} diff --git a/src/types/structs/mod.rs b/src/types/structs/mod.rs new file mode 100644 index 0000000..116caa7 --- /dev/null +++ b/src/types/structs/mod.rs @@ -0,0 +1,3 @@ +pub mod bot_entity; +pub mod countable_time; +pub mod message_sender; diff --git a/src/types/traits/countable_interface.rs b/src/types/traits/countable_interface.rs new file mode 100644 index 0000000..808c5e2 --- /dev/null +++ b/src/types/traits/countable_interface.rs @@ -0,0 +1,12 @@ +#[allow(dead_code)] +pub trait ICountable { + fn new() -> Self; + + fn from_value(value: i64) -> Self; + + fn set_values(&self, value: i64) -> Self; + + fn get_values(&self) -> T; + + fn get_postfix(&self, metrics: M) -> Option; +} diff --git a/src/types/traits/mod.rs b/src/types/traits/mod.rs new file mode 100644 index 0000000..56567b2 --- /dev/null +++ b/src/types/traits/mod.rs @@ -0,0 +1 @@ +pub mod countable_interface; diff --git a/src/utils/general/mod.rs b/src/utils/general/mod.rs new file mode 100644 index 0000000..bd6d59d --- /dev/null +++ b/src/utils/general/mod.rs @@ -0,0 +1 @@ +pub mod unrestrict_date; diff --git a/src/utils/general/unrestrict_date.rs b/src/utils/general/unrestrict_date.rs new file mode 100644 index 0000000..db0f5f7 --- /dev/null +++ b/src/utils/general/unrestrict_date.rs @@ -0,0 +1,5 @@ +use chrono::{Duration, Local, NaiveDateTime}; + +pub fn unrestrict_date(days: i64) -> NaiveDateTime { + Local::now().naive_utc() + Duration::days(days) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..d2c6a56 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod general; +pub mod telegram; diff --git a/src/utils/telegram/admin_check.rs b/src/utils/telegram/admin_check.rs new file mode 100644 index 0000000..e1ff368 --- /dev/null +++ b/src/utils/telegram/admin_check.rs @@ -0,0 +1,11 @@ +use telers::types::{chat_member::ChatMember, User}; + +pub fn is_admin(all_admin_members: &Vec, user: &User) -> bool { + all_admin_members + .iter() + .any(|admin: &ChatMember| match admin { + ChatMember::Administrator(admin) => &admin.user == user, + ChatMember::Owner(owner) => &owner.user == user, + _ => false, + }) +} diff --git a/src/utils/telegram/ban_member.rs b/src/utils/telegram/ban_member.rs new file mode 100644 index 0000000..4d58a64 --- /dev/null +++ b/src/utils/telegram/ban_member.rs @@ -0,0 +1,5 @@ +use telers::{errors::session::ErrorKind, methods::BanChatMember, Bot}; + +pub async fn ban_chat_member(bot: &Bot, chat_id: i64, user_id: i64) -> Result { + bot.send(BanChatMember::new(chat_id, user_id)).await +} diff --git a/src/utils/telegram/demote.rs b/src/utils/telegram/demote.rs new file mode 100644 index 0000000..3580f32 --- /dev/null +++ b/src/utils/telegram/demote.rs @@ -0,0 +1,20 @@ +use telers::{errors::session::ErrorKind, methods::promote_chat_member::PromoteChatMember, Bot}; + +pub async fn demote_user(bot: &Bot, user_id: i64, chat_id: i64) -> Result { + bot.send( + PromoteChatMember::new(chat_id, user_id) + .can_manage_topics(false) + .can_pin_messages(false) + .can_invite_users(false) + .can_change_info(false) + .can_promote_members(false) + .can_restrict_members(false) + .can_manage_voice_chats(false) + .can_delete_messages(false) + .can_edit_messages(false) + .can_post_messages(false) + .can_manage_chat(false) + .is_anonymous(false), + ) + .await +} diff --git a/src/utils/telegram/get_all_admins.rs b/src/utils/telegram/get_all_admins.rs new file mode 100644 index 0000000..bc89fb2 --- /dev/null +++ b/src/utils/telegram/get_all_admins.rs @@ -0,0 +1,6 @@ +use telers::{errors::SessionErrorKind, methods::get_chat_administrators, types::ChatMember, Bot}; + +pub async fn get_all_admins(bot: &Bot, chat_id: i64) -> Result, SessionErrorKind> { + bot.send(get_chat_administrators::GetChatAdministrators::new(chat_id)) + .await +} diff --git a/src/utils/telegram/mod.rs b/src/utils/telegram/mod.rs new file mode 100644 index 0000000..64b9e1f --- /dev/null +++ b/src/utils/telegram/mod.rs @@ -0,0 +1,6 @@ +pub mod admin_check; +pub mod ban_member; +pub mod demote; +pub mod get_all_admins; +pub mod restrict; +pub mod try_do_action; diff --git a/src/utils/telegram/restrict.rs b/src/utils/telegram/restrict.rs new file mode 100644 index 0000000..637fb8d --- /dev/null +++ b/src/utils/telegram/restrict.rs @@ -0,0 +1,32 @@ +use chrono::NaiveDateTime; +use telers::{errors::session::ErrorKind, methods::*, types::ChatPermissions, Bot}; + +pub async fn restrict( + bot: &Bot, + user_id: i64, + duration: NaiveDateTime, + chat_id: i64, +) -> Result { + bot.send( + restrict_chat_member::RestrictChatMember::new( + chat_id, + user_id, + ChatPermissions::new() + .can_send_photos(false) + .can_send_polls(false) + .can_send_audios(false) + .can_send_videos(false) + .can_change_info(false) + .can_invite_users(false) + .can_send_other_messages(false) + .can_pin_messages(false) + .can_send_messages(false) + .can_send_voice_notes(false) + .can_send_video_notes(false) + .can_send_documents(false) + .can_manage_topics(false), + ) + .until_date(duration.and_utc().timestamp()), + ) + .await +} diff --git a/src/utils/telegram/try_do_action.rs b/src/utils/telegram/try_do_action.rs new file mode 100644 index 0000000..017dba1 --- /dev/null +++ b/src/utils/telegram/try_do_action.rs @@ -0,0 +1,41 @@ +use telers::{errors::SessionErrorKind as ErrorKind, event::EventReturn, Bot}; + +use crate::types::structs::message_sender::MessageSender; +use std::future::Future; + +use super::demote::demote_user; + +const DEMOTE_FAILURE_MESSAGE: &str = "\ + Нельзя выдать ограничение пользователю, т.к. невозможно демотнуть \ + участника посредством бота, если ему выдан админ при помощи других \ + админов или владельца чата."; + +pub async fn try_restrict( + future_callback: F, + demote_args: (&Bot, i64, i64), + failure_sender: MessageSender, +) -> Result<(), EventReturn> +where + R: Future>, + F: Copy + Fn() -> R, +{ + let (bot, user_id, chat_id): (&Bot, i64, i64) = demote_args; + + if future_callback().await.is_err() { + if demote_user(bot, user_id, chat_id).await.is_err() { + MessageSender::new(chat_id) + .text(DEMOTE_FAILURE_MESSAGE) + .send(bot) + .await + .unwrap(); + return Err(EventReturn::Cancel); + } + + if future_callback().await.is_err() { + failure_sender.send(bot).await.unwrap(); + return Err(EventReturn::Cancel); + } + } + + Ok(()) +}