use crate::db_connectors::{conversations::*, interactions::*, memories::*};
use crate::{
data::{ConversationInfo, CsmlRequest, Database, EngineError},
utils::{get_default_flow, get_flow_by_id, search_flow},
Context, CsmlBot, CsmlFlow, CsmlResult,
};
use csml_interpreter::{
data::{
context::{get_hashmap_from_json, get_hashmap_from_mem},
ApiInfo, Client, Event,
},
load_components, validate_bot,
};
use std::collections::HashMap;
pub fn init_conversation_info<'a>(
default_flow: String,
event: &Event,
request: &'a CsmlRequest,
bot: &'a CsmlBot,
mut db: Database,
) -> Result<ConversationInfo, EngineError> {
let interaction_id = init_interaction(request.payload.clone(), &request.client, &mut db)?;
let mut context = init_context(default_flow, request.client.clone(), &bot.fn_endpoint);
let flow_found = search_flow(event, &bot, &request.client, &mut db).ok();
let conversation_id =
get_or_create_conversation(&mut context, &bot, flow_found, &request.client, &mut db)?;
context.metadata = get_hashmap_from_json(&request.metadata, &context.flow);
context.current = get_hashmap_from_mem(
&internal_use_get_memories(&request.client, &mut db)?,
&context.flow,
);
let mut data = ConversationInfo {
conversation_id,
interaction_id,
context,
metadata: request.metadata.clone(), request_id: request.request_id.clone(),
callback_url: request.callback_url.clone(),
client: request.client.clone(),
messages: vec![],
db,
};
let flow = data.context.flow.to_owned();
let step = data.context.step.to_owned();
update_conversation(&mut data, Some(flow), Some(step))?;
Ok(data)
}
pub fn init_bot(bot: &mut CsmlBot) -> Result<(), EngineError> {
bot.native_components = match load_components() {
Ok(components) => Some(components),
Err(err) => return Err(EngineError::Interpreter(err.format_error())),
};
match validate_bot(&bot) {
CsmlResult {
flows: Some(flows),
errors: None,
..
} => {
bot.bot_ast = Some(base64::encode(bincode::serialize(&flows).unwrap()));
}
CsmlResult {
errors: Some(errors),
..
} => {
return Err(EngineError::Interpreter(format!(
"invalid bot {:?}",
errors
)))
}
_ => return Err(EngineError::Interpreter(format!("empty bot"))),
}
Ok(())
}
pub fn init_context(flow: String, client: Client, fn_endpoint: &Option<String>) -> Context {
let api_info = match fn_endpoint {
Some(value) => Some(ApiInfo {
client,
fn_endpoint: value.to_owned(),
}),
None => None,
};
Context {
current: HashMap::new(),
metadata: HashMap::new(),
api_info,
hold: None,
step: "start".to_owned(),
flow,
}
}
fn get_or_create_conversation<'a>(
context: &mut Context,
bot: &'a CsmlBot,
flow_found: Option<(&'a CsmlFlow, String)>,
client: &Client,
db: &mut Database,
) -> Result<String, EngineError> {
match get_latest_open(client, db)? {
Some(conversation) => {
match flow_found {
Some((flow, step)) => {
context.step = step;
context.flow = flow.name.to_owned();
}
None => {
let flow = match get_flow_by_id(&conversation.flow_id, &bot.flows) {
Ok(flow) => flow,
Err(..) => {
close_conversation(&conversation.id, &client, db)?;
return create_new_conversation(context, bot, flow_found, client, db);
}
};
context.step = conversation.step_id.to_owned();
context.flow = flow.name.to_owned();
}
};
Ok(conversation.id)
}
None => create_new_conversation(context, bot, flow_found, client, db),
}
}
fn create_new_conversation<'a>(
context: &mut Context,
bot: &'a CsmlBot,
flow_found: Option<(&'a CsmlFlow, String)>,
client: &Client,
db: &mut Database,
) -> Result<String, EngineError> {
let (flow, step) = match flow_found {
Some((flow, step)) => (flow, step),
None => (get_default_flow(bot)?, "start".to_owned()),
};
context.step = step;
context.flow = flow.name.to_owned();
let conversation_id = create_conversation(&flow.id, &context.step, client, db)?;
Ok(conversation_id)
}