Compare commits
10 Commits
e0c00d68f9
...
2a4ed51824
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a4ed51824 | ||
|
|
d1b25b52c1 | ||
|
|
2c5802eaeb | ||
|
|
bd800e88eb | ||
|
|
a2e1354bee | ||
|
|
55d53bd140 | ||
|
|
ea007127ff | ||
|
|
0a60b0469f | ||
|
|
40eec7d38d | ||
|
|
2ccfc19a6c |
@ -1,5 +1,8 @@
|
||||
name: Build && Deploy
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@ -7,6 +7,9 @@ const dialog = {
|
||||
state: "start"
|
||||
},
|
||||
cancel: {
|
||||
buttons: [
|
||||
[{name: {name: "Def"}, callback_name: "defcall"}]
|
||||
],
|
||||
state: "none"
|
||||
},
|
||||
somecomplicatedcmd: {}
|
||||
|
||||
174
src/botscript.rs
174
src/botscript.rs
@ -1,10 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use itertools::Itertools;
|
||||
use quickjs_rusty::serde::from_js;
|
||||
use quickjs_rusty::utils::create_null;
|
||||
use quickjs_rusty::Context;
|
||||
use quickjs_rusty::ContextError;
|
||||
use quickjs_rusty::ExecutionError;
|
||||
use quickjs_rusty::JsFunction;
|
||||
use quickjs_rusty::OwnedJsValue as JsValue;
|
||||
use quickjs_rusty::ValueError;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@ -16,6 +20,8 @@ pub enum ScriptError {
|
||||
ExecutionError(#[from] ExecutionError),
|
||||
#[error("error from anyhow: {0:?}")]
|
||||
SerdeError(#[from] quickjs_rusty::serde::Error),
|
||||
#[error("error value: {0:?}")]
|
||||
ValueError(#[from] ValueError),
|
||||
}
|
||||
|
||||
pub type ScriptResult<T> = Result<T, ScriptError>;
|
||||
@ -37,12 +43,80 @@ pub trait DeserializeJS {
|
||||
|
||||
impl DeserializeJS for JsValue {
|
||||
fn js_into<'a, T: Deserialize<'a>>(&'a self) -> ScriptResult<T> {
|
||||
let rc = from_js(self.context(), &self)?;
|
||||
let rc = from_js(self.context(), self)?;
|
||||
|
||||
Ok(rc)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DeserializerJS {
|
||||
fn_map: HashMap<String, JsFunction>,
|
||||
}
|
||||
|
||||
impl DeserializerJS {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
fn_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_js<'a, T: Deserialize<'a>>(value: &'a JsValue) -> ScriptResult<T> {
|
||||
let mut s = Self::new();
|
||||
|
||||
s.inject_templates(value, "".to_string())?;
|
||||
|
||||
let res = value.js_into()?;
|
||||
|
||||
// val.map_functions(s.fn_map);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn inject_templates(
|
||||
&mut self,
|
||||
value: &JsValue,
|
||||
path: String,
|
||||
) -> ScriptResult<Option<String>> {
|
||||
if let Ok(f) = value.clone().try_into_function() {
|
||||
self.fn_map.insert(path.clone(), f);
|
||||
return Ok(Some(path));
|
||||
} else if let Ok(o) = value.clone().try_into_object() {
|
||||
let path = if path.is_empty() { path } else { path + "." }; // trying to avoid . in the start
|
||||
// of stringified path
|
||||
let res = o
|
||||
.properties_iter()?
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
// since chunks(2) is used and properties iterator over object
|
||||
// always has even elements, unwrap will not fail
|
||||
.map(
|
||||
#[allow(clippy::unwrap_used)]
|
||||
|mut chunk| (chunk.next().unwrap(), chunk.next().unwrap()),
|
||||
)
|
||||
.map(|(k, p)| k.and_then(|k| p.map(|p| (k, p))))
|
||||
.filter_map(|m| m.ok())
|
||||
.try_for_each(|(k, p)| {
|
||||
let k = match k.to_string() {
|
||||
Ok(k) => k,
|
||||
Err(err) => return Err(ScriptError::ValueError(err)),
|
||||
};
|
||||
let res = match self.inject_templates(&p, path.clone() + &k)? {
|
||||
Some(_) => o.set_property(&k, JsValue::new(o.context(), create_null())),
|
||||
None => Ok(()),
|
||||
};
|
||||
match res {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => Err(ScriptError::ExecutionError(err)),
|
||||
}
|
||||
});
|
||||
res?;
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this function since it is suitable only for early development
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn print(s: String) {
|
||||
@ -54,7 +128,99 @@ pub struct BotConfig {
|
||||
version: f64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub trait ResolveValue {
|
||||
type Value;
|
||||
|
||||
fn resolve(self, runner: &Runner) -> ScriptResult<Self::Value>;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum KeyboardDefinition {
|
||||
Rows(Vec<RowDefinition>),
|
||||
Function(BotFunction),
|
||||
}
|
||||
|
||||
impl ResolveValue for KeyboardDefinition {
|
||||
type Value = Vec<<RowDefinition as ResolveValue>::Value>;
|
||||
|
||||
fn resolve(self, runner: &Runner) -> ScriptResult<Self::Value> {
|
||||
match self {
|
||||
KeyboardDefinition::Rows(rows) => rows.into_iter().map(|r| r.resolve(runner)).collect(),
|
||||
KeyboardDefinition::Function(f) => {
|
||||
Self::resolve(f.call_context(runner)?.js_into()?, runner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum RowDefinition {
|
||||
Buttons(Vec<ButtonDefinition>),
|
||||
Function(BotFunction),
|
||||
}
|
||||
|
||||
impl ResolveValue for RowDefinition {
|
||||
type Value = Vec<<ButtonDefinition as ResolveValue>::Value>;
|
||||
|
||||
fn resolve(self, runner: &Runner) -> ScriptResult<Self::Value> {
|
||||
match self {
|
||||
RowDefinition::Buttons(buttons) => {
|
||||
buttons.into_iter().map(|b| b.resolve(runner)).collect()
|
||||
}
|
||||
RowDefinition::Function(f) => Self::resolve(f.call_context(runner)?.js_into()?, runner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum ButtonDefinition {
|
||||
Button(ButtonRaw),
|
||||
ButtonLiteral(String),
|
||||
Function(BotFunction),
|
||||
}
|
||||
|
||||
impl ResolveValue for ButtonDefinition {
|
||||
type Value = ButtonRaw;
|
||||
|
||||
fn resolve(self, runner: &Runner) -> ScriptResult<Self::Value> {
|
||||
match self {
|
||||
ButtonDefinition::Button(button) => Ok(button),
|
||||
ButtonDefinition::ButtonLiteral(l) => Ok(ButtonRaw::from_literal(l)),
|
||||
ButtonDefinition::Function(f) => {
|
||||
Self::resolve(f.call_context(runner)?.js_into()?, runner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ButtonRaw {
|
||||
name: ButtonName,
|
||||
callback_name: String,
|
||||
}
|
||||
|
||||
impl ButtonRaw {
|
||||
pub fn from_literal(literal: String) -> Self {
|
||||
ButtonRaw {
|
||||
name: ButtonName::Literal {
|
||||
literal: literal.clone(),
|
||||
},
|
||||
callback_name: literal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum ButtonName {
|
||||
Value { name: String },
|
||||
Literal { literal: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Button {
|
||||
name: String,
|
||||
}
|
||||
@ -62,7 +228,7 @@ pub struct Button {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct BotMessage {
|
||||
// buttons: Vec<Button>
|
||||
buttons: Option<BotFunction>,
|
||||
buttons: Option<KeyboardDefinition>,
|
||||
state: Option<String>,
|
||||
|
||||
handler: Option<BotFunction>,
|
||||
@ -145,6 +311,8 @@ mod tests {
|
||||
let runner = Runner::init().unwrap();
|
||||
let val = runner.run_script(include_str!("../mainbot.js")).unwrap();
|
||||
println!("config: {:?}", val);
|
||||
let d: RunnerConfig = DeserializerJS::deserialize_js(&val).unwrap();
|
||||
println!("desr rc: {:?}", d);
|
||||
let val = runner.run_script("start_buttons()").unwrap();
|
||||
println!("Val: {:?}", val.to_string());
|
||||
}
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@ -6,11 +6,15 @@ pub mod mongodb_storage;
|
||||
pub mod utils;
|
||||
|
||||
use botscript::{BotMessage, Runner, RunnerConfig};
|
||||
use commands::BotCommand;
|
||||
use db::application::Application;
|
||||
use db::callback_info::CallbackInfo;
|
||||
use db::message_forward::MessageForward;
|
||||
use itertools::Itertools;
|
||||
use log::{error, info, warn};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
use teloxide::sugar::request::RequestReplyExt;
|
||||
use utils::create_callback_button;
|
||||
@ -158,11 +162,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
//
|
||||
let cmdmap: std::sync::Arc<RwLock<HashMap<_, _>>> =
|
||||
std::sync::Arc::new(RwLock::new(bc.rc.dialog.commands));
|
||||
|
||||
let handler = dptree::entry()
|
||||
.inspect(|u: Update| {
|
||||
info!("{u:#?}"); // Print the update to the console with inspect
|
||||
})
|
||||
.branch(
|
||||
Update::filter_message()
|
||||
.filter_map(|m: Message| m.text().and_then(|t| BotCommand::from_str(t).ok()))
|
||||
.filter_map(move |bc: BotCommand| {
|
||||
let rc = std::sync::Arc::clone(&cmdmap);
|
||||
|
||||
let cmdmap = rc.read().expect("RwLock lock on commands map failed");
|
||||
|
||||
cmdmap.get(bc.command()).cloned()
|
||||
})
|
||||
.endpoint(botscript_command_handler),
|
||||
)
|
||||
.branch(
|
||||
Update::filter_callback_query()
|
||||
.filter_async(async |q: CallbackQuery, mut db: DB| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user