wingbot
Version:
Enterprise Messaging Bot Conversation Engine
221 lines (185 loc) • 7.31 kB
JavaScript
/**
* @author David Menger
*/
;
const stateData = require('./stateData');
let handlebars;
try {
// @ts-ignore
handlebars = module.require('handlebars');
} catch (er) {
handlebars = { compile: (text) => () => text };
}
const ATTR_REGEX = /^([^.]*)\.?(.+)?$/;
const SCALAR_TYPES = ['string', 'number', 'boolean'];
const SUBSCRIBE = '_$subscribe';
const UNSUBSCRIBE = '_$unsubscribe';
function getUpdate (attr, value, currentState = {}) {
let param;
let rest = attr && attr.replace(/\u2219/g, '.');
let state = currentState;
const ret = {};
let up = ret;
do {
[, param, rest = null] = `${rest}`.match(ATTR_REGEX);
let set;
if (rest) {
state = state[param] && typeof state[param] === 'object'
? state[param]
: {};
set = {
...state
};
} else {
set = value;
}
Object.assign(up, {
[param]: set
});
up = up[param];
} while (rest);
return ret;
}
function getValue (attr, currentState = {}) {
let param;
let rest = attr && attr.replace(/\u2219/g, '.');
let state = currentState;
do {
if (!state || typeof state !== 'object') {
break;
}
[, param, rest = null] = `${rest}`.match(ATTR_REGEX);
state = state[param];
} while (rest);
return rest ? undefined : state;
}
function isEmpty (val) {
return val === null
|| val === undefined
|| `${val}`.trim() === '';
}
function toArray (previousValue) {
if (Array.isArray(previousValue)) {
return previousValue.slice();
}
if (isEmpty(previousValue)) {
return [];
}
return [previousValue];
}
const ENTITY_HBS_REGEXP = /^\s*\{\{\[?@([^@[\]{}\s]+)(\])?\}\}\s*$/;
const VARIABLE_HBS_REGEXP = /^\s*\{\{\[?([^$@[\]{}\s]+)\]?\}\}\s*$/;
function getSetState (setState, req, res = null, stateOverride = null, configuration = null) {
if (!setState) {
return {};
}
const keys = Object.keys(setState)
.filter((k) => k !== '_');
let obj = {};
let state = stateData(req, res, configuration, stateOverride);
keys.forEach((k) => {
const val = setState[k];
let set;
if (k === SUBSCRIBE && Array.isArray(val)) {
if (res && typeof res.subscribe === 'function') {
val.forEach((v) => res.subscribe(v));
} else {
set = val;
}
} else if (k === UNSUBSCRIBE && Array.isArray(val)) {
if (res && typeof res.unsubscribe === 'function') {
val.forEach((v) => res.unsubscribe(v));
} else {
set = val;
}
} else if (val && typeof val === 'object') {
if (val._$textInput) {
set = req.text();
} else if (val._$entity) {
set = typeof val._$ev !== 'undefined'
? val._$ev
: req.entity(val._$entity, 0, state);
const cleanEntityName = `${val._$entity}`.replace(/^@/, '');
const key = `@${cleanEntityName}`;
if (set === null
&& typeof req.state[key] !== 'undefined'
&& (!res || res.newState[key] !== null)) {
set = req.state[key];
}
} else if (typeof val._$inc !== 'undefined') {
let previousValue = getValue(k, state);
const incremet = typeof val._$inc === 'number' && !Number.isNaN(val._$inc)
? val._$inc
: (parseInt(`${val._$inc}`, 10) || 0);
if (typeof previousValue === 'string') {
previousValue = parseInt(previousValue, 10);
}
if (typeof previousValue !== 'number' || Number.isNaN(previousValue)) {
previousValue = 0;
}
set = incremet + previousValue;
} else if (val._$shift || val._$pop
|| typeof val._$push !== 'undefined'
|| typeof val._$add !== 'undefined'
|| typeof val._$rem !== 'undefined'
|| typeof val._$set !== 'undefined') {
let valAsArray = toArray(getValue(k, state));
if (val._$shift) {
valAsArray.shift();
} else if (val._$pop) {
valAsArray.pop();
} else {
const value = val._$add || val._$rem || val._$push || val._$set;
const [, entity, rear] = `${value}`.match(ENTITY_HBS_REGEXP) || [];
const [, variable] = `${value}`.match(VARIABLE_HBS_REGEXP) || [];
let values;
if (entity && (!rear || req.entities.some((e) => e.entity === entity))) {
values = req.entities.filter((e) => e.entity === entity)
.map((e) => e.value);
if (values.length === 0 && stateOverride && stateOverride[`@${entity}`]) {
values = [stateOverride[`@${entity}`]];
}
} else if (variable) {
values = toArray(getValue(variable, state));
} else if (value === '' || value === null || value === undefined) {
values = [];
} else {
const useValue = typeof value === 'string'
? handlebars.compile(value.replace(/(?<!\\),/g, '#-SpLiT--215315-@#').replace(/\\,/g, ','))(state)
.split(/#-SpLiT--215315-@#/g)
.map((v) => v.trim())
: value;
values = toArray(useValue);
}
if (typeof val._$push !== 'undefined') {
valAsArray.push(...values);
} else if (typeof val._$add !== 'undefined') {
const unique = Array.from(new Set(values).values());
valAsArray.push(...unique.filter((v) => !valAsArray.includes(v)));
} else if (typeof val._$rem !== 'undefined') {
valAsArray = valAsArray
.filter((v) => !values.includes(v));
} else if (typeof val._$set !== 'undefined') {
valAsArray = values;
}
}
set = valAsArray;
} else if (k.match(/^(@|_~)/)
|| !Object.keys(val).some((l) => l.match(/^_\$/))) {
set = val;
}
} else if (typeof val === 'string') {
set = handlebars.compile(val)(state);
} else if (val === null
|| SCALAR_TYPES.includes(typeof val)) {
set = val;
}
if (typeof set !== 'undefined') {
const up = getUpdate(k, set, state);
obj = { ...obj, ...up };
state = { ...state, ...up };
}
});
return obj;
}
module.exports = { getUpdate, getValue, getSetState };