Compare commits

..

10 Commits

Author SHA1 Message Date
Akulij
37d2480b4a update mainbot.js
All checks were successful
Build && Deploy / cargo build (push) Successful in 1m5s
2025-06-06 01:52:18 +05:00
Akulij
a869362bf5 notify admin when literal not found instead of silently failing 2025-06-06 01:51:27 +05:00
Akulij
b114c80097 update handling of varianted messages 2025-06-06 01:38:22 +05:00
Akulij
591244b5a1 craate getter for varianted commands 2025-06-06 01:37:35 +05:00
Akulij
06a6542349 change design of variants implementation in BotConfig 2025-06-06 01:37:03 +05:00
Akulij
0bc7978c99 fix deserialization Message's created_at 2025-06-06 01:35:25 +05:00
Akulij
2e356ac067 make BotMessage's handler to recursively generate BotMessage 2025-06-06 00:19:37 +05:00
Akulij
2662a00776 provide MessageInfo to BotMessage's handler 2025-06-05 23:16:06 +05:00
Akulij
5669de716a create MessageInfo and MessageInfoBuilder 2025-06-05 23:13:39 +05:00
Akulij
6d017b1993 fix: provide default serde value for BotMessage.variants 2025-06-05 23:12:56 +05:00
5 changed files with 100 additions and 41 deletions

View File

@ -13,7 +13,10 @@ const start_msg = {
};
const dialog = {
commands: {
start: start_msg,
start: {
meta: true,
...start_msg
},
},
buttons: {
more_info: {
@ -41,6 +44,17 @@ const dialog = {
state: "none"
},
},
variants: {
start: {
free_tgads: {
...start_msg,
buttons: [
[{ name: { literal: "free_doc_btn" }, callback_name: "free_doc" }],
...start_msg.buttons,
],
},
},
},
}
function leave_application(user) {

View File

@ -1,5 +1,5 @@
use log::{error, info};
use quickjs_rusty::serde::to_js;
use quickjs_rusty::serde::{from_js, to_js};
use std::{
str::FromStr,
sync::{Arc, Mutex, RwLock},
@ -13,7 +13,7 @@ use teloxide::{
};
use crate::{
botscript::{self, BotMessage, RunnerConfig},
botscript::{self, message_info::MessageInfoBuilder, BotMessage, RunnerConfig},
commands::BotCommand,
db::{CallDB, DB},
message_answerer::MessageAnswerer,
@ -38,7 +38,11 @@ pub fn script_handler(r: Arc<Mutex<BotRuntime>>) -> BotHandler {
let r = r.lock().expect("RwLock lock on commands map failed");
let rc = &r.rc;
rc.get_command_message(command)
// it's not necessary, but avoiding some hashmap lookups
match bc.args() {
Some(variant) => rc.get_command_message_varianted(command, variant),
None => rc.get_command_message(command),
}
})
.endpoint(handle_botmessage),
)
@ -69,32 +73,15 @@ async fn handle_botmessage(bot: Bot, mut db: DB, bm: BotMessage, msg: Message) -
let user = update_user_tg(user, &tguser);
user.update_user(&mut db).await?;
let variant = if bm.meta() == true {
let meta = match BotCommand::from_str(msg.text().unwrap_or("")) {
Ok(cmd) => cmd.args().map(|m| m.to_string()),
Err(err) => {
notify_admin(&format!("Error while parsing cmd in `meta`, possibly meta is set not in command, err: {err}")).await;
None
}
};
if let Some(ref meta) = meta {
user.insert_meta(&mut db, meta).await?;
};
meta
} else {
None
let variant = match BotCommand::from_str(msg.text().unwrap_or("")) {
Ok(cmd) => cmd.args().map(|m| m.to_string()),
Err(_) => None,
};
// Filtering to use only defined variants
let variant = match bm
.variants()
.iter()
.any(|v| v == variant.as_ref().map_or("", |v| v))
{
true => variant,
false => None,
if bm.meta() == true {
if let Some(ref meta) = variant {
user.insert_meta(&mut db, meta).await?;
};
};
let is_propagate: bool = match bm.get_handler() {
@ -105,12 +92,16 @@ async fn handle_botmessage(bot: Bot, mut db: DB, bm: BotMessage, msg: Message) -
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
let mi = MessageInfoBuilder::new()
.set_variant(variant.clone())
.build();
let mi = to_js(ctx, &mi).unwrap();
info!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser]) {
match handler.call_args(vec![jsuser, mi]) {
Ok(v) => {
if v.is_bool() {
v.to_bool().unwrap_or(true)
@ -181,12 +172,14 @@ async fn handle_callback(bot: Bot, mut db: DB, bm: BotMessage, q: CallbackQuery)
None => break 'prop true,
};
let jsuser = to_js(ctx, &tguser).unwrap();
let mi = MessageInfoBuilder::new().build();
let mi = to_js(ctx, &mi).unwrap();
println!(
"Calling handler {:?} with msg literal: {:?}",
handler,
bm.literal()
);
match handler.call_args(vec![jsuser]) {
match handler.call_args(vec![jsuser, mi]) {
Ok(v) => {
println!("Ok branch, got value: {v:?}");
if v.is_bool() {

View File

@ -1,11 +1,13 @@
pub mod application;
pub mod db;
pub mod message_info;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, PoisonError};
use std::time::Duration;
use crate::db::raw_calls::RawCallError;
use crate::db::{CallDB, DbError, User, DB};
use crate::notify_admin;
use crate::utils::parcelable::{ParcelType, Parcelable, ParcelableError, ParcelableResult};
use chrono::{DateTime, Days, NaiveTime, ParseError, TimeDelta, Timelike, Utc};
use db::attach_db_obj;
@ -476,9 +478,12 @@ impl ButtonName {
Ok(match value {
Some(value) => Ok(value),
None => Err(ResolveError::IncorrectLiteral(format!(
"not found literal `{literal}` in DB"
))),
None => {
notify_admin(&format!("Literal `{literal}` is not set!!!")).await;
Err(ResolveError::IncorrectLiteral(format!(
"not found literal `{literal}` in DB"
)))
}
}?)
}
}
@ -501,7 +506,6 @@ pub struct BotMessage {
/// flag options to command is meta, so it will be appended to user.metas in db
meta: Option<bool>,
variants: Vec<MessageVariant>,
handler: Option<BotFunction>,
}
@ -564,10 +568,6 @@ impl BotMessage {
pub fn meta(&self) -> bool {
self.meta.unwrap_or(false)
}
pub fn variants(&self) -> &[MessageVariant] {
&self.variants
}
}
impl BotMessage {
@ -640,6 +640,7 @@ pub struct BotDialog {
pub commands: HashMap<String, BotMessage>,
pub buttons: HashMap<String, BotMessage>,
stateful_msg_handlers: HashMap<String, BotMessage>,
variants: HashMap<String, HashMap<String, BotMessage>>,
}
impl Parcelable<BotFunction> for BotDialog {
@ -915,6 +916,28 @@ impl RunnerConfig {
bm.map(|bm| bm.fill_literal(command.to_string()).update_defaults())
}
pub fn get_command_message_varianted(
&self,
command: &str,
variant: &str,
) -> Option<BotMessage> {
if !self.dialog.commands.contains_key(command) {
return None;
}
// fallback to regular if not found
let bm = match self.dialog.variants.get(command).cloned() {
Some(bm) => bm,
None => return self.get_command_message(command),
};
// get variant of message
let bm = match bm.get(variant).cloned() {
Some(bm) => bm,
None => return self.get_command_message(command),
};
Some(bm.fill_literal(command.to_string()).update_defaults())
}
pub fn get_callback_message(&self, callback: &str) -> Option<BotMessage> {
let bm = self.dialog.buttons.get(callback).cloned();

View File

@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
pub struct MessageInfo {
variant: Option<String>,
}
pub struct MessageInfoBuilder {
inner: MessageInfo,
}
impl MessageInfoBuilder {
pub fn new() -> Self {
Self {
inner: Default::default(),
}
}
pub fn set_variant(mut self, variant: Option<String>) -> Self {
self.inner.variant = variant;
self
}
pub fn build(self) -> MessageInfo {
self.inner
}
}

View File

@ -12,6 +12,7 @@ use enum_stringify::EnumStringify;
use futures::stream::TryStreamExt;
use futures::StreamExt;
use mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime;
use mongodb::options::IndexOptions;
use mongodb::{bson::doc, options::ClientOptions, Client};
use mongodb::{Collection, Database, IndexModel};
@ -108,7 +109,8 @@ pub struct Message {
pub message_id: i64,
pub token: String,
pub variant: Option<String>,
pub created_at: DateTime<FixedOffset>,
#[serde(with = "chrono_datetime_as_bson_datetime")]
pub created_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize)]
@ -346,7 +348,7 @@ pub trait CallDB {
doc! {
"$set": {
"token": literal,
"created_at": Into::<DateTime<FixedOffset>>::into(Local::now()),
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
}
},
)
@ -376,7 +378,7 @@ pub trait CallDB {
"$set": {
"token": literal,
"variant": variant,
"created_at": Into::<DateTime<FixedOffset>>::into(Local::now()),
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
}
},
)