mirror of
https://github.com/flibusta-apps/book_bot.git
synced 2025-12-06 07:25:36 +01:00
Add pre-commit
This commit is contained in:
7
.pre-commit-config.yaml
Normal file
7
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/doublify/pre-commit-rust
|
||||||
|
rev: v1.0
|
||||||
|
hooks:
|
||||||
|
- id: fmt
|
||||||
|
- id: cargo-check
|
||||||
|
- id: clippy
|
||||||
@@ -2,9 +2,16 @@ pub mod modules;
|
|||||||
pub mod services;
|
pub mod services;
|
||||||
mod tools;
|
mod tools;
|
||||||
|
|
||||||
use teloxide::{prelude::*, types::BotCommand, adaptors::{Throttle, CacheMe}};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
prelude::*,
|
||||||
|
types::BotCommand,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{bots::approved_bot::services::user_settings::create_or_update_user_settings, bots_manager::USER_ACTIVITY_CACHE};
|
use crate::{
|
||||||
|
bots::approved_bot::services::user_settings::create_or_update_user_settings,
|
||||||
|
bots_manager::USER_ACTIVITY_CACHE,
|
||||||
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
modules::{
|
modules::{
|
||||||
@@ -16,12 +23,12 @@ use self::{
|
|||||||
services::user_settings::{get_user_or_default_lang_codes, update_user_activity},
|
services::user_settings::{get_user_or_default_lang_codes, update_user_activity},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ignore_channel_messages, BotCommands, BotHandler, bots_manager::get_manager_handler, ignore_chat_member_update};
|
use super::{
|
||||||
|
bots_manager::get_manager_handler, ignore_channel_messages, ignore_chat_member_update,
|
||||||
|
BotCommands, BotHandler,
|
||||||
|
};
|
||||||
|
|
||||||
async fn _update_activity(
|
async fn _update_activity(me: teloxide::types::Me, user: teloxide::types::User) -> Option<()> {
|
||||||
me: teloxide::types::Me,
|
|
||||||
user: teloxide::types::User,
|
|
||||||
) -> Option<()> {
|
|
||||||
if USER_ACTIVITY_CACHE.contains_key(&user.id) {
|
if USER_ACTIVITY_CACHE.contains_key(&user.id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -39,7 +46,9 @@ async fn _update_activity(
|
|||||||
user.username.clone().unwrap_or("".to_string()),
|
user.username.clone().unwrap_or("".to_string()),
|
||||||
me.username.clone().unwrap(),
|
me.username.clone().unwrap(),
|
||||||
allowed_langs,
|
allowed_langs,
|
||||||
).await.is_ok()
|
)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
{
|
{
|
||||||
update_result = update_user_activity(user.id).await;
|
update_result = update_user_activity(user.id).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::{pagination::GetPaginationCallbackData, errors::CallbackQueryParseError};
|
use crate::bots::approved_bot::modules::utils::{
|
||||||
|
errors::CallbackQueryParseError, pagination::GetPaginationCallbackData,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AnnotationCallbackData {
|
pub enum AnnotationCallbackData {
|
||||||
@@ -19,17 +20,20 @@ impl FromStr for AnnotationCallbackData {
|
|||||||
.unwrap_or_else(|_| panic!("Broken AnnotationCallbackData regex pattern!"))
|
.unwrap_or_else(|_| panic!("Broken AnnotationCallbackData regex pattern!"))
|
||||||
.captures(s)
|
.captures(s)
|
||||||
.ok_or(CallbackQueryParseError)
|
.ok_or(CallbackQueryParseError)
|
||||||
.map(|caps| (
|
.map(|caps| {
|
||||||
|
(
|
||||||
caps["an_type"].to_string(),
|
caps["an_type"].to_string(),
|
||||||
caps["id"].parse::<u32>().unwrap(),
|
caps["id"].parse::<u32>().unwrap(),
|
||||||
caps["page"].parse::<u32>().unwrap()
|
caps["page"].parse::<u32>().unwrap(),
|
||||||
))
|
)
|
||||||
.map(|(annotation_type, id, page)|
|
})
|
||||||
match annotation_type.as_str() {
|
.map(
|
||||||
|
|(annotation_type, id, page)| match annotation_type.as_str() {
|
||||||
"a" => AnnotationCallbackData::Author { id, page },
|
"a" => AnnotationCallbackData::Author { id, page },
|
||||||
"b" => AnnotationCallbackData::Book { id, page },
|
"b" => AnnotationCallbackData::Book { id, page },
|
||||||
_ => panic!("Unknown AnnotationCallbackData type: {}!", annotation_type),
|
_ => panic!("Unknown AnnotationCallbackData type: {}!", annotation_type),
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::{filter_command::CommandParse, errors::CommandParseError};
|
use crate::bots::approved_bot::modules::utils::{
|
||||||
|
errors::CommandParseError, filter_command::CommandParse,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AnnotationCommand {
|
pub enum AnnotationCommand {
|
||||||
@@ -15,16 +16,18 @@ impl CommandParse<Self> for AnnotationCommand {
|
|||||||
.unwrap_or_else(|_| panic!("Broken AnnotationCommand regexp!"))
|
.unwrap_or_else(|_| panic!("Broken AnnotationCommand regexp!"))
|
||||||
.captures(&s.replace(&format!("@{bot_name}"), ""))
|
.captures(&s.replace(&format!("@{bot_name}"), ""))
|
||||||
.ok_or(CommandParseError)
|
.ok_or(CommandParseError)
|
||||||
.map(|caps| (
|
.map(|caps| {
|
||||||
|
(
|
||||||
caps["an_type"].to_string(),
|
caps["an_type"].to_string(),
|
||||||
caps["id"].parse::<u32>().unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!"))
|
caps["id"]
|
||||||
))
|
.parse::<u32>()
|
||||||
.map(|(annotation_type, id)| {
|
.unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!")),
|
||||||
match annotation_type.as_str() {
|
)
|
||||||
|
})
|
||||||
|
.map(|(annotation_type, id)| match annotation_type.as_str() {
|
||||||
"a" => Ok(AnnotationCommand::Author { id }),
|
"a" => Ok(AnnotationCommand::Author { id }),
|
||||||
"b" => Ok(AnnotationCommand::Book { id }),
|
"b" => Ok(AnnotationCommand::Book { id }),
|
||||||
_ => panic!("Unknown AnnotationCommand type: {}!", annotation_type),
|
_ => panic!("Unknown AnnotationCommand type: {}!", annotation_type),
|
||||||
}
|
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ use std::fmt;
|
|||||||
|
|
||||||
use super::commands::AnnotationCommand;
|
use super::commands::AnnotationCommand;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AnnotationFormatError {
|
pub struct AnnotationFormatError {
|
||||||
pub command: AnnotationCommand,
|
pub command: AnnotationCommand,
|
||||||
pub text: String
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AnnotationFormatError {
|
impl fmt::Display for AnnotationFormatError {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ impl AnnotationFormat for BookAnnotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl AnnotationFormat for AuthorAnnotation {
|
impl AnnotationFormat for AuthorAnnotation {
|
||||||
fn get_file(&self) -> Option<&String> {
|
fn get_file(&self) -> Option<&String> {
|
||||||
self.file.as_ref()
|
self.file.as_ref()
|
||||||
|
|||||||
@@ -1,30 +1,36 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
pub mod formatter;
|
pub mod commands;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub mod formatter;
|
||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
dispatching::UpdateFilterExt,
|
||||||
|
dptree,
|
||||||
|
prelude::*,
|
||||||
|
types::*,
|
||||||
|
};
|
||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||||
|
|
||||||
use crate::bots::{
|
use crate::bots::{
|
||||||
approved_bot::{
|
approved_bot::{
|
||||||
modules::utils::pagination::generic_get_pagination_keyboard,
|
modules::utils::pagination::generic_get_pagination_keyboard,
|
||||||
services::book_library::{
|
services::book_library::{get_author_annotation, get_book_annotation},
|
||||||
get_author_annotation, get_book_annotation,
|
|
||||||
},
|
|
||||||
tools::filter_callback_query,
|
tools::filter_callback_query,
|
||||||
},
|
},
|
||||||
BotHandlerInternal,
|
BotHandlerInternal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{commands::AnnotationCommand, formatter::AnnotationFormat, callback_data::AnnotationCallbackData, errors::AnnotationFormatError};
|
use self::{
|
||||||
|
callback_data::AnnotationCallbackData, commands::AnnotationCommand,
|
||||||
use super::utils::{split_text::split_text_to_chunks, filter_command::filter_command};
|
errors::AnnotationFormatError, formatter::AnnotationFormat,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::utils::{filter_command::filter_command, split_text::split_text_to_chunks};
|
||||||
|
|
||||||
async fn download_image(
|
async fn download_image(
|
||||||
file: &String,
|
file: &String,
|
||||||
@@ -32,7 +38,6 @@ async fn download_image(
|
|||||||
Ok(reqwest::get(file).await?.error_for_status()?)
|
Ok(reqwest::get(file).await?.error_for_status()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn send_annotation_handler<T, Fut>(
|
pub async fn send_annotation_handler<T, Fut>(
|
||||||
message: Message,
|
message: Message,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
@@ -72,9 +77,9 @@ where
|
|||||||
.into_async_read()
|
.into_async_read()
|
||||||
.compat();
|
.compat();
|
||||||
|
|
||||||
#[allow(unused_must_use)] {
|
#[allow(unused_must_use)]
|
||||||
bot
|
{
|
||||||
.send_photo(message.chat.id, InputFile::read(data))
|
bot.send_photo(message.chat.id, InputFile::read(data))
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,10 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !annotation.is_normal_text() {
|
if !annotation.is_normal_text() {
|
||||||
return Err(Box::new(AnnotationFormatError { command, text: annotation.get_text().to_string() }));
|
return Err(Box::new(AnnotationFormatError {
|
||||||
|
command,
|
||||||
|
text: annotation.get_text().to_string(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let annotation_text = annotation.get_text();
|
let annotation_text = annotation.get_text();
|
||||||
@@ -93,15 +101,10 @@ where
|
|||||||
AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 },
|
AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 },
|
||||||
AnnotationCommand::Author { id } => AnnotationCallbackData::Author { id, page: 1 },
|
AnnotationCommand::Author { id } => AnnotationCallbackData::Author { id, page: 1 },
|
||||||
};
|
};
|
||||||
let keyboard = generic_get_pagination_keyboard(
|
let keyboard =
|
||||||
1,
|
generic_get_pagination_keyboard(1, chunked_text.len().try_into()?, callback_data, false);
|
||||||
chunked_text.len().try_into()?,
|
|
||||||
callback_data,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, current_text)
|
||||||
.send_message(message.chat.id, current_text)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -109,7 +112,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn annotation_pagination_handler<T, Fut>(
|
pub async fn annotation_pagination_handler<T, Fut>(
|
||||||
cq: CallbackQuery,
|
cq: CallbackQuery,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
@@ -144,15 +146,10 @@ where
|
|||||||
};
|
};
|
||||||
let current_text = chunked_text.get(page_index - 1).unwrap();
|
let current_text = chunked_text.get(page_index - 1).unwrap();
|
||||||
|
|
||||||
let keyboard = generic_get_pagination_keyboard(
|
let keyboard =
|
||||||
page,
|
generic_get_pagination_keyboard(page, chunked_text.len().try_into()?, callback_data, false);
|
||||||
chunked_text.len().try_into()?,
|
|
||||||
callback_data,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
bot
|
bot.edit_message_text(message.chat.id, message.id, current_text)
|
||||||
.edit_message_text(message.chat.id, message.id, current_text)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -160,7 +157,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn get_annotations_handler() -> crate::bots::BotHandler {
|
pub fn get_annotations_handler() -> crate::bots::BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
.branch(
|
.branch(
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::{pagination::GetPaginationCallbackData, errors::CallbackQueryParseError};
|
use crate::bots::approved_bot::modules::utils::{
|
||||||
|
errors::CallbackQueryParseError, pagination::GetPaginationCallbackData,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum BookCallbackData {
|
pub enum BookCallbackData {
|
||||||
@@ -20,18 +21,20 @@ impl FromStr for BookCallbackData {
|
|||||||
.unwrap_or_else(|_| panic!("Broken BookCallbackData regex pattern!"))
|
.unwrap_or_else(|_| panic!("Broken BookCallbackData regex pattern!"))
|
||||||
.captures(s)
|
.captures(s)
|
||||||
.ok_or(CallbackQueryParseError)
|
.ok_or(CallbackQueryParseError)
|
||||||
.map(|caps| (
|
.map(|caps| {
|
||||||
|
(
|
||||||
caps["an_type"].to_string(),
|
caps["an_type"].to_string(),
|
||||||
caps["id"].parse::<u32>().unwrap(),
|
caps["id"].parse::<u32>().unwrap(),
|
||||||
caps["page"].parse::<u32>().unwrap()
|
caps["page"].parse::<u32>().unwrap(),
|
||||||
))
|
)
|
||||||
.map(|(annotation_type, id, page)|
|
})
|
||||||
match annotation_type.as_str() {
|
.map(
|
||||||
|
|(annotation_type, id, page)| match annotation_type.as_str() {
|
||||||
"a" => Ok(BookCallbackData::Author { id, page }),
|
"a" => Ok(BookCallbackData::Author { id, page }),
|
||||||
"t" => Ok(BookCallbackData::Translator { id, page }),
|
"t" => Ok(BookCallbackData::Translator { id, page }),
|
||||||
"s" => Ok(BookCallbackData::Sequence { id, page }),
|
"s" => Ok(BookCallbackData::Sequence { id, page }),
|
||||||
_ => panic!("Unknown BookCallbackData type: {}!", annotation_type),
|
_ => panic!("Unknown BookCallbackData type: {}!", annotation_type),
|
||||||
}
|
},
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::{filter_command::CommandParse, errors::CommandParseError};
|
use crate::bots::approved_bot::modules::utils::{
|
||||||
|
errors::CommandParseError, filter_command::CommandParse,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum BookCommand {
|
pub enum BookCommand {
|
||||||
@@ -16,17 +17,12 @@ impl CommandParse<Self> for BookCommand {
|
|||||||
.unwrap_or_else(|_| panic!("Broken BookCommand regexp!"))
|
.unwrap_or_else(|_| panic!("Broken BookCommand regexp!"))
|
||||||
.captures(&s.replace(&format!("@{bot_name}"), ""))
|
.captures(&s.replace(&format!("@{bot_name}"), ""))
|
||||||
.ok_or(CommandParseError)
|
.ok_or(CommandParseError)
|
||||||
.map(|caps| (
|
.map(|caps| (caps["an_type"].to_string(), caps["id"].parse().unwrap()))
|
||||||
caps["an_type"].to_string(),
|
.map(|(annotation_type, id)| match annotation_type.as_str() {
|
||||||
caps["id"].parse().unwrap()
|
|
||||||
))
|
|
||||||
.map(|(annotation_type, id)| {
|
|
||||||
match annotation_type.as_str() {
|
|
||||||
"a" => Ok(BookCommand::Author { id }),
|
"a" => Ok(BookCommand::Author { id }),
|
||||||
"t" => Ok(BookCommand::Translator { id }),
|
"t" => Ok(BookCommand::Translator { id }),
|
||||||
"s" => Ok(BookCommand::Sequence { id }),
|
"s" => Ok(BookCommand::Sequence { id }),
|
||||||
_ => panic!("Unknown BookCommand type: {}!", annotation_type),
|
_ => panic!("Unknown BookCommand type: {}!", annotation_type),
|
||||||
}
|
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use smartstring::alias::String as SmartString;
|
use smartstring::alias::String as SmartString;
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, adaptors::{Throttle, CacheMe}};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
dispatching::UpdateFilterExt,
|
||||||
|
dptree,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use tracing::log;
|
use tracing::log;
|
||||||
|
|
||||||
use crate::bots::approved_bot::{
|
use crate::bots::approved_bot::{
|
||||||
services::{
|
services::{
|
||||||
book_library::{
|
book_library::{
|
||||||
formatters::{Format, FormatTitle}, get_author_books, get_sequence_books, get_translator_books,
|
formatters::{Format, FormatTitle},
|
||||||
|
get_author_books, get_sequence_books, get_translator_books,
|
||||||
types::Page,
|
types::Page,
|
||||||
},
|
},
|
||||||
user_settings::get_user_or_default_lang_codes,
|
user_settings::get_user_or_default_lang_codes,
|
||||||
@@ -20,11 +26,10 @@ use crate::bots::approved_bot::{
|
|||||||
tools::filter_callback_query,
|
tools::filter_callback_query,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{commands::BookCommand, callback_data::BookCallbackData};
|
use self::{callback_data::BookCallbackData, commands::BookCommand};
|
||||||
|
|
||||||
use super::utils::{filter_command::filter_command, pagination::generic_get_pagination_keyboard};
|
use super::utils::{filter_command::filter_command, pagination::generic_get_pagination_keyboard};
|
||||||
|
|
||||||
|
|
||||||
async fn send_book_handler<T, P, Fut>(
|
async fn send_book_handler<T, P, Fut>(
|
||||||
message: Message,
|
message: Message,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
@@ -64,8 +69,7 @@ where
|
|||||||
let items_page = match books_getter(id, 1, allowed_langs.clone()).await {
|
let items_page = match books_getter(id, 1, allowed_langs.clone()).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
return Err(err);
|
return Err(err);
|
||||||
@@ -73,7 +77,9 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if items_page.pages == 0 {
|
if items_page.pages == 0 {
|
||||||
bot.send_message(chat_id, "Книги не найдены!").send().await?;
|
bot.send_message(chat_id, "Книги не найдены!")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,8 +93,7 @@ where
|
|||||||
|
|
||||||
let keyboard = generic_get_pagination_keyboard(1, items_page.pages, callback_data, true);
|
let keyboard = generic_get_pagination_keyboard(1, items_page.pages, callback_data, true);
|
||||||
|
|
||||||
bot
|
bot.send_message(chat_id, formatted_page)
|
||||||
.send_message(chat_id, formatted_page)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -120,9 +125,11 @@ where
|
|||||||
let (chat_id, message_id) = match (chat_id, message_id) {
|
let (chat_id, message_id) = match (chat_id, message_id) {
|
||||||
(Some(chat_id), Some(message_id)) => (chat_id, message_id),
|
(Some(chat_id), Some(message_id)) => (chat_id, message_id),
|
||||||
(Some(chat_id), None) => {
|
(Some(chat_id), None) => {
|
||||||
bot.send_message(chat_id, "Повторите поиск сначала").send().await?;
|
bot.send_message(chat_id, "Повторите поиск сначала")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -146,7 +153,9 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if items_page.pages == 0 {
|
if items_page.pages == 0 {
|
||||||
bot.send_message(chat_id, "Книги не найдены!").send().await?;
|
bot.send_message(chat_id, "Книги не найдены!")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,8 +163,7 @@ where
|
|||||||
items_page = match books_getter(id, items_page.pages, allowed_langs.clone()).await {
|
items_page = match books_getter(id, items_page.pages, allowed_langs.clone()).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -168,8 +176,7 @@ 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
|
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?;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use strum_macros::EnumIter;
|
|||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::errors::CommandParseError;
|
use crate::bots::approved_bot::modules::utils::errors::CommandParseError;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, EnumIter)]
|
#[derive(Clone, EnumIter)]
|
||||||
pub enum DownloadQueryData {
|
pub enum DownloadQueryData {
|
||||||
DownloadData { book_id: u32, file_type: String },
|
DownloadData { book_id: u32, file_type: String },
|
||||||
@@ -29,13 +28,13 @@ impl FromStr for DownloadQueryData {
|
|||||||
.unwrap_or_else(|_| panic!("Broken DownloadQueryData regexp!"))
|
.unwrap_or_else(|_| panic!("Broken DownloadQueryData regexp!"))
|
||||||
.captures(s)
|
.captures(s)
|
||||||
.ok_or(CommandParseError)
|
.ok_or(CommandParseError)
|
||||||
.map(|caps| (
|
.map(|caps| {
|
||||||
|
(
|
||||||
caps["book_id"].parse().unwrap(),
|
caps["book_id"].parse().unwrap(),
|
||||||
caps["file_type"].to_string()
|
caps["file_type"].to_string(),
|
||||||
))
|
)
|
||||||
.map(|(book_id, file_type)| {
|
|
||||||
DownloadQueryData::DownloadData { book_id, file_type }
|
|
||||||
})
|
})
|
||||||
|
.map(|(book_id, file_type)| DownloadQueryData::DownloadData { book_id, file_type })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,15 +42,19 @@ impl FromStr for DownloadQueryData {
|
|||||||
pub enum DownloadArchiveQueryData {
|
pub enum DownloadArchiveQueryData {
|
||||||
Sequence { id: u32, file_type: String },
|
Sequence { id: u32, file_type: String },
|
||||||
Author { id: u32, file_type: String },
|
Author { id: u32, file_type: String },
|
||||||
Translator { id: u32, file_type: String }
|
Translator { id: u32, file_type: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for DownloadArchiveQueryData {
|
impl ToString for DownloadArchiveQueryData {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
DownloadArchiveQueryData::Sequence { id, file_type } => format!("da_s_{id}_{file_type}"),
|
DownloadArchiveQueryData::Sequence { id, file_type } => {
|
||||||
|
format!("da_s_{id}_{file_type}")
|
||||||
|
}
|
||||||
DownloadArchiveQueryData::Author { id, file_type } => format!("da_a_{id}_{file_type}"),
|
DownloadArchiveQueryData::Author { id, file_type } => format!("da_a_{id}_{file_type}"),
|
||||||
DownloadArchiveQueryData::Translator { id, file_type } => format!("da_t_{id}_{file_type}"),
|
DownloadArchiveQueryData::Translator { id, file_type } => {
|
||||||
|
format!("da_t_{id}_{file_type}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,20 +74,18 @@ impl FromStr for DownloadArchiveQueryData {
|
|||||||
let id: u32 = caps["id"].parse().unwrap();
|
let id: u32 = caps["id"].parse().unwrap();
|
||||||
let file_type: String = caps["file_type"].to_string();
|
let file_type: String = caps["file_type"].to_string();
|
||||||
|
|
||||||
Ok(
|
Ok(match caps["obj_type"].to_string().as_str() {
|
||||||
match caps["obj_type"].to_string().as_str() {
|
|
||||||
"s" => DownloadArchiveQueryData::Sequence { id, file_type },
|
"s" => DownloadArchiveQueryData::Sequence { id, file_type },
|
||||||
"a" => DownloadArchiveQueryData::Author { id, file_type },
|
"a" => DownloadArchiveQueryData::Author { id, file_type },
|
||||||
"t" => DownloadArchiveQueryData::Translator { id, file_type },
|
"t" => DownloadArchiveQueryData::Translator { id, file_type },
|
||||||
_ => return Err(strum::ParseError::VariantNotFound)
|
_ => return Err(strum::ParseError::VariantNotFound),
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CheckArchiveStatus {
|
pub struct CheckArchiveStatus {
|
||||||
pub task_id: String
|
pub task_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for CheckArchiveStatus {
|
impl ToString for CheckArchiveStatus {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::{filter_command::CommandParse, errors::CommandParseError};
|
use crate::bots::approved_bot::modules::utils::{
|
||||||
|
errors::CommandParseError, filter_command::CommandParse,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StartDownloadCommand {
|
pub struct StartDownloadCommand {
|
||||||
@@ -39,7 +40,7 @@ impl CommandParse<Self> for StartDownloadCommand {
|
|||||||
pub enum DownloadArchiveCommand {
|
pub enum DownloadArchiveCommand {
|
||||||
Sequence { id: u32 },
|
Sequence { id: u32 },
|
||||||
Author { id: u32 },
|
Author { id: u32 },
|
||||||
Translator { id: u32 }
|
Translator { id: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for DownloadArchiveCommand {
|
impl ToString for DownloadArchiveCommand {
|
||||||
@@ -71,7 +72,7 @@ impl CommandParse<Self> for DownloadArchiveCommand {
|
|||||||
"s" => Ok(DownloadArchiveCommand::Sequence { id: obj_id }),
|
"s" => Ok(DownloadArchiveCommand::Sequence { id: obj_id }),
|
||||||
"a" => Ok(DownloadArchiveCommand::Author { id: obj_id }),
|
"a" => Ok(DownloadArchiveCommand::Author { id: obj_id }),
|
||||||
"t" => Ok(DownloadArchiveCommand::Translator { id: obj_id }),
|
"t" => Ok(DownloadArchiveCommand::Translator { id: obj_id }),
|
||||||
_ => Err(CommandParseError)
|
_ => Err(CommandParseError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
|
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
adaptors::{CacheMe, Throttle},
|
adaptors::{CacheMe, Throttle},
|
||||||
dispatching::UpdateFilterExt,
|
dispatching::UpdateFilterExt,
|
||||||
@@ -21,42 +20,46 @@ use tracing::log;
|
|||||||
use crate::{
|
use crate::{
|
||||||
bots::{
|
bots::{
|
||||||
approved_bot::{
|
approved_bot::{
|
||||||
|
modules::download::callback_data::DownloadArchiveQueryData,
|
||||||
services::{
|
services::{
|
||||||
|
batch_downloader::{create_task, get_task, Task, TaskStatus},
|
||||||
|
batch_downloader::{CreateTaskData, TaskObjectType},
|
||||||
book_cache::{
|
book_cache::{
|
||||||
download_file, get_cached_message,
|
download_file, download_file_by_link, get_cached_message, get_download_link,
|
||||||
types::{CachedMessage, DownloadFile}, download_file_by_link, get_download_link,
|
types::{CachedMessage, DownloadFile},
|
||||||
},
|
},
|
||||||
book_library::{get_book, get_author_books_available_types, get_translator_books_available_types, get_sequence_books_available_types},
|
book_library::{
|
||||||
donation_notifications::send_donation_notification, user_settings::get_user_or_default_lang_codes, batch_downloader::{TaskObjectType, CreateTaskData},
|
get_author_books_available_types, get_book, get_sequence_books_available_types,
|
||||||
batch_downloader::{create_task, get_task, TaskStatus, Task}
|
get_translator_books_available_types,
|
||||||
|
|
||||||
},
|
},
|
||||||
tools::filter_callback_query, modules::download::callback_data::DownloadArchiveQueryData,
|
donation_notifications::send_donation_notification,
|
||||||
|
user_settings::get_user_or_default_lang_codes,
|
||||||
|
},
|
||||||
|
tools::filter_callback_query,
|
||||||
},
|
},
|
||||||
BotHandlerInternal,
|
BotHandlerInternal,
|
||||||
},
|
},
|
||||||
bots_manager::BotCache,
|
bots_manager::BotCache,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{callback_data::{CheckArchiveStatus, DownloadQueryData}, commands::{StartDownloadCommand, DownloadArchiveCommand}};
|
use self::{
|
||||||
|
callback_data::{CheckArchiveStatus, DownloadQueryData},
|
||||||
|
commands::{DownloadArchiveCommand, StartDownloadCommand},
|
||||||
|
};
|
||||||
|
|
||||||
use super::utils::filter_command::filter_command;
|
use super::utils::filter_command::filter_command;
|
||||||
|
|
||||||
|
|
||||||
fn get_check_keyboard(task_id: String) -> InlineKeyboardMarkup {
|
fn get_check_keyboard(task_id: String) -> InlineKeyboardMarkup {
|
||||||
InlineKeyboardMarkup {
|
InlineKeyboardMarkup {
|
||||||
inline_keyboard: vec![
|
inline_keyboard: vec![vec![InlineKeyboardButton {
|
||||||
vec![InlineKeyboardButton {
|
|
||||||
kind: teloxide::types::InlineKeyboardButtonKind::CallbackData(
|
kind: teloxide::types::InlineKeyboardButtonKind::CallbackData(
|
||||||
(CheckArchiveStatus { task_id }).to_string(),
|
(CheckArchiveStatus { task_id }).to_string(),
|
||||||
),
|
),
|
||||||
text: String::from("Обновить статус"),
|
text: String::from("Обновить статус"),
|
||||||
}],
|
}]],
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn _send_cached(
|
async fn _send_cached(
|
||||||
message: &Message,
|
message: &Message,
|
||||||
bot: &CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
@@ -94,8 +97,7 @@ async fn send_cached_message(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
send_with_download_from_channel(message, bot, download_data, need_delete_message)
|
send_with_download_from_channel(message, bot, download_data, need_delete_message).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -137,7 +139,10 @@ async fn send_with_download_from_channel(
|
|||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
match download_file(&download_data).await {
|
match download_file(&download_data).await {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
if _send_downloaded_file(&message, bot.clone(), v).await.is_err() {
|
if _send_downloaded_file(&message, bot.clone(), v)
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
send_download_link(message.clone(), bot.clone(), download_data).await?;
|
send_download_link(message.clone(), bot.clone(), download_data).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@@ -147,7 +152,7 @@ async fn send_with_download_from_channel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,18 +167,17 @@ async fn send_download_link(
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.edit_message_text(
|
||||||
.edit_message_text(
|
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.id,
|
message.id,
|
||||||
format!(
|
format!(
|
||||||
"Файл не может быть загружен в чат! \n \
|
"Файл не может быть загружен в чат! \n \
|
||||||
Вы можете скачать его <a href=\"{}\">по ссылке</a> (работает 3 часа)",
|
Вы можете скачать его <a href=\"{}\">по ссылке</a> (работает 3 часа)",
|
||||||
link_data.link
|
link_data.link
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_markup(InlineKeyboardMarkup {
|
.reply_markup(InlineKeyboardMarkup {
|
||||||
@@ -193,22 +197,10 @@ async fn download_handler(
|
|||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
match cache {
|
match cache {
|
||||||
BotCache::Original => {
|
BotCache::Original => {
|
||||||
send_cached_message(
|
send_cached_message(message, bot, download_data, need_delete_message).await
|
||||||
message,
|
|
||||||
bot,
|
|
||||||
download_data,
|
|
||||||
need_delete_message,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
BotCache::NoCache => {
|
BotCache::NoCache => {
|
||||||
send_with_download_from_channel(
|
send_with_download_from_channel(message, bot, download_data, need_delete_message).await
|
||||||
message,
|
|
||||||
bot,
|
|
||||||
download_data,
|
|
||||||
need_delete_message,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,9 +227,7 @@ async fn get_download_keyboard_handler(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| -> Vec<InlineKeyboardButton> {
|
.map(|item| -> Vec<InlineKeyboardButton> {
|
||||||
vec![InlineKeyboardButton {
|
vec![InlineKeyboardButton {
|
||||||
text: {
|
text: { format!("📥 {item}") },
|
||||||
format!("📥 {item}")
|
|
||||||
},
|
|
||||||
kind: InlineKeyboardButtonKind::CallbackData(
|
kind: InlineKeyboardButtonKind::CallbackData(
|
||||||
(DownloadQueryData::DownloadData {
|
(DownloadQueryData::DownloadData {
|
||||||
book_id: book.id,
|
book_id: book.id,
|
||||||
@@ -264,14 +254,18 @@ async fn get_download_archive_keyboard_handler(
|
|||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
command: DownloadArchiveCommand,
|
command: DownloadArchiveCommand,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let allowed_langs = get_user_or_default_lang_codes(
|
let allowed_langs = get_user_or_default_lang_codes(message.from().unwrap().id).await;
|
||||||
message.from().unwrap().id,
|
|
||||||
).await;
|
|
||||||
|
|
||||||
let available_types = match command {
|
let available_types = match command {
|
||||||
DownloadArchiveCommand::Sequence { id } => get_sequence_books_available_types(id, allowed_langs).await,
|
DownloadArchiveCommand::Sequence { id } => {
|
||||||
DownloadArchiveCommand::Author { id } => get_author_books_available_types(id, allowed_langs).await,
|
get_sequence_books_available_types(id, allowed_langs).await
|
||||||
DownloadArchiveCommand::Translator { id } => get_translator_books_available_types(id, allowed_langs).await,
|
}
|
||||||
|
DownloadArchiveCommand::Author { id } => {
|
||||||
|
get_author_books_available_types(id, allowed_langs).await
|
||||||
|
}
|
||||||
|
DownloadArchiveCommand::Translator { id } => {
|
||||||
|
get_translator_books_available_types(id, allowed_langs).await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let available_types = match available_types {
|
let available_types = match available_types {
|
||||||
@@ -280,31 +274,39 @@ async fn get_download_archive_keyboard_handler(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let keyboard = InlineKeyboardMarkup {
|
let keyboard = InlineKeyboardMarkup {
|
||||||
inline_keyboard:
|
inline_keyboard: available_types
|
||||||
available_types.iter()
|
.iter()
|
||||||
.filter(|file_type| !file_type.contains("zip"))
|
.filter(|file_type| !file_type.contains("zip"))
|
||||||
.map(|file_type| {
|
.map(|file_type| {
|
||||||
let callback_data: String = match command {
|
let callback_data: String = match command {
|
||||||
DownloadArchiveCommand::Sequence { id } => DownloadArchiveQueryData::Sequence {
|
DownloadArchiveCommand::Sequence { id } => DownloadArchiveQueryData::Sequence {
|
||||||
id, file_type: file_type.to_string()
|
id,
|
||||||
}.to_string(),
|
file_type: file_type.to_string(),
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
DownloadArchiveCommand::Author { id } => DownloadArchiveQueryData::Author {
|
DownloadArchiveCommand::Author { id } => DownloadArchiveQueryData::Author {
|
||||||
id, file_type: file_type.to_string()
|
id,
|
||||||
}.to_string(),
|
file_type: file_type.to_string(),
|
||||||
DownloadArchiveCommand::Translator { id } => DownloadArchiveQueryData::Translator {
|
}
|
||||||
id, file_type: file_type.to_string()
|
.to_string(),
|
||||||
}.to_string(),
|
DownloadArchiveCommand::Translator { id } => {
|
||||||
|
DownloadArchiveQueryData::Translator {
|
||||||
|
id,
|
||||||
|
file_type: file_type.to_string(),
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![InlineKeyboardButton {
|
vec![InlineKeyboardButton {
|
||||||
text: file_type.to_string(),
|
text: file_type.to_string(),
|
||||||
kind: InlineKeyboardButtonKind::CallbackData(callback_data)
|
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
|
||||||
}]
|
}]
|
||||||
}).collect()
|
})
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, "Выбери формат:")
|
||||||
.send_message(message.chat.id, "Выбери формат:")
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.reply_to_message_id(message.id)
|
.reply_to_message_id(message.id)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -327,15 +329,14 @@ async fn send_archive_link(
|
|||||||
message: Message,
|
message: Message,
|
||||||
task: Task,
|
task: Task,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
bot
|
bot.edit_message_text(
|
||||||
.edit_message_text(
|
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.id,
|
message.id,
|
||||||
format!(
|
format!(
|
||||||
"Файл не может быть загружен в чат! \n \
|
"Файл не может быть загружен в чат! \n \
|
||||||
Вы можете скачать его <a href=\"{}\">по ссылке</a> (работает 3 часа)",
|
Вы можете скачать его <a href=\"{}\">по ссылке</a> (работает 3 часа)",
|
||||||
task.result_link.unwrap()
|
task.result_link.unwrap()
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_markup(InlineKeyboardMarkup {
|
.reply_markup(InlineKeyboardMarkup {
|
||||||
@@ -362,7 +363,7 @@ async fn wait_archive(
|
|||||||
send_error_message(bot, message.chat.id, message.id).await;
|
send_error_message(bot, message.chat.id, message.id).await;
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if task.status != TaskStatus::InProgress {
|
if task.status != TaskStatus::InProgress {
|
||||||
@@ -371,14 +372,13 @@ async fn wait_archive(
|
|||||||
|
|
||||||
let now = Utc::now().format("%H:%M:%S UTC").to_string();
|
let now = Utc::now().format("%H:%M:%S UTC").to_string();
|
||||||
|
|
||||||
bot
|
bot.edit_message_text(
|
||||||
.edit_message_text(
|
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.id,
|
message.id,
|
||||||
format!(
|
format!(
|
||||||
"Статус: \n ⏳ {} \n\nОбновлено в {now}",
|
"Статус: \n ⏳ {} \n\nОбновлено в {now}",
|
||||||
task.status_description
|
task.status_description
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.reply_markup(get_check_keyboard(task.id))
|
.reply_markup(get_check_keyboard(task.id))
|
||||||
.send()
|
.send()
|
||||||
@@ -394,53 +394,52 @@ async fn wait_archive(
|
|||||||
|
|
||||||
if content_size > 20 * 1024 * 1024 {
|
if content_size > 20 * 1024 * 1024 {
|
||||||
send_archive_link(bot.clone(), message.clone(), task.clone()).await?;
|
send_archive_link(bot.clone(), message.clone(), task.clone()).await?;
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloaded_data = match download_file_by_link(
|
let downloaded_data = match download_file_by_link(
|
||||||
task.clone().result_filename.unwrap(),
|
task.clone().result_filename.unwrap(),
|
||||||
task.result_link.clone().unwrap()
|
task.result_link.clone().unwrap(),
|
||||||
).await {
|
)
|
||||||
|
.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;
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match _send_downloaded_file(
|
match _send_downloaded_file(&message, bot.clone(), downloaded_data).await {
|
||||||
&message,
|
|
||||||
bot.clone(),
|
|
||||||
downloaded_data,
|
|
||||||
).await {
|
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
send_archive_link(bot.clone(), message.clone(), task).await?;
|
send_archive_link(bot.clone(), message.clone(), task).await?;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bot
|
bot.delete_message(message.chat.id, message.id).await?;
|
||||||
.delete_message(message.chat.id, message.id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn download_archive(
|
async fn download_archive(
|
||||||
cq: CallbackQuery,
|
cq: CallbackQuery,
|
||||||
download_archive_query_data: DownloadArchiveQueryData,
|
download_archive_query_data: DownloadArchiveQueryData,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
let allowed_langs = get_user_or_default_lang_codes(
|
let allowed_langs = get_user_or_default_lang_codes(cq.from.id).await;
|
||||||
cq.from.id,
|
|
||||||
).await;
|
|
||||||
|
|
||||||
let (id, file_type, task_type) = match download_archive_query_data {
|
let (id, file_type, task_type) = match download_archive_query_data {
|
||||||
DownloadArchiveQueryData::Sequence { id, file_type } => (id, file_type, TaskObjectType::Sequence),
|
DownloadArchiveQueryData::Sequence { id, file_type } => {
|
||||||
DownloadArchiveQueryData::Author { id, file_type } => (id, file_type, TaskObjectType::Author),
|
(id, file_type, TaskObjectType::Sequence)
|
||||||
DownloadArchiveQueryData::Translator { id, file_type } => (id, file_type, TaskObjectType::Translator),
|
}
|
||||||
|
DownloadArchiveQueryData::Author { id, file_type } => {
|
||||||
|
(id, file_type, TaskObjectType::Author)
|
||||||
|
}
|
||||||
|
DownloadArchiveQueryData::Translator { id, file_type } => {
|
||||||
|
(id, file_type, TaskObjectType::Translator)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = cq.message.unwrap();
|
let message = cq.message.unwrap();
|
||||||
@@ -450,7 +449,8 @@ async fn download_archive(
|
|||||||
object_type: task_type,
|
object_type: task_type,
|
||||||
file_format: file_type,
|
file_format: file_type,
|
||||||
allowed_langs,
|
allowed_langs,
|
||||||
}).await;
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
let task = match task {
|
let task = match task {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@@ -458,11 +458,10 @@ async fn download_archive(
|
|||||||
send_error_message(bot, message.chat.id, message.id).await;
|
send_error_message(bot, message.chat.id, message.id).await;
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.edit_message_text(message.chat.id, message.id, "⏳ Подготовка архива...")
|
||||||
.edit_message_text(message.chat.id, message.id, "⏳ Подготовка архива...")
|
|
||||||
.reply_markup(get_check_keyboard(task.id.clone()))
|
.reply_markup(get_check_keyboard(task.id.clone()))
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::utils::command::BotCommands;
|
use teloxide::utils::command::BotCommands;
|
||||||
|
|
||||||
|
|
||||||
#[derive(BotCommands, Clone)]
|
#[derive(BotCommands, Clone)]
|
||||||
#[command(rename_rule = "lowercase")]
|
#[command(rename_rule = "lowercase")]
|
||||||
pub enum HelpCommand {
|
pub enum HelpCommand {
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ pub mod commands;
|
|||||||
|
|
||||||
use crate::bots::BotHandlerInternal;
|
use crate::bots::BotHandlerInternal;
|
||||||
|
|
||||||
use teloxide::{prelude::*, types::ParseMode, adaptors::{Throttle, CacheMe}};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
prelude::*,
|
||||||
|
types::ParseMode,
|
||||||
|
};
|
||||||
|
|
||||||
use self::commands::HelpCommand;
|
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()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use strum_macros::{EnumIter, Display};
|
use strum_macros::{Display, EnumIter};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Display, EnumIter)]
|
#[derive(Clone, Display, EnumIter)]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
|||||||
@@ -1,29 +1,32 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use smartstring::alias::String as SmartString;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use smartstring::alias::String as SmartString;
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{InlineKeyboardButton, InlineKeyboardMarkup},
|
types::{InlineKeyboardButton, InlineKeyboardMarkup},
|
||||||
adaptors::{Throttle, CacheMe},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::bots::{
|
use crate::bots::{
|
||||||
approved_bot::{
|
approved_bot::{
|
||||||
|
modules::random::callback_data::RandomCallbackData,
|
||||||
services::{
|
services::{
|
||||||
book_library::{self, formatters::Format},
|
book_library::{self, formatters::Format},
|
||||||
user_settings::get_user_or_default_lang_codes,
|
user_settings::get_user_or_default_lang_codes,
|
||||||
},
|
},
|
||||||
tools::filter_callback_query, modules::random::callback_data::RandomCallbackData,
|
tools::filter_callback_query,
|
||||||
},
|
},
|
||||||
BotHandlerInternal,
|
BotHandlerInternal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::commands::RandomCommand;
|
use self::commands::RandomCommand;
|
||||||
|
|
||||||
|
async fn random_handler(
|
||||||
async fn random_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> crate::bots::BotHandlerInternal {
|
message: Message,
|
||||||
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
) -> crate::bots::BotHandlerInternal {
|
||||||
const MESSAGE_TEXT: &str = "Что хотим получить?";
|
const MESSAGE_TEXT: &str = "Что хотим получить?";
|
||||||
|
|
||||||
let keyboard = InlineKeyboardMarkup {
|
let keyboard = InlineKeyboardMarkup {
|
||||||
@@ -55,8 +58,7 @@ async fn random_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> crate:
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, MESSAGE_TEXT)
|
||||||
.send_message(message.chat.id, MESSAGE_TEXT)
|
|
||||||
.reply_to_message_id(message.id)
|
.reply_to_message_id(message.id)
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
@@ -76,12 +78,11 @@ where
|
|||||||
let item = match item {
|
let item = match item {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot
|
bot.send_message(cq.from.id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(cq.from.id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
return Err(err);
|
return Err(err);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let item_message = item.format(4096).result;
|
let item_message = item.format(4096).result;
|
||||||
@@ -89,9 +90,7 @@ where
|
|||||||
bot.send_message(cq.from.id, item_message)
|
bot.send_message(cq.from.id, item_message)
|
||||||
.reply_markup(InlineKeyboardMarkup {
|
.reply_markup(InlineKeyboardMarkup {
|
||||||
inline_keyboard: vec![vec![InlineKeyboardButton {
|
inline_keyboard: vec![vec![InlineKeyboardButton {
|
||||||
kind: teloxide::types::InlineKeyboardButtonKind::CallbackData(
|
kind: teloxide::types::InlineKeyboardButtonKind::CallbackData(cq.data.unwrap()),
|
||||||
cq.data.unwrap(),
|
|
||||||
),
|
|
||||||
text: String::from("Повторить?"),
|
text: String::from("Повторить?"),
|
||||||
}]],
|
}]],
|
||||||
})
|
})
|
||||||
@@ -107,7 +106,7 @@ where
|
|||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,18 +127,20 @@ where
|
|||||||
get_random_item_handler_internal(cq, bot, item).await
|
get_random_item_handler_internal(cq, bot, item).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_genre_metas_handler(cq: CallbackQuery, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
async fn get_genre_metas_handler(
|
||||||
|
cq: CallbackQuery,
|
||||||
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
) -> BotHandlerInternal {
|
||||||
let genre_metas = book_library::get_genre_metas().await?;
|
let genre_metas = book_library::get_genre_metas().await?;
|
||||||
|
|
||||||
let message = match cq.message {
|
let message = match cq.message {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
bot
|
bot.send_message(cq.from.id, "Ошибка! Начните заново :(")
|
||||||
.send_message(cq.from.id, "Ошибка! Начните заново :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let keyboard = InlineKeyboardMarkup {
|
let keyboard = InlineKeyboardMarkup {
|
||||||
@@ -160,8 +161,7 @@ async fn get_genre_metas_handler(cq: CallbackQuery, bot: CacheMe<Throttle<Bot>>)
|
|||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.edit_message_reply_markup(message.chat.id, message.id)
|
||||||
.edit_message_reply_markup(message.chat.id, message.id)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -179,8 +179,7 @@ async fn get_genres_by_meta_handler(
|
|||||||
let meta = match genre_metas.get(genre_index as usize) {
|
let meta = match genre_metas.get(genre_index as usize) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
bot
|
bot.send_message(cq.from.id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(cq.from.id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -188,7 +187,8 @@ async fn get_genres_by_meta_handler(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buttons: Vec<Vec<InlineKeyboardButton>> = book_library::get_genres(meta.into()).await?
|
let mut buttons: Vec<Vec<InlineKeyboardButton>> = book_library::get_genres(meta.into())
|
||||||
|
.await?
|
||||||
.items
|
.items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|genre| {
|
.map(|genre| {
|
||||||
@@ -217,8 +217,7 @@ async fn get_genres_by_meta_handler(
|
|||||||
let message = match cq.message {
|
let message = match cq.message {
|
||||||
Some(message) => message,
|
Some(message) => message,
|
||||||
None => {
|
None => {
|
||||||
bot
|
bot.send_message(cq.from.id, "Ошибка! Начните заново :(")
|
||||||
.send_message(cq.from.id, "Ошибка! Начните заново :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -226,8 +225,7 @@ async fn get_genres_by_meta_handler(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.edit_message_reply_markup(message.chat.id, message.id)
|
||||||
.edit_message_reply_markup(message.chat.id, message.id)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -249,30 +247,46 @@ async fn get_random_book_by_genre(
|
|||||||
|
|
||||||
pub fn get_random_handler() -> crate::bots::BotHandler {
|
pub fn get_random_handler() -> crate::bots::BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
.branch(
|
.branch(Update::filter_message().branch(
|
||||||
Update::filter_message()
|
dptree::entry().filter_command::<RandomCommand>().endpoint(
|
||||||
.branch(
|
|message, command, bot| async {
|
||||||
dptree::entry()
|
|
||||||
.filter_command::<RandomCommand>()
|
|
||||||
.endpoint(|message, command, bot| async {
|
|
||||||
match command {
|
match command {
|
||||||
RandomCommand::Random => random_handler(message, bot).await,
|
RandomCommand::Random => random_handler(message, bot).await,
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
)
|
),
|
||||||
)
|
))
|
||||||
.branch(
|
.branch(
|
||||||
Update::filter_callback_query()
|
Update::filter_callback_query()
|
||||||
.chain(filter_callback_query::<RandomCallbackData>())
|
.chain(filter_callback_query::<RandomCallbackData>())
|
||||||
.endpoint(|cq: CallbackQuery, callback_data: RandomCallbackData, bot: CacheMe<Throttle<Bot>>| async move {
|
.endpoint(
|
||||||
|
|cq: CallbackQuery,
|
||||||
|
callback_data: RandomCallbackData,
|
||||||
|
bot: CacheMe<Throttle<Bot>>| async move {
|
||||||
match callback_data {
|
match callback_data {
|
||||||
RandomCallbackData::RandomBook => get_random_item_handler(cq, bot, book_library::get_random_book).await,
|
RandomCallbackData::RandomBook => {
|
||||||
RandomCallbackData::RandomAuthor => get_random_item_handler(cq, bot, book_library::get_random_author).await,
|
get_random_item_handler(cq, bot, book_library::get_random_book)
|
||||||
RandomCallbackData::RandomSequence => get_random_item_handler(cq, bot, book_library::get_random_sequence).await,
|
.await
|
||||||
RandomCallbackData::RandomBookByGenreRequest => get_genre_metas_handler(cq, bot).await,
|
|
||||||
RandomCallbackData::Genres { index } => get_genres_by_meta_handler(cq, bot, index).await,
|
|
||||||
RandomCallbackData::RandomBookByGenre { id } => get_random_book_by_genre(cq, bot, id).await,
|
|
||||||
}
|
}
|
||||||
})
|
RandomCallbackData::RandomAuthor => {
|
||||||
|
get_random_item_handler(cq, bot, book_library::get_random_author)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
RandomCallbackData::RandomSequence => {
|
||||||
|
get_random_item_handler(cq, bot, book_library::get_random_sequence)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
RandomCallbackData::RandomBookByGenreRequest => {
|
||||||
|
get_genre_metas_handler(cq, bot).await
|
||||||
|
}
|
||||||
|
RandomCallbackData::Genres { index } => {
|
||||||
|
get_genres_by_meta_handler(cq, bot, index).await
|
||||||
|
}
|
||||||
|
RandomCallbackData::RandomBookByGenre { id } => {
|
||||||
|
get_random_book_by_genre(cq, bot, id).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use strum_macros::EnumIter;
|
|||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::pagination::GetPaginationCallbackData;
|
use crate::bots::approved_bot::modules::utils::pagination::GetPaginationCallbackData;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, EnumIter)]
|
#[derive(Clone, EnumIter)]
|
||||||
pub enum SearchCallbackData {
|
pub enum SearchCallbackData {
|
||||||
Book { page: u32 },
|
Book { page: u32 },
|
||||||
@@ -56,12 +55,8 @@ impl FromStr for SearchCallbackData {
|
|||||||
impl GetPaginationCallbackData for SearchCallbackData {
|
impl GetPaginationCallbackData for SearchCallbackData {
|
||||||
fn get_pagination_callback_data(&self, target_page: u32) -> String {
|
fn get_pagination_callback_data(&self, target_page: u32) -> String {
|
||||||
match self {
|
match self {
|
||||||
SearchCallbackData::Book { .. } => {
|
SearchCallbackData::Book { .. } => SearchCallbackData::Book { page: target_page },
|
||||||
SearchCallbackData::Book { page: target_page }
|
SearchCallbackData::Authors { .. } => SearchCallbackData::Authors { page: target_page },
|
||||||
}
|
|
||||||
SearchCallbackData::Authors { .. } => {
|
|
||||||
SearchCallbackData::Authors { page: target_page }
|
|
||||||
}
|
|
||||||
SearchCallbackData::Sequences { .. } => {
|
SearchCallbackData::Sequences { .. } => {
|
||||||
SearchCallbackData::Sequences { page: target_page }
|
SearchCallbackData::Sequences { page: target_page }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,18 @@ use smartstring::alias::String as SmartString;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
dispatching::dialogue::GetChatId,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{InlineKeyboardButton, InlineKeyboardMarkup}, dispatching::dialogue::GetChatId, adaptors::{Throttle, CacheMe},
|
types::{InlineKeyboardButton, InlineKeyboardMarkup},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::bots::{
|
use crate::bots::{
|
||||||
approved_bot::{
|
approved_bot::{
|
||||||
services::{
|
services::{
|
||||||
book_library::{
|
book_library::{
|
||||||
formatters::{Format, FormatTitle}, search_author, search_book, search_sequence, search_translator,
|
formatters::{Format, FormatTitle},
|
||||||
|
search_author, search_book, search_sequence, search_translator,
|
||||||
types::Page,
|
types::Page,
|
||||||
},
|
},
|
||||||
user_settings::get_user_or_default_lang_codes,
|
user_settings::get_user_or_default_lang_codes,
|
||||||
@@ -28,7 +31,6 @@ use self::{callback_data::SearchCallbackData, utils::get_query};
|
|||||||
|
|
||||||
use super::utils::pagination::generic_get_pagination_keyboard;
|
use super::utils::pagination::generic_get_pagination_keyboard;
|
||||||
|
|
||||||
|
|
||||||
async fn generic_search_pagination_handler<T, P, Fut>(
|
async fn generic_search_pagination_handler<T, P, Fut>(
|
||||||
cq: CallbackQuery,
|
cq: CallbackQuery,
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
@@ -46,11 +48,11 @@ where
|
|||||||
let query = get_query(cq);
|
let query = get_query(cq);
|
||||||
|
|
||||||
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)) => {
|
(Some(chat_id), Some(query), Some(message_id)) => (chat_id, query, message_id),
|
||||||
(chat_id, query, message_id)
|
|
||||||
}
|
|
||||||
(Some(chat_id), _, _) => {
|
(Some(chat_id), _, _) => {
|
||||||
bot.send_message(chat_id, "Повторите поиск сначала").send().await?;
|
bot.send_message(chat_id, "Повторите поиск сначала")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -70,8 +72,7 @@ where
|
|||||||
let mut items_page = match items_getter(query.clone(), page, allowed_langs.clone()).await {
|
let mut items_page = match items_getter(query.clone(), page, allowed_langs.clone()).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -92,17 +93,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if page > items_page.pages {
|
if page > items_page.pages {
|
||||||
items_page = match items_getter(
|
items_page =
|
||||||
query.clone(),
|
match items_getter(query.clone(), items_page.pages, allowed_langs.clone()).await {
|
||||||
items_page.pages,
|
|
||||||
allowed_langs.clone(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot
|
bot.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
||||||
.send_message(chat_id, "Ошибка! Попробуйте позже :(")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -115,8 +110,7 @@ where
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
bot
|
bot.edit_message_text(chat_id, message_id, formated_page)
|
||||||
.edit_message_text(chat_id, message_id, formated_page)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -156,8 +150,7 @@ pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> B
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, message_text)
|
||||||
.send_message(message.chat.id, message_text)
|
|
||||||
.reply_to_message_id(message.id)
|
.reply_to_message_id(message.id)
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
@@ -167,19 +160,57 @@ pub async fn message_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> B
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_search_handler() -> crate::bots::BotHandler {
|
pub fn get_search_handler() -> crate::bots::BotHandler {
|
||||||
dptree::entry().branch(
|
dptree::entry()
|
||||||
|
.branch(
|
||||||
Update::filter_message()
|
Update::filter_message()
|
||||||
.endpoint(|message, bot| async move { message_handler(message, bot).await }),
|
.endpoint(|message, bot| async move { message_handler(message, bot).await }),
|
||||||
).branch(
|
)
|
||||||
|
.branch(
|
||||||
Update::filter_callback_query()
|
Update::filter_callback_query()
|
||||||
.chain(filter_callback_query::<SearchCallbackData>())
|
.chain(filter_callback_query::<SearchCallbackData>())
|
||||||
.endpoint(|cq: CallbackQuery, callback_data: SearchCallbackData, bot: CacheMe<Throttle<Bot>>| async move {
|
.endpoint(
|
||||||
|
|cq: CallbackQuery,
|
||||||
|
callback_data: SearchCallbackData,
|
||||||
|
bot: CacheMe<Throttle<Bot>>| async move {
|
||||||
match callback_data {
|
match callback_data {
|
||||||
SearchCallbackData::Book { .. } => generic_search_pagination_handler(cq, bot, callback_data, search_book).await,
|
SearchCallbackData::Book { .. } => {
|
||||||
SearchCallbackData::Authors { .. } => generic_search_pagination_handler(cq, bot, callback_data, search_author).await,
|
generic_search_pagination_handler(
|
||||||
SearchCallbackData::Sequences { .. } => generic_search_pagination_handler(cq, bot, callback_data, search_sequence).await,
|
cq,
|
||||||
SearchCallbackData::Translators { .. } => generic_search_pagination_handler(cq, bot, callback_data, search_translator).await,
|
bot,
|
||||||
|
callback_data,
|
||||||
|
search_book,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
})
|
SearchCallbackData::Authors { .. } => {
|
||||||
|
generic_search_pagination_handler(
|
||||||
|
cq,
|
||||||
|
bot,
|
||||||
|
callback_data,
|
||||||
|
search_author,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
SearchCallbackData::Sequences { .. } => {
|
||||||
|
generic_search_pagination_handler(
|
||||||
|
cq,
|
||||||
|
bot,
|
||||||
|
callback_data,
|
||||||
|
search_sequence,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
SearchCallbackData::Translators { .. } => {
|
||||||
|
generic_search_pagination_handler(
|
||||||
|
cq,
|
||||||
|
bot,
|
||||||
|
callback_data,
|
||||||
|
search_translator,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::types::CallbackQuery;
|
use teloxide::types::CallbackQuery;
|
||||||
|
|
||||||
|
|
||||||
pub fn get_query(cq: CallbackQuery) -> Option<String> {
|
pub fn get_query(cq: CallbackQuery) -> Option<String> {
|
||||||
cq.message
|
cq.message
|
||||||
.map(|message| {
|
.map(|message| {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use std::str::FromStr;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use smartstring::alias::String as SmartString;
|
use smartstring::alias::String as SmartString;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum SettingsCallbackData {
|
pub enum SettingsCallbackData {
|
||||||
Settings,
|
Settings,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::macros::BotCommands;
|
use teloxide::macros::BotCommands;
|
||||||
|
|
||||||
|
|
||||||
#[derive(BotCommands, Clone)]
|
#[derive(BotCommands, Clone)]
|
||||||
#[command(rename_rule = "lowercase")]
|
#[command(rename_rule = "lowercase")]
|
||||||
pub enum SettingsCommand {
|
pub enum SettingsCommand {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
@@ -16,12 +16,12 @@ use crate::bots::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{InlineKeyboardButton, InlineKeyboardMarkup, Me}, adaptors::{Throttle, CacheMe},
|
types::{InlineKeyboardButton, InlineKeyboardMarkup, Me},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{commands::SettingsCommand, callback_data::SettingsCallbackData};
|
use self::{callback_data::SettingsCallbackData, commands::SettingsCommand};
|
||||||
|
|
||||||
|
|
||||||
async fn settings_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
async fn settings_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
||||||
let keyboard = InlineKeyboardMarkup {
|
let keyboard = InlineKeyboardMarkup {
|
||||||
@@ -33,8 +33,7 @@ async fn settings_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotH
|
|||||||
}]],
|
}]],
|
||||||
};
|
};
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, "Настройки")
|
||||||
.send_message(message.chat.id, "Настройки")
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -42,7 +41,10 @@ async fn settings_handler(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotH
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lang_keyboard(all_langs: Vec<Lang>, allowed_langs: HashSet<SmartString>) -> InlineKeyboardMarkup {
|
fn get_lang_keyboard(
|
||||||
|
all_langs: Vec<Lang>,
|
||||||
|
allowed_langs: HashSet<SmartString>,
|
||||||
|
) -> InlineKeyboardMarkup {
|
||||||
let buttons = all_langs
|
let buttons = all_langs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|lang| {
|
.map(|lang| {
|
||||||
@@ -78,9 +80,11 @@ async fn settings_callback_handler(
|
|||||||
let message = match cq.message {
|
let message = match cq.message {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(").send().await?;
|
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = cq.from;
|
let user = cq.from;
|
||||||
@@ -103,14 +107,13 @@ async fn settings_callback_handler(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if allowed_langs_set.is_empty() {
|
if allowed_langs_set.is_empty() {
|
||||||
bot
|
bot.answer_callback_query(cq.id)
|
||||||
.answer_callback_query(cq.id)
|
|
||||||
.text("Должен быть активен, хотя бы один язык!")
|
.text("Должен быть активен, хотя бы один язык!")
|
||||||
.show_alert(true)
|
.show_alert(true)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = create_or_update_user_settings(
|
if let Err(err) = create_or_update_user_settings(
|
||||||
@@ -121,23 +124,27 @@ async fn settings_callback_handler(
|
|||||||
me.username.clone().unwrap(),
|
me.username.clone().unwrap(),
|
||||||
allowed_langs_set.clone().into_iter().collect(),
|
allowed_langs_set.clone().into_iter().collect(),
|
||||||
)
|
)
|
||||||
.await {
|
.await
|
||||||
bot.send_message(message.chat.id, "Ошибка! Попробуйте заново(").send().await?;
|
{
|
||||||
|
bot.send_message(message.chat.id, "Ошибка! Попробуйте заново(")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let all_langs = match get_langs().await {
|
let all_langs = match get_langs().await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bot.send_message(message.chat.id, "Ошибка! Попробуйте заново(").send().await?;
|
bot.send_message(message.chat.id, "Ошибка! Попробуйте заново(")
|
||||||
return Err(err)
|
.send()
|
||||||
},
|
.await?;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let keyboard = get_lang_keyboard(all_langs, allowed_langs_set);
|
let keyboard = get_lang_keyboard(all_langs, allowed_langs_set);
|
||||||
|
|
||||||
bot
|
bot.edit_message_reply_markup(message.chat.id, message.id)
|
||||||
.edit_message_reply_markup(message.chat.id, message.id)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1,30 +1,37 @@
|
|||||||
use crate::bots::BotHandlerInternal;
|
use crate::bots::BotHandlerInternal;
|
||||||
|
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
utils::command::BotCommands, adaptors::{Throttle, CacheMe},
|
utils::command::BotCommands,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(BotCommands, Clone)]
|
#[derive(BotCommands, Clone)]
|
||||||
#[command(rename_rule = "lowercase")]
|
#[command(rename_rule = "lowercase")]
|
||||||
enum SupportCommand {
|
enum SupportCommand {
|
||||||
Support,
|
Support,
|
||||||
Donate
|
Donate,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn support_command_handler(
|
pub async fn support_command_handler(
|
||||||
message: Message,
|
message: Message,
|
||||||
bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
) -> BotHandlerInternal {
|
||||||
let is_bot = message.from().unwrap().is_bot;
|
let is_bot = message.from().unwrap().is_bot;
|
||||||
|
|
||||||
let username = if is_bot {
|
let username = if is_bot {
|
||||||
&message.reply_to_message().unwrap().from().unwrap().first_name
|
&message
|
||||||
|
.reply_to_message()
|
||||||
|
.unwrap()
|
||||||
|
.from()
|
||||||
|
.unwrap()
|
||||||
|
.first_name
|
||||||
} else {
|
} else {
|
||||||
&message.from().unwrap().first_name
|
&message.from().unwrap().first_name
|
||||||
};
|
};
|
||||||
|
|
||||||
let message_text = format!("
|
let message_text = format!(
|
||||||
|
"
|
||||||
Привет, {username}!
|
Привет, {username}!
|
||||||
|
|
||||||
Этот бот существует благодаря пожертвованиям от наших пользователей.
|
Этот бот существует благодаря пожертвованиям от наших пользователей.
|
||||||
@@ -50,10 +57,10 @@ Bitcoin - BTC:
|
|||||||
|
|
||||||
The Open Network - TON:
|
The Open Network - TON:
|
||||||
<pre>UQA4MySrq_60b_VMlR6UEmc_0u-neAUTXdtv8oKr_i6uhQNd</pre>
|
<pre>UQA4MySrq_60b_VMlR6UEmc_0u-neAUTXdtv8oKr_i6uhQNd</pre>
|
||||||
");
|
"
|
||||||
|
);
|
||||||
|
|
||||||
bot
|
bot.send_message(message.chat.id, message_text)
|
||||||
.send_message(message.chat.id, message_text)
|
|
||||||
.parse_mode(teloxide::types::ParseMode::Html)
|
.parse_mode(teloxide::types::ParseMode::Html)
|
||||||
.disable_web_page_preview(true)
|
.disable_web_page_preview(true)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -64,7 +71,9 @@ The Open Network - TON:
|
|||||||
pub fn get_support_handler() -> crate::bots::BotHandler {
|
pub fn get_support_handler() -> crate::bots::BotHandler {
|
||||||
dptree::entry().branch(
|
dptree::entry().branch(
|
||||||
Update::filter_message().branch(
|
Update::filter_message().branch(
|
||||||
dptree::entry().filter_command::<SupportCommand>().endpoint(support_command_handler),
|
dptree::entry()
|
||||||
|
.filter_command::<SupportCommand>()
|
||||||
|
.endpoint(support_command_handler),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use regex::Regex;
|
|||||||
|
|
||||||
use crate::bots::approved_bot::modules::utils::pagination::GetPaginationCallbackData;
|
use crate::bots::approved_bot::modules::utils::pagination::GetPaginationCallbackData;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct UpdateLogCallbackData {
|
pub struct UpdateLogCallbackData {
|
||||||
pub from: NaiveDate,
|
pub from: NaiveDate,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::macros::BotCommands;
|
use teloxide::macros::BotCommands;
|
||||||
|
|
||||||
|
|
||||||
#[derive(BotCommands, Clone)]
|
#[derive(BotCommands, Clone)]
|
||||||
#[command(rename_rule = "snake_case")]
|
#[command(rename_rule = "snake_case")]
|
||||||
pub enum UpdateLogCommand {
|
pub enum UpdateLogCommand {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod callback_data;
|
pub mod callback_data;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
|
|
||||||
@@ -9,16 +9,15 @@ use crate::bots::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{InlineKeyboardButton, InlineKeyboardMarkup},
|
types::{InlineKeyboardButton, InlineKeyboardMarkup},
|
||||||
adaptors::{Throttle, CacheMe},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{commands::UpdateLogCommand, callback_data::UpdateLogCallbackData};
|
use self::{callback_data::UpdateLogCallbackData, commands::UpdateLogCommand};
|
||||||
|
|
||||||
use super::utils::pagination::generic_get_pagination_keyboard;
|
use super::utils::pagination::generic_get_pagination_keyboard;
|
||||||
|
|
||||||
|
|
||||||
async fn update_log_command(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
async fn update_log_command(message: Message, bot: CacheMe<Throttle<Bot>>) -> BotHandlerInternal {
|
||||||
let now = Utc::now().date_naive();
|
let now = Utc::now().date_naive();
|
||||||
let d3 = now - Duration::days(3);
|
let d3 = now - Duration::days(3);
|
||||||
@@ -82,9 +81,11 @@ async fn update_log_pagination_handler(
|
|||||||
let message = match cq.message {
|
let message = match cq.message {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(").send().await?;
|
bot.send_message(cq.from.id, "Ошибка! Попробуйте заново(")
|
||||||
return Ok(())
|
.send()
|
||||||
},
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let from = update_callback_data.from.format("%d.%m.%Y");
|
let from = update_callback_data.from.format("%d.%m.%Y");
|
||||||
@@ -94,14 +95,21 @@ async fn update_log_pagination_handler(
|
|||||||
|
|
||||||
let mut items_page = get_uploaded_books(
|
let mut items_page = get_uploaded_books(
|
||||||
update_callback_data.page,
|
update_callback_data.page,
|
||||||
update_callback_data.from.format("%Y-%m-%d").to_string().into(),
|
update_callback_data
|
||||||
update_callback_data.to.format("%Y-%m-%d").to_string().into(),
|
.from
|
||||||
|
.format("%Y-%m-%d")
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
update_callback_data
|
||||||
|
.to
|
||||||
|
.format("%Y-%m-%d")
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if items_page.pages == 0 {
|
if items_page.pages == 0 {
|
||||||
bot
|
bot.send_message(message.chat.id, "Нет новых книг за этот период.")
|
||||||
.send_message(message.chat.id, "Нет новых книг за этот период.")
|
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -110,9 +118,18 @@ async fn update_log_pagination_handler(
|
|||||||
if update_callback_data.page > items_page.pages {
|
if update_callback_data.page > items_page.pages {
|
||||||
items_page = get_uploaded_books(
|
items_page = get_uploaded_books(
|
||||||
items_page.pages,
|
items_page.pages,
|
||||||
update_callback_data.from.format("%Y-%m-%d").to_string().into(),
|
update_callback_data
|
||||||
update_callback_data.to.format("%Y-%m-%d").to_string().into(),
|
.from
|
||||||
).await?;
|
.format("%Y-%m-%d")
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
update_callback_data
|
||||||
|
.to
|
||||||
|
.format("%Y-%m-%d")
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let page = update_callback_data.page;
|
let page = update_callback_data.page;
|
||||||
@@ -123,8 +140,7 @@ async fn update_log_pagination_handler(
|
|||||||
let message_text = format!("{header}{formatted_page}");
|
let message_text = format!("{header}{formatted_page}");
|
||||||
|
|
||||||
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
|
bot.edit_message_text(message.chat.id, message.id, message_text)
|
||||||
.edit_message_text(message.chat.id, message.id, message_text)
|
|
||||||
.reply_markup(keyboard)
|
.reply_markup(keyboard)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CallbackQueryParseError;
|
pub struct CallbackQueryParseError;
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ impl fmt::Display for CallbackQueryParseError {
|
|||||||
|
|
||||||
impl std::error::Error for CallbackQueryParseError {}
|
impl std::error::Error for CallbackQueryParseError {}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CommandParseError;
|
pub struct CommandParseError;
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ use teloxide::{dptree, prelude::*, types::*};
|
|||||||
|
|
||||||
use super::errors::CommandParseError;
|
use super::errors::CommandParseError;
|
||||||
|
|
||||||
|
|
||||||
pub trait CommandParse<T> {
|
pub trait CommandParse<T> {
|
||||||
fn parse(s: &str, bot_name: &str) -> Result<T, CommandParseError>;
|
fn parse(s: &str, bot_name: &str) -> Result<T, CommandParseError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn filter_command<Output>() -> crate::bots::BotHandler
|
pub fn filter_command<Output>() -> crate::bots::BotHandler
|
||||||
where
|
where
|
||||||
Output: CommandParse<Output> + Send + Sync + 'static,
|
Output: CommandParse<Output> + Send + Sync + 'static,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
pub mod errors;
|
||||||
|
pub mod filter_command;
|
||||||
pub mod pagination;
|
pub mod pagination;
|
||||||
pub mod split_text;
|
pub mod split_text;
|
||||||
pub mod filter_command;
|
|
||||||
pub mod errors;
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup};
|
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup};
|
||||||
|
|
||||||
|
|
||||||
pub enum PaginationDelta {
|
pub enum PaginationDelta {
|
||||||
OneMinus,
|
OneMinus,
|
||||||
OnePlus,
|
OnePlus,
|
||||||
@@ -12,7 +11,6 @@ pub trait GetPaginationCallbackData {
|
|||||||
fn get_pagination_callback_data(&self, target_page: u32) -> String;
|
fn get_pagination_callback_data(&self, target_page: u32) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn generic_get_pagination_button<T>(
|
pub fn generic_get_pagination_button<T>(
|
||||||
target_page: u32,
|
target_page: u32,
|
||||||
delta: PaginationDelta,
|
delta: PaginationDelta,
|
||||||
@@ -36,7 +34,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn generic_get_pagination_keyboard<T>(
|
pub fn generic_get_pagination_keyboard<T>(
|
||||||
page: u32,
|
page: u32,
|
||||||
total_pages: u32,
|
total_pages: u32,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ pub fn split_text_to_chunks(text: &str, width: usize) -> Vec<String> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::bots::approved_bot::modules::utils::split_text::split_text_to_chunks;
|
use crate::bots::approved_bot::modules::utils::split_text::split_text_to_chunks;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub enum TaskStatus {
|
|||||||
InProgress,
|
InProgress,
|
||||||
Archiving,
|
Archiving,
|
||||||
Complete,
|
Complete,
|
||||||
Failed
|
Failed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -38,7 +38,7 @@ pub struct Task {
|
|||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
pub result_filename: Option<String>,
|
pub result_filename: Option<String>,
|
||||||
pub result_link: Option<String>,
|
pub result_link: Option<String>,
|
||||||
pub content_size: Option<u64>
|
pub content_size: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_task(
|
pub async fn create_task(
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ use base64::{engine::general_purpose, Engine};
|
|||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::{config, bots::approved_bot::modules::download::callback_data::DownloadQueryData};
|
use crate::{bots::approved_bot::modules::download::callback_data::DownloadQueryData, config};
|
||||||
|
|
||||||
use self::types::{CachedMessage, DownloadFile, DownloadLink};
|
use self::types::{CachedMessage, DownloadFile, DownloadLink};
|
||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct DownloadError {
|
struct DownloadError {
|
||||||
status_code: StatusCode,
|
status_code: StatusCode,
|
||||||
@@ -25,7 +24,10 @@ impl std::error::Error for DownloadError {}
|
|||||||
pub async fn get_cached_message(
|
pub async fn get_cached_message(
|
||||||
download_data: &DownloadQueryData,
|
download_data: &DownloadQueryData,
|
||||||
) -> Result<CachedMessage, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<CachedMessage, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let DownloadQueryData::DownloadData { book_id: id, file_type: format } = download_data;
|
let DownloadQueryData::DownloadData {
|
||||||
|
book_id: id,
|
||||||
|
file_type: format,
|
||||||
|
} = download_data;
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let response = client
|
let response = client
|
||||||
@@ -48,9 +50,12 @@ pub async fn get_cached_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_download_link(
|
pub async fn get_download_link(
|
||||||
download_data: &DownloadQueryData
|
download_data: &DownloadQueryData,
|
||||||
) -> Result<DownloadLink, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<DownloadLink, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let DownloadQueryData::DownloadData { book_id: id, file_type: format } = download_data;
|
let DownloadQueryData::DownloadData {
|
||||||
|
book_id: id,
|
||||||
|
file_type: format,
|
||||||
|
} = download_data;
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let response = client
|
let response = client
|
||||||
@@ -75,7 +80,10 @@ pub async fn get_download_link(
|
|||||||
pub async fn download_file(
|
pub async fn download_file(
|
||||||
download_data: &DownloadQueryData,
|
download_data: &DownloadQueryData,
|
||||||
) -> Result<DownloadFile, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<DownloadFile, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let DownloadQueryData::DownloadData { book_id: id, file_type: format } = download_data;
|
let DownloadQueryData::DownloadData {
|
||||||
|
book_id: id,
|
||||||
|
file_type: format,
|
||||||
|
} = download_data;
|
||||||
|
|
||||||
let response = reqwest::Client::new()
|
let response = reqwest::Client::new()
|
||||||
.get(format!(
|
.get(format!(
|
||||||
@@ -120,10 +128,9 @@ pub async fn download_file(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn download_file_by_link(
|
pub async fn download_file_by_link(
|
||||||
filename: String,
|
filename: String,
|
||||||
link: String
|
link: String,
|
||||||
) -> Result<DownloadFile, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<DownloadFile, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let response = reqwest::Client::new()
|
let response = reqwest::Client::new()
|
||||||
.get(link)
|
.get(link)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct CachedMessage {
|
pub struct CachedMessage {
|
||||||
pub message_id: i32,
|
pub message_id: i32,
|
||||||
@@ -15,5 +14,5 @@ pub struct DownloadFile {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct DownloadLink {
|
pub struct DownloadLink {
|
||||||
pub link: String
|
pub link: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use crate::bots::approved_bot::modules::download::commands::{StartDownloadCommand, DownloadArchiveCommand};
|
use crate::bots::approved_bot::modules::download::commands::{
|
||||||
|
DownloadArchiveCommand, StartDownloadCommand,
|
||||||
|
};
|
||||||
|
|
||||||
use super::types::{
|
use super::types::{
|
||||||
Author, AuthorBook, Book, BookAuthor, BookGenre, SearchBook, Sequence, Translator,
|
Author, AuthorBook, Book, BookAuthor, BookGenre, BookTranslator, Empty, SearchBook, Sequence,
|
||||||
TranslatorBook, SequenceBook, BookTranslator, Empty,
|
SequenceBook, Translator, TranslatorBook,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NO_LIMIT: usize = 4096;
|
const NO_LIMIT: usize = 4096;
|
||||||
@@ -45,7 +47,7 @@ impl FormatTitle for BookAuthor {
|
|||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if *id == 0 {
|
if *id == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = (DownloadArchiveCommand::Author { id: *id }).to_string();
|
let command = (DownloadArchiveCommand::Author { id: *id }).to_string();
|
||||||
@@ -64,7 +66,7 @@ impl FormatTitle for BookTranslator {
|
|||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if *id == 0 {
|
if *id == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = (DownloadArchiveCommand::Translator { id: *id }).to_string();
|
let command = (DownloadArchiveCommand::Translator { id: *id }).to_string();
|
||||||
@@ -78,7 +80,7 @@ impl FormatTitle for Sequence {
|
|||||||
let Sequence { id, name } = self;
|
let Sequence { id, name } = self;
|
||||||
|
|
||||||
if *id == 0 {
|
if *id == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = (DownloadArchiveCommand::Sequence { id: *id }).to_string();
|
let command = (DownloadArchiveCommand::Sequence { id: *id }).to_string();
|
||||||
@@ -115,7 +117,7 @@ impl FormatInline for BookTranslator {
|
|||||||
|
|
||||||
fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
|
fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
match !authors.is_empty() {
|
match !authors.is_empty() {
|
||||||
@@ -126,7 +128,11 @@ fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let post_fix = if authors.len() > count { "\nи др." } else { "" };
|
let post_fix = if authors.len() > count {
|
||||||
|
"\nи др."
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
format!("Авторы:\n{formated_authors}{post_fix}\n")
|
format!("Авторы:\n{formated_authors}{post_fix}\n")
|
||||||
}
|
}
|
||||||
false => "".to_string(),
|
false => "".to_string(),
|
||||||
@@ -135,7 +141,7 @@ fn format_authors(authors: Vec<BookAuthor>, count: usize) -> String {
|
|||||||
|
|
||||||
fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String {
|
fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
match !translators.is_empty() {
|
match !translators.is_empty() {
|
||||||
@@ -146,7 +152,11 @@ fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let post_fix = if translators.len() > count { "\nи др." } else { "" };
|
let post_fix = if translators.len() > count {
|
||||||
|
"\nи др."
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
format!("Переводчики:\n{formated_translators}{post_fix}\n")
|
format!("Переводчики:\n{formated_translators}{post_fix}\n")
|
||||||
}
|
}
|
||||||
false => "".to_string(),
|
false => "".to_string(),
|
||||||
@@ -155,7 +165,7 @@ fn format_translators(translators: Vec<BookTranslator>, count: usize) -> String
|
|||||||
|
|
||||||
fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
|
fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
match !sequences.is_empty() {
|
match !sequences.is_empty() {
|
||||||
@@ -166,7 +176,11 @@ fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let post_fix = if sequences.len() > count { "\nи др." } else { "" };
|
let post_fix = if sequences.len() > count {
|
||||||
|
"\nи др."
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
format!("Серии:\n{formated_sequences}{post_fix}\n")
|
format!("Серии:\n{formated_sequences}{post_fix}\n")
|
||||||
}
|
}
|
||||||
false => "".to_string(),
|
false => "".to_string(),
|
||||||
@@ -175,7 +189,7 @@ fn format_sequences(sequences: Vec<Sequence>, count: usize) -> String {
|
|||||||
|
|
||||||
fn format_genres(genres: Vec<BookGenre>, count: usize) -> String {
|
fn format_genres(genres: Vec<BookGenre>, count: usize) -> String {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
match !genres.is_empty() {
|
match !genres.is_empty() {
|
||||||
@@ -186,7 +200,11 @@ fn format_genres(genres: Vec<BookGenre>, count: usize) -> String {
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let post_fix = if genres.len() > count { "\nи др." } else { "" };
|
let post_fix = if genres.len() > count {
|
||||||
|
"\nи др."
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
format!("Жанры:\n{formated_genres}{post_fix}\n")
|
format!("Жанры:\n{formated_genres}{post_fix}\n")
|
||||||
}
|
}
|
||||||
false => "".to_string(),
|
false => "".to_string(),
|
||||||
@@ -216,7 +234,7 @@ impl Format for Author {
|
|||||||
FormatResult {
|
FormatResult {
|
||||||
result,
|
result,
|
||||||
current_size: result_len,
|
current_size: result_len,
|
||||||
max_size: result_len
|
max_size: result_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +252,7 @@ impl Format for Sequence {
|
|||||||
FormatResult {
|
FormatResult {
|
||||||
result,
|
result,
|
||||||
current_size: result_len,
|
current_size: result_len,
|
||||||
max_size: result_len
|
max_size: result_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +280,7 @@ impl Format for Translator {
|
|||||||
FormatResult {
|
FormatResult {
|
||||||
result,
|
result,
|
||||||
current_size: result_len,
|
current_size: result_len,
|
||||||
max_size: result_len
|
max_size: result_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,7 +302,12 @@ impl FormatVectorsCounts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sub(self) -> Self {
|
fn sub(self) -> Self {
|
||||||
let Self {mut authors, mut translators, mut sequences, mut genres} = self;
|
let Self {
|
||||||
|
mut authors,
|
||||||
|
mut translators,
|
||||||
|
mut sequences,
|
||||||
|
mut genres,
|
||||||
|
} = self;
|
||||||
|
|
||||||
if genres > 0 {
|
if genres > 0 {
|
||||||
genres -= 1;
|
genres -= 1;
|
||||||
@@ -293,8 +316,8 @@ impl FormatVectorsCounts {
|
|||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres
|
genres,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if sequences > 0 {
|
if sequences > 0 {
|
||||||
@@ -304,8 +327,8 @@ impl FormatVectorsCounts {
|
|||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres
|
genres,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if translators > 0 {
|
if translators > 0 {
|
||||||
@@ -315,8 +338,8 @@ impl FormatVectorsCounts {
|
|||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres
|
genres,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if authors > 0 {
|
if authors > 0 {
|
||||||
@@ -326,15 +349,15 @@ impl FormatVectorsCounts {
|
|||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres
|
genres,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres
|
genres,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,14 +377,20 @@ impl FormatVectorsResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn with_max_result_size(self, max_result_size: usize) -> Self {
|
fn with_max_result_size(self, max_result_size: usize) -> Self {
|
||||||
let Self { authors, translators, sequences, genres, .. } = self;
|
let Self {
|
||||||
|
authors,
|
||||||
|
translators,
|
||||||
|
sequences,
|
||||||
|
genres,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
authors,
|
authors,
|
||||||
translators,
|
translators,
|
||||||
sequences,
|
sequences,
|
||||||
genres,
|
genres,
|
||||||
max_result_size
|
max_result_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +401,7 @@ impl Book {
|
|||||||
authors: self.authors.len(),
|
authors: self.authors.len(),
|
||||||
translators: self.translators.len(),
|
translators: self.translators.len(),
|
||||||
sequences: self.sequences.len(),
|
sequences: self.sequences.len(),
|
||||||
genres: self.genres.len()
|
genres: self.genres.len(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = FormatVectorsResult {
|
let mut result = FormatVectorsResult {
|
||||||
@@ -380,7 +409,7 @@ impl Book {
|
|||||||
translators: format_translators(self.translators.clone(), counts.translators),
|
translators: format_translators(self.translators.clone(), counts.translators),
|
||||||
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
||||||
genres: format_genres(self.genres.clone(), counts.genres),
|
genres: format_genres(self.genres.clone(), counts.genres),
|
||||||
max_result_size: 0
|
max_result_size: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_result_size = result.len();
|
let max_result_size = result.len();
|
||||||
@@ -393,7 +422,7 @@ impl Book {
|
|||||||
translators: format_translators(self.translators.clone(), counts.translators),
|
translators: format_translators(self.translators.clone(), counts.translators),
|
||||||
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
sequences: format_sequences(self.sequences.clone(), counts.sequences),
|
||||||
genres: format_genres(self.genres.clone(), counts.genres),
|
genres: format_genres(self.genres.clone(), counts.genres),
|
||||||
max_result_size: 0
|
max_result_size: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,17 +455,23 @@ impl Format for Book {
|
|||||||
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();
|
||||||
let FormatVectorsResult { authors, translators, sequences, genres, max_result_size } = self.format_vectors(
|
let FormatVectorsResult {
|
||||||
max_size - required_data_len
|
authors,
|
||||||
);
|
translators,
|
||||||
|
sequences,
|
||||||
|
genres,
|
||||||
|
max_result_size,
|
||||||
|
} = self.format_vectors(max_size - required_data_len);
|
||||||
|
|
||||||
let result = format!("{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}");
|
let result = format!(
|
||||||
|
"{book_title}{annotations}{authors}{translators}{sequences}{genres}{download_links}"
|
||||||
|
);
|
||||||
let result_len = result.len();
|
let result_len = result.len();
|
||||||
|
|
||||||
FormatResult {
|
FormatResult {
|
||||||
result,
|
result,
|
||||||
current_size: result_len,
|
current_size: result_len,
|
||||||
max_size: max_result_size + required_data_len
|
max_size: max_result_size + required_data_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ use crate::config;
|
|||||||
|
|
||||||
use self::types::Empty;
|
use self::types::Empty;
|
||||||
|
|
||||||
fn get_allowed_langs_params(allowed_langs: SmallVec<[SmartString; 3]>) -> Vec<(&'static str, SmartString)> {
|
fn get_allowed_langs_params(
|
||||||
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
|
) -> Vec<(&'static str, SmartString)> {
|
||||||
allowed_langs
|
allowed_langs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|lang| ("allowed_langs", lang))
|
.map(|lang| ("allowed_langs", lang))
|
||||||
@@ -38,13 +40,11 @@ where
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Failed serialization: url={:?} err={:?}", url, err);
|
log::error!("Failed serialization: url={:?} err={:?}", url, err);
|
||||||
Err(Box::new(err))
|
Err(Box::new(err))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_book(
|
pub async fn get_book(id: u32) -> Result<types::Book, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
id: u32,
|
|
||||||
) -> Result<types::Book , Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
_make_request(&format!("/api/v1/books/{id}"), vec![]).await
|
_make_request(&format!("/api/v1/books/{id}"), vec![]).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +169,10 @@ pub async fn get_author_books(
|
|||||||
id: u32,
|
id: u32,
|
||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> Result<types::Page<types::AuthorBook, types::BookAuthor>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<
|
||||||
|
types::Page<types::AuthorBook, types::BookAuthor>,
|
||||||
|
Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
> {
|
||||||
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()));
|
||||||
@@ -182,7 +185,10 @@ pub async fn get_translator_books(
|
|||||||
id: u32,
|
id: u32,
|
||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> Result<types::Page<types::TranslatorBook, types::BookTranslator>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<
|
||||||
|
types::Page<types::TranslatorBook, types::BookTranslator>,
|
||||||
|
Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
> {
|
||||||
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()));
|
||||||
@@ -195,7 +201,10 @@ pub async fn get_sequence_books(
|
|||||||
id: u32,
|
id: u32,
|
||||||
page: u32,
|
page: u32,
|
||||||
allowed_langs: SmallVec<[SmartString; 3]>,
|
allowed_langs: SmallVec<[SmartString; 3]>,
|
||||||
) -> Result<types::Page<types::SequenceBook, types::Sequence>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<
|
||||||
|
types::Page<types::SequenceBook, types::Sequence>,
|
||||||
|
Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
> {
|
||||||
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()));
|
||||||
@@ -226,7 +235,11 @@ pub async fn get_author_books_available_types(
|
|||||||
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
let params = get_allowed_langs_params(allowed_langs);
|
||||||
|
|
||||||
_make_request(format!("/api/v1/authors/{id}/available_types").as_str(), params).await
|
_make_request(
|
||||||
|
format!("/api/v1/authors/{id}/available_types").as_str(),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_translator_books_available_types(
|
pub async fn get_translator_books_available_types(
|
||||||
@@ -235,14 +248,22 @@ pub async fn get_translator_books_available_types(
|
|||||||
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
let params = get_allowed_langs_params(allowed_langs);
|
||||||
|
|
||||||
_make_request(format!("/api/v1/translators/{id}/available_types").as_str(), params).await
|
_make_request(
|
||||||
|
format!("/api/v1/translators/{id}/available_types").as_str(),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
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]>,
|
||||||
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let params = get_allowed_langs_params(allowed_langs);
|
let params = get_allowed_langs_params(allowed_langs);
|
||||||
|
|
||||||
_make_request(format!("/api/v1/sequences/{id}/available_types").as_str(), params).await
|
_make_request(
|
||||||
|
format!("/api/v1/sequences/{id}/available_types").as_str(),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use smallvec::SmallVec;
|
|||||||
|
|
||||||
use super::formatters::{Format, FormatResult, FormatTitle};
|
use super::formatters::{Format, FormatResult, FormatTitle};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone)]
|
#[derive(Default, Deserialize, Debug, Clone)]
|
||||||
pub struct BookAuthor {
|
pub struct BookAuthor {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
@@ -87,13 +86,13 @@ pub struct Page<T, P> {
|
|||||||
pub pages: u32,
|
pub pages: u32,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub parent_item: Option<P>
|
pub parent_item: Option<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, P> Page<T, P>
|
impl<T, P> Page<T, P>
|
||||||
where
|
where
|
||||||
T: Format + Clone + Debug,
|
T: Format + Clone + Debug,
|
||||||
P: FormatTitle + Clone + Debug
|
P: FormatTitle + Clone + Debug,
|
||||||
{
|
{
|
||||||
pub fn format(&self, page: u32, max_size: usize) -> String {
|
pub fn format(&self, page: u32, max_size: usize) -> String {
|
||||||
let title: String = match &self.parent_item {
|
let title: String = match &self.parent_item {
|
||||||
@@ -105,7 +104,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
format!("{item_title}\n\n\n")
|
format!("{item_title}\n\n\n")
|
||||||
},
|
}
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,7 +123,8 @@ where
|
|||||||
let items_count: usize = self.items.len();
|
let items_count: usize = self.items.len();
|
||||||
let item_size: usize = (max_size - separator_len * items_count) / items_count;
|
let item_size: usize = (max_size - separator_len * items_count) / items_count;
|
||||||
|
|
||||||
let format_result: Vec<FormatResult> = self.items
|
let format_result: Vec<FormatResult> = self
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| item.format(item_size))
|
.map(|item| item.format(item_size))
|
||||||
.collect();
|
.collect();
|
||||||
@@ -232,7 +232,7 @@ impl From<SearchBook> for Book {
|
|||||||
translators: value.translators,
|
translators: value.translators,
|
||||||
sequences: value.sequences,
|
sequences: value.sequences,
|
||||||
genres: vec![],
|
genres: vec![],
|
||||||
pages: None
|
pages: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +262,7 @@ impl From<AuthorBook> for Book {
|
|||||||
translators: value.translators,
|
translators: value.translators,
|
||||||
sequences: value.sequences,
|
sequences: value.sequences,
|
||||||
genres: vec![],
|
genres: vec![],
|
||||||
pages: None
|
pages: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,12 +292,11 @@ impl From<TranslatorBook> for Book {
|
|||||||
translators: vec![],
|
translators: vec![],
|
||||||
sequences: value.sequences,
|
sequences: value.sequences,
|
||||||
genres: vec![],
|
genres: vec![],
|
||||||
pages: None
|
pages: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct SequenceBook {
|
pub struct SequenceBook {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
@@ -323,7 +322,7 @@ impl From<SequenceBook> for Book {
|
|||||||
translators: value.translators,
|
translators: value.translators,
|
||||||
sequences: vec![],
|
sequences: vec![],
|
||||||
genres: vec![],
|
genres: vec![],
|
||||||
pages: None
|
pages: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,36 @@
|
|||||||
use teloxide::{types::Message, adaptors::{CacheMe, Throttle}, Bot};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
types::Message,
|
||||||
|
Bot,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{bots::{BotHandlerInternal, approved_bot::modules::support::support_command_handler}, bots_manager::CHAT_DONATION_NOTIFICATIONS_CACHE};
|
use crate::{
|
||||||
|
bots::{approved_bot::modules::support::support_command_handler, BotHandlerInternal},
|
||||||
|
bots_manager::CHAT_DONATION_NOTIFICATIONS_CACHE,
|
||||||
|
};
|
||||||
|
|
||||||
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: Message,
|
message: Message,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
if CHAT_DONATION_NOTIFICATIONS_CACHE.get(&message.chat.id).await.is_some() {
|
if CHAT_DONATION_NOTIFICATIONS_CACHE
|
||||||
|
.get(&message.chat.id)
|
||||||
|
.await
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if !is_need_donate_notifications(message.chat.id).await? {
|
} else if !is_need_donate_notifications(message.chat.id).await? {
|
||||||
CHAT_DONATION_NOTIFICATIONS_CACHE.insert(message.chat.id, ()).await;
|
CHAT_DONATION_NOTIFICATIONS_CACHE
|
||||||
|
.insert(message.chat.id, ())
|
||||||
|
.await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
CHAT_DONATION_NOTIFICATIONS_CACHE.insert(message.chat.id, ()).await;
|
CHAT_DONATION_NOTIFICATIONS_CACHE
|
||||||
|
.insert(message.chat.id, ())
|
||||||
|
.await;
|
||||||
mark_donate_notification_sent(message.chat.id).await?;
|
mark_donate_notification_sent(message.chat.id).await?;
|
||||||
|
|
||||||
support_command_handler(message, bot).await?;
|
support_command_handler(message, bot).await?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
pub mod batch_downloader;
|
||||||
pub mod book_cache;
|
pub mod book_cache;
|
||||||
pub mod book_library;
|
pub mod book_library;
|
||||||
pub mod user_settings;
|
|
||||||
pub mod donation_notifications;
|
pub mod donation_notifications;
|
||||||
pub mod batch_downloader;
|
pub mod user_settings;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use teloxide::types::{UserId, ChatId};
|
|
||||||
use smartstring::alias::String as SmartString;
|
use smartstring::alias::String as SmartString;
|
||||||
|
use teloxide::types::{ChatId, UserId};
|
||||||
|
|
||||||
use crate::{config, bots_manager::USER_LANGS_CACHE};
|
use crate::{bots_manager::USER_LANGS_CACHE, config};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct Lang {
|
pub struct Lang {
|
||||||
@@ -40,25 +40,20 @@ pub async fn get_user_settings(
|
|||||||
Ok(response.json::<UserSettings>().await?)
|
Ok(response.json::<UserSettings>().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_user_or_default_lang_codes(
|
pub async fn get_user_or_default_lang_codes(user_id: UserId) -> SmallVec<[SmartString; 3]> {
|
||||||
user_id: UserId,
|
|
||||||
) -> SmallVec<[SmartString; 3]> {
|
|
||||||
if let Some(cached_langs) = USER_LANGS_CACHE.get(&user_id).await {
|
if let Some(cached_langs) = USER_LANGS_CACHE.get(&user_id).await {
|
||||||
return cached_langs;
|
return cached_langs;
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_lang_codes = smallvec![
|
let default_lang_codes = smallvec!["ru".into(), "be".into(), "uk".into()];
|
||||||
"ru".into(),
|
|
||||||
"be".into(),
|
|
||||||
"uk".into()
|
|
||||||
];
|
|
||||||
|
|
||||||
match get_user_settings(user_id).await {
|
match get_user_settings(user_id).await {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
let langs: SmallVec<[SmartString; 3]> = v.allowed_langs.into_iter().map(|lang| lang.code).collect();
|
let langs: SmallVec<[SmartString; 3]> =
|
||||||
|
v.allowed_langs.into_iter().map(|lang| lang.code).collect();
|
||||||
USER_LANGS_CACHE.insert(user_id, langs.clone()).await;
|
USER_LANGS_CACHE.insert(user_id, langs.clone()).await;
|
||||||
langs
|
langs
|
||||||
},
|
}
|
||||||
Err(_) => default_lang_codes,
|
Err(_) => default_lang_codes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +104,10 @@ pub async fn update_user_activity(
|
|||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
reqwest::Client::new()
|
reqwest::Client::new()
|
||||||
.post(format!("{}/users/{user_id}/update_activity", &config::CONFIG.user_settings_url))
|
.post(format!(
|
||||||
|
"{}/users/{user_id}/update_activity",
|
||||||
|
&config::CONFIG.user_settings_url
|
||||||
|
))
|
||||||
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
@@ -118,9 +116,14 @@ pub async fn update_user_activity(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_need_donate_notifications(chat_id: ChatId) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn is_need_donate_notifications(
|
||||||
|
chat_id: ChatId,
|
||||||
|
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let response = reqwest::Client::new()
|
let response = reqwest::Client::new()
|
||||||
.get(format!("{}/donate_notifications/{chat_id}/is_need_send", &config::CONFIG.user_settings_url))
|
.get(format!(
|
||||||
|
"{}/donate_notifications/{chat_id}/is_need_send",
|
||||||
|
&config::CONFIG.user_settings_url
|
||||||
|
))
|
||||||
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
@@ -129,9 +132,14 @@ pub async fn is_need_donate_notifications(chat_id: ChatId) -> Result<bool, Box<d
|
|||||||
Ok(response.json::<bool>().await?)
|
Ok(response.json::<bool>().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mark_donate_notification_sent(chat_id: ChatId) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn mark_donate_notification_sent(
|
||||||
|
chat_id: ChatId,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
reqwest::Client::new()
|
reqwest::Client::new()
|
||||||
.post(format!("{}/donate_notifications/{chat_id}", &config::CONFIG.user_settings_url))
|
.post(format!(
|
||||||
|
"{}/donate_notifications/{chat_id}",
|
||||||
|
&config::CONFIG.user_settings_url
|
||||||
|
))
|
||||||
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
.header("Authorization", &config::CONFIG.user_settings_api_key)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use teloxide::{dptree, types::CallbackQuery};
|
use teloxide::{dptree, types::CallbackQuery};
|
||||||
|
|
||||||
|
|
||||||
pub fn filter_callback_query<T>() -> crate::bots::BotHandler
|
pub fn filter_callback_query<T>() -> crate::bots::BotHandler
|
||||||
where
|
where
|
||||||
T: std::str::FromStr + Send + Sync + 'static,
|
T: std::str::FromStr + Send + Sync + 'static,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use teloxide::{prelude::*, adaptors::{Throttle, CacheMe}};
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@@ -23,7 +26,6 @@ pub async fn message_handler(
|
|||||||
register::RegisterStatus::RegisterFail => strings::ALREADY_REGISTERED.to_string(),
|
register::RegisterStatus::RegisterFail => strings::ALREADY_REGISTERED.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
bot.send_message(message.chat.id, message_text)
|
bot.send_message(message.chat.id, message_text)
|
||||||
.reply_to_message_id(message.id)
|
.reply_to_message_id(message.id)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -39,6 +41,9 @@ pub fn get_manager_handler() -> Handler<
|
|||||||
> {
|
> {
|
||||||
Update::filter_message().branch(
|
Update::filter_message().branch(
|
||||||
Message::filter_text()
|
Message::filter_text()
|
||||||
.chain(dptree::filter(|message: Message| { get_token(message.text().unwrap()).is_some() })).endpoint(message_handler),
|
.chain(dptree::filter(|message: Message| {
|
||||||
|
get_token(message.text().unwrap()).is_some()
|
||||||
|
}))
|
||||||
|
.endpoint(message_handler),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use tracing::log;
|
|||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RegisterStatus {
|
pub enum RegisterStatus {
|
||||||
Success { username: String },
|
Success { username: String },
|
||||||
@@ -14,7 +13,6 @@ pub enum RegisterStatus {
|
|||||||
RegisterFail,
|
RegisterFail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn get_bot_username(token: &str) -> Option<String> {
|
async fn get_bot_username(token: &str) -> Option<String> {
|
||||||
match Bot::new(token).get_me().send().await {
|
match Bot::new(token).get_me().send().await {
|
||||||
Ok(v) => v.username.clone(),
|
Ok(v) => v.username.clone(),
|
||||||
@@ -25,7 +23,11 @@ async fn get_bot_username(token: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn make_register_request(user_id: UserId, username: &str, token: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
|
async fn make_register_request(
|
||||||
|
user_id: UserId,
|
||||||
|
username: &str,
|
||||||
|
token: &str,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let body = json!({
|
let body = json!({
|
||||||
"token": token,
|
"token": token,
|
||||||
"user": user_id,
|
"user": user_id,
|
||||||
@@ -46,13 +48,12 @@ async fn make_register_request(user_id: UserId, username: &str, token: &str) ->
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn register(user_id: UserId, message_text: &str) -> RegisterStatus {
|
pub async fn register(user_id: UserId, message_text: &str) -> RegisterStatus {
|
||||||
let token = super::utils::get_token(message_text).unwrap();
|
let token = super::utils::get_token(message_text).unwrap();
|
||||||
|
|
||||||
let bot_username = match get_bot_username(token).await {
|
let bot_username = match get_bot_username(token).await {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return RegisterStatus::WrongToken
|
None => return RegisterStatus::WrongToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
let register_request_status = make_register_request(user_id, &bot_username, token).await;
|
let register_request_status = make_register_request(user_id, &bot_username, token).await;
|
||||||
@@ -63,5 +64,7 @@ pub async fn register(user_id: UserId, message_text: &str) -> RegisterStatus {
|
|||||||
return RegisterStatus::RegisterFail;
|
return RegisterStatus::RegisterFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterStatus::Success { username: bot_username }
|
RegisterStatus::Success {
|
||||||
|
username: bot_username,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
pub fn format_registered_message(username: &str) -> String {
|
pub fn format_registered_message(username: &str) -> String {
|
||||||
format!("@{username} зарегистрирован и через несколько минут будет подключен!", username = username)
|
format!(
|
||||||
|
"@{username} зарегистрирован и через несколько минут будет подключен!",
|
||||||
|
username = username
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
|
pub const ALREADY_REGISTERED: &str = "Ошибка! Возможно бот уже зарегистрирован!";
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
|
||||||
pub fn get_token(message_text: &str) -> Option<&str> {
|
pub fn get_token(message_text: &str) -> Option<&str> {
|
||||||
let re = Regex::new("(?P<token>[0-9]+:[0-9a-zA-Z-_]+)").unwrap();
|
let re = Regex::new("(?P<token>[0-9]+:[0-9a-zA-Z-_]+)").unwrap();
|
||||||
|
|
||||||
match re.find(message_text) {
|
match re.find(message_text) {
|
||||||
Some(v) => Some(v.as_str()),
|
Some(v) => Some(v.as_str()),
|
||||||
None => None
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -42,7 +40,10 @@ mod tests {
|
|||||||
|
|
||||||
let result = get_token(message);
|
let result = get_token(message);
|
||||||
|
|
||||||
assert_eq!(result.unwrap(), "5555555555:AAF-AAAAAAAA1239AA2AAsvy13Axp23RAa");
|
assert_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
"5555555555:AAF-AAAAAAAA1239AA2AAsvy13Axp23RAa"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -18,25 +18,14 @@ type BotCommands = Option<Vec<teloxide::types::BotCommand>>;
|
|||||||
|
|
||||||
fn ignore_channel_messages() -> crate::bots::BotHandler {
|
fn ignore_channel_messages() -> crate::bots::BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
.branch(
|
.branch(Update::filter_channel_post().endpoint(|| async { Ok(()) }))
|
||||||
Update::filter_channel_post()
|
.branch(Update::filter_edited_channel_post().endpoint(|| async { Ok(()) }))
|
||||||
.endpoint(|| async { Ok(()) })
|
|
||||||
).branch(
|
|
||||||
Update::filter_edited_channel_post()
|
|
||||||
.endpoint(|| async { Ok(()) })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_chat_member_update() -> crate::bots::BotHandler {
|
fn ignore_chat_member_update() -> crate::bots::BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
.branch(
|
.branch(Update::filter_chat_member().endpoint(|| async { Ok(()) }))
|
||||||
Update::filter_chat_member()
|
.branch(Update::filter_my_chat_member().endpoint(|| async { Ok(()) }))
|
||||||
.endpoint(|| async { Ok(()) })
|
|
||||||
)
|
|
||||||
.branch(
|
|
||||||
Update::filter_my_chat_member()
|
|
||||||
.endpoint(|| async { Ok(()) })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bot_handler() -> (BotHandler, BotCommands) {
|
pub fn get_bot_handler() -> (BotHandler, BotCommands) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, PartialEq, Clone, Copy)]
|
#[derive(Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum BotCache {
|
pub enum BotCache {
|
||||||
#[serde(rename = "original")]
|
#[serde(rename = "original")]
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
pub mod bot_manager_client;
|
pub mod bot_manager_client;
|
||||||
|
|
||||||
use axum::extract::{State, Path};
|
use axum::extract::{Path, State};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::routing::post;
|
use axum::routing::post;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use smartstring::alias::String as SmartString;
|
use smartstring::alias::String as SmartString;
|
||||||
use teloxide::stop::{mk_stop_token, StopToken, StopFlag};
|
use teloxide::stop::{mk_stop_token, StopFlag, StopToken};
|
||||||
use teloxide::update_listeners::{StatefulListener, UpdateListener};
|
use teloxide::update_listeners::{StatefulListener, UpdateListener};
|
||||||
use tokio::sync::mpsc::{UnboundedSender, self};
|
use tokio::sync::mpsc::{self, UnboundedSender};
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
use tracing::log;
|
use tracing::log;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -24,7 +24,7 @@ use tokio::sync::RwLock;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use teloxide::adaptors::throttle::Limits;
|
use teloxide::adaptors::throttle::Limits;
|
||||||
use teloxide::types::{BotCommand, UpdateKind};
|
use teloxide::types::{BotCommand, UpdateKind};
|
||||||
use tokio::time::{sleep, Duration, self};
|
use tokio::time::{self, sleep, Duration};
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
|
|
||||||
use teloxide::prelude::*;
|
use teloxide::prelude::*;
|
||||||
@@ -35,15 +35,12 @@ use self::bot_manager_client::get_bots;
|
|||||||
pub use self::bot_manager_client::{BotCache, BotData};
|
pub use self::bot_manager_client::{BotCache, BotData};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
|
|
||||||
type UpdateSender = mpsc::UnboundedSender<Result<Update, std::convert::Infallible>>;
|
type UpdateSender = mpsc::UnboundedSender<Result<Update, std::convert::Infallible>>;
|
||||||
|
|
||||||
|
|
||||||
fn tuple_first_mut<A, B>(tuple: &mut (A, B)) -> &mut A {
|
fn tuple_first_mut<A, B>(tuple: &mut (A, B)) -> &mut A {
|
||||||
&mut tuple.0
|
&mut tuple.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub static USER_ACTIVITY_CACHE: Lazy<Cache<UserId, ()>> = Lazy::new(|| {
|
pub static USER_ACTIVITY_CACHE: Lazy<Cache<UserId, ()>> = Lazy::new(|| {
|
||||||
Cache::builder()
|
Cache::builder()
|
||||||
.time_to_idle(Duration::from_secs(5 * 60))
|
.time_to_idle(Duration::from_secs(5 * 60))
|
||||||
@@ -65,9 +62,17 @@ pub static CHAT_DONATION_NOTIFICATIONS_CACHE: Lazy<Cache<ChatId, ()>> = Lazy::ne
|
|||||||
.build()
|
.build()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type Routes = Arc<
|
||||||
type Routes = Arc<RwLock<HashMap<String, (StopToken, ClosableSender<Result<Update, std::convert::Infallible>>)>>>;
|
RwLock<
|
||||||
|
HashMap<
|
||||||
|
String,
|
||||||
|
(
|
||||||
|
StopToken,
|
||||||
|
ClosableSender<Result<Update, std::convert::Infallible>>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
|
||||||
struct ClosableSender<T> {
|
struct ClosableSender<T> {
|
||||||
origin: std::sync::Arc<std::sync::RwLock<Option<mpsc::UnboundedSender<T>>>>,
|
origin: std::sync::Arc<std::sync::RwLock<Option<mpsc::UnboundedSender<T>>>>,
|
||||||
@@ -75,13 +80,17 @@ struct ClosableSender<T> {
|
|||||||
|
|
||||||
impl<T> Clone for ClosableSender<T> {
|
impl<T> Clone for ClosableSender<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self { origin: self.origin.clone() }
|
Self {
|
||||||
|
origin: self.origin.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ClosableSender<T> {
|
impl<T> ClosableSender<T> {
|
||||||
fn new(sender: mpsc::UnboundedSender<T>) -> Self {
|
fn new(sender: mpsc::UnboundedSender<T>) -> Self {
|
||||||
Self { origin: std::sync::Arc::new(std::sync::RwLock::new(Some(sender))) }
|
Self {
|
||||||
|
origin: std::sync::Arc::new(std::sync::RwLock::new(Some(sender))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self) -> Option<mpsc::UnboundedSender<T>> {
|
fn get(&self) -> Option<mpsc::UnboundedSender<T>> {
|
||||||
@@ -93,7 +102,6 @@ impl<T> ClosableSender<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct ServerState {
|
struct ServerState {
|
||||||
routers: Routes,
|
routers: Routes,
|
||||||
@@ -102,7 +110,7 @@ struct ServerState {
|
|||||||
pub struct BotsManager {
|
pub struct BotsManager {
|
||||||
port: u16,
|
port: u16,
|
||||||
|
|
||||||
state: ServerState
|
state: ServerState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotsManager {
|
impl BotsManager {
|
||||||
@@ -111,12 +119,19 @@ impl BotsManager {
|
|||||||
port: 8000,
|
port: 8000,
|
||||||
|
|
||||||
state: ServerState {
|
state: ServerState {
|
||||||
routers: Arc::new(RwLock::new(HashMap::new()))
|
routers: Arc::new(RwLock::new(HashMap::new())),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_listener(&self) -> (StopToken, StopFlag, UnboundedSender<Result<Update, std::convert::Infallible>>, impl UpdateListener<Err = Infallible>) {
|
fn get_listener(
|
||||||
|
&self,
|
||||||
|
) -> (
|
||||||
|
StopToken,
|
||||||
|
StopFlag,
|
||||||
|
UnboundedSender<Result<Update, std::convert::Infallible>>,
|
||||||
|
impl UpdateListener<Err = Infallible>,
|
||||||
|
) {
|
||||||
let (tx, rx): (UpdateSender, _) = mpsc::unbounded_channel();
|
let (tx, rx): (UpdateSender, _) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let (stop_token, stop_flag) = mk_stop_token();
|
let (stop_token, stop_flag) = mk_stop_token();
|
||||||
@@ -126,9 +141,7 @@ impl BotsManager {
|
|||||||
let listener = StatefulListener::new(
|
let listener = StatefulListener::new(
|
||||||
(stream, stop_token.clone()),
|
(stream, stop_token.clone()),
|
||||||
tuple_first_mut,
|
tuple_first_mut,
|
||||||
|state: &mut (_, StopToken)| {
|
|state: &mut (_, StopToken)| state.1.clone(),
|
||||||
state.1.clone()
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
(stop_token, stop_flag, tx, listener)
|
(stop_token, stop_flag, tx, listener)
|
||||||
@@ -202,7 +215,7 @@ impl BotsManager {
|
|||||||
self.start_bot(bot_data).await;
|
self.start_bot(bot_data).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::info!("{:?}", err);
|
log::info!("{:?}", err);
|
||||||
}
|
}
|
||||||
@@ -215,7 +228,6 @@ impl BotsManager {
|
|||||||
Path(token): Path<String>,
|
Path(token): Path<String>,
|
||||||
input: String,
|
input: String,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
|
||||||
let routes = routers.read().await;
|
let routes = routers.read().await;
|
||||||
let tx = routes.get(&token);
|
let tx = routes.get(&token);
|
||||||
|
|
||||||
@@ -232,7 +244,7 @@ impl BotsManager {
|
|||||||
sender.close();
|
sender.close();
|
||||||
};
|
};
|
||||||
return StatusCode::SERVICE_UNAVAILABLE;
|
return StatusCode::SERVICE_UNAVAILABLE;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::from_str::<Update>(&input) {
|
match serde_json::from_str::<Update>(&input) {
|
||||||
|
|||||||
@@ -61,6 +61,4 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
|
pub static CONFIG: Lazy<Config> = Lazy::new(Config::load);
|
||||||
Config::load()
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ use std::str::FromStr;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use sentry::ClientOptions;
|
|
||||||
use sentry::integrations::debug_images::DebugImagesIntegration;
|
use sentry::integrations::debug_images::DebugImagesIntegration;
|
||||||
use sentry::types::Dsn;
|
use sentry::types::Dsn;
|
||||||
|
use sentry::ClientOptions;
|
||||||
|
|
||||||
mod bots;
|
mod bots;
|
||||||
mod bots_manager;
|
mod bots_manager;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
|||||||
Reference in New Issue
Block a user