mirror of
https://github.com/flibusta-apps/book_bot.git
synced 2025-12-06 15:35:35 +01:00
Refactor annotation module
This commit is contained in:
61
src/bots/approved_bot/modules/annotations/callback_data.rs
Normal file
61
src/bots/approved_bot/modules/annotations/callback_data.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::bots::approved_bot::modules::utils::GetPaginationCallbackData;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum AnnotationCallbackData {
|
||||||
|
Book { id: u32, page: u32 },
|
||||||
|
Author { id: u32, page: u32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for AnnotationCallbackData {
|
||||||
|
type Err = strum::ParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let re = Regex::new(r"^(?P<an_type>a|b)_an_(?P<id>\d+)_(?P<page>\d+)$").unwrap();
|
||||||
|
|
||||||
|
let caps = re.captures(s);
|
||||||
|
let caps = match caps {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err(strum::ParseError::VariantNotFound),
|
||||||
|
};
|
||||||
|
|
||||||
|
let annotation_type = &caps["an_type"];
|
||||||
|
let id = caps["id"].parse::<u32>().unwrap();
|
||||||
|
let page = caps["page"].parse::<u32>().unwrap();
|
||||||
|
|
||||||
|
match annotation_type {
|
||||||
|
"a" => Ok(AnnotationCallbackData::Author { id, page }),
|
||||||
|
"b" => Ok(AnnotationCallbackData::Book { id, page }),
|
||||||
|
_ => Err(strum::ParseError::VariantNotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for AnnotationCallbackData {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
AnnotationCallbackData::Book { id, page } => format!("b_an_{id}_{page}"),
|
||||||
|
AnnotationCallbackData::Author { id, page } => format!("a_an_{id}_{page}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetPaginationCallbackData for AnnotationCallbackData {
|
||||||
|
fn get_pagination_callback_data(&self, target_page: u32) -> String {
|
||||||
|
match self {
|
||||||
|
AnnotationCallbackData::Book { id, .. } => AnnotationCallbackData::Book {
|
||||||
|
id: *id,
|
||||||
|
page: target_page,
|
||||||
|
},
|
||||||
|
AnnotationCallbackData::Author { id, .. } => AnnotationCallbackData::Author {
|
||||||
|
id: *id,
|
||||||
|
page: target_page,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/bots/approved_bot/modules/annotations/commands.rs
Normal file
37
src/bots/approved_bot/modules/annotations/commands.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::bots::approved_bot::modules::utils::CommandParse;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum AnnotationCommand {
|
||||||
|
Book { id: u32 },
|
||||||
|
Author { id: u32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandParse<Self> for AnnotationCommand {
|
||||||
|
fn parse(s: &str, bot_name: &str) -> Result<Self, strum::ParseError> {
|
||||||
|
let re = Regex::new(r"^/(?P<an_type>a|b)_an_(?P<id>\d+)$")
|
||||||
|
.unwrap_or_else(|_| panic!("Can't create AnnotationCommand regexp!"));
|
||||||
|
|
||||||
|
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 annotation_type = &caps["an_type"];
|
||||||
|
let id: u32 = caps["id"]
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!"));
|
||||||
|
|
||||||
|
match annotation_type {
|
||||||
|
"a" => Ok(AnnotationCommand::Author { id }),
|
||||||
|
"b" => Ok(AnnotationCommand::Book { id }),
|
||||||
|
_ => Err(strum::ParseError::VariantNotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/bots/approved_bot/modules/annotations/errors.rs
Normal file
18
src/bots/approved_bot/modules/annotations/errors.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::commands::AnnotationCommand;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AnnotationError {
|
||||||
|
pub command: AnnotationCommand,
|
||||||
|
pub text: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AnnotationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for AnnotationError {}
|
||||||
37
src/bots/approved_bot/modules/annotations/formater.rs
Normal file
37
src/bots/approved_bot/modules/annotations/formater.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::bots::approved_bot::services::book_library::types::{AuthorAnnotation, BookAnnotation};
|
||||||
|
|
||||||
|
pub trait AnnotationFormat {
|
||||||
|
fn get_file(&self) -> Option<&String>;
|
||||||
|
fn get_text(&self) -> &str;
|
||||||
|
|
||||||
|
fn is_normal_text(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnnotationFormat for BookAnnotation {
|
||||||
|
fn get_file(&self) -> Option<&String> {
|
||||||
|
self.file.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> &str {
|
||||||
|
self.text.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_normal_text(&self) -> bool {
|
||||||
|
!self.text.replace(['\n', ' '], "").is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl AnnotationFormat for AuthorAnnotation {
|
||||||
|
fn get_file(&self) -> Option<&String> {
|
||||||
|
self.file.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> &str {
|
||||||
|
self.text.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_normal_text(&self) -> bool {
|
||||||
|
!self.text.replace(['\n', ' '], "").is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
use std::{convert::TryInto, str::FromStr};
|
pub mod commands;
|
||||||
|
pub mod callback_data;
|
||||||
|
pub mod formater;
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use regex::Regex;
|
|
||||||
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}};
|
use teloxide::{dispatching::UpdateFilterExt, dptree, prelude::*, types::*, adaptors::{Throttle, CacheMe}};
|
||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||||
|
|
||||||
@@ -10,137 +15,16 @@ use crate::bots::{
|
|||||||
modules::utils::generic_get_pagination_keyboard,
|
modules::utils::generic_get_pagination_keyboard,
|
||||||
services::book_library::{
|
services::book_library::{
|
||||||
get_author_annotation, get_book_annotation,
|
get_author_annotation, get_book_annotation,
|
||||||
types::{AuthorAnnotation, BookAnnotation},
|
|
||||||
},
|
},
|
||||||
tools::filter_callback_query,
|
tools::filter_callback_query,
|
||||||
},
|
},
|
||||||
BotHandlerInternal,
|
BotHandlerInternal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::utils::{filter_command, CommandParse, GetPaginationCallbackData, split_text_to_chunks};
|
use self::{commands::AnnotationCommand, formater::AnnotationFormat, callback_data::AnnotationCallbackData, errors::AnnotationError};
|
||||||
|
|
||||||
#[derive(Clone)]
|
use super::utils::{filter_command, split_text_to_chunks};
|
||||||
pub enum AnnotationCommand {
|
|
||||||
Book { id: u32 },
|
|
||||||
Author { id: u32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandParse<Self> for AnnotationCommand {
|
|
||||||
fn parse(s: &str, bot_name: &str) -> Result<Self, strum::ParseError> {
|
|
||||||
let re = Regex::new(r"^/(?P<an_type>a|b)_an_(?P<id>\d+)$")
|
|
||||||
.unwrap_or_else(|_| panic!("Can't create AnnotationCommand regexp!"));
|
|
||||||
|
|
||||||
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 annotation_type = &caps["an_type"];
|
|
||||||
let id: u32 = caps["id"]
|
|
||||||
.parse()
|
|
||||||
.unwrap_or_else(|_| panic!("Can't get id from AnnotationCommand!"));
|
|
||||||
|
|
||||||
match annotation_type {
|
|
||||||
"a" => Ok(AnnotationCommand::Author { id }),
|
|
||||||
"b" => Ok(AnnotationCommand::Book { id }),
|
|
||||||
_ => Err(strum::ParseError::VariantNotFound),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum AnnotationCallbackData {
|
|
||||||
Book { id: u32, page: u32 },
|
|
||||||
Author { id: u32, page: u32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for AnnotationCallbackData {
|
|
||||||
type Err = strum::ParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let re = Regex::new(r"^(?P<an_type>a|b)_an_(?P<id>\d+)_(?P<page>\d+)$").unwrap();
|
|
||||||
|
|
||||||
let caps = re.captures(s);
|
|
||||||
let caps = match caps {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return Err(strum::ParseError::VariantNotFound),
|
|
||||||
};
|
|
||||||
|
|
||||||
let annotation_type = &caps["an_type"];
|
|
||||||
let id = caps["id"].parse::<u32>().unwrap();
|
|
||||||
let page = caps["page"].parse::<u32>().unwrap();
|
|
||||||
|
|
||||||
match annotation_type {
|
|
||||||
"a" => Ok(AnnotationCallbackData::Author { id, page }),
|
|
||||||
"b" => Ok(AnnotationCallbackData::Book { id, page }),
|
|
||||||
_ => Err(strum::ParseError::VariantNotFound),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for AnnotationCallbackData {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
AnnotationCallbackData::Book { id, page } => format!("b_an_{id}_{page}"),
|
|
||||||
AnnotationCallbackData::Author { id, page } => format!("a_an_{id}_{page}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AnnotationFormat {
|
|
||||||
fn get_file(&self) -> Option<&String>;
|
|
||||||
fn get_text(&self) -> &str;
|
|
||||||
|
|
||||||
fn is_normal_text(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnnotationFormat for BookAnnotation {
|
|
||||||
fn get_file(&self) -> Option<&String> {
|
|
||||||
self.file.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_text(&self) -> &str {
|
|
||||||
self.text.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_normal_text(&self) -> bool {
|
|
||||||
!self.text.replace(['\n', ' '], "").is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetPaginationCallbackData for AnnotationCallbackData {
|
|
||||||
fn get_pagination_callback_data(&self, target_page: u32) -> String {
|
|
||||||
match self {
|
|
||||||
AnnotationCallbackData::Book { id, .. } => AnnotationCallbackData::Book {
|
|
||||||
id: *id,
|
|
||||||
page: target_page,
|
|
||||||
},
|
|
||||||
AnnotationCallbackData::Author { id, .. } => AnnotationCallbackData::Author {
|
|
||||||
id: *id,
|
|
||||||
page: target_page,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnnotationFormat for AuthorAnnotation {
|
|
||||||
fn get_file(&self) -> Option<&String> {
|
|
||||||
self.file.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_text(&self) -> &str {
|
|
||||||
self.text.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_normal_text(&self) -> bool {
|
|
||||||
!self.text.replace(['\n', ' '], "").is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn download_image(
|
async fn download_image(
|
||||||
file: &String,
|
file: &String,
|
||||||
@@ -197,15 +81,12 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !annotation.is_normal_text() {
|
if !annotation.is_normal_text() {
|
||||||
return Ok(()); // TODO: error message
|
return Err(Box::new(AnnotationError { command, text: annotation.get_text().to_string() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
let annotation_text = annotation.get_text();
|
let annotation_text = annotation.get_text();
|
||||||
let chunked_text = split_text_to_chunks(annotation_text, 512);
|
let chunked_text = split_text_to_chunks(annotation_text, 512);
|
||||||
let current_text = match chunked_text.get(0) {
|
let current_text = chunked_text.get(0).unwrap();
|
||||||
Some(v) => v,
|
|
||||||
None => return Ok(()), // TODO: error message
|
|
||||||
};
|
|
||||||
|
|
||||||
let callback_data = match command {
|
let callback_data = match command {
|
||||||
AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 },
|
AnnotationCommand::Book { id } => AnnotationCallbackData::Book { id, page: 1 },
|
||||||
@@ -8,6 +8,7 @@ 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,
|
||||||
|
|||||||
Reference in New Issue
Block a user