Compare commits
11 Commits
bee93b32d1
...
adad94ad43
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adad94ad43 | ||
|
|
714853730a | ||
|
|
b980a653cb | ||
|
|
b8d07d0ad5 | ||
|
|
2e447e87fd | ||
|
|
d749b57811 | ||
|
|
a106891050 | ||
|
|
9cd0765030 | ||
|
|
bc46e0fda4 | ||
|
|
845071c800 | ||
|
|
c936ea38a9 |
84
mainbot.js
84
mainbot.js
@ -1,21 +1,32 @@
|
|||||||
// db - is set globally
|
// db - is set globally
|
||||||
|
|
||||||
|
const PROJECTS_COUNT = 2
|
||||||
|
|
||||||
|
const start_msg = {
|
||||||
|
buttons: [
|
||||||
|
[{ name: { literal: "show_projects" }, callback_name: "project_0" }],
|
||||||
|
[{ name: { literal: "more_info_btn" }, callback_name: "more_info" }],
|
||||||
|
[{ name: { literal: "leave_application" }, callback_name: "leave_application" }],
|
||||||
|
[{ name: { literal: "ask_question_btn" }, callback_name: "ask_question" }],
|
||||||
|
], // default is `null`
|
||||||
|
replace: true,
|
||||||
|
state: "start"
|
||||||
|
};
|
||||||
const dialog = {
|
const dialog = {
|
||||||
commands: {
|
commands: {
|
||||||
start: {
|
start: start_msg,
|
||||||
buttons: start_buttons, // default is `null`
|
|
||||||
state: "start"
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
buttons: [
|
|
||||||
[{name: {name: "Def"}, callback_name: "defcall"}]
|
|
||||||
],
|
|
||||||
state: "none"
|
|
||||||
},
|
|
||||||
somecomplicatedcmd: {}
|
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
more_info: {},
|
more_info: {
|
||||||
|
buttons: [
|
||||||
|
[{ name: { name: "На главную" }, callback_name: "start" }],
|
||||||
|
]
|
||||||
|
},
|
||||||
|
start: start_msg,
|
||||||
|
leave_application: {
|
||||||
|
handler: leave_application
|
||||||
|
},
|
||||||
|
ask_question: {}
|
||||||
},
|
},
|
||||||
stateful_msg_handlers: {
|
stateful_msg_handlers: {
|
||||||
start: {}, // everything is by default, so just send message `start`
|
start: {}, // everything is by default, so just send message `start`
|
||||||
@ -29,36 +40,43 @@ const dialog = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function enter_name() {}
|
function leave_application(user) {
|
||||||
|
print(JSON.stringify(user))
|
||||||
|
user_application(user)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function enter_name() { }
|
||||||
|
|
||||||
const fmt = (number) => number.toString().padStart(2, '0');
|
const fmt = (number) => number.toString().padStart(2, '0');
|
||||||
|
|
||||||
const formatDate = (date) => {
|
function add_project_callbacks(point) {
|
||||||
const [h, m, d, M, y] = [
|
for (const i of Array(PROJECTS_COUNT).keys()) {
|
||||||
date.getHours(),
|
buttons = [
|
||||||
date.getMinutes(),
|
[],
|
||||||
date.getDate(),
|
[{ name: { name: "На главную" }, callback_name: "start" }]
|
||||||
date.getMonth(),
|
]
|
||||||
date.getFullYear()
|
if (i > 0) {
|
||||||
];
|
buttons[0].push({ name: { literal: "prev_project" }, callback_name: `project_${i - 1}` })
|
||||||
return `${fmt(h)}:${fmt(m)} ${fmt(d)}-${fmt(M + 1)}-${y}`
|
}
|
||||||
};
|
if (i < PROJECTS_COUNT - 1) {
|
||||||
|
buttons[0].push({ name: { literal: "next_project" }, callback_name: `project_${i + 1}` })
|
||||||
|
}
|
||||||
|
|
||||||
function start_buttons() {
|
point[`project_${i}`] = {
|
||||||
const now = new Date();
|
replace: true,
|
||||||
const dateFormated = formatDate(now);
|
buttons: buttons
|
||||||
|
}
|
||||||
// return 1
|
}
|
||||||
return [
|
|
||||||
[{name: {name: dateFormated}, callback_name: "no"}],
|
|
||||||
[{name: {name: "Hello!"}, callback_name: "no"}],
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
add_project_callbacks(dialog.buttons)
|
||||||
|
print(JSON.stringify(dialog.buttons))
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
version: 1.1,
|
version: 1.1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// {config, dialog}
|
// {config, dialog}
|
||||||
const c = {config: config, dialog: dialog}
|
const c = { config: config, dialog: dialog }
|
||||||
c
|
c
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use log::info;
|
use log::{error, info};
|
||||||
|
use quickjs_rusty::serde::to_js;
|
||||||
use std::{
|
use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
@ -66,6 +67,39 @@ 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 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();
|
||||||
|
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
|
let buttons = bm
|
||||||
.resolve_buttons(&mut db)
|
.resolve_buttons(&mut db)
|
||||||
.await?
|
.await?
|
||||||
@ -102,6 +136,39 @@ async fn handle_callback(bot: Bot, mut db: DB, bm: BotMessage, q: CallbackQuery)
|
|||||||
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 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();
|
||||||
|
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
|
let buttons = bm
|
||||||
.resolve_buttons(&mut db)
|
.resolve_buttons(&mut db)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod application;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex, PoisonError};
|
use std::sync::{Arc, Mutex, PoisonError};
|
||||||
@ -12,12 +13,12 @@ use itertools::Itertools;
|
|||||||
use quickjs_rusty::serde::from_js;
|
use quickjs_rusty::serde::from_js;
|
||||||
use quickjs_rusty::utils::create_empty_object;
|
use quickjs_rusty::utils::create_empty_object;
|
||||||
use quickjs_rusty::utils::create_string;
|
use quickjs_rusty::utils::create_string;
|
||||||
use quickjs_rusty::Context;
|
|
||||||
use quickjs_rusty::ContextError;
|
use quickjs_rusty::ContextError;
|
||||||
use quickjs_rusty::ExecutionError;
|
use quickjs_rusty::ExecutionError;
|
||||||
use quickjs_rusty::JsFunction;
|
use quickjs_rusty::JsFunction;
|
||||||
use quickjs_rusty::OwnedJsValue as JsValue;
|
use quickjs_rusty::OwnedJsValue as JsValue;
|
||||||
use quickjs_rusty::ValueError;
|
use quickjs_rusty::ValueError;
|
||||||
|
use quickjs_rusty::{Context, OwnedJsObject};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@ -121,6 +122,13 @@ impl BotFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context(&self) -> Option<*mut quickjs_rusty::JSContext> {
|
||||||
|
match &self.func {
|
||||||
|
FunctionMarker::Function(js_function) => Some(js_function.context()),
|
||||||
|
FunctionMarker::StrTemplate(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn call(&self) -> ScriptResult<JsValue> {
|
pub fn call(&self) -> ScriptResult<JsValue> {
|
||||||
self.call_args(Default::default())
|
self.call_args(Default::default())
|
||||||
}
|
}
|
||||||
@ -498,6 +506,10 @@ impl BotMessage {
|
|||||||
pub fn is_replace(&self) -> bool {
|
pub fn is_replace(&self) -> bool {
|
||||||
self.replace
|
self.replace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_handler(&self) -> Option<&BotFunction> {
|
||||||
|
self.handler.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotMessage {
|
impl BotMessage {
|
||||||
@ -653,6 +665,17 @@ impl Runner {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_attacher<F, R>(&mut self, f: F) -> ScriptResult<R>
|
||||||
|
where
|
||||||
|
F: FnOnce(&Context, &mut OwnedJsObject) -> R,
|
||||||
|
{
|
||||||
|
let context = self.context.lock().unwrap();
|
||||||
|
let mut global = context.global()?;
|
||||||
|
|
||||||
|
let res = f(&context, &mut global);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_script(&self, content: &str) -> ScriptResult<JsValue> {
|
pub fn run_script(&self, content: &str) -> ScriptResult<JsValue> {
|
||||||
let ctx = match self.context.lock() {
|
let ctx = match self.context.lock() {
|
||||||
Ok(ctx) => ctx,
|
Ok(ctx) => ctx,
|
||||||
|
|||||||
81
src/botscript/application.rs
Normal file
81
src/botscript/application.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
use quickjs_rusty::{context::Context, serde::from_js, OwnedJsObject};
|
||||||
|
use teloxide::Bot;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::{application::Application, message_forward::MessageForward, CallDB, DB},
|
||||||
|
message_answerer::MessageAnswerer,
|
||||||
|
send_application_to_chat, BotError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::ScriptError;
|
||||||
|
|
||||||
|
pub fn attach_user_application(
|
||||||
|
c: &Context,
|
||||||
|
o: &mut OwnedJsObject,
|
||||||
|
db: &DB,
|
||||||
|
bot: &Bot,
|
||||||
|
) -> Result<(), ScriptError> {
|
||||||
|
let db: std::sync::Arc<RwLock<DB>> = std::sync::Arc::new(RwLock::new(db.clone()));
|
||||||
|
let dbbox = Box::new(db.clone());
|
||||||
|
let db: &'static _ = Box::leak(dbbox);
|
||||||
|
|
||||||
|
let bot: std::sync::Arc<RwLock<Bot>> = std::sync::Arc::new(RwLock::new(bot.clone()));
|
||||||
|
let botbox = Box::new(bot.clone());
|
||||||
|
let bot: &'static _ = Box::leak(botbox);
|
||||||
|
|
||||||
|
let user_application =
|
||||||
|
c.create_callback(move |q: OwnedJsObject| -> Result<_, ScriptError> {
|
||||||
|
let db = db.clone();
|
||||||
|
let user: teloxide::types::User = match from_js(q.context(), &q) {
|
||||||
|
Ok(q) => q,
|
||||||
|
Err(_) => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let application = futures::executor::block_on(
|
||||||
|
Application::new(user.clone()).store_db(&mut db.write().unwrap()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let db2 = db.clone();
|
||||||
|
let msg = tokio::task::block_in_place(move || {
|
||||||
|
Handle::current().block_on(async move {
|
||||||
|
send_application_to_chat(
|
||||||
|
&bot.read().unwrap(),
|
||||||
|
&mut db2.write().unwrap(),
|
||||||
|
&application,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let msg = match msg {
|
||||||
|
Ok(msg) => msg,
|
||||||
|
Err(err) => {
|
||||||
|
info!("Got err: {err}");
|
||||||
|
return Err(ScriptError::MutexError("🤦♂️".to_string()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (chat_id, msg_id) = futures::executor::block_on(
|
||||||
|
MessageAnswerer::new(
|
||||||
|
&bot.read().unwrap(),
|
||||||
|
&mut db.write().unwrap(),
|
||||||
|
user.id.0 as i64,
|
||||||
|
)
|
||||||
|
.answer("left_application_msg", None, None),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
futures::executor::block_on(
|
||||||
|
MessageForward::new(msg.chat.id.0, msg.id.0, chat_id, msg_id, false)
|
||||||
|
.store_db(&mut db.write().unwrap()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let ret = true;
|
||||||
|
Ok(ret)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
o.set_property("user_application", user_application.into_value())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ use chrono::{DateTime, FixedOffset, Local};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::DbResult;
|
use super::DbResult;
|
||||||
|
use super::DB;
|
||||||
use crate::query_call_consume;
|
use crate::query_call_consume;
|
||||||
use crate::CallDB;
|
use crate::CallDB;
|
||||||
|
|
||||||
@ -36,4 +37,13 @@ where
|
|||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub async fn store_db(self, db: &mut DB) -> DbResult<Self> {
|
||||||
|
let db = db.get_database().await;
|
||||||
|
let ci = db.collection::<Self>("applications");
|
||||||
|
|
||||||
|
ci.insert_one(&self).await?;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use bson::doc;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::DbResult;
|
use super::DbResult;
|
||||||
|
use super::DB;
|
||||||
use crate::query_call_consume;
|
use crate::query_call_consume;
|
||||||
use crate::CallDB;
|
use crate::CallDB;
|
||||||
|
|
||||||
@ -42,6 +43,15 @@ impl MessageForward {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub async fn store_db(self, db: &mut DB) -> DbResult<Self> {
|
||||||
|
let db = db.get_database().await;
|
||||||
|
let ci = db.collection::<Self>("message_forward");
|
||||||
|
|
||||||
|
ci.insert_one(&self).await?;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get<D: CallDB>(
|
pub async fn get<D: CallDB>(
|
||||||
db: &mut D,
|
db: &mut D,
|
||||||
chat_id: i64,
|
chat_id: i64,
|
||||||
|
|||||||
@ -23,6 +23,8 @@ use crate::{BotDialogue, BotError, BotResult, CallbackStore, State};
|
|||||||
|
|
||||||
pub fn admin_handler() -> BotHandler {
|
pub fn admin_handler() -> BotHandler {
|
||||||
dptree::entry()
|
dptree::entry()
|
||||||
|
// keep on top to cancel any action
|
||||||
|
.branch(cancel_handler())
|
||||||
.branch(
|
.branch(
|
||||||
Update::filter_callback_query()
|
Update::filter_callback_query()
|
||||||
.filter_async(async |q: CallbackQuery, mut db: DB| {
|
.filter_async(async |q: CallbackQuery, mut db: DB| {
|
||||||
@ -197,6 +199,17 @@ async fn button_edit_callback(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cancel_handler() -> BotHandler {
|
||||||
|
Update::filter_message()
|
||||||
|
.filter(|msg: Message| msg.text() == Some("/cancel"))
|
||||||
|
.enter_dialogue::<Message, MongodbStorage<Json>, State>()
|
||||||
|
.endpoint(async |bot: Bot, msg: Message, dialogue: BotDialogue| {
|
||||||
|
dialogue.exit().await?;
|
||||||
|
bot.send_message(msg.chat.id, "Диалог закончен!").await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn command_handler() -> BotHandler {
|
fn command_handler() -> BotHandler {
|
||||||
Update::filter_message()
|
Update::filter_message()
|
||||||
.filter_async(async |msg: Message, mut db: DB| {
|
.filter_async(async |msg: Message, mut db: DB| {
|
||||||
@ -300,6 +313,10 @@ async fn support_reply_handler(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let text = format!(
|
||||||
|
"Сообщение от поддержки:\n{}\nЧтобы закончить диалог, нажмите на /cancel",
|
||||||
|
text
|
||||||
|
);
|
||||||
let msg = bot
|
let msg = bot
|
||||||
.send_message(ChatId(mf.source_chat_id), text)
|
.send_message(ChatId(mf.source_chat_id), text)
|
||||||
.parse_mode(ParseMode::Html);
|
.parse_mode(ParseMode::Html);
|
||||||
|
|||||||
@ -10,6 +10,7 @@ pub mod mongodb_storage;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use bot_manager::BotManager;
|
use bot_manager::BotManager;
|
||||||
|
use botscript::application::attach_user_application;
|
||||||
use botscript::{BotMessage, Runner, RunnerConfig, ScriptError, ScriptResult};
|
use botscript::{BotMessage, Runner, RunnerConfig, ScriptError, ScriptResult};
|
||||||
use db::application::Application;
|
use db::application::Application;
|
||||||
use db::bots::BotInstance;
|
use db::bots::BotInstance;
|
||||||
@ -131,7 +132,8 @@ impl BotController {
|
|||||||
pub async fn with_db(mut db: DB, token: &str, script: &str) -> ScriptResult<Self> {
|
pub async fn with_db(mut db: DB, token: &str, script: &str) -> ScriptResult<Self> {
|
||||||
let bot = Bot::new(token);
|
let bot = Bot::new(token);
|
||||||
|
|
||||||
let runner = Runner::init_with_db(&mut db)?;
|
let mut runner = Runner::init_with_db(&mut db)?;
|
||||||
|
runner.call_attacher(|c, o| attach_user_application(c, o, &db, &bot))??;
|
||||||
let rc = runner.init_config(script)?;
|
let rc = runner.init_config(script)?;
|
||||||
let rc = Arc::new(RwLock::new(rc));
|
let rc = Arc::new(RwLock::new(rc));
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user