gongbotrs/src/bot_handler.rs
Akulij 198c363038
Some checks failed
Build && Deploy / cargo build (push) Has been cancelled
temporarly use println
2025-05-31 14:57:39 +05:00

241 lines
7.5 KiB
Rust

use log::{error, info};
use quickjs_rusty::serde::to_js;
use std::{
str::FromStr,
sync::{Arc, RwLock},
};
use teloxide::{
dispatching::{dialogue::GetChatId, UpdateFilterExt},
dptree::{self, Handler},
prelude::DependencyMap,
types::{CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update},
Bot,
};
use crate::{
botscript::{self, BotMessage, RunnerConfig},
commands::BotCommand,
db::{CallDB, DB},
message_answerer::MessageAnswerer,
update_user_tg, BotError, BotResult,
};
pub type BotHandler =
Handler<'static, DependencyMap, BotResult<()>, teloxide::dispatching::DpHandlerDescription>;
pub fn script_handler(rc: Arc<RwLock<RunnerConfig>>) -> BotHandler {
let crc = rc.clone();
dptree::entry()
.branch(
Update::filter_message()
// check if message is command
.filter_map(|m: Message| m.text().and_then(|t| BotCommand::from_str(t).ok()))
// check if command is presented in config
.filter_map(move |bc: BotCommand| {
let rc = std::sync::Arc::clone(&rc);
let command = bc.command();
let rc = rc.read().expect("RwLock lock on commands map failed");
rc.get_command_message(command)
})
.endpoint(handle_botmessage),
)
.branch(
Update::filter_callback_query()
.filter_map(move |q: CallbackQuery| {
q.data.and_then(|data| {
let rc = std::sync::Arc::clone(&crc);
let rc = rc.read().expect("RwLock lock on commands map failed");
rc.get_callback_message(&data)
})
})
.endpoint(handle_callback),
)
}
async fn handle_botmessage(bot: Bot, mut db: DB, bm: BotMessage, msg: Message) -> BotResult<()> {
info!("Eval BM: {:?}", bm);
let tguser = match msg.from.clone() {
Some(user) => user,
None => return Ok(()), // do nothing, cause its not usecase of function
};
let user = db
.get_or_init_user(tguser.id.0 as i64, &tguser.first_name)
.await?;
let user = update_user_tg(user, &tguser);
user.update_user(&mut db).await?;
let is_propagate: bool = match bm.get_handler() {
Some(handler) => 'prop: {
let ctx = match handler.context() {
Some(ctx) => ctx,
// falling back to propagation
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
info!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser]) {
Ok(v) => {
if v.is_bool() {
v.to_bool().unwrap_or(true)
} else if v.is_int() {
v.to_int().unwrap_or(1) != 0
} else {
// falling back to propagation
true
}
}
Err(err) => {
error!("Failed to get return of handler, err: {err}");
// falling back to propagation
true
}
}
}
None => true,
};
if !is_propagate {
return Ok(());
}
let buttons = bm
.resolve_buttons(&mut db)
.await?
.map(|buttons| InlineKeyboardMarkup {
inline_keyboard: buttons
.iter()
.map(|r| {
r.iter()
.map(|b| match b {
botscript::ButtonLayout::Callback {
name,
literal: _,
callback,
} => InlineKeyboardButton::callback(name, callback),
})
.collect()
})
.collect(),
});
let literal = bm.literal().map_or("", |s| s.as_str());
let ma = MessageAnswerer::new(&bot, &mut db, msg.chat.id.0);
ma.answer(literal, None, buttons).await?;
Ok(())
}
async fn handle_callback(bot: Bot, mut db: DB, bm: BotMessage, q: CallbackQuery) -> BotResult<()> {
info!("Eval BM: {:?}", bm);
let tguser = q.from.clone();
let user = db
.get_or_init_user(tguser.id.0 as i64, &tguser.first_name)
.await?;
let user = update_user_tg(user, &tguser);
user.update_user(&mut db).await?;
println!("Is handler set: {}", bm.get_handler().is_some());
let is_propagate: bool = match bm.get_handler() {
Some(handler) => 'prop: {
let ctx = match handler.context() {
Some(ctx) => ctx,
// falling back to propagation
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
println!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser]) {
Ok(v) => {
if v.is_bool() {
v.to_bool().unwrap_or(true)
} else if v.is_int() {
v.to_int().unwrap_or(1) != 0
} else {
// falling back to propagation
true
}
}
Err(err) => {
error!("Failed to get return of handler, err: {err}");
// falling back to propagation
true
}
}
}
None => true,
};
if !is_propagate {
return Ok(());
}
let buttons = bm
.resolve_buttons(&mut db)
.await?
.map(|buttons| InlineKeyboardMarkup {
inline_keyboard: buttons
.iter()
.map(|r| {
r.iter()
.map(|b| match b {
botscript::ButtonLayout::Callback {
name,
literal: _,
callback,
} => InlineKeyboardButton::callback(name, callback),
})
.collect()
})
.collect(),
});
let literal = bm.literal().map_or("", |s| s.as_str());
let (chat_id, msg_id) = {
let chat_id = match q.chat_id() {
Some(chat_id) => chat_id.0,
None => tguser.id.0 as i64,
};
let msg_id = q.message.map_or_else(
|| {
Err(BotError::MsgTooOld(
"Failed to get message id, probably message too old".to_string(),
))
},
|m| Ok(m.id().0),
);
(chat_id, msg_id)
};
let ma = MessageAnswerer::new(&bot, &mut db, chat_id);
match bm.is_replace() {
true => {
match msg_id {
Ok(msg_id) => {
ma.replace_message(msg_id, literal, buttons).await?;
}
Err(err) => {
ma.answer(literal, None, buttons).await?;
}
};
}
false => {
ma.answer(literal, None, buttons).await?;
}
}
Ok(())
}