Merge pull request 'dev' (#22) from dev into main
All checks were successful
Build && Deploy / cargo build (push) Successful in 1m5s

Reviewed-on: #22
This commit is contained in:
akulij 2025-06-05 20:53:03 +00:00
commit 7147da0cb5
5 changed files with 164 additions and 17 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,11 +13,11 @@ use teloxide::{
};
use crate::{
botscript::{self, BotMessage, RunnerConfig},
botscript::{self, message_info::MessageInfoBuilder, BotMessage, RunnerConfig},
commands::BotCommand,
db::{CallDB, DB},
message_answerer::MessageAnswerer,
update_user_tg, BotError, BotResult, BotRuntime,
notify_admin, update_user_tg, BotError, BotResult, BotRuntime,
};
pub type BotHandler =
@ -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,6 +73,17 @@ 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 = match BotCommand::from_str(msg.text().unwrap_or("")) {
Ok(cmd) => cmd.args().map(|m| m.to_string()),
Err(_) => 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() {
Some(handler) => 'prop: {
let ctx = match handler.context() {
@ -77,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)
@ -129,7 +148,8 @@ async fn handle_botmessage(bot: Bot, mut db: DB, bm: BotMessage, msg: Message) -
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?;
ma.answer(literal, variant.as_ref().map(|v| v.as_str()), buttons)
.await?;
Ok(())
}
@ -152,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"
)))
}
}?)
}
}
@ -499,17 +504,59 @@ pub struct BotMessage {
buttons: Option<KeyboardDefinition>,
state: Option<String>,
/// flag options to command is meta, so it will be appended to user.metas in db
meta: Option<bool>,
handler: Option<BotFunction>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MessageVariant(String);
impl MessageVariant {
pub fn get_name(&self) -> &str {
&self.0
}
}
impl PartialEq<String> for &MessageVariant {
fn eq(&self, other: &String) -> bool {
self.0 == *other
}
}
impl PartialEq<&str> for &MessageVariant {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl BotMessage {
pub fn fill_literal(&self, l: String) -> Self {
pub fn fill_literal(self, l: String) -> Self {
BotMessage {
literal: self.clone().literal.or(Some(l)),
..self.clone()
..self
}
}
/// chain of modifications on BotMessage
pub fn update_defaults(self) -> Self {
let bm = self;
// if message is `start`, defaulting meta to true, if not set
let bm = match bm.meta {
Some(_) => bm,
None => match &bm.literal {
Some(l) if l == "start" => Self {
meta: Some(true),
..bm
},
_ => bm,
},
};
bm
}
pub fn is_replace(&self) -> bool {
self.replace
}
@ -517,6 +564,10 @@ impl BotMessage {
pub fn get_handler(&self) -> Option<&BotFunction> {
self.handler.as_ref()
}
pub fn meta(&self) -> bool {
self.meta.unwrap_or(false)
}
}
impl BotMessage {
@ -589,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 {
@ -861,7 +913,29 @@ impl RunnerConfig {
pub fn get_command_message(&self, command: &str) -> Option<BotMessage> {
let bm = self.dialog.commands.get(command).cloned();
bm.map(|bm| bm.fill_literal(command.to_string()))
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> {

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

@ -7,11 +7,12 @@ pub mod raw_calls;
use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use chrono::{DateTime, FixedOffset, Local, Utc};
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,6 +109,8 @@ pub struct Message {
pub message_id: i64,
pub token: String,
pub variant: Option<String>,
#[serde(with = "chrono_datetime_as_bson_datetime")]
pub created_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize)]
@ -343,7 +346,10 @@ pub trait CallDB {
"message_id": messageid as i64
},
doc! {
"$set": { "token": literal }
"$set": {
"token": literal,
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
}
},
)
.upsert(true)
@ -369,7 +375,11 @@ pub trait CallDB {
"message_id": messageid as i64
},
doc! {
"$set": { "token": literal, "variant": variant }
"$set": {
"token": literal,
"variant": variant,
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
}
},
)
.upsert(true)