mirror of
https://github.com/flibusta-apps/book_bot.git
synced 2025-12-06 15:35:35 +01:00
Compare commits
20 Commits
ae292b180c
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
255d79bb59 | ||
| fc10c4c576 | |||
| 359a6b6137 | |||
| 17ef8a7f3d | |||
| 0d028b6a66 | |||
| 9fb550404e | |||
| 07c725e0df | |||
|
|
6f2de597b4 | ||
| 6990f01275 | |||
| 7541680070 | |||
|
|
5a7caef4eb | ||
| 5ec0af8fd7 | |||
| fa56371148 | |||
|
|
c71ea486e5 | ||
| 61b0b68194 | |||
| 28892d9fea | |||
| 6b39a837ff | |||
| 9da1c4eed2 | |||
| 13612062ad | |||
| 34f0c9feb6 |
2
.github/workflows/build_docker_image.yml
vendored
2
.github/workflows/build_docker_image.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|||||||
55
.github/workflows/rust-clippy.yml
vendored
Normal file
55
.github/workflows/rust-clippy.yml
vendored
Normal 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
399
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -27,14 +27,14 @@ tokio-stream = "0.1.17"
|
|||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
|
|
||||||
axum = "0.8.3"
|
axum = "0.8.3"
|
||||||
axum-prometheus = "0.8.0"
|
axum-prometheus = "0.9.0"
|
||||||
|
|
||||||
tower = "0.5.2"
|
tower = "0.5.2"
|
||||||
tower-http = { version = "0.6.2", features = ["trace"] }
|
tower-http = { version = "0.6.2", features = ["trace"] }
|
||||||
|
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
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"] }
|
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"] }
|
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"
|
anyhow = "1.0.98"
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ async fn _update_activity(me: teloxide::types::Me, user: teloxide::types::User)
|
|||||||
|
|
||||||
if create_or_update_user_settings(
|
if create_or_update_user_settings(
|
||||||
user.id,
|
user.id,
|
||||||
user.last_name.clone().unwrap_or("".to_string()),
|
&user.last_name.unwrap_or("".to_string()),
|
||||||
user.first_name.clone(),
|
&user.first_name,
|
||||||
user.username.clone().unwrap_or("".to_string()),
|
&user.username.unwrap_or("".to_string()),
|
||||||
me.username.clone().unwrap(),
|
&me.username.clone().unwrap_or("".to_string()),
|
||||||
allowed_langs,
|
allowed_langs,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -64,21 +64,18 @@ async fn _update_activity(me: teloxide::types::Me, user: teloxide::types::User)
|
|||||||
|
|
||||||
fn update_user_activity_handler() -> BotHandler {
|
fn update_user_activity_handler() -> BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
.branch(
|
.branch(Update::filter_callback_query().inspect_async(
|
||||||
Update::filter_callback_query().chain(dptree::filter_map_async(
|
|
||||||
|cq: CallbackQuery, bot: CacheMe<Throttle<Bot>>| async move {
|
|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().inspect_async(
|
||||||
.branch(Update::filter_message().chain(dptree::filter_map_async(
|
|
||||||
|message: Message, bot: CacheMe<Throttle<Bot>>| async move {
|
|message: Message, bot: CacheMe<Throttle<Bot>>| async move {
|
||||||
match message.from {
|
if let Some(user) = message.from {
|
||||||
Some(user) => _update_activity(bot.get_me().await.unwrap(), user.clone()).await,
|
_update_activity(bot.get_me().await.unwrap(), user).await;
|
||||||
None => None,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_approved_handler() -> (BotHandler, BotCommands) {
|
pub fn get_approved_handler() -> (BotHandler, BotCommands) {
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ use tokio_util::compat::FuturesAsyncReadCompatExt;
|
|||||||
|
|
||||||
use crate::bots::{
|
use crate::bots::{
|
||||||
approved_bot::{
|
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},
|
services::book_library::{get_author_annotation, get_book_annotation},
|
||||||
tools::filter_callback_query,
|
tools::filter_callback_query,
|
||||||
},
|
},
|
||||||
@@ -145,17 +147,24 @@ where
|
|||||||
} else {
|
} else {
|
||||||
chunked_text.len()
|
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 =
|
let keyboard =
|
||||||
generic_get_pagination_keyboard(page, chunked_text.len().try_into()?, callback_data, false);
|
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)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
Ok(())
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_annotations_handler() -> crate::bots::BotHandler {
|
pub fn get_annotations_handler() -> crate::bots::BotHandler {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use teloxide::{
|
|||||||
use tracing::log;
|
use tracing::log;
|
||||||
|
|
||||||
use crate::bots::approved_bot::{
|
use crate::bots::approved_bot::{
|
||||||
|
modules::utils::message_text::is_message_text_equals,
|
||||||
services::{
|
services::{
|
||||||
book_library::{
|
book_library::{
|
||||||
formatters::{Format, FormatTitle},
|
formatters::{Format, FormatTitle},
|
||||||
@@ -66,7 +67,7 @@ where
|
|||||||
|
|
||||||
let allowed_langs = get_user_or_default_lang_codes(user_id).await;
|
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,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
@@ -160,7 +161,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if page > items_page.pages {
|
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,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
@@ -176,12 +177,19 @@ where
|
|||||||
|
|
||||||
let keyboard = generic_get_pagination_keyboard(page, items_page.pages, callback_data, true);
|
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)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
Ok(())
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_book_handler() -> crate::bots::BotHandler {
|
pub fn get_book_handler() -> crate::bots::BotHandler {
|
||||||
|
|||||||
@@ -97,12 +97,12 @@ async fn send_cached_message(
|
|||||||
|
|
||||||
if _send_cached(&message, &bot, cached).await.is_ok() {
|
if _send_cached(&message, &bot, cached).await.is_ok() {
|
||||||
if need_delete_message {
|
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;
|
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(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => log::error!("{err:?}"),
|
Err(err) => log::error!("{err:?}"),
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ async fn send_cached_message(
|
|||||||
|
|
||||||
async fn _send_downloaded_file(
|
async fn _send_downloaded_file(
|
||||||
message: &MaybeInaccessibleMessage,
|
message: &MaybeInaccessibleMessage,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
downloaded_data: DownloadFile,
|
downloaded_data: DownloadFile,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let DownloadFile {
|
let DownloadFile {
|
||||||
@@ -134,14 +134,14 @@ async fn _send_downloaded_file(
|
|||||||
.into_async_read()
|
.into_async_read()
|
||||||
.compat();
|
.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)
|
bot.send_document(message.chat().id, document)
|
||||||
.caption(caption)
|
.caption(caption)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
send_donation_notification(bot, message.clone()).await?;
|
send_donation_notification(bot, message).await?;
|
||||||
|
|
||||||
Ok(())
|
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 need_delete_message {
|
||||||
if let MaybeInaccessibleMessage::Regular(message) = message {
|
if let MaybeInaccessibleMessage::Regular(message) = message {
|
||||||
@@ -240,13 +240,13 @@ async fn get_download_archive_keyboard_handler(
|
|||||||
|
|
||||||
let available_types = match command {
|
let available_types = match command {
|
||||||
DownloadArchiveCommand::Sequence { id } => {
|
DownloadArchiveCommand::Sequence { id } => {
|
||||||
get_sequence_books_available_types(id, allowed_langs).await
|
get_sequence_books_available_types(id, &allowed_langs).await
|
||||||
}
|
}
|
||||||
DownloadArchiveCommand::Author { id } => {
|
DownloadArchiveCommand::Author { id } => {
|
||||||
get_author_books_available_types(id, allowed_langs).await
|
get_author_books_available_types(id, &allowed_langs).await
|
||||||
}
|
}
|
||||||
DownloadArchiveCommand::Translator { id } => {
|
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(
|
async fn send_archive_link(
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
message: Box<Message>,
|
chat_id: ChatId,
|
||||||
task: Task,
|
message_id: MessageId,
|
||||||
|
task: &Task,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let link = format!(
|
let link = format!(
|
||||||
"{}/api/download/{}",
|
"{}/api/download/{}",
|
||||||
config::CONFIG.public_batch_downloader_url.clone(),
|
config::CONFIG.public_batch_downloader_url,
|
||||||
task.id
|
task.id
|
||||||
);
|
);
|
||||||
|
|
||||||
bot.edit_message_text(
|
bot.edit_message_text(
|
||||||
message.chat.id,
|
chat_id,
|
||||||
message.id,
|
message_id,
|
||||||
format!(
|
format!(
|
||||||
"Файл не может быть загружен в чат! \n \
|
"Файл не может быть загружен в чат! \n \
|
||||||
Вы можете скачать его <a href=\"{link}\">по ссылке</a> (работает 3 часа)"
|
Вы можете скачать его <a href=\"{link}\">по ссылке</a> (работает 3 часа)"
|
||||||
@@ -352,7 +353,7 @@ async fn wait_archive(
|
|||||||
let task = loop {
|
let task = loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
|
||||||
let task = match get_task(task_id.clone()).await {
|
let task = match get_task(&task_id).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
send_error_message(bot, message.chat.id, message.id).await;
|
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();
|
let content_size = task.content_size.unwrap();
|
||||||
|
|
||||||
if content_size > 1024 * 1024 * 1024 {
|
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let link = format!(
|
let link = format!(
|
||||||
"{}/api/download/{}",
|
"{}/api/download/{}",
|
||||||
config::CONFIG.batch_downloader_url.clone(),
|
config::CONFIG.batch_downloader_url,
|
||||||
task.id
|
task.id
|
||||||
);
|
);
|
||||||
|
|
||||||
let downloaded_data =
|
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 {
|
Ok(v) => match v {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
@@ -416,14 +417,14 @@ async fn wait_archive(
|
|||||||
|
|
||||||
match _send_downloaded_file(
|
match _send_downloaded_file(
|
||||||
&MaybeInaccessibleMessage::Regular(message.clone()),
|
&MaybeInaccessibleMessage::Regular(message.clone()),
|
||||||
bot.clone(),
|
&bot,
|
||||||
downloaded_data,
|
downloaded_data,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
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:?}");
|
log::error!("{err:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use self::commands::HelpCommand;
|
|||||||
pub async fn help_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
pub async fn help_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
||||||
let name = message
|
let name = message
|
||||||
.from
|
.from
|
||||||
.map(|user| user.first_name.clone())
|
.map(|user| user.first_name)
|
||||||
.unwrap_or("пользователь".to_string());
|
.unwrap_or("пользователь".to_string());
|
||||||
|
|
||||||
match bot
|
match bot
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use teloxide::{
|
|||||||
|
|
||||||
use crate::bots::{
|
use crate::bots::{
|
||||||
approved_bot::{
|
approved_bot::{
|
||||||
|
modules::utils::message_text::is_message_text_equals,
|
||||||
services::{
|
services::{
|
||||||
book_library::{
|
book_library::{
|
||||||
formatters::{Format, FormatTitle},
|
formatters::{Format, FormatTitle},
|
||||||
@@ -45,7 +46,7 @@ where
|
|||||||
let chat_id = cq.chat_id();
|
let chat_id = cq.chat_id();
|
||||||
let user_id = cq.from.id;
|
let user_id = cq.from.id;
|
||||||
let message_id = cq.message.as_ref().map(|message| message.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) {
|
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),
|
(Some(chat_id), Some(query), Some(message_id)) => (chat_id, query, message_id),
|
||||||
@@ -93,8 +94,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if page > items_page.pages {
|
if page > items_page.pages {
|
||||||
items_page =
|
items_page = match items_getter(query, items_page.pages, allowed_langs).await {
|
||||||
match items_getter(query.clone(), items_page.pages, allowed_langs.clone()).await {
|
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
@@ -107,15 +107,20 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let formatted_page = items_page.format(page, 4096);
|
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);
|
let keyboard = generic_get_pagination_keyboard(page, items_page.pages, search_data, true);
|
||||||
|
match bot
|
||||||
bot.edit_message_text(chat_id, message_id, formatted_page)
|
.edit_message_text(chat_id, message_id, formatted_page)
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
Ok(())
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
||||||
|
|||||||
@@ -118,10 +118,10 @@ async fn settings_callback_handler(
|
|||||||
|
|
||||||
if let Err(err) = create_or_update_user_settings(
|
if let Err(err) = create_or_update_user_settings(
|
||||||
user.id,
|
user.id,
|
||||||
user.last_name.clone().unwrap_or("".to_string()),
|
&user.last_name.unwrap_or("".to_string()),
|
||||||
user.first_name.clone(),
|
&user.first_name,
|
||||||
user.username.clone().unwrap_or("".to_string()),
|
&user.username.unwrap_or("".to_string()),
|
||||||
me.username.clone().unwrap(),
|
&me.username.clone().unwrap(),
|
||||||
allowed_langs_set.clone().into_iter().collect(),
|
allowed_langs_set.clone().into_iter().collect(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -16,20 +16,20 @@ enum SupportCommand {
|
|||||||
|
|
||||||
pub async fn support_command_handler(
|
pub async fn support_command_handler(
|
||||||
message: Message,
|
message: Message,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let username = match message.clone().from {
|
let username = match message.clone().from {
|
||||||
Some(user) => match user.is_bot {
|
Some(user) => match user.is_bot {
|
||||||
true => match message.reply_to_message() {
|
true => match message.reply_to_message() {
|
||||||
Some(v) => match &v.from {
|
Some(v) => match &v.from {
|
||||||
Some(v) => v.first_name.clone(),
|
Some(v) => &v.first_name,
|
||||||
None => "пользователь".to_string(),
|
None => "пользователь",
|
||||||
},
|
},
|
||||||
None => "пользователь".to_string(),
|
None => "пользователь",
|
||||||
},
|
},
|
||||||
false => user.first_name,
|
false => &user.first_name.clone(),
|
||||||
},
|
},
|
||||||
None => "пользователь".to_string(),
|
None => "пользователь",
|
||||||
};
|
};
|
||||||
|
|
||||||
let message_text = format!(
|
let message_text = format!(
|
||||||
@@ -60,5 +60,7 @@ pub async fn support_command_handler(
|
|||||||
pub fn get_support_handler() -> crate::bots::BotHandler {
|
pub fn get_support_handler() -> crate::bots::BotHandler {
|
||||||
Update::filter_message()
|
Update::filter_message()
|
||||||
.filter_command::<SupportCommand>()
|
.filter_command::<SupportCommand>()
|
||||||
.endpoint(support_command_handler)
|
.endpoint(|message: Message, bot: CacheMe<Throttle<Bot>>| async move {
|
||||||
|
support_command_handler(message, &bot).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ pub mod commands;
|
|||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
|
|
||||||
use crate::bots::{
|
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,
|
BotHandlerInternal,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ async fn update_log_pagination_handler(
|
|||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
update_callback_data: UpdateLogCallbackData,
|
update_callback_data: UpdateLogCallbackData,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let message = match cq.message {
|
let message = match cq.message.clone() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(")
|
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 formatted_page = items_page.format(page, 4096);
|
||||||
|
|
||||||
let message_text = format!("{header}{formatted_page}");
|
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);
|
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)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
Ok(())
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_update_log_handler() -> crate::bots::BotHandler {
|
pub fn get_update_log_handler() -> crate::bots::BotHandler {
|
||||||
|
|||||||
18
src/bots/approved_bot/modules/utils/message_text.rs
Normal file
18
src/bots/approved_bot/modules/utils/message_text.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod filter_command;
|
pub mod filter_command;
|
||||||
|
pub mod message_text;
|
||||||
pub mod pagination;
|
pub mod pagination;
|
||||||
pub mod split_text;
|
pub mod split_text;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub async fn create_task(data: CreateTaskData) -> anyhow::Result<Task> {
|
|||||||
.await?)
|
.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
|
Ok(CLIENT
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"{}/api/check_archive/{task_id}",
|
"{}/api/check_archive/{task_id}",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ pub async fn download_file(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn download_file_by_link(
|
pub async fn download_file_by_link(
|
||||||
filename: String,
|
filename: &str,
|
||||||
link: String,
|
link: String,
|
||||||
) -> anyhow::Result<Option<DownloadFile>> {
|
) -> anyhow::Result<Option<DownloadFile>> {
|
||||||
let response = CLIENT.get(link).send().await?;
|
let response = CLIENT.get(link).send().await?;
|
||||||
@@ -102,7 +102,7 @@ pub async fn download_file_by_link(
|
|||||||
|
|
||||||
Ok(Some(DownloadFile {
|
Ok(Some(DownloadFile {
|
||||||
response,
|
response,
|
||||||
filename,
|
filename: filename.to_string(),
|
||||||
caption: "".to_string(),
|
caption: "".to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
if count == 0 {
|
||||||
return "".to_string();
|
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 {
|
if count == 0 {
|
||||||
return "".to_string();
|
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 {
|
if count == 0 {
|
||||||
return "".to_string();
|
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 {
|
if count == 0 {
|
||||||
return "".to_string();
|
return "".to_string();
|
||||||
}
|
}
|
||||||
@@ -395,20 +395,25 @@ impl FormatVectorsResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Book {
|
fn format_vectors(
|
||||||
fn format_vectors(&self, max_size: usize) -> FormatVectorsResult {
|
authors: &[BookAuthor],
|
||||||
|
translators: &[BookTranslator],
|
||||||
|
sequences: &[Sequence],
|
||||||
|
genres: &[BookGenre],
|
||||||
|
max_size: usize,
|
||||||
|
) -> FormatVectorsResult {
|
||||||
let mut counts = FormatVectorsCounts {
|
let mut counts = FormatVectorsCounts {
|
||||||
authors: self.authors.len(),
|
authors: authors.len(),
|
||||||
translators: self.translators.len(),
|
translators: translators.len(),
|
||||||
sequences: self.sequences.len(),
|
sequences: sequences.len(),
|
||||||
genres: self.genres.len(),
|
genres: genres.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = FormatVectorsResult {
|
let mut result = FormatVectorsResult {
|
||||||
authors: format_authors(self.authors.clone(), counts.authors),
|
authors: format_authors(authors, counts.authors),
|
||||||
translators: format_translators(self.translators.clone(), counts.translators),
|
translators: format_translators(translators, counts.translators),
|
||||||
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
sequences: format_sequences(sequences, counts.sequences),
|
||||||
genres: format_genres(self.genres.clone(), counts.genres),
|
genres: format_genres(genres, counts.genres),
|
||||||
max_result_size: 0,
|
max_result_size: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -418,34 +423,58 @@ impl Book {
|
|||||||
counts = counts.sub();
|
counts = counts.sub();
|
||||||
|
|
||||||
result = FormatVectorsResult {
|
result = FormatVectorsResult {
|
||||||
authors: format_authors(self.authors.clone(), counts.authors),
|
authors: format_authors(authors, counts.authors),
|
||||||
translators: format_translators(self.translators.clone(), counts.translators),
|
translators: format_translators(translators, counts.translators),
|
||||||
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
sequences: format_sequences(sequences, counts.sequences),
|
||||||
genres: format_genres(self.genres.clone(), counts.genres),
|
genres: format_genres(genres, counts.genres),
|
||||||
max_result_size: 0,
|
max_result_size: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
result.with_max_result_size(max_result_size)
|
result.with_max_result_size(max_result_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format for Book {
|
fn format_common(data: FormatData, max_size: usize) -> FormatResult {
|
||||||
fn format(&self, max_size: usize) -> FormatResult {
|
let FormatData {
|
||||||
let book_title = {
|
id,
|
||||||
let Book { title, lang, .. } = self;
|
title,
|
||||||
|
lang,
|
||||||
|
annotation_exists,
|
||||||
|
authors,
|
||||||
|
translators,
|
||||||
|
sequences,
|
||||||
|
genres,
|
||||||
|
year,
|
||||||
|
pages,
|
||||||
|
position,
|
||||||
|
} = data;
|
||||||
|
|
||||||
let year_part = match self.year {
|
let book_title = {
|
||||||
|
let year_part = match year {
|
||||||
0 => "".to_string(),
|
0 => "".to_string(),
|
||||||
v => format!(" | {v}г."),
|
v => format!(" | {v}г."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pages_count = match self.pages {
|
let pages_count = match pages {
|
||||||
Some(1) | None => "".to_string(),
|
Some(1) | None => "".to_string(),
|
||||||
Some(v) => format!(" | {v}с."),
|
Some(v) => format!(" | {v}с."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let position_prefix = match self.position {
|
let position_prefix = match position {
|
||||||
Some(0) | None => "".to_string(),
|
Some(0) | None => "".to_string(),
|
||||||
Some(v) => format!("{v} | "),
|
Some(v) => format!("{v} | "),
|
||||||
};
|
};
|
||||||
@@ -453,15 +482,14 @@ impl Format for Book {
|
|||||||
format!("{position_prefix}📖 {title} | {lang}{year_part}{pages_count}\n")
|
format!("{position_prefix}📖 {title} | {lang}{year_part}{pages_count}\n")
|
||||||
};
|
};
|
||||||
|
|
||||||
let annotations = match self.annotation_exists {
|
let annotations = match annotation_exists {
|
||||||
true => {
|
true => {
|
||||||
let Book { id, .. } = self;
|
|
||||||
format!("📝 Аннотация: /b_an_{id}\n")
|
format!("📝 Аннотация: /b_an_{id}\n")
|
||||||
}
|
}
|
||||||
false => "".to_string(),
|
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 download_links = format!("Скачать:\n📥{download_command}");
|
||||||
|
|
||||||
let required_data_len: usize = format!("{book_title}{annotations}{download_links}").len();
|
let required_data_len: usize = format!("{book_title}{annotations}{download_links}").len();
|
||||||
@@ -471,7 +499,13 @@ impl Format for Book {
|
|||||||
sequences,
|
sequences,
|
||||||
genres,
|
genres,
|
||||||
max_result_size,
|
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!(
|
let result = format!(
|
||||||
"{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}"
|
"{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}"
|
||||||
@@ -484,28 +518,108 @@ impl Format for Book {
|
|||||||
max_size: max_result_size + required_data_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 {
|
impl Format for SearchBook {
|
||||||
fn format(&self, max_size: usize) -> FormatResult {
|
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 {
|
impl Format for AuthorBook {
|
||||||
fn format(&self, max_size: usize) -> FormatResult {
|
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 {
|
impl Format for TranslatorBook {
|
||||||
fn format(&self, max_size: usize) -> FormatResult {
|
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 {
|
impl Format for SequenceBook {
|
||||||
fn format(&self, max_size: usize) -> FormatResult {
|
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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ use self::types::Empty;
|
|||||||
pub static CLIENT: Lazy<reqwest::Client> = Lazy::new(reqwest::Client::new);
|
pub static CLIENT: Lazy<reqwest::Client> = Lazy::new(reqwest::Client::new);
|
||||||
|
|
||||||
fn get_allowed_langs_params(
|
fn get_allowed_langs_params(
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: &SmallVec<[SmartString; 3]>,
|
||||||
) -> Vec<(&'static str, SmartString)> {
|
) -> Vec<(&'static str, SmartString)> {
|
||||||
allowed_langs
|
allowed_langs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|lang| ("allowed_langs", lang))
|
.map(|lang| ("allowed_langs", lang.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ pub async fn get_random_book_by_genre(
|
|||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
genre: Option<u32>,
|
genre: Option<u32>,
|
||||||
) -> anyhow::Result<types::Book> {
|
) -> 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 {
|
if let Some(v) = genre {
|
||||||
params.push(("genre", v.to_string().into()));
|
params.push(("genre", v.to_string().into()));
|
||||||
@@ -70,7 +70,7 @@ pub async fn get_random_book(
|
|||||||
pub async fn get_random_author(
|
pub async fn get_random_author(
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Author> {
|
) -> 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
|
_make_request("/api/v1/authors/random", params).await
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ pub async fn get_random_author(
|
|||||||
pub async fn get_random_sequence(
|
pub async fn get_random_sequence(
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Sequence> {
|
) -> 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
|
_make_request("/api/v1/sequences/random", params).await
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ pub async fn search_book(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::SearchBook, Empty>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -113,7 +113,7 @@ pub async fn search_author(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::Author, Empty>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -126,7 +126,7 @@ pub async fn search_sequence(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::Sequence, Empty>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -139,7 +139,7 @@ pub async fn search_translator(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::Translator, Empty>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -164,7 +164,7 @@ pub async fn get_author_books(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::AuthorBook, types::BookAuthor>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -177,7 +177,7 @@ pub async fn get_translator_books(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::TranslatorBook, types::BookTranslator>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.to_string().into()));
|
params.push(("size", PAGE_SIZE.to_string().into()));
|
||||||
@@ -190,7 +190,7 @@ pub async fn get_sequence_books(
|
|||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<types::Page<types::SequenceBook, types::Sequence>> {
|
) -> 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(("page", page.to_string().into()));
|
||||||
params.push(("size", PAGE_SIZE.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(
|
pub async fn get_author_books_available_types(
|
||||||
id: u32,
|
id: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: &SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<Vec<String>> {
|
) -> anyhow::Result<Vec<String>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
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(
|
pub async fn get_translator_books_available_types(
|
||||||
id: u32,
|
id: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: &SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<Vec<String>> {
|
) -> anyhow::Result<Vec<String>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
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(
|
pub async fn get_sequence_books_available_types(
|
||||||
id: u32,
|
id: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: &SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<Vec<String>> {
|
) -> anyhow::Result<Vec<String>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
let params = get_allowed_langs_params(allowed_langs);
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,6 @@ impl BookGenre {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
pub struct Source {
|
|
||||||
// id: u32,
|
|
||||||
// name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct Author {
|
pub struct Author {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
@@ -149,6 +143,8 @@ where
|
|||||||
.map(|item| item_size - item.current_size)
|
.map(|item| item_size - item.current_size)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
self.items
|
self.items
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -156,17 +152,17 @@ where
|
|||||||
let already_formated_result = &format_result[index];
|
let already_formated_result = &format_result[index];
|
||||||
|
|
||||||
if already_formated_result.current_size == already_formated_result.max_size {
|
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 {
|
} else {
|
||||||
let new_item_size = item_size + free_symbols;
|
let new_item_size = item_size + free_symbols;
|
||||||
let new_formated_result = item.format(new_item_size);
|
let new_formated_result = item.format(new_item_size);
|
||||||
|
|
||||||
free_symbols = new_item_size - new_formated_result.current_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)
|
.join(separator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,17 +188,12 @@ pub struct Book {
|
|||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
// file_type: String,
|
|
||||||
pub available_types: SmallVec<[String; 4]>,
|
pub available_types: SmallVec<[String; 4]>,
|
||||||
// uploaded: String,
|
|
||||||
pub annotation_exists: bool,
|
pub annotation_exists: bool,
|
||||||
pub authors: Vec<BookAuthor>,
|
pub authors: Vec<BookAuthor>,
|
||||||
pub translators: Vec<BookTranslator>,
|
pub translators: Vec<BookTranslator>,
|
||||||
pub sequences: Vec<Sequence>,
|
pub sequences: Vec<Sequence>,
|
||||||
pub genres: Vec<BookGenre>,
|
pub genres: Vec<BookGenre>,
|
||||||
// source: Source,
|
|
||||||
// remote_id: u32,
|
|
||||||
// id_deleted: bool,
|
|
||||||
pub year: i32,
|
pub year: i32,
|
||||||
pub pages: Option<u32>,
|
pub pages: Option<u32>,
|
||||||
pub position: Option<i32>,
|
pub position: Option<i32>,
|
||||||
@@ -213,9 +204,6 @@ pub struct SearchBook {
|
|||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
// file_type: String,
|
|
||||||
pub available_types: SmallVec<[String; 4]>,
|
|
||||||
// uploaded: String,
|
|
||||||
pub annotation_exists: bool,
|
pub annotation_exists: bool,
|
||||||
pub authors: Vec<BookAuthor>,
|
pub authors: Vec<BookAuthor>,
|
||||||
pub translators: Vec<BookTranslator>,
|
pub translators: Vec<BookTranslator>,
|
||||||
@@ -223,121 +211,36 @@ pub struct SearchBook {
|
|||||||
pub year: i32,
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct AuthorBook {
|
pub struct AuthorBook {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
// file_type: String,
|
|
||||||
pub available_types: SmallVec<[String; 4]>,
|
|
||||||
// uploaded: String,
|
|
||||||
pub annotation_exists: bool,
|
pub annotation_exists: bool,
|
||||||
pub translators: Vec<BookTranslator>,
|
pub translators: Vec<BookTranslator>,
|
||||||
pub sequences: Vec<Sequence>,
|
pub sequences: Vec<Sequence>,
|
||||||
pub year: i32,
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct TranslatorBook {
|
pub struct TranslatorBook {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
// file_type: String,
|
|
||||||
pub available_types: SmallVec<[String; 4]>,
|
|
||||||
// uploaded: String,
|
|
||||||
pub annotation_exists: bool,
|
pub annotation_exists: bool,
|
||||||
pub authors: Vec<BookAuthor>,
|
pub authors: Vec<BookAuthor>,
|
||||||
pub sequences: Vec<Sequence>,
|
pub sequences: Vec<Sequence>,
|
||||||
pub year: i32,
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct SequenceBook {
|
pub struct SequenceBook {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub lang: String,
|
pub lang: String,
|
||||||
// file_type: String,
|
|
||||||
pub available_types: SmallVec<[String; 4]>,
|
|
||||||
// uploaded: String,
|
|
||||||
pub authors: Vec<BookAuthor>,
|
pub authors: Vec<BookAuthor>,
|
||||||
pub translators: Vec<BookTranslator>,
|
pub translators: Vec<BookTranslator>,
|
||||||
pub annotation_exists: bool,
|
pub annotation_exists: bool,
|
||||||
pub year: i32,
|
pub year: i32,
|
||||||
pub position: 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ use crate::{
|
|||||||
use super::user_settings::{is_need_donate_notifications, mark_donate_notification_sent};
|
use super::user_settings::{is_need_donate_notifications, mark_donate_notification_sent};
|
||||||
|
|
||||||
pub async fn send_donation_notification(
|
pub async fn send_donation_notification(
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
message: MaybeInaccessibleMessage,
|
message: &MaybeInaccessibleMessage,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
if CHAT_DONATION_NOTIFICATIONS_CACHE
|
if CHAT_DONATION_NOTIFICATIONS_CACHE
|
||||||
.get(&message.chat().id)
|
.get(&message.chat().id)
|
||||||
@@ -31,7 +31,7 @@ pub async fn send_donation_notification(
|
|||||||
mark_donate_notification_sent(message.chat().id).await?;
|
mark_donate_notification_sent(message.chat().id).await?;
|
||||||
|
|
||||||
if let MaybeInaccessibleMessage::Regular(message) = message {
|
if let MaybeInaccessibleMessage::Regular(message) = message {
|
||||||
support_command_handler(*message, bot).await?;
|
support_command_handler(*message.clone(), bot).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
pub async fn create_or_update_user_settings(
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
last_name: String,
|
last_name: &str,
|
||||||
first_name: String,
|
first_name: &str,
|
||||||
username: String,
|
username: &str,
|
||||||
source: String,
|
source: &str,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> anyhow::Result<UserSettings> {
|
) -> anyhow::Result<UserSettings> {
|
||||||
USER_LANGS_CACHE.invalidate(&user_id).await;
|
USER_LANGS_CACHE.invalidate(&user_id).await;
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> a
|
|||||||
|
|
||||||
let message_text = match result {
|
let message_text = match result {
|
||||||
register::RegisterStatus::Success { ref username } => format_registered_message(username),
|
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::LimitExtended => strings::LIMIT_EXTENDED_MESSAGE.to_string(),
|
||||||
register::RegisterStatus::WrongToken => strings::ERROR_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)
|
bot.send_message(message.chat.id, message_text)
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ pub enum RegisterStatus {
|
|||||||
WrongToken,
|
WrongToken,
|
||||||
RegisterFail,
|
RegisterFail,
|
||||||
LimitExtended,
|
LimitExtended,
|
||||||
|
AlreadyExists,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RegisterRequestStatus {
|
pub enum RegisterRequestStatus {
|
||||||
Success,
|
Success,
|
||||||
LimitExtended,
|
LimitExtended,
|
||||||
|
AlreadyExists,
|
||||||
UnknownError,
|
UnknownError,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,9 +44,9 @@ async fn make_register_request(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let result = reqwest::Client::new()
|
let result = reqwest::Client::new()
|
||||||
.post(config::CONFIG.manager_url.clone())
|
.post(&config::CONFIG.manager_url)
|
||||||
.body(body.to_string())
|
.body(body.to_string())
|
||||||
.header("Authorization", config::CONFIG.manager_api_key.clone())
|
.header("Authorization", &config::CONFIG.manager_api_key)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -52,6 +54,7 @@ async fn make_register_request(
|
|||||||
Ok(match result.status().as_u16() {
|
Ok(match result.status().as_u16() {
|
||||||
200 => RegisterRequestStatus::Success,
|
200 => RegisterRequestStatus::Success,
|
||||||
402 => RegisterRequestStatus::LimitExtended,
|
402 => RegisterRequestStatus::LimitExtended,
|
||||||
|
409 => RegisterRequestStatus::AlreadyExists,
|
||||||
_ => RegisterRequestStatus::UnknownError,
|
_ => RegisterRequestStatus::UnknownError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -81,5 +84,6 @@ pub async fn register(user_id: UserId, message_text: &str) -> RegisterStatus {
|
|||||||
},
|
},
|
||||||
RegisterRequestStatus::LimitExtended => RegisterStatus::LimitExtended,
|
RegisterRequestStatus::LimitExtended => RegisterStatus::LimitExtended,
|
||||||
RegisterRequestStatus::UnknownError => RegisterStatus::RegisterFail,
|
RegisterRequestStatus::UnknownError => RegisterStatus::RegisterFail,
|
||||||
|
RegisterRequestStatus::AlreadyExists => RegisterStatus::AlreadyExists,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ pub fn format_registered_message(username: &str) -> String {
|
|||||||
format!("@{username} зарегистрирован и через несколько минут будет подключен!")
|
format!("@{username} зарегистрирован и через несколько минут будет подключен!")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
|
pub const MAY_BE_ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
|
||||||
|
|
||||||
pub const ERROR_MESSAGE: &str = "Ошибка! Что-то не так с ботом!";
|
pub const ERROR_MESSAGE: &str = "Ошибка! Что-то не так с ботом!";
|
||||||
|
|
||||||
pub const LIMIT_EXTENDED_MESSAGE: &str = "Вы достигли максимального количества ботов!";
|
pub const LIMIT_EXTENDED_MESSAGE: &str = "Вы достигли максимального количества ботов!";
|
||||||
|
|
||||||
|
pub const ALREADY_EXISTS_MESSAGE: &str = "Ошибка! Бот с таким токеном уже зарегистрирован!";
|
||||||
|
|||||||
85
src/bots_manager/custom_error_handler.rs
Normal file
85
src/bots_manager/custom_error_handler.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use super::custom_error_handler::CustomErrorHandler;
|
||||||
use teloxide::adaptors::throttle::Limits;
|
use teloxide::adaptors::throttle::Limits;
|
||||||
use teloxide::dispatching::Dispatcher;
|
use teloxide::dispatching::Dispatcher;
|
||||||
use teloxide::error_handlers::LoggingErrorHandler;
|
|
||||||
use teloxide::requests::{Request, Requester, RequesterExt};
|
use teloxide::requests::{Request, Requester, RequesterExt};
|
||||||
use teloxide::stop::StopToken;
|
use teloxide::stop::StopToken;
|
||||||
use teloxide::stop::{mk_stop_token, StopFlag};
|
use teloxide::stop::{mk_stop_token, StopFlag};
|
||||||
@@ -108,7 +109,7 @@ pub async fn start_bot(bot_data: &BotData) {
|
|||||||
dispatcher
|
dispatcher
|
||||||
.dispatch_with_listener(
|
.dispatch_with_listener(
|
||||||
listener,
|
listener,
|
||||||
LoggingErrorHandler::with_custom_text("An error from the update listener"),
|
CustomErrorHandler::with_custom_text("An error from the update listener"),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod axum_server;
|
pub mod axum_server;
|
||||||
pub mod bot_manager_client;
|
pub mod bot_manager_client;
|
||||||
pub mod closable_sender;
|
pub mod closable_sender;
|
||||||
|
pub mod custom_error_handler;
|
||||||
pub mod internal;
|
pub mod internal;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
@@ -93,13 +94,11 @@ impl BotsManager {
|
|||||||
|
|
||||||
let bot_data: BotData = bot_data.clone();
|
let bot_data: BotData = bot_data.clone();
|
||||||
|
|
||||||
BOTS_DATA
|
BOTS_DATA.insert(bot_data.token.clone(), bot_data).await;
|
||||||
.insert(bot_data.token.clone(), bot_data.clone())
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_unininted(bots_data: &[BotData]) {
|
async fn check_uninited(bots_data: &[BotData]) {
|
||||||
let semaphore = Arc::new(Semaphore::const_new(5));
|
let semaphore = Arc::new(Semaphore::const_new(5));
|
||||||
let mut set_webhook_tasks = JoinSet::new();
|
let mut set_webhook_tasks = JoinSet::new();
|
||||||
|
|
||||||
@@ -110,9 +109,9 @@ impl BotsManager {
|
|||||||
|
|
||||||
let bot_data: BotData = bot_data.clone();
|
let bot_data: BotData = bot_data.clone();
|
||||||
|
|
||||||
let semphore = semaphore.clone();
|
let semaphore = semaphore.clone();
|
||||||
set_webhook_tasks.spawn(async move {
|
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;
|
let webhook_status = set_webhook(&bot_data).await;
|
||||||
|
|
||||||
@@ -145,7 +144,7 @@ impl BotsManager {
|
|||||||
let _ = BotsManager::check_bots_data(&bots_data).await;
|
let _ = BotsManager::check_bots_data(&bots_data).await;
|
||||||
|
|
||||||
if !only_bot_data {
|
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 {
|
match result {
|
||||||
Ok(webhook_info) => {
|
Ok(webhook_info) => {
|
||||||
if webhook_info.pending_update_count != 0 {
|
if webhook_info.pending_update_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +186,9 @@ impl BotsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
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;
|
BOTS_DATA.invalidate(token.as_str()).await;
|
||||||
if let Err(d_err) = delete_bot(bot_data.id).await {
|
if let Err(d_err) = delete_bot(bot_data.id).await {
|
||||||
log::error!("Error deleting bot {}: {:?}", bot_data.id, d_err);
|
log::error!("Error deleting bot {}: {:?}", bot_data.id, d_err);
|
||||||
@@ -195,7 +196,7 @@ impl BotsManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::error!("Error getting webhook info: {err:?}");
|
log::error!("Error getting webhook info: {error_message}");
|
||||||
|
|
||||||
WEBHOOK_CHECK_ERRORS_COUNT
|
WEBHOOK_CHECK_ERRORS_COUNT
|
||||||
.insert(bot_data.id, error_count + 1)
|
.insert(bot_data.id, error_count + 1)
|
||||||
@@ -224,11 +225,11 @@ impl BotsManager {
|
|||||||
BotsManager::check(false).await;
|
BotsManager::check(false).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if tick_number % 180 == 60 {
|
if tick_number % 1800 == 600 {
|
||||||
BotsManager::check_pending_updates().await;
|
BotsManager::check_pending_updates().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick_number = (tick_number + 1) % 180;
|
tick_number = (tick_number + 1) % 1800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user