pub mod data;
pub use csml_interpreter::data::{
ast::{Expr, Flow, InstructionScope},
error_info::ErrorInfo,
warnings::Warnings,
Client, CsmlResult,
};
use serde_json::json;
mod db_connectors;
mod error_messages;
mod encrypt;
mod init;
mod interpreter_actions;
mod send;
mod utils;
use data::*;
use db_connectors::{bot, conversations::*, init_db, messages::*, state::*, DbConversation, BotVersion, BotVersionCreated};
use init::*;
use interpreter_actions::interpret_step;
use utils::*;
use csml_interpreter::{
data::{csml_bot::CsmlBot, csml_flow::CsmlFlow, Context, Hold, Memory},
};
use md5::{Digest, Md5};
use std::{collections::HashMap, env, time::SystemTime};
pub fn start_conversation(
request: CsmlRequest,
bot_opt: BotOpt,
) -> Result<serde_json::Map<String, serde_json::Value>, EngineError> {
let now = SystemTime::now();
let formatted_event = format_event(json!(request))?;
let mut db = init_db()?;
let mut bot = bot_opt.search_bot(&mut db);
init_bot(&mut bot)?;
let mut data = init_conversation_info(
get_default_flow(&bot)?.name.to_owned(),
&formatted_event,
&request,
&bot,
db,
)?;
let msgs = vec![request.payload.to_owned()];
add_messages_bulk(&mut data, msgs, 0, "RECEIVE")?;
let flow = get_flow_by_id(&data.context.flow, &bot.flows)?;
check_for_hold(&mut data, &bot.bot_ast, flow)?;
let res = interpret_step(&mut data, formatted_event.to_owned(), &bot);
if let Ok(var) = env::var(DEBUG) {
if var == "true" {
let el = now.elapsed()?;
println!("Total time Manager - {}.{}", el.as_secs(), el.as_millis());
}
}
res
}
pub fn get_open_conversation(client: &Client) -> Result<Option<DbConversation>, EngineError> {
let mut db = init_db()?;
get_latest_open(client, &mut db)
}
pub fn create_bot_version(csml_bot: CsmlBot) -> Result<BotVersionCreated, EngineError> {
let mut db = init_db()?;
let bot_id = csml_bot.id.clone();
match validate_bot(csml_bot.clone()) {
CsmlResult {
errors: Some(errors),
..
} => Err(EngineError::Interpreter(format!("{:?}", errors))),
CsmlResult { .. } => {
let version_id = bot::create_bot_version(bot_id, csml_bot, &mut db)?;
let engine_version = env!("CARGO_PKG_VERSION").to_owned();
Ok(BotVersionCreated{version_id, engine_version })
},
}
}
pub fn get_last_bot_version(bot_id: &str) -> Result<Option<BotVersion>, EngineError>{
let mut db = init_db()?;
bot::get_last_bot_version(bot_id, &mut db)
}
pub fn get_bot_by_version_id(id: &str, bot_id: &str) -> Result<Option<BotVersion>, EngineError> {
let mut db = init_db()?;
bot::get_by_version_id(id, bot_id, &mut db)
}
pub fn get_bot_versions(
bot_id: &str,
limit: Option<i64>,
last_key: Option<String>,
) -> Result<serde_json::Value, EngineError> {
let mut db = init_db()?;
bot::get_bot_versions(bot_id, limit, last_key, &mut db)
}
pub fn delete_bot_version_id(
id: &str,
bot_id: &str,
) -> Result<(), EngineError> {
let mut db = init_db()?;
bot::delete_bot_version(bot_id, id, &mut db)
}
pub fn delete_all_bot_versions(
bot_id: &str,
) -> Result<(), EngineError> {
let mut db = init_db()?;
bot::delete_bot_versions(bot_id, &mut db)
}
pub fn get_steps_from_flow(bot: CsmlBot) -> HashMap<String, Vec<String>> {
csml_interpreter::get_steps_from_flow(bot)
}
pub fn validate_bot(bot: CsmlBot) -> CsmlResult {
csml_interpreter::validate_bot(&bot)
}
pub fn user_close_all_conversations(client: Client) -> Result<(), EngineError> {
let mut db = init_db()?;
delete_state_key(&client, "hold", "position", &mut db)?;
close_all_conversations(&client, &mut db)
}
fn check_for_hold(
data: &mut ConversationInfo,
bot_ast: &Option<String>,
flow: &CsmlFlow,
) -> Result<(), EngineError> {
match get_state_key(&data.client, "hold", "position", &mut data.db) {
Ok(Some(string)) => {
let hold = serde_json::to_value(string)?;
let step_hash = match (hold.get("hash"), hold.get("step_hash")) {
(Some(old_hash), None) => {
let mut hash = Md5::new();
hash.update(flow.content.as_bytes());
let new_hash = format!("{:x}", hash.finalize());
if new_hash != *old_hash {
data.context.step = "start".to_owned();
delete_state_key(&data.client, "hold", "position", &mut data.db)?;
data.context.hold = None;
return Ok(());
}
new_hash
}
(None, Some(step_hash_value)) => {
let step_hash = match get_current_step_hash(bot_ast, data) {
Ok(step_hash) if step_hash != *step_hash_value => {
return clean_hold_and_restart(data)
}
Ok(step_hash) => step_hash,
Err(_) => return clean_hold_and_restart(data),
};
step_hash
}
_ => return Ok(()),
};
data.context.hold = Some(Hold {
index: hold["index"]
.as_u64()
.ok_or(EngineError::Interpreter("hold index bad format".to_owned()))?
as usize,
step_vars: hold["step_vars"].clone(),
step_hash: step_hash,
});
delete_state_key(&data.client, "hold", "position", &mut data.db)?;
}
Ok(None) => (),
Err(_) => (),
};
Ok(())
}