Compare commits

...

3 Commits

Author SHA1 Message Date
359a6b6137 Add CustomErrorHandler and use it
Some checks are pending
Build docker image / Build-Docker-Image (push) Waiting to run
rust-clippy analyze / Run rust-clippy analyzing (push) Waiting to run
Ignore benign 'Bad Request: message to be replied not found' Telegram
error while logging other errors
2025-10-25 20:16:19 +02:00
17ef8a7f3d Handle AlreadyExists (409) during registration 2025-10-25 19:51:43 +02:00
0d028b6a66 Add helper to skip redundant message edits 2025-10-25 19:15:16 +02:00
12 changed files with 132 additions and 26 deletions

View File

@@ -18,7 +18,9 @@ use tokio_util::compat::FuturesAsyncReadCompatExt;
use crate::bots::{
approved_bot::{
modules::utils::pagination::generic_get_pagination_keyboard,
modules::utils::{
message_text::is_message_text_equals, pagination::generic_get_pagination_keyboard,
},
services::book_library::{get_author_annotation, get_book_annotation},
tools::filter_callback_query,
},
@@ -145,17 +147,24 @@ where
} else {
chunked_text.len()
};
let current_text = chunked_text.get(page_index - 1).unwrap();
let new_text = chunked_text.get(page_index - 1).unwrap();
let keyboard =
generic_get_pagination_keyboard(page, chunked_text.len().try_into()?, callback_data, false);
bot.edit_message_text(message.chat().id, message.id(), current_text)
if is_message_text_equals(Some(message.clone()), new_text) {
return Ok(());
}
match bot
.edit_message_text(message.chat().id, message.id(), new_text)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
.await
{
Ok(_) => Ok(()),
Err(err) => Err(err.into()),
}
}
pub fn get_annotations_handler() -> crate::bots::BotHandler {

View File

@@ -15,6 +15,7 @@ use teloxide::{
use tracing::log;
use crate::bots::approved_bot::{
modules::utils::message_text::is_message_text_equals,
services::{
book_library::{
formatters::{Format, FormatTitle},
@@ -176,12 +177,19 @@ where
let keyboard = generic_get_pagination_keyboard(page, items_page.pages, callback_data, true);
bot.edit_message_text(chat_id, message_id, formatted_page)
if is_message_text_equals(cq.message, &formatted_page) {
return Ok(());
}
match bot
.edit_message_text(chat_id, message_id, formatted_page)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
.await
{
Ok(_) => Ok(()),
Err(err) => Err(err.into()),
}
}
pub fn get_book_handler() -> crate::bots::BotHandler {

View File

@@ -14,6 +14,7 @@ use teloxide::{
use crate::bots::{
approved_bot::{
modules::utils::message_text::is_message_text_equals,
services::{
book_library::{
formatters::{Format, FormatTitle},
@@ -45,7 +46,7 @@ where
let chat_id = cq.chat_id();
let user_id = cq.from.id;
let message_id = cq.message.as_ref().map(|message| message.id());
let query = get_query(cq);
let query = get_query(cq.clone());
let (chat_id, query, message_id) = match (chat_id, query, message_id) {
(Some(chat_id), Some(query), Some(message_id)) => (chat_id, query, message_id),
@@ -106,15 +107,20 @@ where
}
let formatted_page = items_page.format(page, 4096);
if is_message_text_equals(cq.message, &formatted_page) {
return Ok(());
}
let keyboard = generic_get_pagination_keyboard(page, items_page.pages, search_data, true);
bot.edit_message_text(chat_id, message_id, formatted_page)
match bot
.edit_message_text(chat_id, message_id, formatted_page)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
.await
{
Ok(_) => Ok(()),
Err(err) => Err(err.into()),
}
}
pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {

View File

@@ -4,7 +4,10 @@ pub mod commands;
use chrono::{prelude::*, Duration};
use crate::bots::{
approved_bot::{services::book_library::get_uploaded_books, tools::filter_callback_query},
approved_bot::{
modules::utils::message_text::is_message_text_equals,
services::book_library::get_uploaded_books, tools::filter_callback_query,
},
BotHandlerInternal,
};
@@ -78,7 +81,7 @@ async fn update_log_pagination_handler(
bot: CacheMe<Throttle<Bot>>,
update_callback_data: UpdateLogCallbackData,
) -> BotHandlerInternal {
let message = match cq.message {
let message = match cq.message.clone() {
Some(v) => v,
None => {
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(")
@@ -138,14 +141,20 @@ async fn update_log_pagination_handler(
let formatted_page = items_page.format(page, 4096);
let message_text = format!("{header}{formatted_page}");
if is_message_text_equals(cq.message, &message_text) {
return Ok(());
}
let keyboard = generic_get_pagination_keyboard(page, total_pages, update_callback_data, true);
bot.edit_message_text(message.chat().id, message.id(), message_text)
match bot
.edit_message_text(message.chat().id, message.id(), message_text)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
.await
{
Ok(_) => Ok(()),
Err(err) => Err(err.into()),
}
}
pub fn get_update_log_handler() -> crate::bots::BotHandler {

View File

@@ -0,0 +1,18 @@
use teloxide::types::*;
pub fn is_message_text_equals(message: Option<MaybeInaccessibleMessage>, text: &str) -> bool {
let message = match message {
Some(v) => v,
None => return false,
};
let message = match message {
MaybeInaccessibleMessage::Inaccessible(_) => return false,
MaybeInaccessibleMessage::Regular(v) => v,
};
match message.text() {
Some(msg_text) => text == msg_text,
None => false,
}
}

View File

@@ -1,4 +1,5 @@
pub mod errors;
pub mod filter_command;
pub mod message_text;
pub mod pagination;
pub mod split_text;

View File

@@ -19,9 +19,10 @@ pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> a
let message_text = match result {
register::RegisterStatus::Success { ref username } => format_registered_message(username),
register::RegisterStatus::RegisterFail => strings::ALREADY_REGISTERED.to_string(),
register::RegisterStatus::RegisterFail => strings::MAY_BE_ALREADY_REGISTERED.to_string(),
register::RegisterStatus::LimitExtended => strings::LIMIT_EXTENDED_MESSAGE.to_string(),
register::RegisterStatus::WrongToken => strings::ERROR_MESSAGE.to_string(),
register::RegisterStatus::AlreadyExists => strings::ALREADY_EXISTS_MESSAGE.to_string(),
};
bot.send_message(message.chat.id, message_text)

View File

@@ -12,12 +12,14 @@ pub enum RegisterStatus {
WrongToken,
RegisterFail,
LimitExtended,
AlreadyExists,
}
#[derive(Debug)]
pub enum RegisterRequestStatus {
Success,
LimitExtended,
AlreadyExists,
UnknownError,
}
@@ -52,6 +54,7 @@ async fn make_register_request(
Ok(match result.status().as_u16() {
200 => RegisterRequestStatus::Success,
402 => RegisterRequestStatus::LimitExtended,
409 => RegisterRequestStatus::AlreadyExists,
_ => RegisterRequestStatus::UnknownError,
})
}
@@ -81,5 +84,6 @@ pub async fn register(user_id: UserId, message_text: &str) -> RegisterStatus {
},
RegisterRequestStatus::LimitExtended => RegisterStatus::LimitExtended,
RegisterRequestStatus::UnknownError => RegisterStatus::RegisterFail,
RegisterRequestStatus::AlreadyExists => RegisterStatus::AlreadyExists,
}
}

View File

@@ -2,8 +2,10 @@ pub fn format_registered_message(username: &str) -> String {
format!("@{username} зарегистрирован и через несколько минут будет подключен!")
}
pub const ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
pub const MAY_BE_ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
pub const ERROR_MESSAGE: &str = "Ошибка! Что-то не так с ботом!";
pub const LIMIT_EXTENDED_MESSAGE: &str = "Вы достигли максимального количества ботов!";
pub const ALREADY_EXISTS_MESSAGE: &str = "Ошибка! Бот с таким токеном уже зарегистрирован!";

View File

@@ -0,0 +1,46 @@
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tracing::log;
pub struct CustomErrorHandler {
pub text: String,
}
impl CustomErrorHandler {
pub fn with_custom_text<T>(text: T) -> Arc<Self>
where
T: Into<String>,
{
Arc::new(Self { text: text.into() })
}
}
impl<E> teloxide::error_handlers::ErrorHandler<E> for CustomErrorHandler
where
E: std::fmt::Debug + Send + 'static,
{
fn handle_error(
self: Arc<Self>,
error: E,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
Box::pin(async move {
let error_string = format!("{:?}", error);
if error_string.contains("Bad Request: message to be replied not found") {
log::debug!("Ignoring Telegram reply error: {:?}", error);
return;
}
log::error!("{}: {:?}", self.text, error);
})
}
}
impl Default for CustomErrorHandler {
fn default() -> Self {
Self {
text: "An error from the update listener".to_string(),
}
}
}

View File

@@ -1,6 +1,7 @@
use super::custom_error_handler::CustomErrorHandler;
use teloxide::adaptors::throttle::Limits;
use teloxide::dispatching::Dispatcher;
use teloxide::error_handlers::LoggingErrorHandler;
use teloxide::requests::{Request, Requester, RequesterExt};
use teloxide::stop::StopToken;
use teloxide::stop::{mk_stop_token, StopFlag};
@@ -108,7 +109,7 @@ pub async fn start_bot(bot_data: &BotData) {
dispatcher
.dispatch_with_listener(
listener,
LoggingErrorHandler::with_custom_text("An error from the update listener"),
CustomErrorHandler::with_custom_text("An error from the update listener"),
)
.await;
});

View File

@@ -1,6 +1,7 @@
pub mod axum_server;
pub mod bot_manager_client;
pub mod closable_sender;
pub mod custom_error_handler;
pub mod internal;
pub mod utils;