UNPKG

@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
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