restarting project

This commit is contained in:
2023-11-11 17:38:00 +01:00
commit 4b9580100a
19 changed files with 3664 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
use sqlite::{State, Error};
use crate::database;
pub struct Database {
ins_connection: &'static database::DB_CONNECTION,//Mutex<Box<sqlite::Connection>>,
}
pub const T_STIKER: i64 = 1;
pub const T_GIF: i64 = 2;
pub const T_PHOTO: i64 = 3;
impl<'a> Database {
pub fn get_database()->Self{
let ret = Self{
ins_connection: &database::DB_CONNECTION,
};
ret.create_table();
ret
}
fn create_table(&self) -> bool {
match self.ins_connection.lock().unwrap()
.execute(
"
CREATE TABLE IF NOT EXISTS bmedia ( id_group TEXT, id_stiker TEXT, media_type INTEGER, id INTEGER PRIMARY KEY AUTOINCREMENT);
",
)
{
Ok(_a) => return true,
Err(e) => {log::error!("error stiker {}",e);return false},
}
}
pub fn media_is_banned(&self, stiker_id: &str, media_type: i64) -> bool {
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT id_stiker FROM bmedia WHERE id_stiker = ? and media_type=?")
.unwrap();
statement.bind(1, stiker_id).unwrap();
statement.bind(2, media_type).unwrap();
statement.next().unwrap();
match statement.read::<String>(0) {
Ok(_s) => true,
Err(_e) => false,
}
}
pub fn add_media(&self, stiker_id: &str, group_id: &str, media_type: i64)->bool{
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("INSERT INTO bmedia (id_stiker, id_group, media_type) VALUES (?,?,?)")
.unwrap();
statement.bind(1, stiker_id).unwrap();
statement.bind(2, group_id).unwrap();
statement.bind(3, media_type).unwrap();
match statement.next(){
Ok(s)=>true,
Err(e)=>false,
}
}
}

139
src/ban_stiker/mod.rs Normal file
View File

@@ -0,0 +1,139 @@
use teloxide::prelude::*;
mod database;
use std::error::Error;
use std::fmt;
pub fn check_media(msg: Message) -> bool {
check_gif(msg.clone()) ||
check_stiker(msg.clone()) ||
check_photo(msg.clone())
}
pub fn check_stiker(msg: Message) -> bool {
let stiker = match msg.sticker() {
Some(s) => s.file.clone(),
None => return false,
};
let db = database::Database::get_database();
db.media_is_banned(stiker.unique_id.as_str(),database::T_STIKER)
}
pub fn check_gif(msg: Message) -> bool {
let gif = match msg.animation() {
Some(s) => s.file.clone(),
None => return false,
};
let db = database::Database::get_database();
db.media_is_banned(gif.unique_id.as_str(),database::T_GIF)
}
pub fn check_photo(msg: Message) -> bool {
match msg.photo() {
Some(s) => {
let db = database::Database::get_database();
for p in s {
if db.media_is_banned(p.file.unique_id.as_str(),database::T_PHOTO) {
return true;
}
}
false
},
None => false,
}
}
pub async fn ban_media(
msg: Message,
bot: Bot,
) -> anyhow::Result<()>{
if !is_admin(msg.clone(), bot).await {
return get_error("No tienes permiso para hacer esto");
}
match msg.reply_to_message() {
Some(s) => {
insert_ban_stiker((*s).clone());
insert_ban_gif((*s).clone());
insert_ban_photo((*s).clone());
Ok(())
},
None => get_error("No has seleccionado nada"),
}
}
fn insert_ban_stiker(msg: Message) -> anyhow::Result<()> {
match msg.sticker() {
Some(s) => {
let db = database::Database::get_database();
match db.add_media(s.file.unique_id.as_str(),
msg.chat.id.to_string().as_str(),
database::T_STIKER)
{
true => Ok(()),
false => get_error("Stiker ya quemado"),
}
},
None => Ok(()),
}
}
fn insert_ban_gif(msg: Message) -> anyhow::Result<()> {
match msg.animation() {
Some(s) => {
let db = database::Database::get_database();
match db.add_media(s.file.unique_id.as_str(),
msg.chat.id.to_string().as_str(),
database::T_GIF)
{
true => Ok(()),
false => get_error("Gif ya quemado"),
}
},
None => Ok(()),
}
}
fn insert_ban_photo(msg: Message) -> anyhow::Result<()> {
match msg.photo() {
Some(s) => {
let db = database::Database::get_database();
for p in s{
match db.add_media(p.file.unique_id.as_str(),
msg.chat.id.to_string().as_str(),
database::T_PHOTO)
{
true => {},
false => return get_error("Foto ya quemado"),
}
}
},
None => {},
}
Ok(())
}
async fn is_admin(msg: Message, bot: Bot)->bool{
match bot.get_chat_member(msg.chat.id, msg.from().unwrap().id).send().await {
Ok(k) => k.kind.is_privileged(),
Err(_e) => false,
}
}
#[derive(Debug)]
struct ErrorBanStiker {
message: String,
}
impl fmt::Display for ErrorBanStiker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl Error for ErrorBanStiker {}
fn get_error(msg: &str)-> anyhow::Result<()> {
Err(anyhow::Error::new(ErrorBanStiker{message: String::from(msg),}))
}

View File

@@ -0,0 +1,66 @@
use std::fs;
use std::collections::LinkedList;
use std::sync::Mutex;
use std::sync::RwLock;
use once_cell::sync::Lazy;
#[cfg(not(debug_assertions))]
const ALLOW_PATH : &str ="/opt/mini_admin_bot/allow_users";
#[cfg(not(debug_assertions))]
const POLE_PATH : &str ="/opt/mini_admin_bot/allow_pole";
#[cfg(debug_assertions)]
const ALLOW_PATH : &str ="allow_users";
#[cfg(debug_assertions)]
const POLE_PATH : &str ="allow_pole";
static mut LIST_IDS: Lazy<Mutex<LinkedList<String>>> = 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> {
let content = fs::read_to_string(path).expect("Something went wrong reading the file");
content.split('\n').into_iter().map(|item| format!("{}", item)).collect::<LinkedList<String>>()
}
pub struct group_permissions {
aproved_groups: RwLock<LinkedList<String>>,
party_groups: RwLock<LinkedList<String>>,
}
impl group_permissions {
pub fn new() -> Self {
Self{
aproved_groups: RwLock::new(read_ids(ALLOW_PATH)),
party_groups: RwLock::new(read_ids(POLE_PATH)),
}
}
pub fn compare(&self, id: &String) -> bool {
self.aproved_groups.read().unwrap().contains(id)
}
pub fn compar_party(&self, id: &String) -> bool {
self.party_groups.read().unwrap().contains(id)
}
}
pub fn compare(id: &String) -> bool{
let ret: bool;
unsafe {
ret=LIST_IDS.lock().unwrap().contains(id);
log::info!("{}",id);
}
ret
}
pub fn compare_pole(id: &String) -> bool{
let ret: bool;
unsafe {
ret=LIST_POLE_IDS.lock().unwrap().contains(id);
log::info!("{}",id);
}
ret
}

20
src/database/mod.rs Normal file
View File

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

138
src/main.rs Normal file
View File

@@ -0,0 +1,138 @@
use teloxide::{prelude::*, utils::command::BotCommands};
use std::sync::Arc;
mod check_permissions;
mod pole_dialogue;
mod ban_stiker;
mod database;
mod spoiler_mangas;
#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase", description = "These commands are supported:")]
enum Command {
#[command(description = "ban a stiker")]
Torquemada,
#[command(description = "display this text.")]
Help,
#[command(description = "list pole points" )]
Top
}
#[tokio::main]
async fn main() {
run().await;
}
async fn run(){
//teloxide::enable_logging!();
pretty_env_logger::init();
log::info!("Starting bot");
let bot = Bot::from_env();
let permissions = Arc::new(check_permissions::group_permissions::new());
let p1 = Arc::clone(&permissions);
let p2 = Arc::clone(&permissions);
//Command::repl(bot.clone(), answer).await;
let handler = Update::filter_message()
.branch(
dptree::filter(move |msg: Message| !p1.compare(&msg.chat.id.to_string()))
.endpoint(|msg: Message, bot: Bot| async move {
if !msg.chat.is_private() {
println!("{}",msg.chat.id.0);
bot.leave_chat(msg.chat.id).await?;
}
Ok(())
}
),
)
.branch(
dptree::filter(|msg: Message| is_channel_user(msg))
.endpoint(|msg: Message, bot: Bot| async move {
bot.delete_message(msg.chat.id, msg.id).await?;
Ok(())
}
),
)
.branch(
Update::filter_message()
.filter_command::<Command>()
.endpoint(|msg: Message, bot: Bot, command: Command | answer(bot, msg, command)),
)
.branch(
dptree::filter(|msg: Message| is_media(msg.clone()) && ban_stiker::check_media(msg))
.endpoint(|msg: Message, bot: Bot| async move {
bot.delete_message(msg.chat.id, msg.id).await?;
Ok(())
}
),
)
.branch(
dptree::filter(move |msg: Message| (is_photo(msg.clone()) && p2.compar_party(&msg.chat.id.to_string())))
.endpoint(|msg: Message, bot: Bot| spoiler_mangas::check_image(msg, bot)),
)
.branch(
dptree::filter(move |msg: Message| permissions.compar_party(&msg.chat.id.to_string()))
.endpoint(|msg: Message, bot: Bot| pole_dialogue::exe_pole(msg, bot)),
);
Dispatcher::builder(
bot,
handler,
)
.build()
.dispatch()
.await;
}
async fn answer(
bot: Bot,
msg: Message,
command: Command,
) -> anyhow::Result<()> {
match command {
Command::Torquemada => {
match ban_stiker::ban_media(msg.clone(), bot.clone()).await {
Ok(_o) => bot.send_message(msg.chat.id, "Otro fichero que se va a la hoguera").await?,
Err(e) => bot.send_message(msg.chat.id, e.to_string()).await?,
}
},
Command::Help => bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?,
Command::Top => pole_dialogue::get_top(msg, bot).await?,
};
Ok(())
}
fn is_media(msg: Message) -> bool {
is_stiker(msg.clone()) || is_gif(msg.clone()) || is_photo(msg.clone())
}
fn is_stiker(msg: Message) -> bool {
match msg.sticker() {
Some(s) => true,
None => false,
}
}
fn is_gif(msg: Message) -> bool {
match msg.animation() {
Some(s) => true,
None => false,
}
}
fn is_photo(msg: Message) -> bool {
match msg.photo() {
Some(s) => true,
None => false,
}
}
fn is_channel_user(msg: Message)->bool{
match msg.from() {
Some(u) => u.is_channel(),
None => false,
}
}

33
src/meson.build Normal file
View File

@@ -0,0 +1,33 @@
test_bot_sources = [
cargo_sources,
'main.rs',
'check_permissions/mod.rs',
'pole_dialogue/mod.rs',
'pole_dialogue/database.rs',
'database/mod.rs',
'ban_stiker/mod.rs',
'ban_stiker/database.rs',
'spoiler_mangas/mod.rs',
]
test_bot_deps = [
]
cargo_script = find_program(join_paths(meson.source_root(), 'build-aux/cargo.sh'))
cargo_release = custom_target(
'cargo-build',
build_by_default: true,
input: test_bot_sources,
output: meson.project_name(),
console: true,
install: true,
install_dir: get_option('bindir'),
command: [
cargo_script,
meson.build_root(),
meson.source_root(),
'@OUTPUT@',
get_option('buildtype'),
meson.project_name(),
]
)

View File

@@ -0,0 +1,208 @@
use sqlite::{State, Error};
use crate::database;
#[cfg(test)]
fn drop_all(con: &sqlite::Connection) -> bool {
let mut result: bool;
match con
.execute(
"
DROP TABLE IF EXISTS poles;
",
)
{
Ok(_a) => result = true,
Err(e) => {log::error!("error drop pole {}",e);result = false},
};
match con
.execute(
"
DROP TABLE IF EXISTS last;
",
)
{
Ok(_a) => result && true,
Err(e) => {log::error!("error drop last {}",e);result && false},
}
}
fn create_databases(con: &sqlite::Connection) -> bool {
return create_poles_database(con) && create_last_database(con)
}
fn create_poles_database(con: &sqlite::Connection) -> bool {
match con
.execute(
"
CREATE TABLE IF NOT EXISTS poles (npole INTEGER, user TEXT, user_name TEXT, id_group TEXT, id INTEGER PRIMARY KEY AUTOINCREMENT);
",
)
{
Ok(_a) => return true,
Err(e) => {log::error!("error pole {}",e);return false},
}
}
fn create_last_database(con: &sqlite::Connection) -> bool {
match con
.execute(
"
CREATE TABLE IF NOT EXISTS last (date TEXT, id_group TEXT, user TEXT, points INTEGER, id INTEGER PRIMARY KEY AUTOINCREMENT);
",
)
{
Ok(_a) => return true,
Err(e) => {log::error!("error last {}",e);return false},
}
}
pub struct DatabasePole {
ins_connection: &'static database::DB_CONNECTION,//Mutex<Box<sqlite::Connection>>,
}
impl<'a> DatabasePole{
#[cfg(not(test))]
pub fn get_database()->Self{
let ret = Self{
ins_connection: &database::DB_CONNECTION,
};
let raw = &**ret.ins_connection.lock().unwrap();
create_databases(raw);
//log::info!("create databases {}",ret.createDatabases());
ret
}
#[cfg(test)]
pub fn get_database()->Self{
let ret = Self{
ins_connection: &DB_CONNECTION,
};
let raw = &**ret.ins_connection.lock().unwrap();
drop_all(raw);
create_databases(raw);
//log::info!("create databases {}",ret.createDatabases());
ret
}
pub fn last_pole(&self, group_id : &str) -> Result<String, Error>{
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT date FROM last WHERE id_group = ? order by date desc")
.unwrap();
statement.bind(1, group_id).unwrap();
statement.next().unwrap();
statement.read::<String>(0)
}
pub fn write_points(&self, group_id : &str, user_id: &str, user_name: &str, date: &str, points: i64){
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT npole FROM poles WHERE user = ? AND id_group = ?")
.unwrap();
statement.bind(1, user_id).unwrap();
statement.bind(2, group_id).unwrap();
match statement.next().unwrap() {
State::Row => {
let mut statement2 = builder
.prepare("UPDATE poles SET npole=? WHERE user=? AND id_group=?")
.unwrap();
statement2.bind(1, statement.read::<i64>(0).unwrap()+points).unwrap();
statement2.bind(2, user_id).unwrap();
statement2.bind(3, group_id).unwrap();
statement2.next().unwrap();
}
State::Done => {
let mut statement2 = builder
.prepare("INSERT INTO poles (npole, user, user_name, id_group) VALUES (?,?,?,?)")
.unwrap();
statement2.bind(1, points).unwrap();
statement2.bind(2, user_id).unwrap();
statement2.bind(3, user_name).unwrap();
statement2.bind(4, group_id).unwrap();
statement2.next().unwrap();
}
};
let mut statement3 = builder
.prepare("INSERT INTO last (date, id_group, user, points) VALUES (?,?,?,?)")
.unwrap();
statement3.bind(1, date).unwrap();
statement3.bind(2, group_id).unwrap();
statement3.bind(3, user_id).unwrap();
statement3.bind(4, points).unwrap();
statement3.next().unwrap();
//while let State::Row = statement3.next().unwrap() {
// log::info!("name = {}", statement.read::<String>(0).unwrap());
//}
}
pub fn check_user_pole(&self, group_id : &str, user_id: &str, date: &str) -> i64{
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT points FROM last WHERE id_group = ? AND user = ? AND date = :date")
.unwrap();
statement.bind(1, group_id).unwrap();
statement.bind(2, user_id).unwrap();
statement.bind_by_name(":date", date).unwrap();
match statement.next().unwrap() {
State::Row => statement.read::<i64>(0).unwrap(),
State::Done => 0,
}
}
pub fn check_group_points(&self, group_id : &str, date: &str) -> i64{
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT points FROM last WHERE id_group = ? AND date = :date")
.unwrap();
statement.bind(1, group_id).unwrap();
statement.bind_by_name(":date", date).unwrap();
match statement.next().unwrap() {
State::Row => statement.read::<i64>(0).unwrap(),
State::Done => 0,
}
}
pub fn get_top_users(&self, group_id : &str) -> Vec<(i64,String)>{
let builder = self.ins_connection.lock().unwrap();
let mut statement = builder
.prepare("SELECT npole, user_name FROM poles where id_group=? order by npole desc limit 10")
.unwrap();
statement.bind(1, group_id).unwrap();
let mut ret = Vec::new();
for i in 1 .. 10 {
match statement.next().unwrap() {
State::Row => ret.push((
statement.read::<i64>(0).unwrap(),
statement.read::<String>(1).unwrap()
)),
State::Done => return ret,
}
}
ret
}
}
#[cfg(test)]
#[test]
fn write_points(){
let data = DatabasePole::get_database();
let expected = 3;
data.write_points("000", "000", "2020-01-01", expected);
assert_eq!(expected, data.check_user_pole("000", "000", "2020-01-01"));
}
#[cfg(test)]
#[test]
fn last_pole(){
let data = DatabasePole::get_database();
let group_id = "000";
data.write_points(group_id, "000", "2020-01-02", 3);
assert_eq!("2020-01-02", data.last_pole(group_id).unwrap());
}

View File

@@ -0,0 +1,4 @@
pub struct Group {
id: str,
lastPole: DateTime<Local>,
}

150
src/pole_dialogue/mod.rs Normal file
View File

@@ -0,0 +1,150 @@
use teloxide::prelude::*;
use chrono::Local;
use std::cmp::Ordering::Equal;
use std::str;
mod database;
fn change_day(last_day: &str) -> bool{
last_day.cmp(&Local::now().format("%Y-%m-%d").to_string()) != Equal
}
fn check_pole(group_id: &str) -> bool {
let data: database::DatabasePole = database::DatabasePole::get_database();
let slast_pole = match data.last_pole(group_id) {
Ok(s) => s,
Err(_e) => return true,
};
change_day(&slast_pole)
}
fn do_pole(group_id: &str, user_id: &str, user_name: &str){
let data: database::DatabasePole = database::DatabasePole::get_database();
data.write_points(group_id, user_id, user_name, &Local::now().format("%Y-%m-%d").to_string(),Rewards::POLE as i64);
}
fn do_plata(group_id: &str, user_id: &str, user_name: &str){
let data: database::DatabasePole = database::DatabasePole::get_database();
data.write_points(group_id, user_id, user_name, &Local::now().format("%Y-%m-%d").to_string(),Rewards::PLATA as i64);
}
fn do_fail(group_id: &str, user_id: &str, user_name: &str){
let data: database::DatabasePole = database::DatabasePole::get_database();
data.write_points(group_id, user_id, user_name, &Local::now().format("%Y-%m-%d").to_string(),Rewards::FAIL as i64);
}
fn get_alias(msg: &teloxide::prelude::Message) -> String {
match &msg.from().unwrap().username {
Some(alias) => format!("@{}",alias),
None => format!("{}",&*msg.from().unwrap().first_name),
}
}
fn check_user_points(msg: &teloxide::prelude::Message, rw: Rewards) -> bool{
let data: database::DatabasePole = database::DatabasePole::get_database();
let ret = data.check_user_pole(&msg.chat.id.to_string(),
&msg.from().unwrap().id.to_string(),
&Local::now().format("%Y-%m-%d").to_string());
check_group_points(msg, rw) && (ret == 0)
}
enum Rewards {
POLE = 3,
PLATA = 2,
FAIL = 1,
}
fn check_group_points(msg: &teloxide::prelude::Message, rw: Rewards) -> bool {
let data: database::DatabasePole = database::DatabasePole::get_database();
let ret = data.check_group_points(&msg.chat.id.to_string(),
&Local::now().format("%Y-%m-%d").to_string());
match rw {
Rewards::PLATA => ret < (Rewards::PLATA as i64 + Rewards::POLE as i64),
Rewards::FAIL => ret < (Rewards::FAIL as i64 + Rewards::PLATA as i64 + Rewards::POLE as i64),
_=> false,
}
}
pub async fn exe_pole(
msg: Message,
bot: Bot,
) -> anyhow::Result<()>{
let text_lower = match msg.text(){
Some(t) => t.to_lowercase(),
None => return Ok(()),
};
if pole_conditions(msg.clone()){
do_pole(&msg.chat.id.to_string(),
&*msg.from().unwrap().id.to_string(),
&get_alias(&msg));
bot.send_message(msg.chat.id, format!("{} ha hecho la pole",get_alias(&msg))).await?;
} else if plata_conditions(msg.clone()) {
do_plata(&msg.chat.id.to_string(),
&*msg.from().unwrap().id.to_string(),
&get_alias(&msg));
bot.send_message(msg.chat.id, format!("{} ha hecho la plata", get_alias(&msg))).await?;
} else if bronce_conditions(msg.clone()) {
do_fail(&msg.chat.id.to_string(),
&*msg.from().unwrap().id.to_string(),
&get_alias(&msg));
bot.send_message(msg.chat.id, format!("{} buen fail", get_alias(&msg))).await?;
}
return Ok(());
}
fn pole_conditions(msg: Message) -> bool{
let text_lower = match msg.text(){
Some(t) => t.to_lowercase(),
None => return false,
};
if text_lower.contains("pole") || text_lower.contains("oro") {
if check_pole(&msg.chat.id.to_string()) {
return true;
}
}
false
}
fn plata_conditions(msg: Message) -> bool{
let text_lower = match msg.text(){
Some(t) => t.to_lowercase(),
None => return false,
};
if text_lower.contains("plata") || text_lower.contains("subpole") {
if check_user_points(&msg, Rewards::PLATA) {
return true;
}
}
false
}
fn bronce_conditions(msg: Message) -> bool{
let text_lower = match msg.text(){
Some(t) => t.to_lowercase(),
None => return false,
};
if text_lower.contains("fail") || text_lower.contains("bronce") {
if check_user_points(&msg, Rewards::FAIL) {
return true;
}
}
false
}
pub fn get_top(msg: Message, bot: Bot) -> <Bot as Requester>::SendMessage{
let db = database::DatabasePole::get_database();
let top = db.get_top_users(&msg.chat.id.0.to_string());
let mut repl = String::new();
for u in top {
repl.push_str(&(u.1 + ": " + &u.0.to_string() + " puntos\n"));
}
bot.send_message(msg.chat.id, format!("{}", repl))
}
#[cfg(test)]
#[test]
fn compare_dates(){
assert_eq!(false, change_day("2020-01-01"));
assert_eq!(true, change_day("3025-01-01"));
}

106
src/spoiler_mangas/mod.rs Normal file
View File

@@ -0,0 +1,106 @@
use teloxide::prelude::*;
use teloxide::{
net::Download,
requests::{Requester,MultipartRequest},
types::{MessageEntity, MessageEntityKind},
payloads::SendPhoto,
Bot,
};
use image::{DynamicImage,GenericImageView};
use std::io::Cursor;
use tokio::io::BufStream;
const TOLERANCE: i16 = 10;
const PERCENT_ACEPTANCE: u8 = 15;
fn check_percent(img: DynamicImage) -> u8 {
let mut cont = 0;
for pixel in img.pixels() {
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_br:i16 = (pixel.2[0] as i16 - pixel.2[2] as i16).abs();
let diference_pixel = diference_rg + diference_gb + diference_br;
if diference_pixel > TOLERANCE {
cont += 1;
}
}
((cont as f64 /(img.width() * img.height()) as f64)*100.0) as u8
}
pub fn append_text(new_msg: MultipartRequest<SendPhoto>, old_msg: Message) -> MultipartRequest<SendPhoto> {
match old_msg.caption() {
Some(caption) => {
//new_msg.caption(caption).caption_entities(generate_entity_spoiler(caption.len())).has_spoiler(true)
new_msg.caption(caption).caption_entities(generate_captions(old_msg.caption_entities().unwrap().to_vec(),caption.encode_utf16().count()))
},
None => new_msg,
}
}
pub fn generate_captions(captions: Vec<MessageEntity>, len: usize) -> Vec<MessageEntity> {
let mut ret = Vec::new();
let mut last_hole = 0;
for cap in captions {
match cap.kind {
MessageEntityKind::TextMention { user: ref User } => {
if cap.offset == 0 {
last_hole = cap.length;
}else if cap.offset >= last_hole {
ret.push(MessageEntity::spoiler(last_hole,cap.offset-last_hole));
last_hole = cap.offset + cap.length;
}
ret.push(cap.clone());
},
_ => continue,
}
}
if last_hole < len {
ret.push(MessageEntity::spoiler(last_hole,len - last_hole));
}
ret
}
pub async fn check_image(msg: Message, bot: Bot) -> anyhow::Result<()> {
match msg.photo() {
Some(s) => {
let mut percent = 0;
let mut id : Option<(String, u32)> = None;
for p in s {
let file = bot.get_file(p.file.id.clone()).await?;
let empty_memory: Vec<u8> = Vec::new();
let cursor = Cursor::new(empty_memory);
let mut reader = BufStream::new(cursor);
bot.download_file(&file.path, &mut reader).await?;
let raw = reader.into_inner().into_inner().leak();
match image::load_from_memory(raw) {
Ok(img) => {
let img_percent = check_percent(img);
if img_percent >= percent {
percent = img_percent;
id = match id {
Some(i) => {
if i.1 > p.width {
Some((p.clone().file.id, p.clone().width))
} else {
Some(i)
}
},
None => Some((p.clone().file.id, p.clone().width)),
}
}
},
Err(_e) => continue,
}
}
if percent < PERCENT_ACEPTANCE {
bot.delete_message(msg.chat.id, msg.id).await?;
let response = match id {
Some(i) => bot.send_photo(msg.chat.id, teloxide::types::InputFile::file_id(i.0)).has_spoiler(true),
None => return Ok(()),
};
append_text(response, msg).await?;
}
Ok(())
},
None => Ok(()),
}
}