Files
book_bot/src/bots/approved_bot/modules/annotations/mod.rs

217 lines
6.8 KiB
Rust

pub mod commands;
pub mod callback_data;
pub mod formater;
pub mod errors;
use std::convert::TryInto;
use futures::TryStreamExt;
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}};
use tokio_util::compat::FuturesAsyncReadCompatExt;
use crate::bots::{
approved_bot::{
modules::utils::generic_get_pagination_keyboard,
services::book_library::{
get_author_annotation, get_book_annotation,
},
tools::filter_callback_query,
},
BotHandlerInternal,
};
use self::{commands::AnnotationCommand, formater::AnnotationFormat, callback_data::AnnotationCallbackData, errors::AnnotationError};
use super::utils::{filter_command, split_text_to_chunks};
async fn download_image(
file: &String,
) -> Result<reqwest::Response, Box<dyn std::error::Error + Send + Sync>> {
Ok(reqwest::get(file).await?.error_for_status()?)
}
pub async fn send_annotation_handler<T, Fut>(
message: Message,
bot: CacheMe<Throttle<Bot>>,
command: AnnotationCommand,
annotation_getter: fn(id: u32) -> Fut,
) -> BotHandlerInternal
where
T: AnnotationFormat,
Fut: std::future::Future<Output = Result<T, Box<dyn std::error::Error + Send + Sync>>>,
{
let id = match command {
AnnotationCommand::Book { id } => id,
AnnotationCommand::Author { id } => id,
};
let annotation = annotation_getter(id).await?;
if annotation.get_file().is_none() && !annotation.is_normal_text() {
return match bot
.send_message(message.chat.id, "Аннотация недоступна :(")
.reply_to_message_id(message.id)
.send()
.await
{
Ok(_) => Ok(()),
Err(err) => Err(Box::new(err)),
};
};
if let Some(file) = annotation.get_file() {
let image_response = download_image(file).await;
if let Ok(v) = image_response {
let data = v
.bytes_stream()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
.into_async_read()
.compat();
#[allow(unused_must_use)] {
bot
.send_photo(message.chat.id, InputFile::read(data))
.send()
.await;
}
}
};
if !annotation.is_normal_text() {
return Err(Box::new(AnnotationError { command, text: annotation.get_text().to_string() }));
}
let annotation_text = annotation.get_text();
let chunked_text = split_text_to_chunks(annotation_text, 512);
let current_text = chunked_text.get(0).unwrap();
let callback_data = match command {
AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 },
AnnotationCommand::Author { id } => AnnotationCallbackData::Author { id, page: 1 },
};
let keyboard = generic_get_pagination_keyboard(
1,
chunked_text.len().try_into()?,
callback_data,
false,
);
bot
.send_message(message.chat.id, current_text)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
}
pub async fn annotation_pagination_handler<T, Fut>(
cq: CallbackQuery,
bot: CacheMe<Throttle<Bot>>,
callback_data: AnnotationCallbackData,
annotation_getter: fn(id: u32) -> Fut,
) -> BotHandlerInternal
where
T: AnnotationFormat,
Fut: std::future::Future<Output = Result<T, Box<dyn std::error::Error + Send + Sync>>>,
{
let (id, page) = match callback_data {
AnnotationCallbackData::Book { id, page } => (id, page),
AnnotationCallbackData::Author { id, page } => (id, page),
};
let annotation = annotation_getter(id).await?;
let message = match cq.message {
Some(v) => v,
None => return Ok(()),
};
let request_page: usize = page.try_into().unwrap();
let annotation_text = annotation.get_text();
let chunked_text = split_text_to_chunks(annotation_text, 512);
let page_index = if request_page <= chunked_text.len() {
request_page
} else {
chunked_text.len()
};
let current_text = chunked_text.get(page_index - 1).unwrap();
let keyboard = generic_get_pagination_keyboard(
page,
chunked_text.len().try_into()?,
callback_data,
false,
);
bot
.edit_message_text(message.chat.id, message.id, current_text)
.reply_markup(keyboard)
.send()
.await?;
Ok(())
}
pub fn get_annotations_handler() -> crate::bots::BotHandler {
dptree::entry()
.branch(
Update::filter_message()
.chain(filter_command::<AnnotationCommand>())
.endpoint(
|message: Message, bot: CacheMe<Throttle<Bot>>, command: AnnotationCommand| async move {
match command {
AnnotationCommand::Book { .. } => {
send_annotation_handler(message, bot, command, get_book_annotation)
.await
}
AnnotationCommand::Author { .. } => {
send_annotation_handler(
message,
bot,
command,
get_author_annotation,
)
.await
}
}
},
),
)
.branch(
Update::filter_callback_query()
.chain(filter_callback_query::<AnnotationCallbackData>())
.endpoint(
|cq: CallbackQuery,
bot: CacheMe<Throttle<Bot>>,
callback_data: AnnotationCallbackData| async move {
match callback_data {
AnnotationCallbackData::Book { .. } => {
annotation_pagination_handler(
cq,
bot,
callback_data,
get_book_annotation,
)
.await
}
AnnotationCallbackData::Author { .. } => {
annotation_pagination_handler(
cq,
bot,
callback_data,
get_author_annotation,
)
.await
}
}
},
),
)
}