mirror of
https://github.com/flibusta-apps/book_bot.git
synced 2025-12-06 15:35:35 +01:00
Add need download pipeline
This commit is contained in:
@@ -1,18 +1,34 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}};
|
use strum_macros::EnumIter;
|
||||||
|
use teloxide::{
|
||||||
|
adaptors::{CacheMe, Throttle},
|
||||||
|
dispatching::UpdateFilterExt,
|
||||||
|
dptree,
|
||||||
|
prelude::*,
|
||||||
|
types::*,
|
||||||
|
};
|
||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bots::{
|
bots::{
|
||||||
approved_bot::services::{book_cache::{
|
approved_bot::{
|
||||||
download_file, get_cached_message,
|
services::{
|
||||||
types::{CachedMessage, DownloadFile},
|
book_cache::{
|
||||||
}, donation_notificatioins::send_donation_notification},
|
download_file, get_cached_message,
|
||||||
|
types::{CachedMessage, DownloadFile},
|
||||||
|
},
|
||||||
|
book_library::get_book,
|
||||||
|
donation_notificatioins::send_donation_notification,
|
||||||
|
},
|
||||||
|
tools::filter_callback_query,
|
||||||
|
},
|
||||||
BotHandlerInternal,
|
BotHandlerInternal,
|
||||||
},
|
},
|
||||||
bots_manager::{BotCache, AppState},
|
bots_manager::{AppState, BotCache},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::utils::{filter_command, CommandParse};
|
use super::utils::{filter_command, CommandParse};
|
||||||
@@ -46,6 +62,64 @@ impl CommandParse<Self> for DownloadData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StartDownloadData {
|
||||||
|
pub id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandParse<Self> for StartDownloadData {
|
||||||
|
fn parse(s: &str, bot_name: &str) -> Result<Self, strum::ParseError> {
|
||||||
|
let re = Regex::new(r"^/d_(?P<book_id>\d+)$").unwrap();
|
||||||
|
|
||||||
|
let full_bot_name = format!("@{bot_name}");
|
||||||
|
let after_replace = s.replace(&full_bot_name, "");
|
||||||
|
|
||||||
|
let caps = re.captures(&after_replace);
|
||||||
|
let caps = match caps {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err(strum::ParseError::VariantNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let book_id: u32 = caps["book_id"].parse().unwrap();
|
||||||
|
|
||||||
|
Ok(StartDownloadData { id: book_id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, EnumIter)]
|
||||||
|
pub enum DownloadQueryData {
|
||||||
|
DownloadData { book_id: u32, file_type: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for DownloadQueryData {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
DownloadQueryData::DownloadData { book_id, file_type } => {
|
||||||
|
format!("d_{book_id}_{file_type}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DownloadQueryData {
|
||||||
|
type Err = strum::ParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let re = Regex::new(r"^d_(?P<book_id>\d+)_(?P<file_type>\w+)$").unwrap();
|
||||||
|
|
||||||
|
let caps = re.captures(s);
|
||||||
|
let caps = match caps {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err(strum::ParseError::VariantNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let book_id: u32 = caps["book_id"].parse().unwrap();
|
||||||
|
let file_type: String = caps["file_type"].to_string();
|
||||||
|
|
||||||
|
Ok(DownloadQueryData::DownloadData { book_id, file_type })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn _send_cached(
|
async fn _send_cached(
|
||||||
message: &Message,
|
message: &Message,
|
||||||
bot: &CacheMe<Throttle<Bot>>,
|
bot: &CacheMe<Throttle<Bot>>,
|
||||||
@@ -70,16 +144,22 @@ async fn send_cached_message(
|
|||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
download_data: DownloadData,
|
download_data: DownloadData,
|
||||||
donation_notification_cache: Cache<ChatId, bool>,
|
donation_notification_cache: Cache<ChatId, bool>,
|
||||||
|
need_delete_message: bool,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
if let Ok(v) = get_cached_message(&download_data).await {
|
if let Ok(v) = get_cached_message(&download_data).await {
|
||||||
if _send_cached(&message, &bot, v).await.is_ok() {
|
if _send_cached(&message, &bot, v).await.is_ok() {
|
||||||
|
if need_delete_message {
|
||||||
|
bot.delete_message(message.chat.id, message.id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
send_donation_notification(bot.clone(), message, donation_notification_cache).await?;
|
send_donation_notification(bot.clone(), message, donation_notification_cache).await?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
send_with_download_from_channel(message, bot, download_data, donation_notification_cache).await?;
|
send_with_download_from_channel(message, bot, download_data, donation_notification_cache, need_delete_message)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -104,8 +184,7 @@ async fn _send_downloaded_file(
|
|||||||
|
|
||||||
let document: InputFile = InputFile::read(data).file_name(filename);
|
let document: InputFile = InputFile::read(data).file_name(filename);
|
||||||
|
|
||||||
bot
|
bot.send_document(message.chat.id, document)
|
||||||
.send_document(message.chat.id, document)
|
|
||||||
.caption(caption)
|
.caption(caption)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
@@ -120,9 +199,18 @@ async fn send_with_download_from_channel(
|
|||||||
bot: CacheMe<Throttle<Bot>>,
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
download_data: DownloadData,
|
download_data: DownloadData,
|
||||||
donation_notification_cache: Cache<ChatId, bool>,
|
donation_notification_cache: Cache<ChatId, bool>,
|
||||||
|
need_delete_message: bool,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
match download_file(&download_data).await {
|
match download_file(&download_data).await {
|
||||||
Ok(v) => Ok(_send_downloaded_file(&message, bot, v, donation_notification_cache).await?),
|
Ok(v) => {
|
||||||
|
_send_downloaded_file(&message, bot.clone(), v, donation_notification_cache).await?;
|
||||||
|
|
||||||
|
if need_delete_message {
|
||||||
|
bot.delete_message(message.chat.id, message.id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,31 +221,136 @@ async fn download_handler(
|
|||||||
cache: BotCache,
|
cache: BotCache,
|
||||||
download_data: DownloadData,
|
download_data: DownloadData,
|
||||||
donation_notification_cache: Cache<ChatId, bool>,
|
donation_notification_cache: Cache<ChatId, bool>,
|
||||||
|
need_delete_message: bool,
|
||||||
) -> BotHandlerInternal {
|
) -> BotHandlerInternal {
|
||||||
match cache {
|
match cache {
|
||||||
BotCache::Original => send_cached_message(message, bot, download_data, donation_notification_cache).await,
|
BotCache::Original => {
|
||||||
BotCache::NoCache => send_with_download_from_channel(message, bot, download_data, donation_notification_cache).await,
|
send_cached_message(
|
||||||
|
message,
|
||||||
|
bot,
|
||||||
|
download_data,
|
||||||
|
donation_notification_cache,
|
||||||
|
need_delete_message,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
BotCache::NoCache => {
|
||||||
|
send_with_download_from_channel(
|
||||||
|
message,
|
||||||
|
bot,
|
||||||
|
download_data,
|
||||||
|
donation_notification_cache,
|
||||||
|
need_delete_message,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_download_hander() -> crate::bots::BotHandler {
|
async fn get_download_keyboard_handler(
|
||||||
dptree::entry().branch(
|
message: Message,
|
||||||
Update::filter_message()
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
.chain(filter_command::<DownloadData>())
|
download_data: StartDownloadData,
|
||||||
.endpoint(
|
) -> BotHandlerInternal {
|
||||||
|message: Message,
|
let book = match get_book(download_data.id).await {
|
||||||
bot: CacheMe<Throttle<Bot>>,
|
Ok(v) => v,
|
||||||
cache: BotCache,
|
Err(err) => {
|
||||||
download_data: DownloadData,
|
bot.send_message(message.chat.id, "Ошибка! Попробуйте позже :(")
|
||||||
app_state: AppState| async move {
|
.send()
|
||||||
download_handler(
|
.await?;
|
||||||
message,
|
|
||||||
bot,
|
return Err(err);
|
||||||
cache,
|
}
|
||||||
download_data,
|
};
|
||||||
app_state.chat_donation_notifications_cache
|
|
||||||
).await
|
let keyboard = InlineKeyboardMarkup {
|
||||||
},
|
inline_keyboard: book
|
||||||
),
|
.available_types
|
||||||
)
|
.into_iter()
|
||||||
|
.map(|item| -> Vec<InlineKeyboardButton> {
|
||||||
|
vec![InlineKeyboardButton {
|
||||||
|
text: item.clone(),
|
||||||
|
kind: InlineKeyboardButtonKind::CallbackData(
|
||||||
|
(DownloadQueryData::DownloadData {
|
||||||
|
book_id: book.id,
|
||||||
|
file_type: item,
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
bot.send_message(message.chat.id, "Выбери формат:")
|
||||||
|
.reply_markup(keyboard)
|
||||||
|
.reply_to_message_id(message.id)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_download_hander() -> crate::bots::BotHandler {
|
||||||
|
dptree::entry()
|
||||||
|
.branch(
|
||||||
|
Update::filter_message()
|
||||||
|
.chain(filter_command::<DownloadData>())
|
||||||
|
.endpoint(
|
||||||
|
|message: Message,
|
||||||
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
cache: BotCache,
|
||||||
|
download_data: DownloadData,
|
||||||
|
app_state: AppState| async move {
|
||||||
|
download_handler(
|
||||||
|
message,
|
||||||
|
bot,
|
||||||
|
cache,
|
||||||
|
download_data,
|
||||||
|
app_state.chat_donation_notifications_cache,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.branch(
|
||||||
|
Update::filter_message()
|
||||||
|
.chain(filter_command::<StartDownloadData>())
|
||||||
|
.endpoint(
|
||||||
|
|message: Message,
|
||||||
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
download_data: StartDownloadData| async move {
|
||||||
|
get_download_keyboard_handler(message, bot, download_data).await
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.branch(
|
||||||
|
Update::filter_callback_query()
|
||||||
|
.chain(filter_callback_query::<DownloadQueryData>())
|
||||||
|
.endpoint(
|
||||||
|
|cq: CallbackQuery,
|
||||||
|
download_query_data: DownloadQueryData,
|
||||||
|
bot: CacheMe<Throttle<Bot>>,
|
||||||
|
cache: BotCache,
|
||||||
|
app_state: AppState| async move {
|
||||||
|
match download_query_data {
|
||||||
|
DownloadQueryData::DownloadData { book_id, file_type } => {
|
||||||
|
download_handler(
|
||||||
|
cq.message.unwrap(),
|
||||||
|
bot,
|
||||||
|
cache,
|
||||||
|
DownloadData {
|
||||||
|
format: file_type,
|
||||||
|
id: book_id,
|
||||||
|
},
|
||||||
|
app_state.chat_donation_notifications_cache,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ where
|
|||||||
Ok(response.json::<T>().await?)
|
Ok(response.json::<T>().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_book(
|
||||||
|
id: u32,
|
||||||
|
) -> Result<types::Book , Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
_make_request(&format!("/api/v1/books/{id}"), vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_random_book_by_genre(
|
pub async fn get_random_book_by_genre(
|
||||||
allowed_langs: Vec<String>,
|
allowed_langs: Vec<String>,
|
||||||
genre: Option<u32>,
|
genre: Option<u32>,
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl BookGenre {
|
|||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct Source {
|
pub struct Source {
|
||||||
// id: u32,
|
// id: u32,
|
||||||
// name: String
|
// name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user