Compare commits

..

20 Commits

Author SHA1 Message Date
dependabot[bot]
255d79bb59 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 11:05:14 +00:00
fc10c4c576 Log backtrace and error chain in custom handler
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
rust-clippy analyze / Run rust-clippy analyzing (push) Has been cancelled
2025-10-26 19:28:25 +01:00
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
9fb550404e Remove unused Source struct from book library types
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
rust-clippy analyze / Run rust-clippy analyzing (push) Has been cancelled
2025-10-14 17:48:16 +02:00
07c725e0df Merge pull request #42 from flibusta-apps/dependabot/github_actions/github/codeql-action-4
Bump github/codeql-action from 3 to 4
2025-10-14 17:43:04 +02:00
dependabot[bot]
6f2de597b4 Bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-08 11:05:43 +00:00
6990f01275 Merge pull request #40 from flibusta-apps/dependabot/github_actions/actions/checkout-5
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
rust-clippy analyze / Run rust-clippy analyzing (push) Has been cancelled
Bump actions/checkout from 4 to 5
2025-09-10 17:05:00 +02:00
7541680070 Update deps
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
rust-clippy analyze / Run rust-clippy analyzing (push) Has been cancelled
2025-08-31 13:58:23 +02:00
dependabot[bot]
5a7caef4eb Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 18:22:31 +00:00
5ec0af8fd7 Create rust-clippy.yml
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
rust-clippy analyze / Run rust-clippy analyzing (push) Has been cancelled
2025-08-17 23:30:52 +02:00
fa56371148 Merge pull request #39 from flibusta-apps/dependabot/github_actions/actions/checkout-5
Bump actions/checkout from 4 to 5
2025-08-17 23:15:39 +02:00
dependabot[bot]
c71ea486e5 Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 16:29:20 +00:00
61b0b68194 Increase tick interval for pending updates to 1800
Some checks failed
Build docker image / Build-Docker-Image (push) Has been cancelled
2025-08-10 15:43:20 +02:00
28892d9fea Fix pending update count check in webhook handling
Some checks are pending
Build docker image / Build-Docker-Image (push) Waiting to run
2025-08-10 14:51:23 +02:00
6b39a837ff Update invalid token check to match new error message 2025-08-10 14:18:41 +02:00
9da1c4eed2 Fix typo in check_uninited function name and improve error logging 2025-08-10 13:52:09 +02:00
13612062ad Fix
Some checks are pending
Build docker image / Build-Docker-Image (push) Waiting to run
2025-08-09 23:18:10 +02:00
34f0c9feb6 Update deps and refactor 2025-08-09 23:10:40 +02:00
28 changed files with 720 additions and 525 deletions

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

55
.github/workflows/rust-clippy.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# rust-clippy is a tool that runs a bunch of lints to catch common
# mistakes in your Rust code and help improve your Rust code.
# More details at https://github.com/rust-lang/rust-clippy
# and https://rust-lang.github.io/rust-clippy/
name: rust-clippy analyze
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '38 20 * * 2'
jobs:
rust-clippy-analyze:
name: Run rust-clippy analyzing
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1
with:
profile: minimal
toolchain: stable
components: clippy
override: true
- name: Install required cargo
run: cargo install clippy-sarif sarif-fmt
- name: Run rust-clippy
run:
cargo clippy
--all-features
--message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
continue-on-error: true
- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: rust-clippy-results.sarif
wait-for-processing: true

399
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,14 +27,14 @@ tokio-stream = "0.1.17"
futures = "0.3.31"
axum = "0.8.3"
axum-prometheus = "0.8.0"
axum-prometheus = "0.9.0"
tower = "0.5.2"
tower-http = { version = "0.6.2", features = ["trace"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
sentry-tracing = "0.41.0"
sentry-tracing = "0.42.0"
reqwest = { version = "0.12.15", features = ["json", "stream"] }
@@ -66,5 +66,5 @@ smartstring = { version = "1.0.1", features = ["serde"] }
moka = { version = "0.12.10", features = ["future"] }
sentry = { version = "0.41.0", features = ["debug-images"] }
sentry = { version = "0.42.0", features = ["debug-images"] }
anyhow = "1.0.98"

View File

@@ -41,10 +41,10 @@ async fn _update_activity(me: teloxide::types::Me, user: teloxide::types::User)
if create_or_update_user_settings(
user.id,
user.last_name.clone().unwrap_or("".to_string()),
user.first_name.clone(),
user.username.clone().unwrap_or("".to_string()),
me.username.clone().unwrap(),
&user.last_name.unwrap_or("".to_string()),
&user.first_name,
&user.username.unwrap_or("".to_string()),
&me.username.clone().unwrap_or("".to_string()),
allowed_langs,
)
.await
@@ -64,21 +64,18 @@ async fn _update_activity(me: teloxide::types::Me, user: teloxide::types::User)
fn update_user_activity_handler() -> BotHandler {
dptree::entry()
.branch(
Update::filter_callback_query().chain(dptree::filter_map_async(
.branch(Update::filter_callback_query().inspect_async(
|cq: CallbackQuery, bot: CacheMe<Throttle<Bot>>| async move {
_update_activity(bot.get_me().await.unwrap(), cq.from).await
_update_activity(bot.get_me().await.unwrap(), cq.from).await;
},
)),
)
.branch(Update::filter_message().chain(dptree::filter_map_async(
))
.branch(Update::filter_message().inspect_async(
|message: Message, bot: CacheMe<Throttle<Bot>>| async move {
match message.from {
Some(user) => _update_activity(bot.get_me().await.unwrap(), user.clone()).await,
None => None,
if let Some(user) = message.from {
_update_activity(bot.get_me().await.unwrap(), user).await;
}
},
)))
))
}
pub fn get_approved_handler() -> (BotHandler, BotCommands) {

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},
@@ -66,7 +67,7 @@ where
let allowed_langs = get_user_or_default_lang_codes(user_id).await;
let items_page = match books_getter(id, 1, allowed_langs.clone()).await {
let items_page = match books_getter(id, 1, allowed_langs).await {
Ok(v) => v,
Err(err) => {
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
@@ -160,7 +161,7 @@ where
};
if page > items_page.pages {
items_page = match books_getter(id, items_page.pages, allowed_langs.clone()).await {
items_page = match books_getter(id, items_page.pages, allowed_langs).await {
Ok(v) => v,
Err(err) => {
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
@@ -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

@@ -97,12 +97,12 @@ async fn send_cached_message(
if _send_cached(&message, &bot, cached).await.is_ok() {
if need_delete_message {
if let MaybeInaccessibleMessage::Regular(message) = message.clone() {
if let MaybeInaccessibleMessage::Regular(message) = &message {
let _ = bot.delete_message(message.chat.id, message.id).await;
}
}
match send_donation_notification(bot.clone(), message).await {
match send_donation_notification(&bot, &message).await {
Ok(_) => (),
Err(err) => log::error!("{err:?}"),
}
@@ -119,7 +119,7 @@ async fn send_cached_message(
async fn _send_downloaded_file(
message: &MaybeInaccessibleMessage,
bot: CacheMe<Throttle<Bot>>,
bot: &CacheMe<Throttle<Bot>>,
downloaded_data: DownloadFile,
) -> BotHandlerInternal {
let DownloadFile {
@@ -134,14 +134,14 @@ async fn _send_downloaded_file(
.into_async_read()
.compat();
let document = InputFile::read(data).file_name(filename.clone());
let document = InputFile::read(data).file_name(filename);
bot.send_document(message.chat().id, document)
.caption(caption)
.send()
.await?;
send_donation_notification(bot, message.clone()).await?;
send_donation_notification(bot, message).await?;
Ok(())
}
@@ -159,7 +159,7 @@ async fn send_with_download_from_channel(
}
};
_send_downloaded_file(&message, bot.clone(), downloaded_file).await?;
_send_downloaded_file(&message, &bot, downloaded_file).await?;
if need_delete_message {
if let MaybeInaccessibleMessage::Regular(message) = message {
@@ -240,13 +240,13 @@ async fn get_download_archive_keyboard_handler(
let available_types = match command {
DownloadArchiveCommand::Sequence { id } => {
get_sequence_books_available_types(id, allowed_langs).await
get_sequence_books_available_types(id, &allowed_langs).await
}
DownloadArchiveCommand::Author { id } => {
get_author_books_available_types(id, allowed_langs).await
get_author_books_available_types(id, &allowed_langs).await
}
DownloadArchiveCommand::Translator { id } => {
get_translator_books_available_types(id, allowed_langs).await
get_translator_books_available_types(id, &allowed_langs).await
}
};
@@ -307,19 +307,20 @@ async fn send_error_message(bot: CacheMe<Throttle<Bot>>, chat_id: ChatId, messag
}
async fn send_archive_link(
bot: CacheMe<Throttle<Bot>>,
message: Box<Message>,
task: Task,
bot: &CacheMe<Throttle<Bot>>,
chat_id: ChatId,
message_id: MessageId,
task: &Task,
) -> BotHandlerInternal {
let link = format!(
"{}/api/download/{}",
config::CONFIG.public_batch_downloader_url.clone(),
config::CONFIG.public_batch_downloader_url,
task.id
);
bot.edit_message_text(
message.chat.id,
message.id,
chat_id,
message_id,
format!(
"Файл не может быть загружен в чат! \n \
Вы можете скачать его <a href=\"{link}\">по ссылке</a> (работает 3 часа)"
@@ -352,7 +353,7 @@ async fn wait_archive(
let task = loop {
interval.tick().await;
let task = match get_task(task_id.clone()).await {
let task = match get_task(&task_id).await {
Ok(v) => v,
Err(err) => {
send_error_message(bot, message.chat.id, message.id).await;
@@ -388,18 +389,18 @@ async fn wait_archive(
let content_size = task.content_size.unwrap();
if content_size > 1024 * 1024 * 1024 {
send_archive_link(bot.clone(), message.clone(), task.clone()).await?;
send_archive_link(&bot, message.chat.id, message.id, &task).await?;
return Ok(());
}
let link = format!(
"{}/api/download/{}",
config::CONFIG.batch_downloader_url.clone(),
config::CONFIG.batch_downloader_url,
task.id
);
let downloaded_data =
match download_file_by_link(task.clone().result_filename.unwrap(), link).await {
match download_file_by_link(&task.clone().result_filename.unwrap(), link).await {
Ok(v) => match v {
Some(v) => v,
None => {
@@ -416,14 +417,14 @@ async fn wait_archive(
match _send_downloaded_file(
&MaybeInaccessibleMessage::Regular(message.clone()),
bot.clone(),
&bot,
downloaded_data,
)
.await
{
Ok(_) => (),
Err(err) => {
send_archive_link(bot.clone(), message.clone(), task).await?;
send_archive_link(&bot, message.chat.id, message.id, &task).await?;
log::error!("{err:?}");
}
}

View File

@@ -13,7 +13,7 @@ use self::commands::HelpCommand;
pub async fn help_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
let name = message
.from
.map(|user| user.first_name.clone())
.map(|user| user.first_name)
.unwrap_or("пользователь".to_string());
match bot

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),
@@ -93,8 +94,7 @@ where
};
if page > items_page.pages {
items_page =
match items_getter(query.clone(), items_page.pages, allowed_langs.clone()).await {
items_page = match items_getter(query, items_page.pages, allowed_langs).await {
Ok(v) => v,
Err(err) => {
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
@@ -107,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

@@ -118,10 +118,10 @@ async fn settings_callback_handler(
if let Err(err) = create_or_update_user_settings(
user.id,
user.last_name.clone().unwrap_or("".to_string()),
user.first_name.clone(),
user.username.clone().unwrap_or("".to_string()),
me.username.clone().unwrap(),
&user.last_name.unwrap_or("".to_string()),
&user.first_name,
&user.username.unwrap_or("".to_string()),
&me.username.clone().unwrap(),
allowed_langs_set.clone().into_iter().collect(),
)
.await

View File

@@ -16,20 +16,20 @@ enum SupportCommand {
pub async fn support_command_handler(
message: Message,
bot: CacheMe<Throttle<Bot>>,
bot: &CacheMe<Throttle<Bot>>,
) -> BotHandlerInternal {
let username = match message.clone().from {
Some(user) => match user.is_bot {
true => match message.reply_to_message() {
Some(v) => match &v.from {
Some(v) => v.first_name.clone(),
None => "пользователь".to_string(),
Some(v) => &v.first_name,
None => "пользователь",
},
None => "пользователь".to_string(),
None => "пользователь",
},
false => user.first_name,
false => &user.first_name.clone(),
},
None => "пользователь".to_string(),
None => "пользователь",
};
let message_text = format!(
@@ -60,5 +60,7 @@ pub async fn support_command_handler(
pub fn get_support_handler() -> crate::bots::BotHandler {
Update::filter_message()
.filter_command::<SupportCommand>()
.endpoint(support_command_handler)
.endpoint(|message: Message, bot: CacheMe<Throttle<Bot>>| async move {
support_command_handler(message, &bot).await
})
}

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

@@ -56,7 +56,7 @@ pub async fn create_task(data: CreateTaskData) -> anyhow::Result<Task> {
.await?)
}
pub async fn get_task(task_id: String) -> anyhow::Result<Task> {
pub async fn get_task(task_id: &str) -> anyhow::Result<Task> {
Ok(CLIENT
.get(format!(
"{}/api/check_archive/{task_id}",

View File

@@ -91,7 +91,7 @@ pub async fn download_file(
}
pub async fn download_file_by_link(
filename: String,
filename: &str,
link: String,
) -> anyhow::Result<Option<DownloadFile>> {
let response = CLIENT.get(link).send().await?;
@@ -102,7 +102,7 @@ pub async fn download_file_by_link(
Ok(Some(DownloadFile {
response,
filename,
filename: filename.to_string(),
caption: "".to_string(),
}))
}

View File

@@ -115,7 +115,7 @@ impl FormatInline for BookTranslator {
}
}
fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
fn format_authors(authors: &[BookAuthor], count: usize) -> String {
if count == 0 {
return "".to_string();
}
@@ -139,7 +139,7 @@ fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
}
}
fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String {
fn format_translators(translators: &[BookTranslator], count: usize) -> String {
if count == 0 {
return "".to_string();
}
@@ -163,7 +163,7 @@ fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String
}
}
fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
fn format_sequences(sequences: &[Sequence], count: usize) -> String {
if count == 0 {
return "".to_string();
}
@@ -187,7 +187,7 @@ fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
}
}
fn format_genres(genres: Vec<BookGenre>, count: usize) -> String {
fn format_genres(genres: &[BookGenre], count: usize) -> String {
if count == 0 {
return "".to_string();
}
@@ -395,20 +395,25 @@ impl FormatVectorsResult {
}
}
impl Book {
fn format_vectors(&self, max_size: usize) -> FormatVectorsResult {
fn format_vectors(
authors: &[BookAuthor],
translators: &[BookTranslator],
sequences: &[Sequence],
genres: &[BookGenre],
max_size: usize,
) -> FormatVectorsResult {
let mut counts = FormatVectorsCounts {
authors: self.authors.len(),
translators: self.translators.len(),
sequences: self.sequences.len(),
genres: self.genres.len(),
authors: authors.len(),
translators: translators.len(),
sequences: sequences.len(),
genres: genres.len(),
};
let mut result = FormatVectorsResult {
authors: format_authors(self.authors.clone(), counts.authors),
translators: format_translators(self.translators.clone(), counts.translators),
sequences: format_sequences(self.sequences.clone(), counts.sequences),
genres: format_genres(self.genres.clone(), counts.genres),
authors: format_authors(authors, counts.authors),
translators: format_translators(translators, counts.translators),
sequences: format_sequences(sequences, counts.sequences),
genres: format_genres(genres, counts.genres),
max_result_size: 0,
};
@@ -418,34 +423,58 @@ impl Book {
counts = counts.sub();
result = FormatVectorsResult {
authors: format_authors(self.authors.clone(), counts.authors),
translators: format_translators(self.translators.clone(), counts.translators),
sequences: format_sequences(self.sequences.clone(), counts.sequences),
genres: format_genres(self.genres.clone(), counts.genres),
authors: format_authors(authors, counts.authors),
translators: format_translators(translators, counts.translators),
sequences: format_sequences(sequences, counts.sequences),
genres: format_genres(genres, counts.genres),
max_result_size: 0,
};
}
result.with_max_result_size(max_result_size)
}
}
impl Format for Book {
fn format(&self, max_size: usize) -> FormatResult {
let book_title = {
let Book { title, lang, .. } = self;
struct FormatData<'a> {
pub id: u32,
pub title: &'a str,
pub lang: &'a str,
pub annotation_exists: bool,
pub authors: &'a [BookAuthor],
pub translators: &'a [BookTranslator],
pub sequences: &'a [Sequence],
pub genres: &'a [BookGenre],
pub year: i32,
pub pages: Option<u32>,
pub position: Option<i32>,
}
let year_part = match self.year {
fn format_common(data: FormatData, max_size: usize) -> FormatResult {
let FormatData {
id,
title,
lang,
annotation_exists,
authors,
translators,
sequences,
genres,
year,
pages,
position,
} = data;
let book_title = {
let year_part = match year {
0 => "".to_string(),
v => format!(" | {v}г."),
};
let pages_count = match self.pages {
let pages_count = match pages {
Some(1) | None => "".to_string(),
Some(v) => format!(" | {v}с."),
};
let position_prefix = match self.position {
let position_prefix = match position {
Some(0) | None => "".to_string(),
Some(v) => format!("{v} | "),
};
@@ -453,15 +482,14 @@ impl Format for Book {
format!("{position_prefix}📖 {title} | {lang}{year_part}{pages_count}\n")
};
let annotations = match self.annotation_exists {
let annotations = match annotation_exists {
true => {
let Book { id, .. } = self;
format!("📝 Аннотация: /b_an_{id}\n")
}
false => "".to_string(),
};
let download_command = (StartDownloadCommand { id: self.id }).to_string();
let download_command = (StartDownloadCommand { id }).to_string();
let download_links = format!("Скачать:\n📥{download_command}");
let required_data_len: usize = format!("{book_title}{annotations}{download_links}").len();
@@ -471,7 +499,13 @@ impl Format for Book {
sequences,
genres,
max_result_size,
} = self.format_vectors(max_size - required_data_len);
} = format_vectors(
authors,
translators,
sequences,
genres,
max_size - required_data_len,
);
let result = format!(
"{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}"
@@ -483,29 +517,109 @@ impl Format for Book {
current_size: result_len,
max_size: max_result_size + required_data_len,
}
}
impl Format for Book {
fn format(&self, max_size: usize) -> FormatResult {
format_common(
FormatData {
id: self.id,
title: &self.title,
lang: &self.lang,
annotation_exists: self.annotation_exists,
authors: &self.authors,
translators: &self.translators,
sequences: &self.sequences,
genres: &self.genres,
year: self.year,
pages: self.pages,
position: self.position,
},
max_size,
)
}
}
impl Format for SearchBook {
fn format(&self, max_size: usize) -> FormatResult {
Into::<Book>::into(self.clone()).format(max_size)
format_common(
FormatData {
id: self.id,
title: &self.title,
lang: &self.lang,
annotation_exists: self.annotation_exists,
authors: &self.authors,
translators: &self.translators,
sequences: &self.sequences,
genres: &[],
year: self.year,
pages: None,
position: None,
},
max_size,
)
}
}
impl Format for AuthorBook {
fn format(&self, max_size: usize) -> FormatResult {
Into::<Book>::into(self.clone()).format(max_size)
format_common(
FormatData {
id: self.id,
title: &self.title,
lang: &self.lang,
annotation_exists: self.annotation_exists,
authors: &[],
translators: &self.translators,
sequences: &self.sequences,
genres: &[],
year: self.year,
pages: None,
position: None,
},
max_size,
)
}
}
impl Format for TranslatorBook {
fn format(&self, max_size: usize) -> FormatResult {
Into::<Book>::into(self.clone()).format(max_size)
format_common(
FormatData {
id: self.id,
title: &self.title,
lang: &self.lang,
annotation_exists: self.annotation_exists,
authors: &self.authors,
translators: &[],
sequences: &self.sequences,
genres: &[],
year: self.year,
pages: None,
position: None,
},
max_size,
)
}
}
impl Format for SequenceBook {
fn format(&self, max_size: usize) -> FormatResult {
Into::<Book>::into(self.clone()).format(max_size)
format_common(
FormatData {
id: self.id,
title: &self.title,
lang: &self.lang,
annotation_exists: self.annotation_exists,
authors: &self.authors,
translators: &self.translators,
sequences: &[],
genres: &[],
year: self.year,
pages: None,
position: Some(self.position),
},
max_size,
)
}
}

View File

@@ -15,11 +15,11 @@ use self::types::Empty;
pub static CLIENT: Lazy<reqwest::Client> = Lazy::new(reqwest::Client::new);
fn get_allowed_langs_params(
allowed_langs: SmallVec<[SmartString; 3]>,
allowed_langs: &SmallVec<[SmartString; 3]>,
) -> Vec<(&'static str, SmartString)> {
allowed_langs
.into_iter()
.map(|lang| ("allowed_langs", lang))
.map(|lang| ("allowed_langs", lang.clone()))
.collect()
}
@@ -52,7 +52,7 @@ pub async fn get_random_book_by_genre(
allowed_langs: SmallVec<[SmartString; 3]>,
genre: Option<u32>,
) -> anyhow::Result<types::Book> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
if let Some(v) = genre {
params.push(("genre", v.to_string().into()));
@@ -70,7 +70,7 @@ pub async fn get_random_book(
pub async fn get_random_author(
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Author> {
let params = get_allowed_langs_params(allowed_langs);
let params = get_allowed_langs_params(&allowed_langs);
_make_request("/api/v1/authors/random", params).await
}
@@ -78,7 +78,7 @@ pub async fn get_random_author(
pub async fn get_random_sequence(
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Sequence> {
let params = get_allowed_langs_params(allowed_langs);
let params = get_allowed_langs_params(&allowed_langs);
_make_request("/api/v1/sequences/random", params).await
}
@@ -100,7 +100,7 @@ pub async fn search_book(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::SearchBook, Empty>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -113,7 +113,7 @@ pub async fn search_author(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::Author, Empty>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -126,7 +126,7 @@ pub async fn search_sequence(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::Sequence, Empty>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -139,7 +139,7 @@ pub async fn search_translator(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::Translator, Empty>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -164,7 +164,7 @@ pub async fn get_author_books(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::AuthorBook, types::BookAuthor>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -177,7 +177,7 @@ pub async fn get_translator_books(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::TranslatorBook, types::BookTranslator>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -190,7 +190,7 @@ pub async fn get_sequence_books(
page: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<types::Page<types::SequenceBook, types::Sequence>> {
let mut params = get_allowed_langs_params(allowed_langs);
let mut params = get_allowed_langs_params(&allowed_langs);
params.push(("page", page.to_string().into()));
params.push(("size", PAGE_SIZE.to_string().into()));
@@ -216,7 +216,7 @@ pub async fn get_uploaded_books(
pub async fn get_author_books_available_types(
id: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
allowed_langs: &SmallVec<[SmartString; 3]>,
) -> anyhow::Result<Vec<String>> {
let params = get_allowed_langs_params(allowed_langs);
@@ -229,7 +229,7 @@ pub async fn get_author_books_available_types(
pub async fn get_translator_books_available_types(
id: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
allowed_langs: &SmallVec<[SmartString; 3]>,
) -> anyhow::Result<Vec<String>> {
let params = get_allowed_langs_params(allowed_langs);
@@ -242,7 +242,7 @@ pub async fn get_translator_books_available_types(
pub async fn get_sequence_books_available_types(
id: u32,
allowed_langs: SmallVec<[SmartString; 3]>,
allowed_langs: &SmallVec<[SmartString; 3]>,
) -> anyhow::Result<Vec<String>> {
let params = get_allowed_langs_params(allowed_langs);

View File

@@ -32,12 +32,6 @@ impl BookGenre {
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct Source {
// id: u32,
// name: String
}
#[derive(Deserialize, Debug, Clone)]
pub struct Author {
pub id: u32,
@@ -149,6 +143,8 @@ where
.map(|item| item_size - item.current_size)
.sum();
use std::borrow::Cow;
self.items
.iter()
.enumerate()
@@ -156,17 +152,17 @@ where
let already_formated_result = &format_result[index];
if already_formated_result.current_size == already_formated_result.max_size {
already_formated_result.result.clone()
Cow::Borrowed(already_formated_result.result.as_str())
} else {
let new_item_size = item_size + free_symbols;
let new_formated_result = item.format(new_item_size);
free_symbols = new_item_size - new_formated_result.current_size;
new_formated_result.result
Cow::Owned(new_formated_result.result)
}
})
.collect::<Vec<String>>()
.collect::<Vec<Cow<str>>>()
.join(separator)
}
}
@@ -192,17 +188,12 @@ pub struct Book {
pub id: u32,
pub title: String,
pub lang: String,
// file_type: String,
pub available_types: SmallVec<[String; 4]>,
// uploaded: String,
pub annotation_exists: bool,
pub authors: Vec<BookAuthor>,
pub translators: Vec<BookTranslator>,
pub sequences: Vec<Sequence>,
pub genres: Vec<BookGenre>,
// source: Source,
// remote_id: u32,
// id_deleted: bool,
pub year: i32,
pub pages: Option<u32>,
pub position: Option<i32>,
@@ -213,9 +204,6 @@ pub struct SearchBook {
pub id: u32,
pub title: String,
pub lang: String,
// file_type: String,
pub available_types: SmallVec<[String; 4]>,
// uploaded: String,
pub annotation_exists: bool,
pub authors: Vec<BookAuthor>,
pub translators: Vec<BookTranslator>,
@@ -223,121 +211,36 @@ pub struct SearchBook {
pub year: i32,
}
impl From<SearchBook> for Book {
fn from(value: SearchBook) -> Self {
Book {
id: value.id,
title: value.title,
lang: value.lang,
available_types: value.available_types,
annotation_exists: value.annotation_exists,
authors: value.authors,
translators: value.translators,
sequences: value.sequences,
genres: vec![],
pages: None,
year: value.year,
position: None,
}
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct AuthorBook {
pub id: u32,
pub title: String,
pub lang: String,
// file_type: String,
pub available_types: SmallVec<[String; 4]>,
// uploaded: String,
pub annotation_exists: bool,
pub translators: Vec<BookTranslator>,
pub sequences: Vec<Sequence>,
pub year: i32,
}
impl From<AuthorBook> for Book {
fn from(value: AuthorBook) -> Self {
Book {
id: value.id,
title: value.title,
lang: value.lang,
available_types: value.available_types,
annotation_exists: value.annotation_exists,
authors: vec![],
translators: value.translators,
sequences: value.sequences,
genres: vec![],
pages: None,
year: value.year,
position: None,
}
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct TranslatorBook {
pub id: u32,
pub title: String,
pub lang: String,
// file_type: String,
pub available_types: SmallVec<[String; 4]>,
// uploaded: String,
pub annotation_exists: bool,
pub authors: Vec<BookAuthor>,
pub sequences: Vec<Sequence>,
pub year: i32,
}
impl From<TranslatorBook> for Book {
fn from(value: TranslatorBook) -> Self {
Book {
id: value.id,
title: value.title,
lang: value.lang,
available_types: value.available_types,
annotation_exists: value.annotation_exists,
authors: value.authors,
translators: vec![],
sequences: value.sequences,
genres: vec![],
pages: None,
year: value.year,
position: None,
}
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct SequenceBook {
pub id: u32,
pub title: String,
pub lang: String,
// file_type: String,
pub available_types: SmallVec<[String; 4]>,
// uploaded: String,
pub authors: Vec<BookAuthor>,
pub translators: Vec<BookTranslator>,
pub annotation_exists: bool,
pub year: i32,
pub position: i32,
}
impl From<SequenceBook> for Book {
fn from(value: SequenceBook) -> Self {
Book {
id: value.id,
title: value.title,
lang: value.lang,
available_types: value.available_types,
annotation_exists: value.annotation_exists,
authors: value.authors,
translators: value.translators,
sequences: vec![],
genres: vec![],
pages: None,
year: value.year,
position: Some(value.position),
}
}
}

View File

@@ -12,8 +12,8 @@ use crate::{
use super::user_settings::{is_need_donate_notifications, mark_donate_notification_sent};
pub async fn send_donation_notification(
bot: CacheMe<Throttle<Bot>>,
message: MaybeInaccessibleMessage,
bot: &CacheMe<Throttle<Bot>>,
message: &MaybeInaccessibleMessage,
) -> BotHandlerInternal {
if CHAT_DONATION_NOTIFICATIONS_CACHE
.get(&message.chat().id)
@@ -31,7 +31,7 @@ pub async fn send_donation_notification(
mark_donate_notification_sent(message.chat().id).await?;
if let MaybeInaccessibleMessage::Regular(message) = message {
support_command_handler(*message, bot).await?;
support_command_handler(*message.clone(), bot).await?;
}
};

View File

@@ -74,10 +74,10 @@ pub async fn get_user_or_default_lang_codes(user_id: UserId) -> SmallVec<[SmartS
pub async fn create_or_update_user_settings(
user_id: UserId,
last_name: String,
first_name: String,
username: String,
source: String,
last_name: &str,
first_name: &str,
username: &str,
source: &str,
allowed_langs: SmallVec<[SmartString; 3]>,
) -> anyhow::Result<UserSettings> {
USER_LANGS_CACHE.invalidate(&user_id).await;

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,
}
@@ -42,9 +44,9 @@ async fn make_register_request(
});
let result = reqwest::Client::new()
.post(config::CONFIG.manager_url.clone())
.post(&config::CONFIG.manager_url)
.body(body.to_string())
.header("Authorization", config::CONFIG.manager_api_key.clone())
.header("Authorization", &config::CONFIG.manager_api_key)
.header("Content-Type", "application/json")
.send()
.await?;
@@ -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,85 @@
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;
}
let backtrace = std::backtrace::Backtrace::force_capture();
let error_chain = if let Some(std_error) =
(&error as &dyn std::any::Any).downcast_ref::<Box<dyn std::error::Error>>()
{
let mut chain = Vec::new();
let mut source = std_error.source();
while let Some(err) = source {
chain.push(format!(" Caused by: {}", err));
source = err.source();
}
if chain.is_empty() {
String::new()
} else {
format!("\nError chain:\n{}", chain.join("\n"))
}
} else {
String::new()
};
let backtrace_info = match backtrace.status() {
std::backtrace::BacktraceStatus::Captured => {
format!("\nBacktrace:\n{}", backtrace)
}
std::backtrace::BacktraceStatus::Disabled => {
"\nBacktrace: disabled (compile with debug info for stack traces)".to_string()
}
std::backtrace::BacktraceStatus::Unsupported => {
"\nBacktrace: unsupported on this platform".to_string()
}
_ => String::new(),
};
log::error!(
"{}: {:?}{}{}",
self.text,
error,
error_chain,
backtrace_info
);
})
}
}
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;
@@ -93,13 +94,11 @@ impl BotsManager {
let bot_data: BotData = bot_data.clone();
BOTS_DATA
.insert(bot_data.token.clone(), bot_data.clone())
.await;
BOTS_DATA.insert(bot_data.token.clone(), bot_data).await;
}
}
async fn check_unininted(bots_data: &[BotData]) {
async fn check_uninited(bots_data: &[BotData]) {
let semaphore = Arc::new(Semaphore::const_new(5));
let mut set_webhook_tasks = JoinSet::new();
@@ -110,9 +109,9 @@ impl BotsManager {
let bot_data: BotData = bot_data.clone();
let semphore = semaphore.clone();
let semaphore = semaphore.clone();
set_webhook_tasks.spawn(async move {
let _permit = semphore.acquire().await.unwrap();
let _permit = semaphore.acquire().await.unwrap();
let webhook_status = set_webhook(&bot_data).await;
@@ -145,7 +144,7 @@ impl BotsManager {
let _ = BotsManager::check_bots_data(&bots_data).await;
if !only_bot_data {
let _ = BotsManager::check_unininted(&bots_data).await;
let _ = BotsManager::check_uninited(&bots_data).await;
}
}
@@ -176,7 +175,7 @@ impl BotsManager {
match result {
Ok(webhook_info) => {
if webhook_info.pending_update_count != 0 {
if webhook_info.pending_update_count == 0 {
continue;
}
@@ -187,7 +186,9 @@ impl BotsManager {
}
}
Err(err) => {
if err.to_string().contains("Api(InvalidToken)") {
let error_message = err.to_string();
if error_message.contains("Invalid bot token") {
BOTS_DATA.invalidate(token.as_str()).await;
if let Err(d_err) = delete_bot(bot_data.id).await {
log::error!("Error deleting bot {}: {:?}", bot_data.id, d_err);
@@ -195,7 +196,7 @@ impl BotsManager {
continue;
}
log::error!("Error getting webhook info: {err:?}");
log::error!("Error getting webhook info: {error_message}");
WEBHOOK_CHECK_ERRORS_COUNT
.insert(bot_data.id, error_count + 1)
@@ -224,11 +225,11 @@ impl BotsManager {
BotsManager::check(false).await;
}
if tick_number % 180 == 60 {
if tick_number % 1800 == 600 {
BotsManager::check_pending_updates().await;
}
tick_number = (tick_number + 1) % 180;
tick_number = (tick_number + 1) % 1800;
}
}
}