@botonic/plugin-flow-builder 0.56.0 → 0.57.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.
- package/lib/cjs/api.d.ts +3 -2
- package/lib/cjs/api.js +20 -17
- package/lib/cjs/api.js.map +1 -1
- package/lib/cjs/bot-context-manager.d.ts +17 -0
- package/lib/cjs/bot-context-manager.js +93 -0
- package/lib/cjs/bot-context-manager.js.map +1 -0
- package/lib/cjs/constants.d.ts +2 -1
- package/lib/cjs/constants.js +3 -2
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/content-fields/contact-reasons-conditional-matcher.d.ts +4 -0
- package/lib/cjs/content-fields/contact-reasons-conditional-matcher.js +12 -0
- package/lib/cjs/content-fields/contact-reasons-conditional-matcher.js.map +1 -0
- package/lib/cjs/content-fields/flow-contact-reasons-conditional.d.ts +13 -0
- package/lib/cjs/content-fields/flow-contact-reasons-conditional.js +45 -0
- package/lib/cjs/content-fields/flow-contact-reasons-conditional.js.map +1 -0
- package/lib/cjs/content-fields/flow-handoff.d.ts +7 -1
- package/lib/cjs/content-fields/flow-handoff.js +38 -3
- package/lib/cjs/content-fields/flow-handoff.js.map +1 -1
- package/lib/cjs/content-fields/hubtype-fields/contact-reasons-conditional.d.ts +16 -0
- package/lib/cjs/content-fields/hubtype-fields/contact-reasons-conditional.js +3 -0
- package/lib/cjs/content-fields/hubtype-fields/contact-reasons-conditional.js.map +1 -0
- package/lib/cjs/content-fields/hubtype-fields/handoff.d.ts +6 -1
- package/lib/cjs/content-fields/hubtype-fields/node-types.d.ts +2 -1
- package/lib/cjs/content-fields/hubtype-fields/node-types.js +1 -0
- package/lib/cjs/content-fields/hubtype-fields/node-types.js.map +1 -1
- package/lib/cjs/content-fields/hubtype-fields/nodes.d.ts +2 -1
- package/lib/cjs/content-fields/index.d.ts +3 -2
- package/lib/cjs/content-fields/index.js +3 -1
- package/lib/cjs/content-fields/index.js.map +1 -1
- package/lib/cjs/flow-factory.js +2 -0
- package/lib/cjs/flow-factory.js.map +1 -1
- package/lib/cjs/index.d.ts +5 -9
- package/lib/cjs/index.js +16 -64
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/types.d.ts +2 -2
- package/lib/cjs/types.js.map +1 -1
- package/lib/esm/api.d.ts +3 -2
- package/lib/esm/api.js +20 -17
- package/lib/esm/api.js.map +1 -1
- package/lib/esm/bot-context-manager.d.ts +17 -0
- package/lib/esm/bot-context-manager.js +93 -0
- package/lib/esm/bot-context-manager.js.map +1 -0
- package/lib/esm/constants.d.ts +2 -1
- package/lib/esm/constants.js +3 -2
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/content-fields/contact-reasons-conditional-matcher.d.ts +4 -0
- package/lib/esm/content-fields/contact-reasons-conditional-matcher.js +12 -0
- package/lib/esm/content-fields/contact-reasons-conditional-matcher.js.map +1 -0
- package/lib/esm/content-fields/flow-contact-reasons-conditional.d.ts +13 -0
- package/lib/esm/content-fields/flow-contact-reasons-conditional.js +45 -0
- package/lib/esm/content-fields/flow-contact-reasons-conditional.js.map +1 -0
- package/lib/esm/content-fields/flow-handoff.d.ts +7 -1
- package/lib/esm/content-fields/flow-handoff.js +38 -3
- package/lib/esm/content-fields/flow-handoff.js.map +1 -1
- package/lib/esm/content-fields/hubtype-fields/contact-reasons-conditional.d.ts +16 -0
- package/lib/esm/content-fields/hubtype-fields/contact-reasons-conditional.js +3 -0
- package/lib/esm/content-fields/hubtype-fields/contact-reasons-conditional.js.map +1 -0
- package/lib/esm/content-fields/hubtype-fields/handoff.d.ts +6 -1
- package/lib/esm/content-fields/hubtype-fields/node-types.d.ts +2 -1
- package/lib/esm/content-fields/hubtype-fields/node-types.js +1 -0
- package/lib/esm/content-fields/hubtype-fields/node-types.js.map +1 -1
- package/lib/esm/content-fields/hubtype-fields/nodes.d.ts +2 -1
- package/lib/esm/content-fields/index.d.ts +3 -2
- package/lib/esm/content-fields/index.js +3 -1
- package/lib/esm/content-fields/index.js.map +1 -1
- package/lib/esm/flow-factory.js +2 -0
- package/lib/esm/flow-factory.js.map +1 -1
- package/lib/esm/index.d.ts +5 -9
- package/lib/esm/index.js +16 -64
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/types.d.ts +2 -2
- package/lib/esm/types.js.map +1 -1
- package/package.json +2 -2
- package/src/api.ts +29 -20
- package/src/bot-context-manager.ts +153 -0
- package/src/constants.ts +2 -1
- package/src/content-fields/contact-reasons-conditional-matcher.ts +24 -0
- package/src/content-fields/flow-contact-reasons-conditional.tsx +60 -0
- package/src/content-fields/flow-handoff.tsx +49 -7
- package/src/content-fields/hubtype-fields/contact-reasons-conditional.ts +18 -0
- package/src/content-fields/hubtype-fields/handoff.ts +11 -1
- package/src/content-fields/hubtype-fields/node-types.ts +1 -0
- package/src/content-fields/hubtype-fields/nodes.ts +2 -0
- package/src/content-fields/index.ts +3 -0
- package/src/flow-factory.ts +7 -0
- package/src/index.ts +26 -113
- package/src/types.ts +1 -2
package/src/api.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BotContext } from '@botonic/core'
|
|
2
2
|
import axios from 'axios'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AI_AGENTS_FLOW_NAME,
|
|
6
|
+
PUSH_FLOW_PAYLOAD,
|
|
7
|
+
SEPARATOR,
|
|
8
|
+
UUID_REGEXP,
|
|
9
|
+
} from './constants'
|
|
5
10
|
import {
|
|
6
11
|
type HtBotActionNode,
|
|
7
12
|
type HtCaptureUserInputNode,
|
|
@@ -31,7 +36,7 @@ export class FlowBuilderApi {
|
|
|
31
36
|
url: string
|
|
32
37
|
flowUrl: string
|
|
33
38
|
flow: HtFlowBuilderData
|
|
34
|
-
|
|
39
|
+
botContext: BotContext
|
|
35
40
|
|
|
36
41
|
private constructor() {}
|
|
37
42
|
|
|
@@ -39,12 +44,12 @@ export class FlowBuilderApi {
|
|
|
39
44
|
const newApi = new FlowBuilderApi()
|
|
40
45
|
|
|
41
46
|
newApi.url = options.url
|
|
42
|
-
newApi.
|
|
47
|
+
newApi.botContext = options.botContext
|
|
43
48
|
// TODO: Refactor later to combine logic from `FlowBuilderApi.create`, `resolveFlowUrl` and `getAccessToken` to be in one place
|
|
44
49
|
if (process.env.NODE_ENV === ProcessEnvNodeEnvs.DEVELOPMENT) {
|
|
45
50
|
await newApi.updateSessionWithUserInfo(options.accessToken)
|
|
46
51
|
}
|
|
47
|
-
const updatedRequest = newApi.
|
|
52
|
+
const updatedRequest = newApi.botContext
|
|
48
53
|
newApi.flowUrl = options.flowUrl.replace(
|
|
49
54
|
'{bot_id}',
|
|
50
55
|
updatedRequest.session.bot.id
|
|
@@ -65,8 +70,8 @@ export class FlowBuilderApi {
|
|
|
65
70
|
const response = await axios.get(url, {
|
|
66
71
|
headers: { Authorization: `Bearer ${token}` },
|
|
67
72
|
})
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
73
|
+
this.botContext.session.organization_id = response.data.organization_id
|
|
74
|
+
this.botContext.session.bot.id = response.data.bot_id
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
getNodeByFlowId(id: string): HtNodeWithContent {
|
|
@@ -260,23 +265,23 @@ export class FlowBuilderApi {
|
|
|
260
265
|
}
|
|
261
266
|
|
|
262
267
|
getCaptureUserInputId(): string | undefined {
|
|
263
|
-
return this.
|
|
268
|
+
return this.botContext.session.capture_user_input?.node_id
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
setCaptureUserInputId(id: string): void {
|
|
267
|
-
if (this.
|
|
268
|
-
this.
|
|
272
|
+
if (this.botContext.session.capture_user_input) {
|
|
273
|
+
this.botContext.session.capture_user_input.node_id = id
|
|
269
274
|
}
|
|
270
|
-
this.
|
|
275
|
+
this.botContext.session.capture_user_input = { node_id: id }
|
|
271
276
|
}
|
|
272
277
|
|
|
273
278
|
removeCaptureUserInputId(): void {
|
|
274
|
-
this.
|
|
279
|
+
this.botContext.session.capture_user_input = undefined
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
setUserExtraDataVariable(key: string, value: string): void {
|
|
278
|
-
if (this.
|
|
279
|
-
this.
|
|
283
|
+
if (this.botContext.session.user.extra_data) {
|
|
284
|
+
this.botContext.session.user.extra_data[key] = value
|
|
280
285
|
}
|
|
281
286
|
}
|
|
282
287
|
|
|
@@ -289,18 +294,18 @@ export class FlowBuilderApi {
|
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
getSessionWhatsappRequestContactId(): string | undefined {
|
|
292
|
-
return this.
|
|
297
|
+
return this.botContext.session.whatsapp_request_contact?.id
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
setWhatsappRequestContactId(id: string): void {
|
|
296
|
-
if (this.
|
|
297
|
-
this.
|
|
301
|
+
if (this.botContext.session.whatsapp_request_contact) {
|
|
302
|
+
this.botContext.session.whatsapp_request_contact.id = id
|
|
298
303
|
}
|
|
299
|
-
this.
|
|
304
|
+
this.botContext.session.whatsapp_request_contact = { id }
|
|
300
305
|
}
|
|
301
306
|
|
|
302
307
|
removeWhatsappRequestContactId(): void {
|
|
303
|
-
this.
|
|
308
|
+
this.botContext.session.whatsapp_request_contact = undefined
|
|
304
309
|
}
|
|
305
310
|
|
|
306
311
|
getWhatsappRequestContactInfoNode():
|
|
@@ -320,9 +325,13 @@ export class FlowBuilderApi {
|
|
|
320
325
|
const defaultLocaleCode = this.flow.default_locale_code
|
|
321
326
|
|
|
322
327
|
return new FlowLocale(
|
|
323
|
-
this.
|
|
328
|
+
this.botContext,
|
|
324
329
|
flowLocales,
|
|
325
330
|
defaultLocaleCode
|
|
326
331
|
).resolve()
|
|
327
332
|
}
|
|
333
|
+
|
|
334
|
+
isPushFlowPayload(payload?: string): boolean {
|
|
335
|
+
return payload?.startsWith(PUSH_FLOW_PAYLOAD) ?? false
|
|
336
|
+
}
|
|
328
337
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BotContext,
|
|
3
|
+
INPUT,
|
|
4
|
+
PROVIDER,
|
|
5
|
+
WhatsappInputOrigin,
|
|
6
|
+
} from '@botonic/core'
|
|
7
|
+
|
|
8
|
+
import type { FlowBuilderApi } from './api'
|
|
9
|
+
import {
|
|
10
|
+
EMPTY_PAYLOAD,
|
|
11
|
+
ON_CLOSE_HANDOFF_PAYLOAD,
|
|
12
|
+
SEPARATOR,
|
|
13
|
+
SOURCE_INFO_SEPARATOR,
|
|
14
|
+
} from './constants'
|
|
15
|
+
import { FlowHandoff } from './content-fields'
|
|
16
|
+
import {
|
|
17
|
+
type HtBotActionNode,
|
|
18
|
+
type HtHandoffNode,
|
|
19
|
+
HtNodeWithContentType,
|
|
20
|
+
} from './content-fields/hubtype-fields'
|
|
21
|
+
import { getNextPayloadByUserInput } from './user-input'
|
|
22
|
+
import type { SmartIntentsInferenceConfig } from './user-input/smart-intent'
|
|
23
|
+
import { inputHasTextOrTranscript } from './utils/input'
|
|
24
|
+
|
|
25
|
+
export class BotContextManager {
|
|
26
|
+
constructor(
|
|
27
|
+
private readonly cmsApi: FlowBuilderApi,
|
|
28
|
+
private readonly smartIntentsConfig: SmartIntentsInferenceConfig
|
|
29
|
+
) {}
|
|
30
|
+
|
|
31
|
+
async updateBeforeRoutes(botContext: BotContext): Promise<void> {
|
|
32
|
+
// When AI Agent is executed in Whatsapp, button payloads come as referral and must be converted to text being processed by the agent.
|
|
33
|
+
this.convertWhatsappAiAgentEmptyPayloads(botContext)
|
|
34
|
+
|
|
35
|
+
// If the payload is a push-flow payload, set first interaction to false
|
|
36
|
+
this.updateFirstInteractionOnPushFlowPayload(botContext)
|
|
37
|
+
|
|
38
|
+
this.resolveWhatsappContactRequestPayload(botContext)
|
|
39
|
+
this.resolveOnCloseHandoffPayload(botContext)
|
|
40
|
+
|
|
41
|
+
await this.resolvePayloadFromUserTextInput(botContext)
|
|
42
|
+
|
|
43
|
+
this.cmsApi.removeCaptureUserInputId()
|
|
44
|
+
this.removePayloadSourceSuffix(botContext)
|
|
45
|
+
this.resolveBotActionPayload(botContext)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async updateFirstInteractionOnPushFlowPayload(
|
|
49
|
+
botContext: BotContext
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
if (this.cmsApi.isPushFlowPayload(botContext.input.payload)) {
|
|
52
|
+
botContext.session.is_first_interaction = false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private async resolvePayloadFromUserTextInput(
|
|
57
|
+
botContext: BotContext
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
const checkUserTextInput =
|
|
60
|
+
inputHasTextOrTranscript(botContext.input) && !botContext.input.payload
|
|
61
|
+
|
|
62
|
+
if (!checkUserTextInput) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const resolvedLocale = this.cmsApi.getResolvedLocale()
|
|
67
|
+
const nextPayload = await getNextPayloadByUserInput(
|
|
68
|
+
this.cmsApi,
|
|
69
|
+
resolvedLocale,
|
|
70
|
+
botContext,
|
|
71
|
+
this.smartIntentsConfig
|
|
72
|
+
)
|
|
73
|
+
botContext.input.payload = nextPayload
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private resolveWhatsappContactRequestPayload(botContext: BotContext): void {
|
|
77
|
+
if (
|
|
78
|
+
botContext.input.type !== INPUT.CONTACT ||
|
|
79
|
+
botContext.input.origin !== WhatsappInputOrigin.ContactRequest
|
|
80
|
+
) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const whatsappRequestContactInfoNode =
|
|
85
|
+
this.cmsApi.getWhatsappRequestContactInfoNode()
|
|
86
|
+
|
|
87
|
+
if (whatsappRequestContactInfoNode) {
|
|
88
|
+
botContext.input.payload = this.cmsApi.getPayload(
|
|
89
|
+
whatsappRequestContactInfoNode.content.button.target
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.cmsApi.removeWhatsappRequestContactId()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private resolveOnCloseHandoffPayload(botContext: BotContext): void {
|
|
97
|
+
if (botContext.input.payload?.startsWith(ON_CLOSE_HANDOFF_PAYLOAD)) {
|
|
98
|
+
const handoffNodeId = botContext.input.payload.split(SEPARATOR)[1]
|
|
99
|
+
const handoffNode = this.cmsApi.getNodeById<HtHandoffNode>(handoffNodeId)
|
|
100
|
+
if (handoffNode && handoffNode.type === HtNodeWithContentType.HANDOFF) {
|
|
101
|
+
const flowHandoff = FlowHandoff.fromHubtypeCMS(
|
|
102
|
+
handoffNode,
|
|
103
|
+
this.cmsApi.getResolvedLocale(),
|
|
104
|
+
this.cmsApi
|
|
105
|
+
)
|
|
106
|
+
botContext.input.payload = flowHandoff.resolveOnClosePayload(botContext)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private convertWhatsappAiAgentEmptyPayloads(botContext: BotContext): void {
|
|
112
|
+
if (botContext.session.user.provider === PROVIDER.WHATSAPP) {
|
|
113
|
+
const shouldUseReferral =
|
|
114
|
+
botContext.input.referral &&
|
|
115
|
+
botContext.input.payload?.startsWith(EMPTY_PAYLOAD)
|
|
116
|
+
|
|
117
|
+
if (shouldUseReferral) {
|
|
118
|
+
botContext.input.type = INPUT.TEXT
|
|
119
|
+
botContext.input.data = botContext.input.referral
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private removePayloadSourceSuffix(botContext: BotContext): void {
|
|
125
|
+
if (botContext.input.payload) {
|
|
126
|
+
botContext.input.payload = this.removeSourceSuffix(
|
|
127
|
+
botContext.input.payload
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private resolveBotActionPayload(botContext: BotContext): void {
|
|
133
|
+
if (
|
|
134
|
+
botContext.input.payload &&
|
|
135
|
+
this.cmsApi.isBotAction(botContext.input.payload)
|
|
136
|
+
) {
|
|
137
|
+
const cmsBotAction = this.cmsApi.getNodeById<HtBotActionNode>(
|
|
138
|
+
botContext.input.payload
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
botContext.input.payload =
|
|
142
|
+
this.cmsApi.createPayloadWithParams(cmsBotAction)
|
|
143
|
+
|
|
144
|
+
// Re-execute convertWhatsappAiAgentEmptyPayloads function to handle
|
|
145
|
+
// the case that a BotAction has a payload equals to EMPTY_PAYLOAD
|
|
146
|
+
this.convertWhatsappAiAgentEmptyPayloads(botContext)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private removeSourceSuffix(payload: string): string {
|
|
151
|
+
return payload.split(SOURCE_INFO_SEPARATOR)[0]
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -2,7 +2,6 @@ export const FLOW_BUILDER_API_URL_PROD =
|
|
|
2
2
|
process.env.HUBTYPE_API_URL || 'https://api.hubtype.com'
|
|
3
3
|
export const SEPARATOR = '|'
|
|
4
4
|
export const SOURCE_INFO_SEPARATOR = `${SEPARATOR}source_`
|
|
5
|
-
export const EMPTY_PAYLOAD = 'fb-empty-payload'
|
|
6
5
|
export const VARIABLE_PATTERN_GLOBAL = /{([^{}]+)}/g
|
|
7
6
|
export const ACCESS_TOKEN_VARIABLE_KEY = '_access_token'
|
|
8
7
|
export const REG_EXP_PATTERN = /^\/(.*)\/([gimyus]*)$/
|
|
@@ -10,6 +9,8 @@ export const UUID_REGEXP =
|
|
|
10
9
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
11
10
|
|
|
12
11
|
/* ********** PAYLOADS ********** */
|
|
12
|
+
export const EMPTY_PAYLOAD = 'fb-empty-payload'
|
|
13
|
+
export const ON_CLOSE_HANDOFF_PAYLOAD = 'fb-on-close-handoff'
|
|
13
14
|
export const AGENT_RATING_PAYLOAD = 'agent-rating'
|
|
14
15
|
export const PUSH_FLOW_PAYLOAD = 'push-flow'
|
|
15
16
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HubtypeCaseContactReason } from '@botonic/core'
|
|
2
|
+
|
|
3
|
+
import type { HtContactReasonBranch } from './hubtype-fields/contact-reasons-conditional'
|
|
4
|
+
|
|
5
|
+
export function contactReasonMatchesBranch(
|
|
6
|
+
sessionContactReason: HubtypeCaseContactReason,
|
|
7
|
+
branch: HtContactReasonBranch
|
|
8
|
+
): boolean {
|
|
9
|
+
return (
|
|
10
|
+
sessionContactReason.project_id === branch.project_id &&
|
|
11
|
+
sessionContactReason.name === branch.name
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function findMatchingContactReasonBranch(
|
|
16
|
+
sessionContactReasons: HubtypeCaseContactReason[],
|
|
17
|
+
contactReasonBranches: HtContactReasonBranch[]
|
|
18
|
+
): HtContactReasonBranch | undefined {
|
|
19
|
+
return contactReasonBranches.find(branch =>
|
|
20
|
+
sessionContactReasons.some(sessionContactReason =>
|
|
21
|
+
contactReasonMatchesBranch(sessionContactReason, branch)
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { BotContext, HubtypeCaseContactReason } from '@botonic/core'
|
|
2
|
+
import { findMatchingContactReasonBranch } from './contact-reasons-conditional-matcher'
|
|
3
|
+
import { ContentFieldsBase } from './content-fields-base'
|
|
4
|
+
import type { HtNodeLink } from './hubtype-fields/common'
|
|
5
|
+
import type {
|
|
6
|
+
HtContactReasonBranch,
|
|
7
|
+
HtContactReasonsNode,
|
|
8
|
+
} from './hubtype-fields/contact-reasons-conditional'
|
|
9
|
+
|
|
10
|
+
export class FlowContactReasonsConditional extends ContentFieldsBase {
|
|
11
|
+
public contactReasons: HtContactReasonBranch[] = []
|
|
12
|
+
public defaultTarget?: HtNodeLink
|
|
13
|
+
|
|
14
|
+
static fromHubtypeCMS(
|
|
15
|
+
component: HtContactReasonsNode,
|
|
16
|
+
botContext: BotContext
|
|
17
|
+
): FlowContactReasonsConditional {
|
|
18
|
+
const newContactReasonsConditional = new FlowContactReasonsConditional(
|
|
19
|
+
component.id
|
|
20
|
+
)
|
|
21
|
+
newContactReasonsConditional.contactReasons =
|
|
22
|
+
component.content.contact_reasons
|
|
23
|
+
newContactReasonsConditional.defaultTarget =
|
|
24
|
+
component.content.default_target
|
|
25
|
+
newContactReasonsConditional.setFollowUp(botContext)
|
|
26
|
+
return newContactReasonsConditional
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private setFollowUp(botContext: BotContext): void {
|
|
30
|
+
const sessionContactReasons: HubtypeCaseContactReason[] | undefined =
|
|
31
|
+
botContext.session._hubtype_case_contact_reasons
|
|
32
|
+
|
|
33
|
+
if (sessionContactReasons) {
|
|
34
|
+
const matchingBranch = findMatchingContactReasonBranch(
|
|
35
|
+
sessionContactReasons,
|
|
36
|
+
this.contactReasons
|
|
37
|
+
)
|
|
38
|
+
if (matchingBranch?.target) {
|
|
39
|
+
this.followUp = matchingBranch.target
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.followUp ??= this.defaultTarget
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async trackFlow(_botContext: BotContext): Promise<void> {
|
|
47
|
+
// await trackFlow(botContext)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async processContent(_botContext: BotContext): Promise<void> {
|
|
52
|
+
// await this.filterContent(botContext, this)
|
|
53
|
+
// await this.trackFlow(botContext)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toBotonic() {
|
|
58
|
+
return <></>
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -6,17 +6,22 @@ import {
|
|
|
6
6
|
isWebchat,
|
|
7
7
|
} from '@botonic/core'
|
|
8
8
|
import { WebchatSettings } from '@botonic/react'
|
|
9
|
-
|
|
10
9
|
import type { FlowBuilderApi } from '../api'
|
|
10
|
+
import { ON_CLOSE_HANDOFF_PAYLOAD, SEPARATOR } from '../constants'
|
|
11
11
|
import { getCommonFlowContentEventArgsForContentId } from '../tracking'
|
|
12
12
|
import { ContentFieldsBase } from './content-fields-base'
|
|
13
|
-
import type { HtHandoffNode, HtQueueLocale } from './hubtype-fields'
|
|
13
|
+
import type { HtHandoffNode, HtNodeLink, HtQueueLocale } from './hubtype-fields'
|
|
14
14
|
|
|
15
15
|
export class FlowHandoff extends ContentFieldsBase {
|
|
16
16
|
public queue?: HtQueueLocale
|
|
17
17
|
public onFinishPayload?: string
|
|
18
18
|
public handoffAutoAssign: boolean
|
|
19
19
|
public hasQueuePositionChangedNotificationsEnabled: boolean
|
|
20
|
+
public hasDifferentiatedCloseTypes: boolean
|
|
21
|
+
public resolvedByAgent?: HtNodeLink
|
|
22
|
+
public discardedByAgent?: HtNodeLink
|
|
23
|
+
public discardedByUser?: HtNodeLink
|
|
24
|
+
public discardedBySystem?: HtNodeLink
|
|
20
25
|
public isTestIntegration: boolean
|
|
21
26
|
|
|
22
27
|
static fromHubtypeCMS(
|
|
@@ -30,14 +35,21 @@ export class FlowHandoff extends ContentFieldsBase {
|
|
|
30
35
|
locale,
|
|
31
36
|
cmsHandoff.content.queue
|
|
32
37
|
)
|
|
38
|
+
newHandoff.hasDifferentiatedCloseTypes =
|
|
39
|
+
cmsHandoff.content.has_differentiated_close_types
|
|
40
|
+
newHandoff.resolvedByAgent = cmsHandoff.content.resolved_by_agent
|
|
41
|
+
newHandoff.discardedByAgent = cmsHandoff.content.discarded_by_agent
|
|
42
|
+
newHandoff.discardedByUser = cmsHandoff.content.discarded_by_user
|
|
43
|
+
newHandoff.discardedBySystem = cmsHandoff.content.discarded_by_system
|
|
44
|
+
newHandoff.handoffAutoAssign = cmsHandoff.content.has_auto_assign
|
|
45
|
+
newHandoff.hasQueuePositionChangedNotificationsEnabled =
|
|
46
|
+
cmsHandoff.content.has_queue_position_changed_notifications_enabled
|
|
47
|
+
|
|
33
48
|
newHandoff.onFinishPayload = FlowHandoff.getOnFinishPayload(
|
|
34
49
|
cmsHandoff,
|
|
35
50
|
cmsApi
|
|
36
51
|
)
|
|
37
|
-
newHandoff.
|
|
38
|
-
newHandoff.hasQueuePositionChangedNotificationsEnabled =
|
|
39
|
-
cmsHandoff.content.has_queue_position_changed_notifications_enabled
|
|
40
|
-
newHandoff.followUp = cmsHandoff.follow_up
|
|
52
|
+
newHandoff.followUp = undefined
|
|
41
53
|
|
|
42
54
|
return newHandoff
|
|
43
55
|
}
|
|
@@ -46,13 +58,43 @@ export class FlowHandoff extends ContentFieldsBase {
|
|
|
46
58
|
cmsHandoff: HtHandoffNode,
|
|
47
59
|
cmsApi: FlowBuilderApi
|
|
48
60
|
): string | undefined {
|
|
49
|
-
if (cmsHandoff.
|
|
61
|
+
if (cmsHandoff.content.has_differentiated_close_types) {
|
|
62
|
+
return `${ON_CLOSE_HANDOFF_PAYLOAD}${SEPARATOR}${cmsHandoff.id}`
|
|
63
|
+
} else if (cmsHandoff.target?.id) {
|
|
50
64
|
return cmsApi.getPayload(cmsHandoff.target)
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
return undefined
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
resolveOnClosePayload(botContext: BotContext): string {
|
|
71
|
+
const contactReasons =
|
|
72
|
+
botContext.session._hubtype_case_contact_reasons || []
|
|
73
|
+
|
|
74
|
+
if (contactReasons.some(reason => reason.name === 'discarded')) {
|
|
75
|
+
if (this.discardedByAgent?.id) {
|
|
76
|
+
return this.discardedByAgent.id
|
|
77
|
+
}
|
|
78
|
+
console.error('No discarded by agent target found')
|
|
79
|
+
}
|
|
80
|
+
if (contactReasons.some(reason => reason.name === 'discarded_by_user')) {
|
|
81
|
+
if (this.discardedByUser?.id) {
|
|
82
|
+
return this.discardedByUser.id
|
|
83
|
+
}
|
|
84
|
+
console.error('No discarded by user target found')
|
|
85
|
+
}
|
|
86
|
+
if (contactReasons.some(reason => reason.name === 'discarded_by_system')) {
|
|
87
|
+
if (this.discardedBySystem?.id) {
|
|
88
|
+
return this.discardedBySystem.id
|
|
89
|
+
}
|
|
90
|
+
console.error('No discarded by system target found')
|
|
91
|
+
}
|
|
92
|
+
if (this.resolvedByAgent?.id) {
|
|
93
|
+
return this.resolvedByAgent.id
|
|
94
|
+
}
|
|
95
|
+
throw new Error('No resolved by agent target found')
|
|
96
|
+
}
|
|
97
|
+
|
|
56
98
|
async doHandoff(botContext: BotContext): Promise<void> {
|
|
57
99
|
const handOffBuilder = new HandOffBuilder(botContext.session)
|
|
58
100
|
handOffBuilder.withAutoAssignOnWaiting(this.handoffAutoAssign)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HtBaseNode, HtNodeLink } from './common'
|
|
2
|
+
import type { HtNodeWithContentType } from './node-types'
|
|
3
|
+
|
|
4
|
+
export interface HtContactReasonBranch {
|
|
5
|
+
id: string
|
|
6
|
+
name: string
|
|
7
|
+
project_id: string
|
|
8
|
+
project_name?: string
|
|
9
|
+
target?: HtNodeLink
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface HtContactReasonsNode extends HtBaseNode {
|
|
13
|
+
type: HtNodeWithContentType.CONTACT_REASONS_CONDITION
|
|
14
|
+
content: {
|
|
15
|
+
contact_reasons: HtContactReasonBranch[]
|
|
16
|
+
default_target?: HtNodeLink
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
HtBaseNode,
|
|
3
|
+
HtNodeLink,
|
|
4
|
+
HtPayloadLocale,
|
|
5
|
+
HtQueueLocale,
|
|
6
|
+
} from './common'
|
|
2
7
|
import type { HtNodeWithContentType } from './node-types'
|
|
3
8
|
|
|
4
9
|
export interface HtHandoffNode extends HtBaseNode {
|
|
@@ -8,5 +13,10 @@ export interface HtHandoffNode extends HtBaseNode {
|
|
|
8
13
|
payload: HtPayloadLocale[]
|
|
9
14
|
has_auto_assign: boolean
|
|
10
15
|
has_queue_position_changed_notifications_enabled: boolean
|
|
16
|
+
has_differentiated_close_types: boolean
|
|
17
|
+
resolved_by_agent?: HtNodeLink
|
|
18
|
+
discarded_by_agent?: HtNodeLink
|
|
19
|
+
discarded_by_user?: HtNodeLink
|
|
20
|
+
discarded_by_system?: HtNodeLink
|
|
11
21
|
}
|
|
12
22
|
}
|
|
@@ -21,6 +21,7 @@ export enum HtNodeWithContentType {
|
|
|
21
21
|
CAPTURE_USER_INPUT = 'capture-user-input',
|
|
22
22
|
CUSTOM_CONDITION = 'custom-condition',
|
|
23
23
|
DO_NOTHING = 'do-nothing',
|
|
24
|
+
CONTACT_REASONS_CONDITION = 'contact-reasons-condition',
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export enum HtNodeWithoutContentType {
|
|
@@ -3,6 +3,7 @@ import type { HtAiAgentRouterNode } from './ai-agent-router'
|
|
|
3
3
|
import type { HtBotActionNode } from './bot-action'
|
|
4
4
|
import type { HtCaptureUserInputNode } from './capture-user-input'
|
|
5
5
|
import type { HtCarouselNode } from './carousel'
|
|
6
|
+
import type { HtContactReasonsNode } from './contact-reasons-conditional'
|
|
6
7
|
import type { HtCustomConditionalV2Node } from './custom-conditional-v2'
|
|
7
8
|
import type { HtDoNothingNode } from './do-nothing'
|
|
8
9
|
// import { HtChannelConditionalNode } from './channel-conditional'
|
|
@@ -50,6 +51,7 @@ export type HtNodeWithContent =
|
|
|
50
51
|
| HtCaptureUserInputNode
|
|
51
52
|
| HtCustomConditionalV2Node
|
|
52
53
|
| HtDoNothingNode
|
|
54
|
+
| HtContactReasonsNode
|
|
53
55
|
// | HtChannelConditionalNode
|
|
54
56
|
// | HtCountryConditionalNode
|
|
55
57
|
// | HtCustomConditionalNode
|
|
@@ -5,6 +5,7 @@ import { FlowBotAction } from './flow-bot-action'
|
|
|
5
5
|
import { FlowCaptureUserInput } from './flow-capture-user-input'
|
|
6
6
|
import { FlowCarousel } from './flow-carousel'
|
|
7
7
|
import { FlowChannelConditional } from './flow-channel-conditional'
|
|
8
|
+
import { FlowContactReasonsConditional } from './flow-contact-reasons-conditional'
|
|
8
9
|
import { FlowCountryConditional } from './flow-country-conditional'
|
|
9
10
|
import { FlowCustomConditional } from './flow-custom-conditional'
|
|
10
11
|
import { FlowDoNothing } from './flow-do-nothing'
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
FlowCaptureUserInput,
|
|
35
36
|
FlowCarousel,
|
|
36
37
|
FlowChannelConditional,
|
|
38
|
+
FlowContactReasonsConditional,
|
|
37
39
|
FlowCountryConditional,
|
|
38
40
|
FlowCustomConditional,
|
|
39
41
|
FlowCustomConditionalV2,
|
|
@@ -75,3 +77,4 @@ export type FlowContent =
|
|
|
75
77
|
| FlowAiAgentRouter
|
|
76
78
|
| FlowCustomConditionalV2
|
|
77
79
|
| FlowDoNothing
|
|
80
|
+
| FlowContactReasonsConditional
|
package/src/flow-factory.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
FlowBotAction,
|
|
8
8
|
FlowCarousel,
|
|
9
9
|
FlowChannelConditional,
|
|
10
|
+
FlowContactReasonsConditional,
|
|
10
11
|
type FlowContent,
|
|
11
12
|
FlowCountryConditional,
|
|
12
13
|
FlowCustomConditional,
|
|
@@ -122,6 +123,12 @@ export class FlowFactory {
|
|
|
122
123
|
case HtNodeWithContentType.DO_NOTHING:
|
|
123
124
|
return FlowDoNothing.fromHubtypeCMS(hubtypeContent)
|
|
124
125
|
|
|
126
|
+
case HtNodeWithContentType.CONTACT_REASONS_CONDITION:
|
|
127
|
+
return FlowContactReasonsConditional.fromHubtypeCMS(
|
|
128
|
+
hubtypeContent,
|
|
129
|
+
this.botContext
|
|
130
|
+
)
|
|
131
|
+
|
|
125
132
|
default:
|
|
126
133
|
return undefined
|
|
127
134
|
}
|