@botonic/plugin-flow-builder
Version:
Botonic plugin for **Hubtype Flow Builder**: run and bridge flow-driven logic from bots on the **current** line.
162 lines • 9.28 kB
JavaScript
import { __awaiter } from "tslib";
import { v7 as uuidv7 } from 'uuid';
import { FlowBuilderApi } from './api';
import { EMPTY_PAYLOAD, SEPARATOR, SOURCE_INFO_SEPARATOR } from './constants';
import { HtNodeWithContentType, } from './content-fields/hubtype-fields/index';
import { FlowFactory } from './flow-factory';
import { CustomFunction, DEFAULT_FUNCTION_NAMES } from './functions';
import { FlowBuilderJSONVersion, } from './types';
import { getNextPayloadByUserInput } from './user-input/index';
import { inputHasTextOrTranscript } from './utils/input';
export class BotonicPluginFlowBuilder {
constructor(options) {
var _a, _b, _c, _d, _e;
this.jsonPath = (options === null || options === void 0 ? void 0 : options.flowVersion) || FlowBuilderJSONVersion.LATEST;
this.localFlow = options === null || options === void 0 ? void 0 : options.flow;
this.trackEvent = options === null || options === void 0 ? void 0 : options.trackEvent;
this.getKnowledgeBaseResponse = options === null || options === void 0 ? void 0 : options.getKnowledgeBaseResponse;
this.getAiAgentResponse = options === null || options === void 0 ? void 0 : options.getAiAgentResponse;
const customFunctions = (options === null || options === void 0 ? void 0 : options.customFunctions) || {};
this.functions = customFunctions;
this.smartIntentsConfig = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.smartIntentsConfig), { useLatest: this.jsonPath === FlowBuilderJSONVersion.LATEST });
this.inShadowing = {
allowKeywords: ((_a = options === null || options === void 0 ? void 0 : options.inShadowing) === null || _a === void 0 ? void 0 : _a.allowKeywords) || false,
allowSmartIntents: ((_b = options === null || options === void 0 ? void 0 : options.inShadowing) === null || _b === void 0 ? void 0 : _b.allowSmartIntents) || false,
allowKnowledgeBases: ((_c = options === null || options === void 0 ? void 0 : options.inShadowing) === null || _c === void 0 ? void 0 : _c.allowKnowledgeBases) || false,
allowAiAgents: ((_d = options === null || options === void 0 ? void 0 : options.inShadowing) === null || _d === void 0 ? void 0 : _d.allowAiAgents) || false,
};
this.contentFilters = (options === null || options === void 0 ? void 0 : options.contentFilters) || [];
this.disableAIAgentInFirstInteraction =
(_e = options === null || options === void 0 ? void 0 : options.disableAIAgentInFirstInteraction) !== null && _e !== void 0 ? _e : false;
}
pre(botonicContext) {
return __awaiter(this, void 0, void 0, function* () {
// When AI Agent is executed in Whatsapp, button payloads come as referral and must be converted to text being processed by the agent.
this.convertWhatsappAiAgentEmptyPayloads(botonicContext);
this.botonicContext = botonicContext;
this.cmsApi = yield FlowBuilderApi.create(botonicContext, this.localFlow);
const checkUserTextInput = inputHasTextOrTranscript(botonicContext.input) &&
!botonicContext.input.payload;
if (checkUserTextInput) {
const resolvedLocale = this.cmsApi.getResolvedLocale();
const nextPayload = yield getNextPayloadByUserInput(this.cmsApi, resolvedLocale, botonicContext, this.smartIntentsConfig);
botonicContext.input.payload = nextPayload;
}
yield this.updateRequestBeforeRoutes(botonicContext);
});
}
convertWhatsappAiAgentEmptyPayloads(botonicContext) {
if (botonicContext.session.isWhatsapp()) {
const referral = botonicContext.input.referral;
const payload = botonicContext.input.payload;
const shouldUseReferral = referral && (payload === null || payload === void 0 ? void 0 : payload.startsWith(EMPTY_PAYLOAD));
if (shouldUseReferral && referral) {
botonicContext.input.setAsText(referral);
}
}
}
updateRequestBeforeRoutes(botonicContext) {
return __awaiter(this, void 0, void 0, function* () {
yield this.cmsApi.removeCaptureUserInputId();
const payload = botonicContext.input.payload;
if (payload) {
const cleaned = this.removeSourceSuffix(payload);
botonicContext.input.payload = cleaned;
if (this.cmsApi.isBotAction(cleaned)) {
const cmsBotAction = this.cmsApi.getNodeById(cleaned);
botonicContext.input.payload =
this.cmsApi.createPayloadWithParams(cmsBotAction);
// Re-execute convertWhatsappAiAgentEmptyPayloads function to handle
// the case that a BotAction has a payload equals to EMPTY_PAYLOAD
this.convertWhatsappAiAgentEmptyPayloads(botonicContext);
}
}
});
}
removeSourceSuffix(payload) {
return payload.split(SOURCE_INFO_SEPARATOR)[0];
}
post(botonicContext) {
botonicContext.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_1, prevContents_1) {
return __awaiter(this, arguments, void 0, function* (id, prevContents, depth = 0) {
const node = this.cmsApi.getNodeById(id);
return yield this.getContentsByNode(node, prevContents, depth);
});
}
getStartContents() {
return __awaiter(this, void 0, void 0, function* () {
const startNode = this.cmsApi.getStartNode();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
yield this.botonicContext.updateBotSession({ flowThreadId: uuidv7() });
return yield this.getContentsByNode(startNode);
});
}
getContentsByNode(node_1, prevContents_1) {
return __awaiter(this, arguments, void 0, function* (node, prevContents, depth = 0) {
const contents = prevContents !== null && prevContents !== void 0 ? prevContents : [];
const resolvedLocale = this.cmsApi.getResolvedLocale();
if (node.type === HtNodeWithContentType.FUNCTION &&
!DEFAULT_FUNCTION_NAMES.includes(node.content.action)) {
const customFunctionResolver = new CustomFunction(this.functions,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.botonicContext, resolvedLocale);
const targetId = yield customFunctionResolver.call(node);
return this.getContentsById(targetId, contents, depth);
}
const flowFactory = new FlowFactory(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.botonicContext, this.cmsApi, resolvedLocale);
const content = yield flowFactory.getFlowContent(node);
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;
}
// Prevent infinite recursion when followUp chain forms a cycle
if ((content === null || content === void 0 ? void 0 : content.followUp) &&
depth < BotonicPluginFlowBuilder.MAX_FOLLOW_UP_DEPTH) {
return this.getContentsById(content.followUp.id, contents, depth + 1);
}
return contents;
});
}
getPayloadParams(payload) {
const payloadParams = JSON.parse(payload.split(SEPARATOR)[1] || '{}');
return payloadParams;
}
getFlowName(flowId) {
return this.cmsApi.getFlowName(flowId);
}
getRatingSubmittedInfo(payload) {
const buttonId = payload === null || payload === void 0 ? void 0 : payload.split(SEPARATOR)[1];
const ratingNode = this.cmsApi.getRatingNodeByButtonId(buttonId);
const ratingButton = this.cmsApi.getRatingButtonById(ratingNode, buttonId);
const possibleOptions = ratingNode.content.buttons.map(button => button.text);
const possibleValues = ratingNode.content.buttons.map(button => button.value);
return Object.assign(Object.assign({}, ratingButton), { possibleOptions,
possibleValues });
}
setInShadowing(inShadowing) {
this.inShadowing = inShadowing;
}
}
BotonicPluginFlowBuilder.MAX_FOLLOW_UP_DEPTH = 10;
export { FlowBuilderAction } from './action/index';
export { AGENT_RATING_PAYLOAD, EMPTY_PAYLOAD } from './constants';
export * from './content-fields/index';
export { trackFlowContent } from './tracking';
//# sourceMappingURL=index.js.map