clean warnings and add tests

This commit is contained in:
Guillermo Roche 2025-05-11 21:20:05 +02:00
parent baf7ba2ffb
commit 91f6c5f9f9
Signed by: groche97
GPG Key ID: 041FB85BEEA4B9B0
14 changed files with 262 additions and 166 deletions

View File

@ -1,6 +1,4 @@
use crate::database; use crate::database;
use sqlite::{Error, State};
pub struct Database { pub struct Database {
ins_connection: &'static database::DB_CONNECTION, //Mutex<Box<sqlite::Connection>>, ins_connection: &'static database::DB_CONNECTION, //Mutex<Box<sqlite::Connection>>,
} }

View File

@ -4,9 +4,7 @@ use std::error::Error;
use std::fmt; use std::fmt;
pub fn check_media(msg: Message) -> bool { pub fn check_media(msg: Message) -> bool {
check_gif(msg.clone()) || check_gif(msg.clone()) || check_stiker(msg.clone()) || check_photo(msg.clone())
check_stiker(msg.clone()) ||
check_photo(msg.clone())
} }
pub fn check_stiker(msg: Message) -> bool { pub fn check_stiker(msg: Message) -> bool {
@ -15,7 +13,7 @@ pub fn check_stiker(msg: Message) -> bool {
None => return false, None => return false,
}; };
let db = database::Database::get_database(); let db = database::Database::get_database();
db.media_is_banned(stiker.unique_id.as_str(),database::T_STIKER) db.media_is_banned(stiker.unique_id.as_str(), database::T_STIKER)
} }
pub fn check_gif(msg: Message) -> bool { pub fn check_gif(msg: Message) -> bool {
@ -24,7 +22,7 @@ pub fn check_gif(msg: Message) -> bool {
None => return false, None => return false,
}; };
let db = database::Database::get_database(); let db = database::Database::get_database();
db.media_is_banned(gif.unique_id.as_str(),database::T_GIF) db.media_is_banned(gif.unique_id.as_str(), database::T_GIF)
} }
pub fn check_photo(msg: Message) -> bool { pub fn check_photo(msg: Message) -> bool {
@ -32,30 +30,27 @@ pub fn check_photo(msg: Message) -> bool {
Some(s) => { Some(s) => {
let db = database::Database::get_database(); let db = database::Database::get_database();
for p in s { for p in s {
if db.media_is_banned(p.file.unique_id.as_str(),database::T_PHOTO) { if db.media_is_banned(p.file.unique_id.as_str(), database::T_PHOTO) {
return true; return true;
} }
} }
false false
}, }
None => false, None => false,
} }
} }
pub async fn ban_media( pub async fn ban_media(msg: Message, bot: Bot) -> anyhow::Result<()> {
msg: Message,
bot: Bot,
) -> anyhow::Result<()>{
if !is_admin(msg.clone(), bot).await { if !is_admin(msg.clone(), bot).await {
return get_error("No tienes permiso para hacer esto"); return get_error("No tienes permiso para hacer esto");
} }
match msg.reply_to_message() { match msg.reply_to_message() {
Some(s) => { Some(s) => {
insert_ban_stiker((*s).clone()); _ = insert_ban_stiker((*s).clone());
insert_ban_gif((*s).clone()); _ = insert_ban_gif((*s).clone());
insert_ban_photo((*s).clone()); _ = insert_ban_photo((*s).clone());
Ok(()) Ok(())
}, }
None => get_error("No has seleccionado nada"), None => get_error("No has seleccionado nada"),
} }
} }
@ -64,15 +59,15 @@ fn insert_ban_stiker(msg: Message) -> anyhow::Result<()> {
match msg.sticker() { match msg.sticker() {
Some(s) => { Some(s) => {
let db = database::Database::get_database(); let db = database::Database::get_database();
match db.add_media(s.file.unique_id.as_str(), match db.add_media(
msg.chat.id.to_string().as_str(), s.file.unique_id.as_str(),
database::T_STIKER) msg.chat.id.to_string().as_str(),
{ database::T_STIKER,
true => Ok(()), ) {
false => get_error("Stiker ya quemado"), true => Ok(()),
} false => get_error("Stiker ya quemado"),
}
}, }
None => Ok(()), None => Ok(()),
} }
} }
@ -81,15 +76,15 @@ fn insert_ban_gif(msg: Message) -> anyhow::Result<()> {
match msg.animation() { match msg.animation() {
Some(s) => { Some(s) => {
let db = database::Database::get_database(); let db = database::Database::get_database();
match db.add_media(s.file.unique_id.as_str(), match db.add_media(
msg.chat.id.to_string().as_str(), s.file.unique_id.as_str(),
database::T_GIF) msg.chat.id.to_string().as_str(),
{ database::T_GIF,
true => Ok(()), ) {
false => get_error("Gif ya quemado"), true => Ok(()),
} false => get_error("Gif ya quemado"),
}
}, }
None => Ok(()), None => Ok(()),
} }
} }
@ -98,23 +93,28 @@ fn insert_ban_photo(msg: Message) -> anyhow::Result<()> {
match msg.photo() { match msg.photo() {
Some(s) => { Some(s) => {
let db = database::Database::get_database(); let db = database::Database::get_database();
for p in s{ for p in s {
match db.add_media(p.file.unique_id.as_str(), match db.add_media(
msg.chat.id.to_string().as_str(), p.file.unique_id.as_str(),
database::T_PHOTO) msg.chat.id.to_string().as_str(),
{ database::T_PHOTO,
true => {}, ) {
true => {}
false => return get_error("Foto ya quemado"), false => return get_error("Foto ya quemado"),
} }
} }
}, }
None => {}, None => {}
} }
Ok(()) Ok(())
} }
async fn is_admin(msg: Message, bot: Bot)->bool{ async fn is_admin(msg: Message, bot: Bot) -> bool {
match bot.get_chat_member(msg.chat.id, msg.from().unwrap().id).send().await { match bot
.get_chat_member(msg.chat.id, msg.from().unwrap().id)
.send()
.await
{
Ok(k) => k.kind.is_privileged(), Ok(k) => k.kind.is_privileged(),
Err(_e) => false, Err(_e) => false,
} }
@ -133,7 +133,8 @@ impl fmt::Display for ErrorBanStiker {
impl Error for ErrorBanStiker {} impl Error for ErrorBanStiker {}
fn get_error(msg: &str)-> anyhow::Result<()> { fn get_error(msg: &str) -> anyhow::Result<()> {
Err(anyhow::Error::new(ErrorBanStiker{message: String::from(msg),})) Err(anyhow::Error::new(ErrorBanStiker {
} message: String::from(msg),
}))
}

View File

@ -1,9 +1,5 @@
#[cfg(test)]
use mockall::{automock, mock, predicate::*};
use once_cell::sync::Lazy;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::fs; use std::fs;
use std::sync::Mutex;
use std::sync::RwLock; use std::sync::RwLock;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
@ -16,12 +12,7 @@ const ALLOW_PATH: &str = "allow_users";
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
const POLE_PATH: &str = "allow_pole"; const POLE_PATH: &str = "allow_pole";
static mut LIST_IDS: Lazy<Mutex<LinkedList<String>>> = #[cfg(not(test))]
Lazy::new(|| Mutex::new(read_ids(ALLOW_PATH)));
static mut LIST_POLE_IDS: Lazy<Mutex<LinkedList<String>>> =
Lazy::new(|| Mutex::new(read_ids(POLE_PATH)));
fn read_ids<'a>(path: &str) -> LinkedList<String> { fn read_ids<'a>(path: &str) -> LinkedList<String> {
let content = fs::read_to_string(path).expect("Something went wrong reading the file"); let content = fs::read_to_string(path).expect("Something went wrong reading the file");
content content
@ -31,12 +22,22 @@ fn read_ids<'a>(path: &str) -> LinkedList<String> {
.collect::<LinkedList<String>>() .collect::<LinkedList<String>>()
} }
#[cfg(test)]
fn read_ids<'a>(path: &str) -> LinkedList<String> {
let mut ret = LinkedList::new();
ret.push_front("id_1".to_string());
ret.push_front("id_2".to_string());
if !path.eq(POLE_PATH) {
ret.push_front("id_3".to_string());
}
ret
}
pub struct GroupPermissions { pub struct GroupPermissions {
aproved_groups: RwLock<LinkedList<String>>, aproved_groups: RwLock<LinkedList<String>>,
party_groups: RwLock<LinkedList<String>>, party_groups: RwLock<LinkedList<String>>,
} }
#[cfg_attr(test, automock)]
impl GroupPermissions { impl GroupPermissions {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -54,20 +55,16 @@ impl GroupPermissions {
} }
} }
pub fn compare(id: &String) -> bool { #[test]
let ret: bool; pub fn test_compare() {
unsafe { let gp = GroupPermissions::new();
ret = LIST_IDS.lock().unwrap().contains(id); assert_eq!(true, gp.compare(&"id_3".to_string()));
log::info!("{}", id); assert_eq!(false, gp.compare(&"id_4".to_string()));
}
ret
} }
pub fn compare_pole(id: &String) -> bool { #[test]
let ret: bool; pub fn test_compare_pole() {
unsafe { let gp = GroupPermissions::new();
ret = LIST_POLE_IDS.lock().unwrap().contains(id); assert_eq!(true, gp.compar_party(&"id_2".to_string()));
log::info!("{}", id); assert_eq!(false, gp.compar_party(&"id_3".to_string()));
}
ret
} }

View File

@ -2,19 +2,19 @@ use lazy_static::lazy_static;
use std::sync::Mutex; use std::sync::Mutex;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
const db_path : &str ="/opt/mini_admin_bot/data.db"; const DB_PATH: &str = "/opt/mini_admin_bot/data.db";
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
const db_path : &str ="polesDB"; const DB_PATH: &str = "polesDB";
#[cfg(not(test))] #[cfg(not(test))]
lazy_static! { lazy_static! {
pub static ref DB_CONNECTION : Mutex<Box<sqlite::Connection>> = pub static ref DB_CONNECTION: Mutex<Box<sqlite::Connection>> =
Mutex::new(Box::new(sqlite::open(db_path).unwrap())); Mutex::new(Box::new(sqlite::open(DB_PATH).unwrap()));
} }
#[cfg(test)] #[cfg(test)]
lazy_static! { lazy_static! {
pub static ref DB_CONNECTION : Mutex<Box<sqlite::Connection>> = pub static ref DB_CONNECTION: Mutex<Box<sqlite::Connection>> =
Mutex::new(Box::new(sqlite::open("polesDBTest").unwrap())); Mutex::new(Box::new(sqlite::open("polesDBTest").unwrap()));
} }

View File

@ -6,7 +6,7 @@ pub mod check_permissions;
mod database; mod database;
mod filter_files; mod filter_files;
mod pole_dialogue; mod pole_dialogue;
mod rewrite_links; pub mod rewrite_links;
mod spoiler_mangas; mod spoiler_mangas;
mod telegram_utils; mod telegram_utils;
@ -39,7 +39,6 @@ pub async fn run() {
let permissions = Arc::new(check_permissions::GroupPermissions::new()); let permissions = Arc::new(check_permissions::GroupPermissions::new());
let p1 = Arc::clone(&permissions); let p1 = Arc::clone(&permissions);
let p2 = Arc::clone(&permissions); let p2 = Arc::clone(&permissions);
//Command::repl(bot.clone(), answer).await;
let handler = Update::filter_message() let handler = Update::filter_message()
.branch( .branch(
dptree::filter(move |msg: Message| !p1.compare(&msg.chat.id.to_string())).endpoint( dptree::filter(move |msg: Message| !p1.compare(&msg.chat.id.to_string())).endpoint(
@ -72,14 +71,12 @@ pub async fn run() {
Ok(()) Ok(())
}), }),
) )
/*
Now is useless because the group doesn't make any spoiler
.branch( .branch(
dptree::filter(move |msg: Message| { dptree::filter(move |msg: Message| {
(is_photo(msg.clone()) && p2.compar_party(&msg.chat.id.to_string())) is_photo(msg.clone()) && p2.compar_party(&msg.chat.id.to_string())
}) })
.endpoint(|msg: Message, bot: Bot| spoiler_mangas::check_image(msg, bot)), .endpoint(|msg: Message, bot: Bot| spoiler_mangas::check_image(msg, bot)),
)*/ )
.branch( .branch(
dptree::filter(move |msg: Message| filter_files::analyce_name::check_file(msg.clone())) dptree::filter(move |msg: Message| filter_files::analyce_name::check_file(msg.clone()))
.endpoint(|msg: Message, bot: Bot| filter_files::action::take_actions(msg, bot)), .endpoint(|msg: Message, bot: Bot| filter_files::action::take_actions(msg, bot)),
@ -131,21 +128,21 @@ fn is_media(msg: Message) -> bool {
fn is_stiker(msg: Message) -> bool { fn is_stiker(msg: Message) -> bool {
match msg.sticker() { match msg.sticker() {
Some(s) => true, Some(_) => true,
None => false, None => false,
} }
} }
fn is_gif(msg: Message) -> bool { fn is_gif(msg: Message) -> bool {
match msg.animation() { match msg.animation() {
Some(s) => true, Some(_) => true,
None => false, None => false,
} }
} }
fn is_photo(msg: Message) -> bool { fn is_photo(msg: Message) -> bool {
match msg.photo() { match msg.photo() {
Some(s) => true, Some(_) => true,
None => false, None => false,
} }
} }

View File

@ -3,7 +3,7 @@ use sqlite::{Error, State};
#[cfg(test)] #[cfg(test)]
fn drop_all(con: &sqlite::Connection) -> bool { fn drop_all(con: &sqlite::Connection) -> bool {
let mut result: bool; let result: bool;
match con.execute( match con.execute(
" "
DROP TABLE IF EXISTS poles; DROP TABLE IF EXISTS poles;
@ -188,7 +188,7 @@ impl<'a> DatabasePole {
.unwrap(); .unwrap();
statement.bind((1, group_id)).unwrap(); statement.bind((1, group_id)).unwrap();
let mut ret = Vec::new(); let mut ret = Vec::new();
for i in 1..10 { for _ in 1..10 {
match statement.next().unwrap() { match statement.next().unwrap() {
State::Row => ret.push(( State::Row => ret.push((
statement.read::<i64, _>(0).unwrap(), statement.read::<i64, _>(0).unwrap(),

View File

@ -59,7 +59,7 @@ fn do_fail(group_id: &str, user_id: &str, user_name: &str) {
); );
} }
fn check_user_points(msg: &teloxide::prelude::Message, rw: Rewards) -> bool { fn check_user_points(msg: &Message, rw: Rewards) -> bool {
let data: database::DatabasePole = database::DatabasePole::get_database(); let data: database::DatabasePole = database::DatabasePole::get_database();
let ret = data.check_user_pole( let ret = data.check_user_pole(
&msg.chat.id.to_string(), &msg.chat.id.to_string(),
@ -75,7 +75,7 @@ enum Rewards {
FAIL = 1, FAIL = 1,
} }
fn check_group_points(msg: &teloxide::prelude::Message, rw: Rewards) -> bool { fn check_group_points(msg: &Message, rw: Rewards) -> bool {
let data: database::DatabasePole = database::DatabasePole::get_database(); let data: database::DatabasePole = database::DatabasePole::get_database();
let ret = data.check_group_points(&msg.chat.id.to_string(), &get_actual_day()); let ret = data.check_group_points(&msg.chat.id.to_string(), &get_actual_day());
match rw { match rw {
@ -86,7 +86,7 @@ fn check_group_points(msg: &teloxide::prelude::Message, rw: Rewards) -> bool {
} }
pub async fn exe_pole(msg: Message, bot: Bot) -> anyhow::Result<()> { pub async fn exe_pole(msg: Message, bot: Bot) -> anyhow::Result<()> {
let text_lower = match msg.text() { let _text_lower = match msg.text() {
Some(t) => t.to_lowercase(), Some(t) => t.to_lowercase(),
None => return Ok(()), None => return Ok(()),
}; };

View File

@ -2,17 +2,19 @@ use phf::phf_map;
use curl::easy::Easy; use curl::easy::Easy;
static URLS: phf::Map<&'static str, &'static str> = phf_map! { static URLS: phf::Map<&'static str, (&'static str, bool)> = phf_map! {
"www.tiktok.com" => "vxtiktok.com", "www.tiktok.com" => ("vxtiktok.com", false),
"vm.tiktok.com" => "vxtiktok.com", "vm.tiktok.com" => ("vxtiktok.com", true),
"lite.tiktok.com" => "vxtiktok.com", "vt.tiktok.com" => ("vxtiktok.com", true),
"https://x.com" => "https://fxtwitter.com", "lite.tiktok.com" => ("vxtiktok.com", true),
"https://twitter.com" => "https://fxtwitter.com", "www.instagram.com" => ("ddinstagram.com", false),
"https://x.com" => ("https://fxtwitter.com", false),
"https://twitter.com" => ("https://fxtwitter.com",false),
}; };
pub fn filter_string(url: String, domain: String) -> Option<String> { pub fn filter_string(url: String, domain: String) -> Option<String> {
let ret = match URLS.get(domain.as_str()) { let ret = match URLS.get(domain.as_str()) {
Some(fixed_domain) => url.replacen(domain.as_str(),fixed_domain,1), Some(fixed_domain) => url.replacen(domain.as_str(), fixed_domain.0, 1),
None => return None, None => return None,
}; };
@ -22,19 +24,10 @@ pub fn filter_string(url: String, domain: String) -> Option<String> {
}) })
} }
fn get_domain(url: String) -> Option<String> {
for domain in URLS.keys() {
if url.contains(domain) {
return Some(String::from(*domain))
}
}
None
}
pub fn check_domains(text: String) -> bool { pub fn check_domains(text: String) -> bool {
for domain in URLS.keys() { for domain in URLS.keys() {
if text.contains(domain) { if text.contains(domain) {
return true return true;
} }
} }
false false
@ -44,14 +37,14 @@ pub fn get_domain_from_text(text: String) -> (String, String) {
for word in text.split(' ') { for word in text.split(' ') {
for domain in URLS.keys() { for domain in URLS.keys() {
if word.contains(domain) { if word.contains(domain) {
if String::from(*domain).contains("vm.tiktok.com") || String::from(*domain).contains("lite.tiktok.com") { if URLS[domain].1 {
let url = match get_tiktok_redirection(String::from(word)) { let url = match get_tiktok_redirection(String::from(word)) {
Ok(furl) => furl, Ok(furl) => furl,
Err(_e) => String::from(word), Err(_e) => String::from(word),
}; };
return (String::from(url), String::from("www.tiktok.com")) return (String::from(url), String::from("www.tiktok.com"));
} }
return (String::from(word), String::from(*domain)) return (String::from(word), String::from(*domain));
} }
} }
} }
@ -67,3 +60,37 @@ fn get_tiktok_redirection(url: String) -> Result<String, curl::Error> {
None => url, None => url,
}) })
} }
#[test]
fn test_check_domains() {
assert_eq!(
true,
check_domains("https://vm.tiktok.com/ZGeouHd2t/".to_string())
);
assert_eq!(
false,
check_domains("https://randomwebsite.com".to_string())
);
}
#[test]
fn test_rewrite_clean_tiktok() {
let domain = get_domain_from_text(
"https://www.tiktok.com/@kramkang/video/7417808362957589778".to_string(),
);
assert_eq!(domain.1, "www.tiktok.com");
assert_eq!(
domain.0,
"https://www.tiktok.com/@kramkang/video/7417808362957589778"
);
}
#[test]
fn test_rewrite_refered_tiktok() {
let url_and_domain = get_domain_from_text("https://vm.tiktok.com/ZGeouHd2t/".to_string());
let domain = filter_string(url_and_domain.0, url_and_domain.1);
assert_eq!(
domain,
Some("https://vxtiktok.com/@/video/7417808362957589778".to_string())
);
}

View File

@ -1,39 +1,39 @@
use crate::telegram_utils::*;
use image::{DynamicImage, GenericImageView};
use std::io::Cursor;
use teloxide::prelude::*; use teloxide::prelude::*;
use teloxide::{ use teloxide::{
net::Download, net::Download,
requests::{Requester,MultipartRequest},
types::{MessageEntity, MessageEntityKind},
payloads::SendPhoto, payloads::SendPhoto,
requests::{MultipartRequest, Requester},
types::{MessageEntity, MessageEntityKind},
Bot, Bot,
}; };
use image::{DynamicImage,GenericImageView};
use std::io::Cursor;
use tokio::io::BufStream; use tokio::io::BufStream;
use crate::telegram_utils::*;
const TOLERANCE: i16 = 10; const TOLERANCE: i16 = 10;
const PERCENT_ACEPTANCE: u8 = 5; const PERCENT_ACEPTANCE: u8 = 5;
const WHITE : u8 = 160; const WHITE: u8 = 160;
const WHITE_ACEPTANCE: u8 = 40; const WHITE_ACEPTANCE: u8 = 40;
fn check_percent(img: DynamicImage) -> (u8,u8) { fn check_percent(img: DynamicImage) -> (u8, u8) {
let mut cont = 0; let mut cont = 0;
let mut cont_wite = 0; let mut cont_wite = 0;
for pixel in img.pixels() { for pixel in img.pixels() {
let diference_rg:i16 = (pixel.2[0] as i16 - pixel.2[1] as i16).abs(); let diference_rg: i16 = (pixel.2[0] as i16 - pixel.2[1] as i16).abs();
let diference_gb:i16 = (pixel.2[1] as i16 - pixel.2[2] as i16).abs(); let diference_gb: i16 = (pixel.2[1] as i16 - pixel.2[2] as i16).abs();
let diference_br:i16 = (pixel.2[0] as i16 - pixel.2[2] as i16).abs(); let diference_br: i16 = (pixel.2[0] as i16 - pixel.2[2] as i16).abs();
let diference_pixel = diference_rg + diference_gb + diference_br; let diference_pixel = diference_rg + diference_gb + diference_br;
if pixel.2[0] > WHITE && pixel.2[1] > WHITE && pixel.2[2] > WHITE { if pixel.2[0] > WHITE && pixel.2[1] > WHITE && pixel.2[2] > WHITE {
cont_wite+=1 cont_wite += 1
} }
if diference_pixel > TOLERANCE { if diference_pixel > TOLERANCE {
cont += 1; cont += 1;
} }
} }
let percent = ((cont as f64 /(img.width() * img.height()) as f64)*100.0) as u8; let percent = ((cont as f64 / (img.width() * img.height()) as f64) * 100.0) as u8;
let white = ((cont_wite as f64 /(img.width() * img.height()) as f64)*100.0) as u8; let white = ((cont_wite as f64 / (img.width() * img.height()) as f64) * 100.0) as u8;
(percent,white) (percent, white)
} }
fn generate_from(msg: &Message, append_text: bool) -> String { fn generate_from(msg: &Message, append_text: bool) -> String {
@ -45,42 +45,58 @@ fn generate_from(msg: &Message, append_text: bool) -> String {
msg_from msg_from
} }
pub fn append_text(new_msg: MultipartRequest<SendPhoto>, old_msg: Message) -> MultipartRequest<SendPhoto> { pub fn append_text(
new_msg: MultipartRequest<SendPhoto>,
old_msg: Message,
) -> MultipartRequest<SendPhoto> {
let msg_from; let msg_from;
match old_msg.caption() { match old_msg.caption() {
Some(caption) => { Some(caption) => {
msg_from = generate_from(&old_msg, true); msg_from = generate_from(&old_msg, true);
let from_offset = msg_from.encode_utf16().count(); let from_offset = msg_from.encode_utf16().count();
//new_msg.caption(caption).caption_entities(generate_entity_spoiler(caption.len())).has_spoiler(true) //new_msg.caption(caption).caption_entities(generate_entity_spoiler(caption.len())).has_spoiler(true)
new_msg.caption(msg_from + caption).caption_entities(generate_captions(old_msg.caption_entities().unwrap().to_vec(),caption.encode_utf16().count(), from_offset)) new_msg
}, .caption(msg_from + caption)
.caption_entities(generate_captions(
old_msg.caption_entities().unwrap().to_vec(),
caption.encode_utf16().count(),
from_offset,
))
}
None => { None => {
msg_from = generate_from(&old_msg, false); msg_from = generate_from(&old_msg, false);
new_msg.caption(msg_from) new_msg.caption(msg_from)
}, }
} }
} }
pub fn generate_captions(captions: Vec<MessageEntity>, len: usize, offset: usize) -> Vec<MessageEntity> { pub fn generate_captions(
captions: Vec<MessageEntity>,
len: usize,
offset: usize,
) -> Vec<MessageEntity> {
let mut ret = Vec::new(); let mut ret = Vec::new();
let mut last_hole = 0; let mut last_hole = 0;
for mut cap in captions { for mut cap in captions {
match cap.kind { match cap.kind {
MessageEntityKind::TextMention { user: ref User } => { MessageEntityKind::TextMention { user: ref _user } => {
if cap.offset == 0 { if cap.offset == 0 {
last_hole = cap.length; last_hole = cap.length;
}else if cap.offset >= last_hole { } else if cap.offset >= last_hole {
ret.push(MessageEntity::spoiler(last_hole+offset,cap.offset-last_hole)); ret.push(MessageEntity::spoiler(
last_hole + offset,
cap.offset - last_hole,
));
last_hole = cap.offset + cap.length; last_hole = cap.offset + cap.length;
} }
cap.offset += offset; cap.offset += offset;
ret.push(cap.clone()); ret.push(cap.clone());
}, }
_ => continue, _ => continue,
} }
} }
if last_hole < len { if last_hole < len {
ret.push(MessageEntity::spoiler(last_hole+offset,len - last_hole)); ret.push(MessageEntity::spoiler(last_hole + offset, len - last_hole));
} }
ret ret
} }
@ -89,7 +105,7 @@ pub async fn check_image(msg: Message, bot: Bot) -> anyhow::Result<()> {
Some(s) => { Some(s) => {
let mut percent = 0; let mut percent = 0;
let mut white = 0; let mut white = 0;
let mut id : Option<(String, u32)> = None; let mut id: Option<(String, u32)> = None;
let mut failed = true; let mut failed = true;
for p in s { for p in s {
let file = bot.get_file(p.file.id.clone()).await?; let file = bot.get_file(p.file.id.clone()).await?;
@ -112,30 +128,34 @@ pub async fn check_image(msg: Message, bot: Bot) -> anyhow::Result<()> {
} else { } else {
Some(i) Some(i)
} }
}, }
None => Some((p.clone().file.id, p.clone().width)), None => Some((p.clone().file.id, p.clone().width)),
} }
} }
}, }
Err(_e) => continue, Err(_e) => continue,
} }
} }
if !failed && percent < PERCENT_ACEPTANCE && white > WHITE_ACEPTANCE { if !failed && percent < PERCENT_ACEPTANCE && white > WHITE_ACEPTANCE {
bot.delete_message(msg.chat.id, msg.id).await?; bot.delete_message(msg.chat.id, msg.id).await?;
let response = match id { let response = match id {
Some(i) => bot.send_photo(msg.chat.id, teloxide::types::InputFile::file_id(i.0)).has_spoiler(true), Some(i) => bot
None => { .send_photo(msg.chat.id, teloxide::types::InputFile::file_id(i.0))
match msg.photo().unwrap().iter().max_by_key(|p| p.width) { .has_spoiler(true),
Some(f) => bot.send_photo(msg.chat.id, teloxide::types::InputFile::file_id(f.file.id.clone())).has_spoiler(true), None => match msg.photo().unwrap().iter().max_by_key(|p| p.width) {
None => return Ok(()), Some(f) => bot
} .send_photo(
msg.chat.id,
teloxide::types::InputFile::file_id(f.file.id.clone()),
)
.has_spoiler(true),
None => return Ok(()),
}, },
}; };
append_text(response, msg).await?; append_text(response, msg).await?;
} }
Ok(()) Ok(())
}, }
None => Ok(()), None => Ok(()),
} }
} }

View File

@ -1,6 +0,0 @@
use mini_admin_bot::ban_stiker;
#[test]
pub fn db_media_is_banned() {
assert_eq!(0, 0);
}

1
tests/mocks/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod teloxide_mocks;

View File

@ -0,0 +1,54 @@
use chrono::DateTime;
use teloxide::types::{
Chat, ChatId, ChatKind, ChatPublic, MediaKind::Text, MediaText, Message, MessageCommon,
MessageId, MessageKind::Common, PublicChatGroup, PublicChatKind::Group,
};
pub fn generate_msg_mock(text: &str) -> Message {
let chat_id = ChatId { 0: 0 };
let public_chat_kind = Group(PublicChatGroup { permissions: None });
let chat_kind = ChatKind::Public(ChatPublic {
title: None,
kind: public_chat_kind,
description: None,
invite_link: None,
has_protected_content: None,
});
let chat = Chat {
has_aggressive_anti_spam_enabled: false,
has_hidden_members: false,
id: chat_id,
pinned_message: None,
photo: None,
message_auto_delete_time: None,
kind: chat_kind,
};
let media_kind = Text(MediaText {
text: text.to_string(),
entities: Vec::new(),
});
let message_kind = Common(MessageCommon {
from: None,
sender_chat: None,
author_signature: None,
forward: None,
reply_to_message: None,
edit_date: None,
media_kind,
reply_markup: None,
is_topic_message: false,
is_automatic_forward: false,
has_protected_content: false,
});
Message {
chat,
via_bot: None,
thread_id: None,
kind: message_kind,
date: DateTime::from_timestamp_nanos(0),
id: MessageId { 0: 0 },
}
}

View File

@ -1,12 +0,0 @@
use mini_admin_bot::check_permissions;
use mockall::{automock, mock, predicate::*};
#[test]
fn test_permissions() {
let mut mock = check_permissions::MockGroupPermissions::new();
mock.expect_compare()
.with("user_id")
.times(1)
.returning(true);
assert_eq!(true, mock.compare("user_id"));
}

View File

@ -0,0 +1,19 @@
use mini_admin_bot::rewrite_links;
mod mocks;
#[test]
pub fn test_contain_links() {
assert_eq!(
false,
rewrite_links::check_contain_links::contain_links(
mocks::teloxide_mocks::generate_msg_mock("hola")
)
);
assert_eq!(
true,
rewrite_links::check_contain_links::contain_links(
mocks::teloxide_mocks::generate_msg_mock("https://x.com")
)
);
}