Add need download pipeline

This commit is contained in:
2023-05-26 21:55:29 +02:00
parent 20afda53b9
commit 669719392b
3 changed files with 232 additions and 33 deletions

View File

@@ -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
}
}
},
),
)
} }

View File

@@ -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>,

View File

@@ -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)]