@botonic/plugin-flow-builder 0.21.0-alpha.9 → 0.21.1
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.
- package/lib/cjs/action.js +21 -32
- package/lib/cjs/action.js.map +1 -1
- package/lib/cjs/content-fields/content-fields-base.d.ts +2 -1
- package/lib/cjs/content-fields/content-fields-base.js +6 -1
- package/lib/cjs/content-fields/content-fields-base.js.map +1 -1
- package/lib/cjs/content-fields/flow-button.js +4 -2
- package/lib/cjs/content-fields/flow-button.js.map +1 -1
- package/lib/cjs/content-fields/flow-video.d.ts +8 -0
- package/lib/cjs/content-fields/flow-video.js +26 -0
- package/lib/cjs/content-fields/flow-video.js.map +1 -0
- package/lib/cjs/content-fields/index.d.ts +1 -0
- package/lib/cjs/content-fields/index.js +3 -1
- package/lib/cjs/content-fields/index.js.map +1 -1
- package/lib/cjs/content-fields/types.d.ts +2 -1
- package/lib/cjs/flow-builder-models.d.ts +26 -4
- package/lib/cjs/flow-builder-models.js +2 -0
- package/lib/cjs/flow-builder-models.js.map +1 -1
- package/lib/cjs/handoff.d.ts +2 -1
- package/lib/cjs/handoff.js +21 -14
- package/lib/cjs/handoff.js.map +1 -1
- package/lib/cjs/helpers.d.ts +5 -0
- package/lib/cjs/helpers.js +14 -0
- package/lib/cjs/helpers.js.map +1 -0
- package/lib/cjs/index.d.ts +8 -3
- package/lib/cjs/index.js +35 -17
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/action.js +22 -33
- package/lib/esm/action.js.map +1 -1
- package/lib/esm/content-fields/content-fields-base.d.ts +2 -1
- package/lib/esm/content-fields/content-fields-base.js +6 -1
- package/lib/esm/content-fields/content-fields-base.js.map +1 -1
- package/lib/esm/content-fields/flow-button.js +4 -2
- package/lib/esm/content-fields/flow-button.js.map +1 -1
- package/lib/esm/content-fields/flow-video.d.ts +8 -0
- package/lib/esm/content-fields/flow-video.js +21 -0
- package/lib/esm/content-fields/flow-video.js.map +1 -0
- package/lib/esm/content-fields/index.d.ts +1 -0
- package/lib/esm/content-fields/index.js +1 -0
- package/lib/esm/content-fields/index.js.map +1 -1
- package/lib/esm/content-fields/types.d.ts +2 -1
- package/lib/esm/flow-builder-models.d.ts +26 -4
- package/lib/esm/flow-builder-models.js +2 -0
- package/lib/esm/flow-builder-models.js.map +1 -1
- package/lib/esm/handoff.d.ts +2 -1
- package/lib/esm/handoff.js +21 -14
- package/lib/esm/handoff.js.map +1 -1
- package/lib/esm/helpers.d.ts +5 -0
- package/lib/esm/helpers.js +10 -0
- package/lib/esm/helpers.js.map +1 -0
- package/lib/esm/index.d.ts +8 -3
- package/lib/esm/index.js +36 -18
- package/lib/esm/index.js.map +1 -1
- package/package.json +3 -4
- package/src/action.tsx +25 -35
- package/src/content-fields/content-fields-base.ts +11 -2
- package/src/content-fields/flow-button.tsx +3 -1
- package/src/content-fields/flow-video.tsx +22 -0
- package/src/content-fields/index.ts +1 -0
- package/src/content-fields/types.ts +2 -1
- package/src/flow-builder-models.ts +30 -2
- package/src/handoff.ts +25 -16
- package/src/helpers.ts +18 -0
- package/src/index.ts +52 -21
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Video } from '@botonic/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { VideoNode } from '../flow-builder-models'
|
|
5
|
+
import { ContentFieldsBase } from './content-fields-base'
|
|
6
|
+
|
|
7
|
+
export class FlowVideo extends ContentFieldsBase {
|
|
8
|
+
public src = ''
|
|
9
|
+
public code = ''
|
|
10
|
+
|
|
11
|
+
static fromHubtypeCMS(component: VideoNode, locale: string): FlowVideo {
|
|
12
|
+
const newVideo = new FlowVideo(component.id)
|
|
13
|
+
newVideo.code = component.code
|
|
14
|
+
newVideo.src = this.getVideoByLocale(locale, component.content.video)
|
|
15
|
+
return newVideo
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
toBotonic(index: number): JSX.Element {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
return <Video key={index} src={this.src} />
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlowCarousel } from './flow-carousel'
|
|
2
2
|
import { FlowImage } from './flow-image'
|
|
3
3
|
import { FlowText } from './flow-text'
|
|
4
|
+
import { FlowVideo } from './flow-video'
|
|
4
5
|
|
|
5
|
-
export type FlowContent = FlowText | FlowImage | FlowCarousel
|
|
6
|
+
export type FlowContent = FlowText | FlowImage | FlowCarousel | FlowVideo
|
|
@@ -18,6 +18,8 @@ export enum NodeType {
|
|
|
18
18
|
URL = 'url',
|
|
19
19
|
PAYLOAD = 'payload',
|
|
20
20
|
FUNCTION = 'function',
|
|
21
|
+
FALLBACK = 'fallback',
|
|
22
|
+
VIDEO = 'video',
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export interface BaseNode {
|
|
@@ -67,8 +69,8 @@ export interface Button {
|
|
|
67
69
|
text: TextLocale[]
|
|
68
70
|
target?: NodeLink
|
|
69
71
|
hidden: string
|
|
70
|
-
url?: UrlLocale
|
|
71
|
-
payload?: PayloadLocale
|
|
72
|
+
url?: UrlLocale[]
|
|
73
|
+
payload?: PayloadLocale[]
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export interface TextNodeContent {
|
|
@@ -207,6 +209,30 @@ export interface FunctionNode extends Node {
|
|
|
207
209
|
content: FunctionNodeContent
|
|
208
210
|
}
|
|
209
211
|
|
|
212
|
+
export interface FallbackNodeContent {
|
|
213
|
+
first_message: NodeLink
|
|
214
|
+
second_message: NodeLink
|
|
215
|
+
}
|
|
216
|
+
export interface FallbackNode extends Node {
|
|
217
|
+
type: NodeType.FALLBACK
|
|
218
|
+
content: FallbackNodeContent
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface VideoLocale {
|
|
222
|
+
url: string
|
|
223
|
+
is_embedded?: boolean
|
|
224
|
+
locale: string
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface VideoNodeContent {
|
|
228
|
+
video: VideoLocale[]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface VideoNode extends Node {
|
|
232
|
+
type: NodeType.VIDEO
|
|
233
|
+
content: VideoNodeContent
|
|
234
|
+
}
|
|
235
|
+
|
|
210
236
|
export type NodeComponent =
|
|
211
237
|
| TextNode
|
|
212
238
|
| ImageNode
|
|
@@ -218,3 +244,5 @@ export type NodeComponent =
|
|
|
218
244
|
| StartNode
|
|
219
245
|
| PayloadNode
|
|
220
246
|
| FunctionNode
|
|
247
|
+
| FallbackNode
|
|
248
|
+
| VideoNode
|
package/src/handoff.ts
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
import { HandOffBuilder } from '@botonic/core'
|
|
2
2
|
import { ActionRequest } from '@botonic/react'
|
|
3
3
|
|
|
4
|
+
import { HandoffNode } from './flow-builder-models'
|
|
5
|
+
import { getFlowBuilderPlugin } from './helpers'
|
|
6
|
+
|
|
4
7
|
export async function doHandoff(
|
|
5
8
|
request: ActionRequest,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
agentEmail?: string
|
|
9
|
+
locale: string,
|
|
10
|
+
handoffNode: HandoffNode
|
|
9
11
|
): Promise<void> {
|
|
12
|
+
const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
|
|
13
|
+
const handoffTargetNode = await flowBuilderPlugin.getHandoffContent(
|
|
14
|
+
handoffNode.target?.id
|
|
15
|
+
)
|
|
10
16
|
// @ts-ignore
|
|
11
|
-
const
|
|
12
|
-
const
|
|
17
|
+
const handOffBuilder = new HandOffBuilder(request.session) // handOffBuilder.withQueue(handoffNode.content.queue)
|
|
18
|
+
const handoffQueues = handoffNode.content.queue
|
|
19
|
+
const queueFound = handoffQueues.find(q => q.locale === locale)
|
|
20
|
+
if (queueFound) handOffBuilder.withQueue(queueFound.id)
|
|
21
|
+
// TODO: Retrieve params from FlowBuilder
|
|
22
|
+
// const handoffParams = {
|
|
23
|
+
// agentEmail: 'test@gmail.com',
|
|
24
|
+
// note: 'This is a note that will be attached to the case as a reminder',
|
|
25
|
+
// }
|
|
13
26
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
// if (handoffParams.note) {
|
|
28
|
+
// handOffBuilder.withNote(handoffParams.note)
|
|
29
|
+
// }
|
|
17
30
|
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
}
|
|
31
|
+
// if (handoffParams.agentEmail) {
|
|
32
|
+
// handOffBuilder.withAgentEmail(handoffParams.agentEmail)
|
|
33
|
+
// }
|
|
21
34
|
|
|
22
|
-
|
|
23
|
-
handOffBuilder.withAgentEmail(agentEmail)
|
|
24
|
-
}
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
26
|
-
handOffBuilder.withOnFinishPayload(handoffContent.target?.id!)
|
|
35
|
+
handOffBuilder.withOnFinishPayload(handoffTargetNode.id)
|
|
27
36
|
await handOffBuilder.handOff()
|
|
28
37
|
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Plugin } from '@botonic/core'
|
|
2
|
+
import BotonicPluginFlowBuilder from './index'
|
|
3
|
+
|
|
4
|
+
const FLOW_BUILDER_PLUGIN_NAME = 'BotonicPluginFlowBuilder'
|
|
5
|
+
|
|
6
|
+
export function getFlowBuilderPlugin(plugins: {
|
|
7
|
+
[id: string]: Plugin
|
|
8
|
+
}): BotonicPluginFlowBuilder {
|
|
9
|
+
const flowBuilderPlugin = Object.values(plugins).find(
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
plugin => plugin.name === FLOW_BUILDER_PLUGIN_NAME
|
|
12
|
+
) as BotonicPluginFlowBuilder
|
|
13
|
+
if (!flowBuilderPlugin)
|
|
14
|
+
throw new Error(
|
|
15
|
+
`You must include '@botonic/plugin-flow-builder' in your plugins file.`
|
|
16
|
+
)
|
|
17
|
+
return flowBuilderPlugin
|
|
18
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,14 +12,17 @@ import {
|
|
|
12
12
|
FlowContent,
|
|
13
13
|
FlowImage,
|
|
14
14
|
FlowText,
|
|
15
|
+
FlowVideo,
|
|
15
16
|
} from './content-fields'
|
|
16
17
|
import {
|
|
18
|
+
FallbackNode,
|
|
17
19
|
FlowBuilderData,
|
|
18
20
|
FunctionNode,
|
|
19
21
|
HandoffNode,
|
|
20
22
|
IntentNode,
|
|
21
23
|
KeywordNode,
|
|
22
24
|
NodeComponent,
|
|
25
|
+
NodeLink,
|
|
23
26
|
NodeType,
|
|
24
27
|
StartNode,
|
|
25
28
|
} from './flow-builder-models'
|
|
@@ -51,10 +54,10 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
async readFlowContent(): Promise<FlowBuilderData> {
|
|
54
|
-
const
|
|
57
|
+
const { data } = await axios.get(this.flowUrl, {
|
|
55
58
|
headers: { Authorization: `Bearer ${this.getAccessToken()}` },
|
|
56
59
|
})
|
|
57
|
-
return
|
|
60
|
+
return data
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
async pre(request: PluginPreRequest): Promise<void> {
|
|
@@ -71,10 +74,12 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
71
74
|
return content
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
async getHandoffContent(
|
|
77
|
+
async getHandoffContent(
|
|
78
|
+
handoffTargetId: string | undefined
|
|
79
|
+
): Promise<HandoffNode> {
|
|
75
80
|
const flow = await this.flow
|
|
76
81
|
const content = flow.nodes.find(
|
|
77
|
-
node => node.
|
|
82
|
+
node => node.id === handoffTargetId
|
|
78
83
|
) as HandoffNode
|
|
79
84
|
if (!content) throw Error(`Handoff node not found`)
|
|
80
85
|
return content
|
|
@@ -91,6 +96,8 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
91
96
|
return FlowImage.fromHubtypeCMS(hubtypeContent, locale)
|
|
92
97
|
case NodeType.CAROUSEL:
|
|
93
98
|
return FlowCarousel.fromHubtypeCMS(hubtypeContent, locale)
|
|
99
|
+
case NodeType.VIDEO:
|
|
100
|
+
return FlowVideo.fromHubtypeCMS(hubtypeContent, locale)
|
|
94
101
|
default:
|
|
95
102
|
return undefined
|
|
96
103
|
}
|
|
@@ -98,20 +105,34 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
98
105
|
|
|
99
106
|
async getStartId(): Promise<string> {
|
|
100
107
|
const flow = await this.flow
|
|
101
|
-
const startNode = flow.nodes.find(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return
|
|
108
|
+
const startNode = flow.nodes.find(
|
|
109
|
+
node => node.type === NodeType.START_UP
|
|
110
|
+
) as StartNode | undefined
|
|
111
|
+
if (!startNode) throw new Error('start-up id must be defined')
|
|
112
|
+
return startNode.target.id
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
async getFallbackId(): Promise<string> {
|
|
116
|
+
const flow = await this.flow
|
|
117
|
+
const fallbackNode = flow.nodes.find(
|
|
118
|
+
node => node.type === NodeType.FALLBACK
|
|
119
|
+
) as FallbackNode | undefined
|
|
120
|
+
if (!fallbackNode) throw new Error('fallback node must be defined')
|
|
121
|
+
const fallbackFirstMessage = fallbackNode.content.first_message
|
|
122
|
+
const fallbackSecondMessage = fallbackNode.content.second_message
|
|
123
|
+
if (!fallbackSecondMessage) return fallbackFirstMessage.id
|
|
124
|
+
const fallbackIds = [fallbackFirstMessage.id, fallbackSecondMessage.id]
|
|
125
|
+
return fallbackIds[Math.floor(Math.random() * fallbackIds.length)]
|
|
126
|
+
}
|
|
108
127
|
async getContents(
|
|
109
128
|
id: string,
|
|
110
129
|
locale: string,
|
|
111
130
|
prevContents?: FlowContent[]
|
|
112
|
-
): Promise<FlowContent[]> {
|
|
131
|
+
): Promise<{ contents: FlowContent[]; handoffNode: HandoffNode }> {
|
|
113
132
|
const contents = prevContents || []
|
|
114
133
|
const hubtypeContent: any = await this.getContent(id)
|
|
134
|
+
const isHandoff = hubtypeContent.type === NodeType.HANDOFF
|
|
135
|
+
|
|
115
136
|
if (hubtypeContent.content.elements) {
|
|
116
137
|
for (const i in hubtypeContent.content.elements) {
|
|
117
138
|
const button = hubtypeContent.content.elements[i].button
|
|
@@ -151,10 +172,10 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
151
172
|
}
|
|
152
173
|
// execute function
|
|
153
174
|
// return this.getContents(function result_mapping target, locale, contents)
|
|
154
|
-
return contents
|
|
175
|
+
return { contents, handoffNode: isHandoff && hubtypeContent }
|
|
155
176
|
}
|
|
156
177
|
|
|
157
|
-
async
|
|
178
|
+
async getPayloadByIntent(
|
|
158
179
|
input: Input,
|
|
159
180
|
locale: string
|
|
160
181
|
): Promise<string | undefined> {
|
|
@@ -163,14 +184,17 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
163
184
|
const intents = flow.nodes.filter(
|
|
164
185
|
node => node.type === NodeType.INTENT
|
|
165
186
|
) as IntentNode[]
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
187
|
+
const inputIntent = input.intent
|
|
188
|
+
const inputConfidence = input.confidence
|
|
189
|
+
if (inputIntent) {
|
|
190
|
+
const matchedIntentNode = intents.find(
|
|
191
|
+
node =>
|
|
192
|
+
inputIntent &&
|
|
193
|
+
this.hasIntent(node, inputIntent, locale) &&
|
|
194
|
+
inputConfidence &&
|
|
195
|
+
this.hasMetConfidenceThreshold(node, inputConfidence)
|
|
170
196
|
)
|
|
171
|
-
|
|
172
|
-
return matchedIntents[0].target?.id
|
|
173
|
-
}
|
|
197
|
+
return matchedIntentNode?.target?.id
|
|
174
198
|
}
|
|
175
199
|
} catch (error) {
|
|
176
200
|
console.error('Error getting payload by input: ', error)
|
|
@@ -180,10 +204,17 @@ export default class BotonicPluginFlowBuilder implements Plugin {
|
|
|
180
204
|
}
|
|
181
205
|
|
|
182
206
|
hasIntent(node: IntentNode, intent: string, locale: string): boolean {
|
|
183
|
-
|
|
207
|
+
return node.content.intents.some(
|
|
184
208
|
i => i.locale === locale && i.values.includes(intent)
|
|
185
209
|
)
|
|
186
|
-
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
hasMetConfidenceThreshold(
|
|
213
|
+
node: IntentNode,
|
|
214
|
+
predictedConfidence: number
|
|
215
|
+
): boolean {
|
|
216
|
+
const nodeConfidence = node.content.confidence / 100
|
|
217
|
+
return predictedConfidence >= nodeConfidence
|
|
187
218
|
}
|
|
188
219
|
|
|
189
220
|
async getPayloadByKeyword(
|