Merge pull request 'dev' (#22) from dev into main
All checks were successful
Build && Deploy / cargo build (push) Successful in 1m5s
All checks were successful
Build && Deploy / cargo build (push) Successful in 1m5s
Reviewed-on: #22
This commit is contained in:
commit
7147da0cb5
16
mainbot.js
16
mainbot.js
@ -13,7 +13,10 @@ const start_msg = {
|
|||||||
};
|
};
|
||||||
const dialog = {
|
const dialog = {
|
||||||
commands: {
|
commands: {
|
||||||
start: start_msg,
|
start: {
|
||||||
|
meta: true,
|
||||||
|
...start_msg
|
||||||
|
},
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
more_info: {
|
more_info: {
|
||||||
@ -41,6 +44,17 @@ const dialog = {
|
|||||||
state: "none"
|
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) {
|
function leave_application(user) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use quickjs_rusty::serde::to_js;
|
use quickjs_rusty::serde::{from_js, to_js};
|
||||||
use std::{
|
use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{Arc, Mutex, RwLock},
|
||||||
@ -13,11 +13,11 @@ use teloxide::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
botscript::{self, BotMessage, RunnerConfig},
|
botscript::{self, message_info::MessageInfoBuilder, BotMessage, RunnerConfig},
|
||||||
commands::BotCommand,
|
commands::BotCommand,
|
||||||
db::{CallDB, DB},
|
db::{CallDB, DB},
|
||||||
message_answerer::MessageAnswerer,
|
message_answerer::MessageAnswerer,
|
||||||
update_user_tg, BotError, BotResult, BotRuntime,
|
notify_admin, update_user_tg, BotError, BotResult, BotRuntime,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type BotHandler =
|
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 r = r.lock().expect("RwLock lock on commands map failed");
|
||||||
let rc = &r.rc;
|
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),
|
.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);
|
let user = update_user_tg(user, &tguser);
|
||||||
user.update_user(&mut db).await?;
|
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() {
|
let is_propagate: bool = match bm.get_handler() {
|
||||||
Some(handler) => 'prop: {
|
Some(handler) => 'prop: {
|
||||||
let ctx = match handler.context() {
|
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,
|
None => break 'prop true,
|
||||||
};
|
};
|
||||||
let jsuser = to_js(ctx, &tguser).unwrap();
|
let jsuser = to_js(ctx, &tguser).unwrap();
|
||||||
|
let mi = MessageInfoBuilder::new()
|
||||||
|
.set_variant(variant.clone())
|
||||||
|
.build();
|
||||||
|
let mi = to_js(ctx, &mi).unwrap();
|
||||||
info!(
|
info!(
|
||||||
"Calling handler {:?} with msg literal: {:?}",
|
"Calling handler {:?} with msg literal: {:?}",
|
||||||
handler,
|
handler,
|
||||||
bm.literal()
|
bm.literal()
|
||||||
);
|
);
|
||||||
match handler.call_args(vec![jsuser]) {
|
match handler.call_args(vec![jsuser, mi]) {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
if v.is_bool() {
|
if v.is_bool() {
|
||||||
v.to_bool().unwrap_or(true)
|
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 literal = bm.literal().map_or("", |s| s.as_str());
|
||||||
|
|
||||||
let ma = MessageAnswerer::new(&bot, &mut db, msg.chat.id.0);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -152,12 +172,14 @@ async fn handle_callback(bot: Bot, mut db: DB, bm: BotMessage, q: CallbackQuery)
|
|||||||
None => break 'prop true,
|
None => break 'prop true,
|
||||||
};
|
};
|
||||||
let jsuser = to_js(ctx, &tguser).unwrap();
|
let jsuser = to_js(ctx, &tguser).unwrap();
|
||||||
|
let mi = MessageInfoBuilder::new().build();
|
||||||
|
let mi = to_js(ctx, &mi).unwrap();
|
||||||
println!(
|
println!(
|
||||||
"Calling handler {:?} with msg literal: {:?}",
|
"Calling handler {:?} with msg literal: {:?}",
|
||||||
handler,
|
handler,
|
||||||
bm.literal()
|
bm.literal()
|
||||||
);
|
);
|
||||||
match handler.call_args(vec![jsuser]) {
|
match handler.call_args(vec![jsuser, mi]) {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
println!("Ok branch, got value: {v:?}");
|
println!("Ok branch, got value: {v:?}");
|
||||||
if v.is_bool() {
|
if v.is_bool() {
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod message_info;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex, PoisonError};
|
use std::sync::{Arc, Mutex, PoisonError};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::db::raw_calls::RawCallError;
|
use crate::db::raw_calls::RawCallError;
|
||||||
use crate::db::{CallDB, DbError, User, DB};
|
use crate::db::{CallDB, DbError, User, DB};
|
||||||
|
use crate::notify_admin;
|
||||||
use crate::utils::parcelable::{ParcelType, Parcelable, ParcelableError, ParcelableResult};
|
use crate::utils::parcelable::{ParcelType, Parcelable, ParcelableError, ParcelableResult};
|
||||||
use chrono::{DateTime, Days, NaiveTime, ParseError, TimeDelta, Timelike, Utc};
|
use chrono::{DateTime, Days, NaiveTime, ParseError, TimeDelta, Timelike, Utc};
|
||||||
use db::attach_db_obj;
|
use db::attach_db_obj;
|
||||||
@ -476,9 +478,12 @@ impl ButtonName {
|
|||||||
|
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Some(value) => Ok(value),
|
Some(value) => Ok(value),
|
||||||
None => Err(ResolveError::IncorrectLiteral(format!(
|
None => {
|
||||||
|
notify_admin(&format!("Literal `{literal}` is not set!!!")).await;
|
||||||
|
Err(ResolveError::IncorrectLiteral(format!(
|
||||||
"not found literal `{literal}` in DB"
|
"not found literal `{literal}` in DB"
|
||||||
))),
|
)))
|
||||||
|
}
|
||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,17 +504,59 @@ pub struct BotMessage {
|
|||||||
buttons: Option<KeyboardDefinition>,
|
buttons: Option<KeyboardDefinition>,
|
||||||
state: Option<String>,
|
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>,
|
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 {
|
impl BotMessage {
|
||||||
pub fn fill_literal(&self, l: String) -> Self {
|
pub fn fill_literal(self, l: String) -> Self {
|
||||||
BotMessage {
|
BotMessage {
|
||||||
literal: self.clone().literal.or(Some(l)),
|
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 {
|
pub fn is_replace(&self) -> bool {
|
||||||
self.replace
|
self.replace
|
||||||
}
|
}
|
||||||
@ -517,6 +564,10 @@ impl BotMessage {
|
|||||||
pub fn get_handler(&self) -> Option<&BotFunction> {
|
pub fn get_handler(&self) -> Option<&BotFunction> {
|
||||||
self.handler.as_ref()
|
self.handler.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn meta(&self) -> bool {
|
||||||
|
self.meta.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotMessage {
|
impl BotMessage {
|
||||||
@ -589,6 +640,7 @@ pub struct BotDialog {
|
|||||||
pub commands: HashMap<String, BotMessage>,
|
pub commands: HashMap<String, BotMessage>,
|
||||||
pub buttons: HashMap<String, BotMessage>,
|
pub buttons: HashMap<String, BotMessage>,
|
||||||
stateful_msg_handlers: HashMap<String, BotMessage>,
|
stateful_msg_handlers: HashMap<String, BotMessage>,
|
||||||
|
variants: HashMap<String, HashMap<String, BotMessage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parcelable<BotFunction> for BotDialog {
|
impl Parcelable<BotFunction> for BotDialog {
|
||||||
@ -861,7 +913,29 @@ impl RunnerConfig {
|
|||||||
pub fn get_command_message(&self, command: &str) -> Option<BotMessage> {
|
pub fn get_command_message(&self, command: &str) -> Option<BotMessage> {
|
||||||
let bm = self.dialog.commands.get(command).cloned();
|
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> {
|
pub fn get_callback_message(&self, callback: &str) -> Option<BotMessage> {
|
||||||
|
|||||||
27
src/botscript/message_info.rs
Normal file
27
src/botscript/message_info.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,11 +7,12 @@ pub mod raw_calls;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, FixedOffset, Local, Utc};
|
||||||
use enum_stringify::EnumStringify;
|
use enum_stringify::EnumStringify;
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime;
|
||||||
use mongodb::options::IndexOptions;
|
use mongodb::options::IndexOptions;
|
||||||
use mongodb::{bson::doc, options::ClientOptions, Client};
|
use mongodb::{bson::doc, options::ClientOptions, Client};
|
||||||
use mongodb::{Collection, Database, IndexModel};
|
use mongodb::{Collection, Database, IndexModel};
|
||||||
@ -108,6 +109,8 @@ pub struct Message {
|
|||||||
pub message_id: i64,
|
pub message_id: i64,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub variant: Option<String>,
|
pub variant: Option<String>,
|
||||||
|
#[serde(with = "chrono_datetime_as_bson_datetime")]
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -343,7 +346,10 @@ pub trait CallDB {
|
|||||||
"message_id": messageid as i64
|
"message_id": messageid as i64
|
||||||
},
|
},
|
||||||
doc! {
|
doc! {
|
||||||
"$set": { "token": literal }
|
"$set": {
|
||||||
|
"token": literal,
|
||||||
|
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.upsert(true)
|
.upsert(true)
|
||||||
@ -369,7 +375,11 @@ pub trait CallDB {
|
|||||||
"message_id": messageid as i64
|
"message_id": messageid as i64
|
||||||
},
|
},
|
||||||
doc! {
|
doc! {
|
||||||
"$set": { "token": literal, "variant": variant }
|
"$set": {
|
||||||
|
"token": literal,
|
||||||
|
"variant": variant,
|
||||||
|
"created_at": Into::<DateTime<Utc>>::into(Local::now()),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.upsert(true)
|
.upsert(true)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user