Compare commits

...

14 Commits

Author SHA1 Message Date
doryan 7b31cccf31 Now the admin_check function gets a second argument in the form of user_id, and this function is used in try_restrict 2024-06-08 03:12:16 +04:00
doryan 0ec0c94501 Function for checks for the existence of target user have been implemented for enum TargetUser 2024-06-08 03:09:17 +04:00
doryan 89119936c8 Сheck for the existence of target user have been added 2024-06-08 03:08:00 +04:00
doryan ac9fbcf344 Admin checks have been implemented in "Inner middleware" 2024-06-08 03:04:34 +04:00
doryan d803f6931f Unnecessary target checks have been removed 2024-06-08 03:02:53 +04:00
doryan a9ab73ec72 Some corrections to the command help text 2024-06-08 03:00:47 +04:00
doryan 2e02d9df47 Help response text has been moved to html file 2024-06-08 02:58:39 +04:00
doryan 9a64001926 Merge pull request 'Reformat output chat messages' (#1) from dogma/gluon:fix-messages into main
Reviewed-on: #1
2024-06-07 11:47:41 +03:00
Dogma Toscarina 2f0ae08d4f handlers/{actions,command_handlers}: reformat messages
Avoid interactivity, make messages short.
2024-06-07 10:45:49 +03:00
Dogma Toscarina 06b3a473ed middlewares/admin_check_middleware: shorten error message 2024-06-07 10:41:53 +03:00
Dogma Toscarina 6e59ee1eb8 utils/telegram/try_do: shorten DEMOTE_FAILURE_MESSAGE
It would be annoying to get this long message spamming the chat window.

Such error messages should describe what has gone wrong, but not give
any guidances, advices or directions; they belong elsewhere
(documentation, wiki, etc).
2024-06-07 10:41:53 +03:00
Dogma Toscarina 18b938bf95 handlers/dice_handler/dice: make message short and concise. 2024-06-07 10:41:53 +03:00
Dogma Toscarina 9f5174c442 types/enums/time_metrics: fix grammar 2024-06-07 10:41:53 +03:00
Dogma Toscarina c6222d42f8 assets: fix help messages (mute, unmute)
There are quite some problems with the way commands are explained:

* Interactivity - command arguments gets explained as if one is
  following a guide ("insert this, enter that"). Better explain them
  without any "guidance"; use present tense to describe commands.

* Duplicity - the purpose of the command is explained over and over
  when arguments are being described.

* Separation - there is no need to divide required and optional
  command arguments into their respective sections. Their amount did
  not grow up that much for this to make sense.

* mute_command_help.html: i64 would be sufficient for developers, but
  not end users. It does not stop anyone from specifying overflowing
  integer, after all.
2024-06-07 10:41:36 +03:00
16 changed files with 94 additions and 68 deletions

View File

@ -0,0 +1,12 @@
<b>Команды (для админов)</b>
<code>/mute {ID | REPLY} &lt;DURATION&gt; [TIME METRIC]</code> - выдаёт мут на заданное время;
<code>/unmute {ID | REPLY}</code> - снимает мут;
Для более подробного описания, введите команды без аргументов.
<b>Эмодзи (для админов)</b>
🎲 - выдаёт мут пользователю на случайно заданное время (от 1 до 6 дней);
🎰 - выдаёт мут пользователю на случайно заданное время (от 1 до 63 дней), в случае джекпота участник отправляется в бан.

View File

@ -3,6 +3,7 @@ use std::include_str;
pub mod files {
use super::*;
pub const HELP_COMMAND_TEXT: &str = include_str!("help_command.html");
pub const MUTE_COMMAND_HELP: &str = include_str!("mute_command_help.html");
pub const UNMUTE_COMMAND_HELP: &str = include_str!("unmute_command_help.html");
}

View File

@ -1,10 +1,12 @@
<code>/mute [ID | REPLY] &lt;DURATION: i64 (long long int)&gt; [TIME METRIC]</code>
<code>/mute {ID | REPLY} &lt;DURATION&gt; [TIME METRIC]</code>
<b>Обязательные критерий:</b>
<b>Выдаёт мут.</b>
<b><em>1. Участник чата</em></b>. Введите ID участника чата, либо ответьте на его сообщение, чтобы его замьютить;
<b><em>2. Длительность</em></b>. Введите длительность мьюта, длительность должна быть не меньше нуля.
Использовать при ответе на сообщение или указать ID пользователя.
<b>Необязательные критерий:</b>
<b><em>1. ID | REPLY.</em></b> ID или ответ на сообщение пользователя;
<b><em>2. DURATION.</em></b> Продолжительность мута (DURATION &gt; 0);
<b><em>3. TIME METRIC.</em></b> Временная метрика. Измеряется в минутах/часах/днях/неделях/месяцах. По умолчанию применяется в днях.
<b><u>Команда доступна только администраторам.</u></b>
<b><em>1. Временная метрика.</em></b> Если она не указана, то автоматически участнику чата выдаётся мьют в днях.

View File

@ -1,6 +1,8 @@
<code>/unmute [ID | REPLY]</code>
<code>/unmute {ID | REPLY}</code>
<b>Обязательные критерий:</b>
<b>Cнимает мут с пользователя.</b>
<b><em>1. Участник чата</em></b>. Введите ID участника чата, либо ответьте на его сообщение, чтобы c него снять мьют.
<b><em>1. ID | REPLY.</em></b> ID или ответ на сообщение пользователя.
<b><u>Команда доступна только администраторам.</u></b>

View File

@ -25,7 +25,7 @@ pub async fn ban_member(
Some(id) => id,
None => {
sender_builder
.text("Ответьте на сообщение участника чата, которого вы хотите забанить")
.text("Нет ID или ответа на сообщение пользователя.")
.reply_to(message.id())
.build()
.send(&bot)
@ -39,21 +39,19 @@ pub async fn ban_member(
let callback = || async { ban_chat_member(&bot, user_id, chat_id).await };
let demote_args: (&Bot, i64, i64) = (&bot, user_id, chat_id);
sender_builder
.set_text("Невозможно забанить участника чата, демотните своими силами и попробуйте снова");
sender_builder.set_text("Невозможно забанить пользователя.");
if try_restrict(callback, demote_args, sender_builder.clone().build())
.await
.is_err()
{
sender_builder.build().send(&bot).await?;
Ok(EventReturn::Cancel)
} else {
let banned_user_name: String = user.get_user_name(&bot, &message).await.unwrap();
sender_builder
.reply_to(message.id())
.text(format!("Пользователь {} забанен.", banned_user_name))
.text(format!("Пользователь {banned_user_name} забанен."))
.build()
.send(&bot)
.await?;

View File

@ -25,19 +25,7 @@ pub async fn mute_member(
) -> HandlerResult {
let (bot, message, mut sender_builder): ExtractedEntityData = handler_entity.extract();
let user_id: i64 = match user.get_id() {
Some(id) => id,
None => {
sender_builder
.text("Ответьте на сообщение участника чата, которого вы хотите замьютить")
.reply_to(message.id())
.build()
.send(&bot)
.await
.unwrap();
return Ok(EventReturn::Cancel);
}
};
let user_id: i64 = user.get_id().unwrap();
sleep(Duration::from_millis(time.1)).await;
@ -46,13 +34,12 @@ pub async fn mute_member(
let callback = || async { restrict(&bot, user_id, unmute_date, chat_id).await };
sender_builder.set_text("Невозможно замьютить участника чата, демотните и попробуйте снова");
sender_builder.set_text("Невозможно выдать мут.");
if try_restrict(callback, demote_args, sender_builder.clone().build())
.await
.is_err()
{
sender_builder.build().send(&bot).await?;
Ok(EventReturn::Cancel)
} else {
let muted_user_name: String = user.get_user_name(&bot, &message).await.unwrap();
@ -60,8 +47,7 @@ pub async fn mute_member(
sender_builder
.reply_to(message.id())
.text(format!(
"Пользователь {} замьючен на {:?} {}.",
muted_user_name, mute_duration, postfix
"Пользователю {muted_user_name} выдан мут на {mute_duration} {postfix}."
))
.build()
.send(&bot)

View File

@ -28,9 +28,7 @@ pub async fn unmute_member(
if let Err(error) = bot.send(bot_action).await {
sender_builder
.text(format!(
"Невозможно снять мьют с участника чата по причине: {error:?}"
))
.text(format!("Невозможно снять мут с пользователя: {error:?}."))
.build()
.send(&bot)
.await?;
@ -40,7 +38,7 @@ pub async fn unmute_member(
sender_builder
.reply_to(message.id())
.text(format!("С пользователя {} был снят мьют.", muted_user_name))
.text(format!("С пользователя {muted_user_name} снят мут."))
.build()
.send(&bot)
.await

View File

@ -1,25 +1,16 @@
use telers::{
enums::parse_mode::ParseMode,
event::{telegram::HandlerResult, EventReturn},
types::Message,
Bot,
};
use crate::types::structs::message_sender::MessageSender;
const HELP_TEXT: &str = "\
/help - помощь по боту.\n\
/unmute - снимает с участника чата мьют, для подробностей, введите команду без аргументов \
(только для админов).\n\
/mute - накладывает на участника чата мьют, для подробностей, введите команду без аргументов \
(только для админов).\n\
🎲 - выдаёт мут, для этого нужно отправить ТОЛЬКО эмодзи в ответ на сообщение участника. \
чата, которого вы хотите замьютить (только для админов).\n\
🎰 - выдаёт бан в случае джекпота, напротив, мьют, всё так же кидайте этот эмодзи в ответ \
на сообщение участника чата, которого вы хотите замьютить/забанить (только для админов).";
use crate::{assets::files::HELP_COMMAND_TEXT, types::structs::message_sender::MessageSender};
pub async fn help(bot: Bot, message: Message) -> HandlerResult {
MessageSender::builder(message.chat().id())
.text(HELP_TEXT)
.text(HELP_COMMAND_TEXT)
.parse_mode(ParseMode::HTML)
.build()
.send(&bot)
.await

View File

@ -35,8 +35,9 @@ pub async fn mute(bot: Bot, message: Message, command: CommandObject) -> Handler
if let Ok(id) = raw_id.parse::<i64>() {
TargetUser::Id(id)
} else {
handler_entity.message_sender_builder
.text("Ответьте на сообщение или укажите первым аргументом ID человека, которого вы хотите замьютить")
handler_entity
.message_sender_builder
.text("Нет ID или ответа на сообщение пользователя.")
.build()
.send(&handler_entity.bot_instance)
.await?;
@ -59,7 +60,7 @@ pub async fn mute(bot: Bot, message: Message, command: CommandObject) -> Handler
handler_entity
.message_sender_builder
.set_text("Укажите число, характеризующее длительность мьюта.");
.set_text("Не указана длительность мута.");
match args.get(duration_argument_position).cloned() {
Some(duration_str) => {

View File

@ -27,7 +27,7 @@ pub async fn unmute(bot: Bot, message: Message, command: CommandObject) -> Handl
Some(raw_id) => {
handler_entity
.message_sender_builder
.set_text("Укажите id пользователя, с которого вы хотите снять мьют");
.set_text("Нет ID или ответа на сообщение пользователя.");
if let Ok(parsed_id) = raw_id.parse::<i64>() {
let on_id: TargetUser = TargetUser::Id(parsed_id);

View File

@ -22,8 +22,20 @@ pub async fn dice_handler(bot: Bot, message: Message) -> HandlerResult {
let handler_entity: HandlerEntity = HandlerEntity::new(bot, message, sender);
let (mute_time, emoji): (TimeMetrics, &str) = (TimeMetrics::Days(dice.value), &dice.emoji);
let target: TargetUser = TargetUser::Reply(handler_entity.message_reciever.clone());
if !target.exist() {
handler_entity
.message_sender_builder
.text("Нет ответа на сообщение пользователя.")
.build()
.send(&handler_entity.bot_instance)
.await?;
return Ok(EventReturn::Cancel);
}
match emoji {
"🎲" => {
mute_member(handler_entity, chat_id, target, (mute_time, DICE_DELAY_MS)).await?;
@ -44,7 +56,7 @@ pub async fn dice_handler(bot: Bot, message: Message) -> HandlerResult {
_ => {
handler_entity
.message_sender_builder
.text("Такой эмодзи не имеет привязки к какому либо действию бота.")
.text("Эмодзи не имеет привязанных действий.")
.build()
.send(&handler_entity.bot_instance)
.await?;

View File

@ -30,13 +30,12 @@ impl InnerMiddleware for AdminCheck {
let chat_id: i64 = message.chat_id().unwrap();
if is_admin(&admins_list, message.from().unwrap()) {
if is_admin(&admins_list, message.from().unwrap().id) {
let response = next(request).await?;
return Ok(response);
} else {
println!("lol");
MessageSender::builder(chat_id)
.text("У ваc нет прав администратора, чтобы использовать эту команду.")
.text("Недостаточно прав для использования данной команды.")
.build()
.send(&bot)
.await

View File

@ -15,6 +15,21 @@ pub enum TargetUser {
}
impl TargetUser {
pub fn exist(&self) -> bool {
match self {
Self::Id(_id) => true,
Self::Reply(msg) => {
if let Some(replied_msg) = msg.reply_to_message() {
return replied_msg.from().map(|user| user.id).is_some();
} else {
false
}
}
Self::CurrentMessage(msg) => msg.from().map(|user| user.id).is_some(),
Self::None => false,
}
}
pub fn get_id(&self) -> Option<i64> {
match self {
Self::Id(id) => Some(*id),

View File

@ -21,7 +21,7 @@ impl TimeMetrics {
"w" | "weeks" | "week" | "недель" | "недели" | "неделя" | "н" => {
Self::Weeks(duration)
}
"m" | "mounths" | "mounth" | "месяц" | "месяца" | "месяцев" | "мес" => {
"m" | "months" | "month" | "месяц" | "месяца" | "месяцев" | "мес" => {
Self::Mounths(duration)
}
_ => Self::Days(duration),

View File

@ -1,11 +1,11 @@
use telers::types::{chat_member::ChatMember, User};
use telers::types::chat_member::ChatMember;
pub fn is_admin(all_admin_members: &Vec<ChatMember>, user: &User) -> bool {
pub fn is_admin(all_admin_members: &Vec<ChatMember>, user_id: i64) -> bool {
all_admin_members
.iter()
.any(|admin: &ChatMember| match admin {
ChatMember::Administrator(admin) => &admin.user == user,
ChatMember::Owner(owner) => &owner.user == user,
ChatMember::Administrator(admin) => admin.user.id == user_id,
ChatMember::Owner(owner) => owner.user.id == user_id,
_ => false,
})
}

View File

@ -1,15 +1,12 @@
use telers::{errors::SessionErrorKind as ErrorKind, event::EventReturn, Bot};
use telers::{errors::SessionErrorKind as ErrorKind, event::EventReturn, types::ChatMember, Bot};
use crate::types::structs::message_sender::MessageSender;
use std::future::Future;
use super::demote::demote_user;
use super::{admin_check::is_admin, demote::demote_user, get_all_admins::get_all_admins};
const DEMOTE_FAILURE_MESSAGE: &str = "\
Нельзя выдать ограничение пользователю, т.к. невозможно демотнуть \
участника посредством бота, если ему выдан админ при помощи других \
админов или владельца чата.\
";
const DEMOTE_FAILURE_MESSAGE: &str = "Команда не может быть выполнена: \
не удалось удалить административные привилегии пользователя.";
pub async fn try_restrict<F, R>(
future_callback: F,
@ -22,6 +19,18 @@ where
{
let (bot, user_id, chat_id): (&Bot, i64, i64) = demote_args;
let admins: Vec<ChatMember> = get_all_admins(bot, chat_id).await.unwrap();
if is_admin(&admins, user_id) {
MessageSender::builder(chat_id)
.text("Нельзя применить эту команду на администраторе или бота.")
.build()
.send(bot)
.await
.unwrap();
return Err(EventReturn::Cancel);
}
if future_callback().await.is_err() {
if demote_user(bot, user_id, chat_id).await.is_err() {
MessageSender::builder(chat_id)