@botonic/plugin-flow-builder
Version:
Use Flow Builder to show your contents
194 lines • 9.75 kB
JavaScript
import { __awaiter } from "tslib";
import { v7 as uuidv7 } from 'uuid';
import { FlowBuilderApi } from './api';
import { FLOW_BUILDER_API_URL_PROD, SEPARATOR, SOURCE_INFO_SEPARATOR, } from './constants';
import { FlowAiAgent, FlowBotAction, FlowCarousel, FlowHandoff, FlowImage, FlowKnowledgeBase, FlowRating, FlowText, FlowVideo, FlowWhatsappButtonList, FlowWhatsappCtaUrlButtonNode, } from './content-fields';
import { HtNodeWithContentType, } from './content-fields/hubtype-fields';
import { DEFAULT_FUNCTIONS } from './functions';
import { FlowBuilderJSONVersion, } from './types';
import { getNodeByUserInput } from './user-input';
import { inputHasTextData, resolveGetAccessToken } from './utils';
// TODO: Create a proper service to wrap all calls and allow api versioning
export default class BotonicPluginFlowBuilder {
constructor(options) {
var _a, _b, _c;
this.apiUrl = options.apiUrl || FLOW_BUILDER_API_URL_PROD;
this.jsonVersion = options.jsonVersion || FlowBuilderJSONVersion.LATEST;
this.flow = options.flow;
this.getAccessToken = resolveGetAccessToken(options.getAccessToken);
this.trackEvent = options.trackEvent;
this.getKnowledgeBaseResponse = options.getKnowledgeBaseResponse;
this.getAiAgentResponse = options.getAiAgentResponse;
this.smartIntentsConfig = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.smartIntentsConfig), { useLatest: this.jsonVersion === FlowBuilderJSONVersion.LATEST });
const customFunctions = options.customFunctions || {};
this.functions = Object.assign(Object.assign({}, DEFAULT_FUNCTIONS), customFunctions);
this.inShadowing = {
allowKeywords: ((_a = options.inShadowing) === null || _a === void 0 ? void 0 : _a.allowKeywords) || false,
allowSmartIntents: ((_b = options.inShadowing) === null || _b === void 0 ? void 0 : _b.allowSmartIntents) || false,
allowKnowledgeBases: ((_c = options.inShadowing) === null || _c === void 0 ? void 0 : _c.allowKnowledgeBases) || false,
};
this.contentFilters = options.contentFilters || [];
this.customRatingMessageEnabled =
options.customRatingMessageEnabled || false;
}
resolveFlowUrl(request) {
if (request.session.is_test_integration) {
return `${this.apiUrl}/v1/bot_flows/{bot_id}/versions/${FlowBuilderJSONVersion.DRAFT}/`;
}
return `${this.apiUrl}/v1/bot_flows/{bot_id}/versions/${this.jsonVersion}/`;
}
pre(request) {
return __awaiter(this, void 0, void 0, function* () {
this.currentRequest = request;
this.cmsApi = yield FlowBuilderApi.create({
flowUrl: this.resolveFlowUrl(request),
url: this.apiUrl,
flow: this.flow,
accessToken: this.getAccessToken(request.session),
request: this.currentRequest,
});
const checkUserTextInput = inputHasTextData(request.input) && !request.input.payload;
if (checkUserTextInput) {
const resolvedLocale = this.cmsApi.getResolvedLocale();
const nodeByUserInput = yield getNodeByUserInput(this.cmsApi, resolvedLocale, request, this.smartIntentsConfig);
request.input.payload = this.cmsApi.getPayload(nodeByUserInput === null || nodeByUserInput === void 0 ? void 0 : nodeByUserInput.target);
}
this.updateRequestBeforeRoutes(request);
});
}
updateRequestBeforeRoutes(request) {
if (request.input.payload) {
request.input.payload = this.removeSourceSuffix(request.input.payload);
if (this.cmsApi.isBotAction(request.input.payload)) {
const cmsBotAction = this.cmsApi.getNodeById(request.input.payload);
request.input.payload =
this.cmsApi.createPayloadWithParams(cmsBotAction);
}
}
}
removeSourceSuffix(payload) {
return payload.split(SOURCE_INFO_SEPARATOR)[0];
}
post(request) {
request.input.nluResolution = undefined;
}
getContentsByContentID(contentID, prevContents) {
return __awaiter(this, void 0, void 0, function* () {
const node = this.cmsApi.getNodeByContentID(contentID);
return yield this.getContentsByNode(node, prevContents);
});
}
getUUIDByContentID(contentID) {
const node = this.cmsApi.getNodeByContentID(contentID);
return node.id;
}
getContentsById(id, prevContents) {
return __awaiter(this, void 0, void 0, function* () {
const node = this.cmsApi.getNodeById(id);
return yield this.getContentsByNode(node, prevContents);
});
}
getStartContents() {
return __awaiter(this, void 0, void 0, function* () {
const startNode = this.cmsApi.getStartNode();
this.currentRequest.session.flow_thread_id = uuidv7();
return yield this.getContentsByNode(startNode);
});
}
getContentsByNode(node, prevContents) {
return __awaiter(this, void 0, void 0, function* () {
const contents = prevContents || [];
const resolvedLocale = this.cmsApi.getResolvedLocale();
if (node.type === HtNodeWithContentType.FUNCTION) {
const targetId = yield this.callFunction(node, resolvedLocale);
return this.getContentsById(targetId, contents);
}
const content = this.getFlowContent(node, resolvedLocale);
if (content) {
contents.push(content);
}
// If node is BOT_ACTION not add more contents to render, next nodes render after execute action
if (node.type === HtNodeWithContentType.BOT_ACTION) {
return contents;
}
// TODO: prevent infinite recursive calls
if (node.follow_up) {
return this.getContentsById(node.follow_up.id, contents);
}
return contents;
});
}
getFlowContent(hubtypeContent, locale) {
switch (hubtypeContent.type) {
case HtNodeWithContentType.TEXT:
return FlowText.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
case HtNodeWithContentType.IMAGE:
return FlowImage.fromHubtypeCMS(hubtypeContent, locale);
case HtNodeWithContentType.CAROUSEL:
return FlowCarousel.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
case HtNodeWithContentType.VIDEO:
return FlowVideo.fromHubtypeCMS(hubtypeContent, locale);
case HtNodeWithContentType.WHATSAPP_BUTTON_LIST:
return FlowWhatsappButtonList.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
case HtNodeWithContentType.WHATSAPP_CTA_URL_BUTTON:
return FlowWhatsappCtaUrlButtonNode.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
case HtNodeWithContentType.HANDOFF:
return FlowHandoff.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
case HtNodeWithContentType.KNOWLEDGE_BASE:
return FlowKnowledgeBase.fromHubtypeCMS(hubtypeContent);
case HtNodeWithContentType.AI_AGENT:
return FlowAiAgent.fromHubtypeCMS(hubtypeContent);
case HtNodeWithContentType.RATING:
return FlowRating.fromHubtypeCMS(hubtypeContent, locale);
case HtNodeWithContentType.BOT_ACTION:
return FlowBotAction.fromHubtypeCMS(hubtypeContent, locale, this.cmsApi);
default:
return undefined;
}
}
callFunction(functionNode, locale) {
return __awaiter(this, void 0, void 0, function* () {
const functionNodeId = functionNode.id;
const functionArguments = this.getArgumentsByLocale(functionNode.content.arguments, locale);
const nameValues = functionArguments.map(arg => {
return { [arg.name]: arg.value };
});
const args = Object.assign({
request: this.currentRequest,
results: functionNode.content.result_mapping.map(r => r.result),
}, ...nameValues);
const functionResult = yield this.functions[functionNode.content.action](args);
// TODO define result_mapping per locale??
const result = functionNode.content.result_mapping.find(r => r.result === functionResult);
if (!(result === null || result === void 0 ? void 0 : result.target)) {
throw new Error(`No result found for result_mapping for node with id: ${functionNodeId}`);
}
return result.target.id;
});
}
getArgumentsByLocale(args, locale) {
let resultArguments = [];
for (const arg of args) {
if ('locale' in arg && arg.locale === locale) {
resultArguments = [...resultArguments, ...arg.values];
}
if ('type' in arg) {
resultArguments = [...resultArguments, arg];
}
}
return resultArguments;
}
getPayloadParams(payload) {
const payloadParams = JSON.parse(payload.split(SEPARATOR)[1] || '{}');
return payloadParams;
}
getFlowName(flowId) {
return this.cmsApi.getFlowName(flowId);
}
}
export * from './action';
export * from './content-fields';
export { trackFlowContent } from './tracking';
export { FlowBuilderJSONVersion, } from './types';
export * from './webview';
//# sourceMappingURL=index.js.map