@botonic/plugin-flow-builder 0.21.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -0
  2. package/lib/action.d.ts +15 -0
  3. package/lib/action.js +62 -0
  4. package/lib/action.js.map +1 -0
  5. package/lib/content-fields/button.d.ts +10 -0
  6. package/lib/content-fields/button.js +33 -0
  7. package/lib/content-fields/button.js.map +1 -0
  8. package/lib/content-fields/carousel.d.ts +10 -0
  9. package/lib/content-fields/carousel.js +37 -0
  10. package/lib/content-fields/carousel.js.map +1 -0
  11. package/lib/content-fields/content-base.d.ts +10 -0
  12. package/lib/content-fields/content-base.js +15 -0
  13. package/lib/content-fields/content-base.js.map +1 -0
  14. package/lib/content-fields/element.d.ts +11 -0
  15. package/lib/content-fields/element.js +25 -0
  16. package/lib/content-fields/element.js.map +1 -0
  17. package/lib/content-fields/image.d.ts +9 -0
  18. package/lib/content-fields/image.js +28 -0
  19. package/lib/content-fields/image.js.map +1 -0
  20. package/lib/content-fields/text.d.ts +12 -0
  21. package/lib/content-fields/text.js +35 -0
  22. package/lib/content-fields/text.js.map +1 -0
  23. package/lib/functions/conditional-provider.d.ts +4 -0
  24. package/lib/functions/conditional-provider.js +11 -0
  25. package/lib/functions/conditional-provider.js.map +1 -0
  26. package/lib/functions/conditional-queue-status.d.ts +3 -0
  27. package/lib/functions/conditional-queue-status.js +25 -0
  28. package/lib/functions/conditional-queue-status.js.map +1 -0
  29. package/lib/functions/index.d.ts +6 -0
  30. package/lib/functions/index.js +10 -0
  31. package/lib/functions/index.js.map +1 -0
  32. package/lib/handoff.d.ts +2 -0
  33. package/lib/handoff.js +39 -0
  34. package/lib/handoff.js.map +1 -0
  35. package/lib/hubtype-models.d.ts +152 -0
  36. package/lib/hubtype-models.js +50 -0
  37. package/lib/hubtype-models.js.map +1 -0
  38. package/lib/index.d.ts +30 -0
  39. package/lib/index.js +171 -0
  40. package/lib/index.js.map +1 -0
  41. package/lib/utils.d.ts +2 -0
  42. package/lib/utils.js +10 -0
  43. package/lib/utils.js.map +1 -0
  44. package/package.json +53 -0
  45. package/src/action.tsx +62 -0
  46. package/src/content-fields/button.tsx +34 -0
  47. package/src/content-fields/carousel.tsx +42 -0
  48. package/src/content-fields/content-base.ts +15 -0
  49. package/src/content-fields/element.tsx +24 -0
  50. package/src/content-fields/image.tsx +22 -0
  51. package/src/content-fields/text.tsx +35 -0
  52. package/src/functions/conditional-provider.ts +5 -0
  53. package/src/functions/conditional-queue-status.ts +9 -0
  54. package/src/functions/index.ts +7 -0
  55. package/src/handoff.ts +27 -0
  56. package/src/hubtype-models.ts +190 -0
  57. package/src/index.ts +210 -0
  58. package/src/utils.ts +6 -0
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeContentType = exports.InputType = exports.InputContentType = exports.StartFieldsType = exports.MediaContentType = exports.SubContentType = exports.NonMessageContentType = exports.MessageContentType = exports.ButtonStyle = void 0;
4
+ var ButtonStyle;
5
+ (function (ButtonStyle) {
6
+ ButtonStyle["BUTTON"] = "button";
7
+ ButtonStyle["QUICK_REPLY"] = "quick-reply";
8
+ })(ButtonStyle = exports.ButtonStyle || (exports.ButtonStyle = {}));
9
+ var MessageContentType;
10
+ (function (MessageContentType) {
11
+ MessageContentType["CAROUSEL"] = "carousel";
12
+ MessageContentType["IMAGE"] = "image";
13
+ MessageContentType["TEXT"] = "text";
14
+ MessageContentType["KEYWORD"] = "keyword";
15
+ MessageContentType["HANDOFF"] = "handoff";
16
+ MessageContentType["FUNCTION"] = "function";
17
+ MessageContentType["INTENT"] = "intent";
18
+ })(MessageContentType = exports.MessageContentType || (exports.MessageContentType = {}));
19
+ // TODO: refactor types correctly
20
+ var NonMessageContentType;
21
+ (function (NonMessageContentType) {
22
+ NonMessageContentType["INTENT"] = "intent";
23
+ NonMessageContentType["PAYLOAD"] = "payload";
24
+ NonMessageContentType["QUEUE"] = "queue";
25
+ NonMessageContentType["URL"] = "url";
26
+ })(NonMessageContentType = exports.NonMessageContentType || (exports.NonMessageContentType = {}));
27
+ var SubContentType;
28
+ (function (SubContentType) {
29
+ SubContentType["BUTTON"] = "button";
30
+ SubContentType["ELEMENT"] = "element";
31
+ })(SubContentType = exports.SubContentType || (exports.SubContentType = {}));
32
+ var MediaContentType;
33
+ (function (MediaContentType) {
34
+ MediaContentType["ASSET"] = "asset";
35
+ })(MediaContentType = exports.MediaContentType || (exports.MediaContentType = {}));
36
+ var StartFieldsType;
37
+ (function (StartFieldsType) {
38
+ StartFieldsType["STARTUP"] = "startUp";
39
+ })(StartFieldsType = exports.StartFieldsType || (exports.StartFieldsType = {}));
40
+ var InputContentType;
41
+ (function (InputContentType) {
42
+ InputContentType["INPUT"] = "user-input";
43
+ })(InputContentType = exports.InputContentType || (exports.InputContentType = {}));
44
+ var InputType;
45
+ (function (InputType) {
46
+ InputType["INTENTS"] = "intents";
47
+ InputType["KEYWORDS"] = "keywords";
48
+ })(InputType = exports.InputType || (exports.InputType = {}));
49
+ exports.NodeContentType = Object.assign(Object.assign({}, MessageContentType), InputContentType);
50
+ //# sourceMappingURL=hubtype-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hubtype-models.js","sourceRoot":"","sources":["../src/hubtype-models.ts"],"names":[],"mappings":";;;AAAA,IAAY,WAGX;AAHD,WAAY,WAAW;IACrB,gCAAiB,CAAA;IACjB,0CAA2B,CAAA;AAC7B,CAAC,EAHW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAGtB;AAED,IAAY,kBAQX;AARD,WAAY,kBAAkB;IAC5B,2CAAqB,CAAA;IACrB,qCAAe,CAAA;IACf,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,yCAAmB,CAAA;IACnB,2CAAqB,CAAA;IACrB,uCAAiB,CAAA;AACnB,CAAC,EARW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAQ7B;AAED,iCAAiC;AACjC,IAAY,qBAKX;AALD,WAAY,qBAAqB;IAC/B,0CAAiB,CAAA;IACjB,4CAAmB,CAAA;IACnB,wCAAe,CAAA;IACf,oCAAW,CAAA;AACb,CAAC,EALW,qBAAqB,GAArB,6BAAqB,KAArB,6BAAqB,QAKhC;AAED,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,mCAAiB,CAAA;IACjB,qCAAmB,CAAA;AACrB,CAAC,EAHW,cAAc,GAAd,sBAAc,KAAd,sBAAc,QAGzB;AAED,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,mCAAe,CAAA;AACjB,CAAC,EAFW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAE3B;AAED,IAAY,eAEX;AAFD,WAAY,eAAe;IACzB,sCAAmB,CAAA;AACrB,CAAC,EAFW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAE1B;AAED,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,wCAAoB,CAAA;AACtB,CAAC,EAFW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAE3B;AAED,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,gCAAmB,CAAA;IACnB,kCAAqB,CAAA;AACvB,CAAC,EAHW,SAAS,GAAT,iBAAS,KAAT,iBAAS,QAGpB;AAEY,QAAA,eAAe,mCACvB,kBAAkB,GAClB,gBAAgB,EACpB"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { Input, Plugin, PluginPostRequest, PluginPreRequest } from '@botonic/core';
2
+ import { FlowContent } from './content-fields/content-base';
3
+ import { HtFlowBuilderData, HtFunctionNode, HtHandoffNode, HtIntentNode, HtKeywordNode, HtNodeComponent } from './hubtype-models';
4
+ declare type BotonicPluginFlowBuilderOptions = {
5
+ flowUrl: string;
6
+ flow: any;
7
+ customFunctions: Record<any, any>;
8
+ };
9
+ export default class BotonicPluginFlowBuilder implements Plugin {
10
+ readonly options: BotonicPluginFlowBuilderOptions;
11
+ private flowUrl;
12
+ private flow;
13
+ private functions;
14
+ private currentRequest;
15
+ constructor(options: BotonicPluginFlowBuilderOptions);
16
+ readFlowContent(): Promise<HtFlowBuilderData>;
17
+ pre(request: PluginPreRequest): Promise<void>;
18
+ post(_request: PluginPostRequest): Promise<void>;
19
+ getContent(id: string): Promise<HtNodeComponent>;
20
+ getHandoffContent(): Promise<HtHandoffNode>;
21
+ getFlowContent(hubtypeContent: HtNodeComponent, locale: string): FlowContent | undefined;
22
+ getContents(id: string, locale: string, prevContents?: FlowContent[]): Promise<FlowContent[]>;
23
+ getPayloadByInput(input: Input, locale: string): Promise<string | undefined>;
24
+ hasIntent(node: HtIntentNode, intent: string, locale: string): boolean;
25
+ getPayloadByKeyword(input: Input, locale: string): Promise<string | undefined>;
26
+ matchKeywords(node: HtKeywordNode, input: string, locale: string): boolean;
27
+ containsAnyKeywords(input: string, keywords: string[]): boolean;
28
+ callFunction(functionNode: HtFunctionNode, locale: string): Promise<string>;
29
+ }
30
+ export {};
package/lib/index.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const axios_1 = __importDefault(require("axios"));
16
+ const carousel_1 = require("./content-fields/carousel");
17
+ const image_1 = require("./content-fields/image");
18
+ const text_1 = require("./content-fields/text");
19
+ const functions_1 = require("./functions");
20
+ const hubtype_models_1 = require("./hubtype-models");
21
+ class BotonicPluginFlowBuilder {
22
+ constructor(options) {
23
+ this.options = options;
24
+ this.flowUrl = options.flowUrl;
25
+ this.flow = options.flow || this.readFlowContent();
26
+ const customFunctions = options.customFunctions || {};
27
+ this.functions = Object.assign(Object.assign({}, functions_1.DEFAULT_FUNCTIONS), customFunctions);
28
+ }
29
+ readFlowContent() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ const response = yield axios_1.default.get(this.flowUrl);
32
+ const data = yield response.data;
33
+ //@ts-ignore
34
+ return Promise.resolve(data);
35
+ });
36
+ }
37
+ pre(request) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ this.currentRequest = request;
40
+ });
41
+ }
42
+ post(_request) {
43
+ return __awaiter(this, void 0, void 0, function* () { });
44
+ }
45
+ getContent(id) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const flow = yield this.flow;
48
+ const content = flow.nodes.find((c) => c.id === id);
49
+ if (!content)
50
+ throw Error(`text with id: '${id}' not found`);
51
+ return content;
52
+ });
53
+ }
54
+ getHandoffContent() {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ const flow = yield this.flow;
57
+ const content = flow.nodes.find((c) => c.type === 'handoff');
58
+ if (!content)
59
+ throw Error(`Handoff node not found`);
60
+ return content;
61
+ });
62
+ }
63
+ getFlowContent(hubtypeContent, locale) {
64
+ switch (hubtypeContent.type) {
65
+ case hubtype_models_1.MessageContentType.TEXT:
66
+ return text_1.FlowText.fromHubtypeCMS(hubtypeContent, locale);
67
+ case hubtype_models_1.MessageContentType.IMAGE:
68
+ return image_1.FlowImage.fromHubtypeCMS(hubtypeContent, locale);
69
+ case hubtype_models_1.MessageContentType.CAROUSEL:
70
+ return carousel_1.FlowCarousel.fromHubtypeCMS(hubtypeContent, locale);
71
+ default:
72
+ return undefined;
73
+ }
74
+ }
75
+ getContents(id, locale, prevContents) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ const contents = prevContents || [];
78
+ const hubtypeContent = yield this.getContent(id);
79
+ const content = yield this.getFlowContent(hubtypeContent, locale);
80
+ if (hubtypeContent.type === hubtype_models_1.MessageContentType.FUNCTION) {
81
+ const targetId = yield this.callFunction(hubtypeContent, locale);
82
+ return this.getContents(targetId, locale, contents);
83
+ }
84
+ else {
85
+ if (content)
86
+ contents.push(content);
87
+ // TODO: prevent infinite recursive calls
88
+ if (hubtypeContent.follow_up)
89
+ return this.getContents(hubtypeContent.follow_up.id, locale, contents);
90
+ }
91
+ // execute function
92
+ // return this.getContents(function result_mapping target, locale, contents)
93
+ return contents;
94
+ });
95
+ }
96
+ getPayloadByInput(input, locale) {
97
+ var _a;
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ try {
100
+ const flow = yield this.flow;
101
+ const intents = flow.nodes.filter(node => node.type == hubtype_models_1.MessageContentType.INTENT);
102
+ if (input.intent) {
103
+ const matchedIntents = intents.filter(node =>
104
+ //@ts-ignore
105
+ this.hasIntent(node, input.intent, locale));
106
+ if (matchedIntents.length > 0) {
107
+ return (_a = matchedIntents[0].target) === null || _a === void 0 ? void 0 : _a.id;
108
+ }
109
+ }
110
+ }
111
+ catch (error) {
112
+ console.error('Error getting payload by input: ', error);
113
+ }
114
+ return undefined;
115
+ });
116
+ }
117
+ hasIntent(node, intent, locale) {
118
+ const result = node.content.intents.find(i => i.locale === locale && i.values.includes(intent));
119
+ return Boolean(result);
120
+ }
121
+ getPayloadByKeyword(input, locale) {
122
+ var _a;
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ try {
125
+ const flow = yield this.flow;
126
+ const keywordNodes = flow.nodes.filter(node => node.type == hubtype_models_1.MessageContentType.KEYWORD);
127
+ const matchedKeywordNodes = keywordNodes.filter(node =>
128
+ //@ts-ignore
129
+ this.matchKeywords(node, input.data, locale));
130
+ if (matchedKeywordNodes.length > 0) {
131
+ return (_a = matchedKeywordNodes[0].target) === null || _a === void 0 ? void 0 : _a.id;
132
+ }
133
+ }
134
+ catch (error) {
135
+ console.error('Error getting payload by input: ', error);
136
+ }
137
+ return undefined;
138
+ });
139
+ }
140
+ matchKeywords(node, input, locale) {
141
+ const result = node.content.keywords.find(i => i.locale === locale && this.containsAnyKeywords(input, i.values));
142
+ return Boolean(result);
143
+ }
144
+ containsAnyKeywords(input, keywords) {
145
+ for (let i = 0; i < keywords.length; i++) {
146
+ if (input.includes(keywords[i])) {
147
+ return true;
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+ callFunction(functionNode, locale) {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ // Check if target is missing or missing arguments
155
+ // TODO: get arguments by locale
156
+ const nameValues = functionNode.content.arguments
157
+ .find(arg => arg.locale === locale)
158
+ .values.map(value => ({ [value.name]: value.value }));
159
+ const args = Object.assign({
160
+ session: this.currentRequest.session,
161
+ results: [functionNode.content.result_mapping.map(r => r.result)],
162
+ }, ...nameValues);
163
+ const functionResult = yield this.functions[functionNode.content.subtype](args);
164
+ // TODO define result_mapping per locale??
165
+ const result = functionNode.content.result_mapping.find(r => r.result === functionResult);
166
+ return result.target.id;
167
+ });
168
+ }
169
+ }
170
+ exports.default = BotonicPluginFlowBuilder;
171
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/utils.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { HtMediaFileLocale } from './hubtype-models';
2
+ export declare function getImageByLocale(locale: string, image: HtMediaFileLocale[]): string;
package/lib/utils.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getImageByLocale = void 0;
4
+ function getImageByLocale(locale, image) {
5
+ var _a;
6
+ const result = image.find(t => t.locale == locale);
7
+ return (_a = result === null || result === void 0 ? void 0 : result.file) !== null && _a !== void 0 ? _a : '';
8
+ }
9
+ exports.getImageByLocale = getImageByLocale;
10
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAEA,SAAgB,gBAAgB,CAAC,MAAc,EAAE,KAA0B;;IACzE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAA;IAClD,OAAO,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,mCAAI,EAAE,CAAA;AAC3B,CAAC;AAHD,4CAGC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@botonic/plugin-flow-builder",
3
+ "version": "0.21.0-alpha.0",
4
+ "main": "lib/index.js",
5
+ "description": "Use Flow Builder to show your contents",
6
+ "scripts": {
7
+ "build": "rm -rf lib && ../../node_modules/.bin/tsc",
8
+ "build:watch": "../../node_modules/.bin/tsc --watch",
9
+ "test": "echo \"Error: no test specified\" && exit 1",
10
+ "cloc": "../../scripts/qa/cloc-package.sh .",
11
+ "prepare": "node ../../preinstall.js",
12
+ "prepublishOnly": "rm -rf lib && npm run build",
13
+ "lint": "npm run lint_core -- --fix",
14
+ "lint_ci": "npm run lint_core -- -c ../.eslintrc_slow.js",
15
+ "lint_core": "../../node_modules/.bin/eslint_d --cache --quiet 'src/**/*.ts*'"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/hubtype/botonic.git"
20
+ },
21
+ "author": "",
22
+ "bugs": {
23
+ "url": "https://github.com/hubtype/botonic/issues"
24
+ },
25
+ "files": [
26
+ "lib/**",
27
+ "src/**",
28
+ "README.md"
29
+ ],
30
+ "devDependencies": {
31
+ "@types/node": "^18.13.0",
32
+ "@types/react": "^18.0.28",
33
+ "typescript": "^4.9.5"
34
+ },
35
+ "keywords": [
36
+ "bot-framework",
37
+ "chatbot",
38
+ "flow-builder",
39
+ "conversational-app",
40
+ "conversational-ui",
41
+ "javascript"
42
+ ],
43
+ "eslintConfig": {
44
+ "extends": "../.eslintrc.js",
45
+ "root": true
46
+ },
47
+ "dependencies": {
48
+ "@babel/runtime": "^7.20.13",
49
+ "@botonic/core": "^0.21.0",
50
+ "@botonic/react": "^0.21.0",
51
+ "axios": "^1.3.3"
52
+ }
53
+ }
package/src/action.tsx ADDED
@@ -0,0 +1,62 @@
1
+ import { ActionRequest, RequestContext, Text } from '@botonic/react'
2
+ import React from 'react'
3
+
4
+ import { FlowContent } from './content-fields/content-base'
5
+ import { doHandoff } from './handoff'
6
+ import { HtHandoffNode } from './hubtype-models'
7
+
8
+ // TODO: remove this from here and use the new "start" attribute in the flow
9
+ export const START = '08c7df06-0c7c-4f06-b8c1-4157582efeb2'
10
+
11
+ type FlowBuilderActionProps = {
12
+ content?: FlowContent[]
13
+ handoffMsg?: HtHandoffNode
14
+ }
15
+
16
+ export default class FlowBuilderAction extends React.Component<FlowBuilderActionProps> {
17
+ static contextType = RequestContext
18
+
19
+ static async botonicInit(request: ActionRequest): Promise<any> {
20
+ const flowBuilderPlugin = request.plugins.hubtypeFlowBuilder as any
21
+
22
+ let payload = request.input.payload ? request.input.payload : START
23
+ if (!request.input.payload) {
24
+ const intentPayload = await flowBuilderPlugin.getPayloadByInput(
25
+ request.input,
26
+ 'es-ES'
27
+ )
28
+ if (intentPayload) {
29
+ payload = intentPayload
30
+ }
31
+ const keywordPayload = await flowBuilderPlugin.getPayloadByKeyword(
32
+ request.input,
33
+ 'es-ES'
34
+ )
35
+ if (keywordPayload) {
36
+ payload = keywordPayload
37
+ }
38
+ }
39
+ // We use only Spanish because they are the backend examples
40
+ const content = await flowBuilderPlugin.getContents(payload, 'es-ES')
41
+
42
+ if (content.length == 0) {
43
+ const handoffMsg = await doHandoff(request)
44
+ return { handoffMsg }
45
+ }
46
+ return { content }
47
+ }
48
+
49
+ render() {
50
+ // @ts-ignore
51
+ const { content: contents, handoffMsg } = this.props
52
+ if (handoffMsg) {
53
+ // @ts-ignore
54
+ return <Text>{handoffMsg}</Text>
55
+ } else {
56
+ return contents!.map((content, index) => {
57
+ console.log({ content, index })
58
+ return content.toBotonic(index)
59
+ })
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,34 @@
1
+ import { Button, Reply } from '@botonic/react'
2
+ import React from 'react'
3
+
4
+ import { ButtonStyle, HtButton } from '../hubtype-models'
5
+ import { ContentFieldsBase } from './content-base'
6
+
7
+ export class FlowButton extends ContentFieldsBase {
8
+ public text = ''
9
+ public url?: string
10
+ public payload?: string
11
+
12
+ static fromHubtypeCMS(component: HtButton, locale: string): FlowButton {
13
+ const newButton = new FlowButton(component.id)
14
+ newButton.text = FlowButton.getTextByLocale(locale, component.text)
15
+ newButton.payload = component.target?.id
16
+ return newButton
17
+ }
18
+
19
+ renderButton(index: number, buttonStyle: ButtonStyle) {
20
+ if (buttonStyle == ButtonStyle.QUICK_REPLY) {
21
+ return (
22
+ <Reply payload={this.payload} key={index}>
23
+ {this.text}
24
+ </Reply>
25
+ )
26
+ }
27
+ return (
28
+ // @ts-ignore
29
+ <Button payload={this.payload} key={index}>
30
+ {this.text}
31
+ </Button>
32
+ )
33
+ }
34
+ }
@@ -0,0 +1,42 @@
1
+ import { Button, Carousel, Element, Pic, Subtitle, Title } from '@botonic/react'
2
+ import React from 'react'
3
+
4
+ import { HtCarouselNode } from '../hubtype-models'
5
+ import { ContentFieldsBase } from './content-base'
6
+ import { FlowElement } from './element'
7
+
8
+ export class FlowCarousel extends ContentFieldsBase {
9
+ public code = ''
10
+ public elements: FlowElement[] = []
11
+
12
+ static fromHubtypeCMS(
13
+ component: HtCarouselNode,
14
+ locale: string
15
+ ): FlowCarousel {
16
+ const newCarousel = new FlowCarousel(component.id)
17
+ newCarousel.code = component.code
18
+ newCarousel.elements = component.content.elements.map(ele =>
19
+ FlowElement.fromHubtypeCMS(ele, locale)
20
+ )
21
+ return newCarousel
22
+ }
23
+
24
+ toBotonic(index: number) {
25
+ return (
26
+ <Carousel key={index}>
27
+ {this.elements.map((e, eIndex) => {
28
+ console.log('TO BOTONIC BOTONIC PACKAGES', { e })
29
+ return (
30
+ <Element key={eIndex}>
31
+ <Pic src={e.image} />
32
+ <Title style=''>{e.title}</Title>
33
+ <Subtitle style=''>{e.subtitle}</Subtitle>
34
+ {/* @ts-ignore */}
35
+ <Button payload={e.buttons?.payload}>{e.buttons?.text}</Button>,
36
+ </Element>
37
+ )
38
+ })}
39
+ </Carousel>
40
+ )
41
+ }
42
+ }
@@ -0,0 +1,15 @@
1
+ import { HtTextLocale } from '../hubtype-models'
2
+ import { FlowCarousel } from './carousel'
3
+ import { FlowImage } from './image'
4
+ import { FlowText } from './text'
5
+
6
+ export abstract class ContentFieldsBase {
7
+ constructor(private readonly id: string) {}
8
+
9
+ static getTextByLocale(locale: string, text: HtTextLocale[]) {
10
+ const result = text.find(t => t.locale == locale)
11
+ return result?.message ?? ''
12
+ }
13
+ }
14
+
15
+ export type FlowContent = FlowText | FlowImage | FlowCarousel
@@ -0,0 +1,24 @@
1
+ import { HtElement } from '../hubtype-models'
2
+ import { getImageByLocale } from '../utils'
3
+ import { FlowButton } from './button'
4
+ import { ContentFieldsBase } from './content-base'
5
+
6
+ export class FlowElement extends ContentFieldsBase {
7
+ public title = ''
8
+ public subtitle = ''
9
+ public buttons: FlowButton | undefined
10
+ public image = ''
11
+ public hidden = false
12
+
13
+ static fromHubtypeCMS(component: HtElement, locale: string): FlowElement {
14
+ const newElement = new FlowElement(component.id)
15
+ newElement.title = FlowElement.getTextByLocale(locale, component.title)
16
+ newElement.subtitle = FlowElement.getTextByLocale(
17
+ locale,
18
+ component.subtitle
19
+ )
20
+ newElement.image = getImageByLocale(locale, component.image)
21
+ newElement.buttons = FlowButton.fromHubtypeCMS(component.button, locale)
22
+ return newElement
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ import { Image } from '@botonic/react'
2
+ import React from 'react'
3
+
4
+ import { HtImageNode } from '../hubtype-models'
5
+ import { getImageByLocale } from '../utils'
6
+ import { ContentFieldsBase } from './content-base'
7
+
8
+ export class FlowImage extends ContentFieldsBase {
9
+ public src = ''
10
+ public code = ''
11
+
12
+ static fromHubtypeCMS(component: HtImageNode, locale: string): FlowImage {
13
+ const newImage = new FlowImage(component.id)
14
+ newImage.code = component.code
15
+ newImage.src = getImageByLocale(locale, component.content.image)
16
+ return newImage
17
+ }
18
+
19
+ toBotonic(index: number) {
20
+ return <Image src={this.src} key={index} />
21
+ }
22
+ }
@@ -0,0 +1,35 @@
1
+ import { Text } from '@botonic/react'
2
+ import React from 'react'
3
+
4
+ import { ButtonStyle, HtTextNode } from '../hubtype-models'
5
+ import { FlowButton } from './button'
6
+ import { ContentFieldsBase } from './content-base'
7
+
8
+ export class FlowText extends ContentFieldsBase {
9
+ public text = ''
10
+ public code = ''
11
+ public buttons: FlowButton[] = []
12
+ public buttonStyle = ButtonStyle.BUTTON
13
+
14
+ static fromHubtypeCMS(component: HtTextNode, locale: string): FlowText {
15
+ const newText = new FlowText(component.id)
16
+ newText.code = component.code
17
+ newText.buttonStyle = component.content.buttons_style || ButtonStyle.BUTTON
18
+ newText.text = FlowText.getTextByLocale(locale, component.content.text)
19
+ newText.buttons = component.content.buttons.map(button =>
20
+ FlowButton.fromHubtypeCMS(button, locale)
21
+ )
22
+ return newText
23
+ }
24
+
25
+ toBotonic(index: number) {
26
+ return (
27
+ <Text key={index}>
28
+ {this.text}
29
+ {this.buttons.map((button, index) =>
30
+ button.renderButton(index, this.buttonStyle)
31
+ )}
32
+ </Text>
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,5 @@
1
+ export function conditionalProvider({ session, results }): string {
2
+ const provider = session.user.provider
3
+ if (results.includes(provider)) return provider
4
+ return 'default'
5
+ }
@@ -0,0 +1,9 @@
1
+ import axios from 'axios'
2
+
3
+ export async function conditionalQueueStatus({ queueId }): Promise<string> {
4
+ const response = await axios.get(
5
+ `https://api.hubtype.com/v1/queues/${queueId}/availability/`
6
+ )
7
+ const isAvailable = response.data.available
8
+ return isAvailable ? 'open' : 'closed'
9
+ }
@@ -0,0 +1,7 @@
1
+ import { conditionalProvider } from './conditional-provider'
2
+ import { conditionalQueueStatus } from './conditional-queue-status'
3
+
4
+ export const DEFAULT_FUNCTIONS = {
5
+ 'conditional-queue-status': conditionalQueueStatus,
6
+ 'conditional-provider': conditionalProvider,
7
+ }
package/src/handoff.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { getOpenQueues, HandOffBuilder } from '@botonic/core'
2
+ import { ActionRequest } from '@botonic/react'
3
+
4
+ // TODO: Remove all "getOpenQueues" logic and open/closed messages.
5
+ // This function should just do a basic handoff
6
+ // TODO: add missing options: withNote, withAgent
7
+
8
+ export async function doHandoff(request: ActionRequest) {
9
+ // @ts-ignore
10
+ const flowBuilderPlugin = request.plugins.hubtypeFlowBuilder as any
11
+ const handoffContent = await flowBuilderPlugin.getHandoffContent()
12
+ console.log(handoffContent)
13
+ // @ts-ignore
14
+ let openQueues = await getOpenQueues(request.session)
15
+ const queueName = 'Test'
16
+ if (openQueues.queues.indexOf(queueName) !== -1) {
17
+ // @ts-ignore
18
+ const handOffBuilder = new HandOffBuilder(request.session)
19
+ handOffBuilder.withQueue('Test')
20
+ handOffBuilder.withOnFinishPayload(handoffContent.target?.id!)
21
+ await handOffBuilder.handOff()
22
+
23
+ return handoffContent.content.message[0].message
24
+ }
25
+
26
+ return handoffContent.content.failMessage[0].message
27
+ }