@botonic/plugin-flow-builder 0.57.0-rslib-esm.1 → 0.58.0-alpha-new-stack.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 (98) hide show
  1. package/lib/action/ai-agent-from-user-input.js +8 -0
  2. package/lib/action/ai-agent-from-user-input.js.map +1 -1
  3. package/lib/action/first-interaction.js +10 -0
  4. package/lib/action/first-interaction.js.map +1 -1
  5. package/lib/action/get-contents.js +5 -2
  6. package/lib/action/get-contents.js.map +1 -1
  7. package/lib/action/index.js +8 -1
  8. package/lib/action/index.js.map +1 -1
  9. package/lib/api.d.ts +8 -3
  10. package/lib/api.js +37 -13
  11. package/lib/api.js.map +1 -1
  12. package/lib/bot-context-manager.d.ts +17 -0
  13. package/lib/bot-context-manager.js +101 -0
  14. package/lib/bot-context-manager.js.map +1 -0
  15. package/lib/constants.d.ts +2 -1
  16. package/lib/constants.js +4 -3
  17. package/lib/constants.js.map +1 -1
  18. package/lib/content-fields/contact-reasons-conditional-matcher.d.ts +4 -0
  19. package/lib/content-fields/contact-reasons-conditional-matcher.js +10 -0
  20. package/lib/content-fields/contact-reasons-conditional-matcher.js.map +1 -0
  21. package/lib/content-fields/flow-ai-agent-base.js +7 -1
  22. package/lib/content-fields/flow-ai-agent-base.js.map +1 -1
  23. package/lib/content-fields/flow-ai-agent-router.js +2 -1
  24. package/lib/content-fields/flow-ai-agent-router.js.map +1 -1
  25. package/lib/content-fields/flow-ai-agent.js +2 -1
  26. package/lib/content-fields/flow-ai-agent.js.map +1 -1
  27. package/lib/content-fields/flow-contact-reasons-conditional.d.ts +14 -0
  28. package/lib/content-fields/flow-contact-reasons-conditional.js +68 -0
  29. package/lib/content-fields/flow-contact-reasons-conditional.js.map +1 -0
  30. package/lib/content-fields/flow-do-nothing.d.ts +9 -0
  31. package/lib/content-fields/flow-do-nothing.js +33 -0
  32. package/lib/content-fields/flow-do-nothing.js.map +1 -0
  33. package/lib/content-fields/flow-handoff.d.ts +7 -1
  34. package/lib/content-fields/flow-handoff.js +41 -3
  35. package/lib/content-fields/flow-handoff.js.map +1 -1
  36. package/lib/content-fields/flow-whatsapp-request-contact-info.d.ts +2 -1
  37. package/lib/content-fields/flow-whatsapp-request-contact-info.js +11 -0
  38. package/lib/content-fields/flow-whatsapp-request-contact-info.js.map +1 -1
  39. package/lib/content-fields/hubtype-fields/contact-reasons-conditional.d.ts +16 -0
  40. package/lib/content-fields/hubtype-fields/contact-reasons-conditional.js +4 -0
  41. package/lib/content-fields/hubtype-fields/contact-reasons-conditional.js.map +1 -0
  42. package/lib/content-fields/hubtype-fields/do-nothing.d.ts +6 -0
  43. package/lib/content-fields/hubtype-fields/do-nothing.js +4 -0
  44. package/lib/content-fields/hubtype-fields/do-nothing.js.map +1 -0
  45. package/lib/content-fields/hubtype-fields/handoff.d.ts +11 -1
  46. package/lib/content-fields/hubtype-fields/handoff.js +7 -1
  47. package/lib/content-fields/hubtype-fields/handoff.js.map +1 -1
  48. package/lib/content-fields/hubtype-fields/index.d.ts +1 -0
  49. package/lib/content-fields/hubtype-fields/index.js +2 -0
  50. package/lib/content-fields/hubtype-fields/index.js.map +1 -1
  51. package/lib/content-fields/hubtype-fields/node-types.d.ts +3 -1
  52. package/lib/content-fields/hubtype-fields/node-types.js +2 -0
  53. package/lib/content-fields/hubtype-fields/node-types.js.map +1 -1
  54. package/lib/content-fields/hubtype-fields/nodes.d.ts +3 -1
  55. package/lib/content-fields/hubtype-fields/nodes.js.map +1 -1
  56. package/lib/content-fields/hubtype-fields/whatsapp-request-contact-info.d.ts +2 -0
  57. package/lib/content-fields/hubtype-fields/whatsapp-request-contact-info.js.map +1 -1
  58. package/lib/content-fields/index.d.ts +4 -2
  59. package/lib/content-fields/index.js +6 -0
  60. package/lib/content-fields/index.js.map +1 -1
  61. package/lib/flow-factory.js +5 -1
  62. package/lib/flow-factory.js.map +1 -1
  63. package/lib/index.d.ts +5 -8
  64. package/lib/index.js +17 -55
  65. package/lib/index.js.map +1 -1
  66. package/lib/types.d.ts +2 -2
  67. package/lib/types.js.map +1 -1
  68. package/lib/utils/hubtype-assistant-content.d.ts +1 -0
  69. package/lib/utils/hubtype-assistant-content.js +13 -0
  70. package/lib/utils/hubtype-assistant-content.js.map +1 -1
  71. package/package.json +4 -4
  72. package/src/action/ai-agent-from-user-input.ts +7 -0
  73. package/src/action/first-interaction.ts +6 -0
  74. package/src/action/get-contents.ts +17 -2
  75. package/src/action/index.tsx +7 -0
  76. package/src/api.ts +52 -15
  77. package/src/bot-context-manager.ts +153 -0
  78. package/src/constants.ts +2 -1
  79. package/src/content-fields/contact-reasons-conditional-matcher.ts +24 -0
  80. package/src/content-fields/flow-ai-agent-base.tsx +12 -1
  81. package/src/content-fields/flow-ai-agent-router.tsx +1 -0
  82. package/src/content-fields/flow-ai-agent.tsx +1 -0
  83. package/src/content-fields/flow-contact-reasons-conditional.tsx +90 -0
  84. package/src/content-fields/flow-do-nothing.tsx +29 -0
  85. package/src/content-fields/flow-handoff.tsx +54 -7
  86. package/src/content-fields/flow-whatsapp-request-contact-info.tsx +19 -1
  87. package/src/content-fields/hubtype-fields/contact-reasons-conditional.ts +18 -0
  88. package/src/content-fields/hubtype-fields/do-nothing.ts +7 -0
  89. package/src/content-fields/hubtype-fields/handoff.ts +17 -1
  90. package/src/content-fields/hubtype-fields/index.ts +1 -0
  91. package/src/content-fields/hubtype-fields/node-types.ts +2 -0
  92. package/src/content-fields/hubtype-fields/nodes.ts +4 -0
  93. package/src/content-fields/hubtype-fields/whatsapp-request-contact-info.ts +2 -0
  94. package/src/content-fields/index.ts +6 -0
  95. package/src/flow-factory.ts +11 -0
  96. package/src/index.ts +26 -88
  97. package/src/types.ts +1 -2
  98. package/src/utils/hubtype-assistant-content.ts +14 -0
@@ -0,0 +1,29 @@
1
+ import type { BotContext } from '@botonic/core'
2
+
3
+ import { trackOneContent } from '../tracking'
4
+ import { ContentFieldsBase } from './content-fields-base'
5
+ import type { HtDoNothingNode } from './hubtype-fields'
6
+
7
+ export class FlowDoNothing extends ContentFieldsBase {
8
+ static fromHubtypeCMS(cmsDoNothing: HtDoNothingNode): FlowDoNothing {
9
+ const newDoNothing = new FlowDoNothing(cmsDoNothing.id)
10
+ newDoNothing.code = cmsDoNothing.code
11
+ newDoNothing.followUp = cmsDoNothing.follow_up
12
+
13
+ return newDoNothing
14
+ }
15
+
16
+ async trackFlow(botContext: BotContext): Promise<void> {
17
+ await trackOneContent(botContext, this)
18
+ }
19
+
20
+ async processContent(botContext: BotContext): Promise<void> {
21
+ await this.filterContent(botContext, this)
22
+ await this.trackFlow(botContext)
23
+ return
24
+ }
25
+
26
+ toBotonic(): JSX.Element {
27
+ return <></>
28
+ }
29
+ }
@@ -6,17 +6,27 @@ 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 {
14
+ DiscardType,
15
+ type HtHandoffNode,
16
+ type HtNodeLink,
17
+ type HtQueueLocale,
18
+ } from './hubtype-fields'
14
19
 
15
20
  export class FlowHandoff extends ContentFieldsBase {
16
21
  public queue?: HtQueueLocale
17
22
  public onFinishPayload?: string
18
23
  public handoffAutoAssign: boolean
19
24
  public hasQueuePositionChangedNotificationsEnabled: boolean
25
+ public hasDifferentiatedCloseTypes: boolean
26
+ public resolvedByAgent?: HtNodeLink
27
+ public discardedByAgent?: HtNodeLink
28
+ public discardedByUser?: HtNodeLink
29
+ public discardedBySystem?: HtNodeLink
20
30
  public isTestIntegration: boolean
21
31
 
22
32
  static fromHubtypeCMS(
@@ -30,14 +40,21 @@ export class FlowHandoff extends ContentFieldsBase {
30
40
  locale,
31
41
  cmsHandoff.content.queue
32
42
  )
43
+ newHandoff.hasDifferentiatedCloseTypes =
44
+ cmsHandoff.content.has_differentiated_close_types
45
+ newHandoff.resolvedByAgent = cmsHandoff.content.resolved_by_agent
46
+ newHandoff.discardedByAgent = cmsHandoff.content.discarded_by_agent
47
+ newHandoff.discardedByUser = cmsHandoff.content.discarded_by_user
48
+ newHandoff.discardedBySystem = cmsHandoff.content.discarded_by_system
49
+ newHandoff.handoffAutoAssign = cmsHandoff.content.has_auto_assign
50
+ newHandoff.hasQueuePositionChangedNotificationsEnabled =
51
+ cmsHandoff.content.has_queue_position_changed_notifications_enabled
52
+
33
53
  newHandoff.onFinishPayload = FlowHandoff.getOnFinishPayload(
34
54
  cmsHandoff,
35
55
  cmsApi
36
56
  )
37
- newHandoff.handoffAutoAssign = cmsHandoff.content.has_auto_assign
38
- newHandoff.hasQueuePositionChangedNotificationsEnabled =
39
- cmsHandoff.content.has_queue_position_changed_notifications_enabled
40
- newHandoff.followUp = cmsHandoff.follow_up
57
+ newHandoff.followUp = undefined
41
58
 
42
59
  return newHandoff
43
60
  }
@@ -46,13 +63,43 @@ export class FlowHandoff extends ContentFieldsBase {
46
63
  cmsHandoff: HtHandoffNode,
47
64
  cmsApi: FlowBuilderApi
48
65
  ): string | undefined {
49
- if (cmsHandoff.target?.id) {
66
+ if (cmsHandoff.content.has_differentiated_close_types) {
67
+ return `${ON_CLOSE_HANDOFF_PAYLOAD}${SEPARATOR}${cmsHandoff.id}`
68
+ } else if (cmsHandoff.target?.id) {
50
69
  return cmsApi.getPayload(cmsHandoff.target)
51
70
  }
52
71
 
53
72
  return undefined
54
73
  }
55
74
 
75
+ resolveOnClosePayload(botContext: BotContext): string {
76
+ const contactReasons =
77
+ botContext.session._hubtype_case_contact_reasons || []
78
+
79
+ if (contactReasons.some(reason => reason.name === DiscardType.ByAgent)) {
80
+ if (this.discardedByAgent?.id) {
81
+ return this.discardedByAgent.id
82
+ }
83
+ console.error('No discarded by agent target found')
84
+ }
85
+ if (contactReasons.some(reason => reason.name === DiscardType.ByUser)) {
86
+ if (this.discardedByUser?.id) {
87
+ return this.discardedByUser.id
88
+ }
89
+ console.error('No discarded by user target found')
90
+ }
91
+ if (contactReasons.some(reason => reason.name === DiscardType.BySystem)) {
92
+ if (this.discardedBySystem?.id) {
93
+ return this.discardedBySystem.id
94
+ }
95
+ console.error('No discarded by system target found')
96
+ }
97
+ if (this.resolvedByAgent?.id) {
98
+ return this.resolvedByAgent.id
99
+ }
100
+ throw new Error('No resolved by agent target found')
101
+ }
102
+
56
103
  async doHandoff(botContext: BotContext): Promise<void> {
57
104
  const handOffBuilder = new HandOffBuilder(botContext.session)
58
105
  handOffBuilder.withAutoAssignOnWaiting(this.handoffAutoAssign)
@@ -1,4 +1,8 @@
1
- import { type BotContext, isWhatsapp } from '@botonic/core'
1
+ import {
2
+ type BotContext,
3
+ isWhatsapp,
4
+ type RequestContactInfoMessage,
5
+ } from '@botonic/core'
2
6
  import { Text, WhatsappRequestContactInfo } from '@botonic/react'
3
7
 
4
8
  import { trackOneContent } from '../tracking'
@@ -26,6 +30,20 @@ export class FlowWhatsappRequestContactInfoNode extends ContentFieldsBase {
26
30
  return requestContactInfo
27
31
  }
28
32
 
33
+ static fromAIAgent(
34
+ id: string,
35
+ message: RequestContactInfoMessage,
36
+ botContext: BotContext
37
+ ): JSX.Element {
38
+ botContext.session.whatsapp_request_contact = undefined
39
+
40
+ if (!isWhatsapp(botContext.session)) {
41
+ return <Text key={id}>{message.content.text}</Text>
42
+ }
43
+
44
+ return <WhatsappRequestContactInfo key={id} body={message.content.text} />
45
+ }
46
+
29
47
  async trackFlow(botContext: BotContext): Promise<void> {
30
48
  await trackOneContent(botContext, this)
31
49
  }
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ import type { HtBaseNode } from './common'
2
+ import type { HtNodeWithContentType } from './node-types'
3
+
4
+ export interface HtDoNothingNode extends HtBaseNode {
5
+ id: string
6
+ type: HtNodeWithContentType.DO_NOTHING
7
+ }
@@ -1,6 +1,17 @@
1
- import type { HtBaseNode, HtPayloadLocale, HtQueueLocale } from './common'
1
+ import type {
2
+ HtBaseNode,
3
+ HtNodeLink,
4
+ HtPayloadLocale,
5
+ HtQueueLocale,
6
+ } from './common'
2
7
  import type { HtNodeWithContentType } from './node-types'
3
8
 
9
+ export enum DiscardType {
10
+ ByAgent = 'discarded',
11
+ ByUser = 'discarded_by_user',
12
+ BySystem = 'discarded_by_system',
13
+ }
14
+
4
15
  export interface HtHandoffNode extends HtBaseNode {
5
16
  type: HtNodeWithContentType.HANDOFF
6
17
  content: {
@@ -8,5 +19,10 @@ export interface HtHandoffNode extends HtBaseNode {
8
19
  payload: HtPayloadLocale[]
9
20
  has_auto_assign: boolean
10
21
  has_queue_position_changed_notifications_enabled: boolean
22
+ has_differentiated_close_types: boolean
23
+ resolved_by_agent?: HtNodeLink
24
+ discarded_by_agent?: HtNodeLink
25
+ discarded_by_user?: HtNodeLink
26
+ discarded_by_system?: HtNodeLink
11
27
  }
12
28
  }
@@ -6,6 +6,7 @@ export * from './capture-user-input'
6
6
  export * from './carousel'
7
7
  export * from './common'
8
8
  export * from './custom-conditional-v2'
9
+ export * from './do-nothing'
9
10
  export * from './fallback'
10
11
  export * from './function'
11
12
  export * from './go-to-flow'
@@ -20,6 +20,8 @@ export enum HtNodeWithContentType {
20
20
  GO_TO_FLOW = 'go-to-flow',
21
21
  CAPTURE_USER_INPUT = 'capture-user-input',
22
22
  CUSTOM_CONDITION = 'custom-condition',
23
+ DO_NOTHING = 'do-nothing',
24
+ CONTACT_REASONS_CONDITION = 'contact-reasons-condition',
23
25
  }
24
26
 
25
27
  export enum HtNodeWithoutContentType {
@@ -3,7 +3,9 @@ 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'
8
+ import type { HtDoNothingNode } from './do-nothing'
7
9
  // import { HtChannelConditionalNode } from './channel-conditional'
8
10
  // import { HtCountryConditionalNode } from './country-conditional'
9
11
  // import { HtCustomConditionalNode } from './custom-conditional'
@@ -48,6 +50,8 @@ export type HtNodeWithContent =
48
50
  | HtWhatsappTemplateNode
49
51
  | HtCaptureUserInputNode
50
52
  | HtCustomConditionalV2Node
53
+ | HtDoNothingNode
54
+ | HtContactReasonsNode
51
55
  // | HtChannelConditionalNode
52
56
  // | HtCountryConditionalNode
53
57
  // | HtCustomConditionalNode
@@ -1,3 +1,4 @@
1
+ import type { HtButton } from './button'
1
2
  import type { HtBaseNode, HtTextLocale } from './common'
2
3
  import type { HtNodeWithContentType } from './node-types'
3
4
 
@@ -5,5 +6,6 @@ export interface HtWhatsappRequestContactInfoNode extends HtBaseNode {
5
6
  type: HtNodeWithContentType.WHATSAPP_REQUEST_CONTACT_INFO
6
7
  content: {
7
8
  text: HtTextLocale[]
9
+ button: HtButton
8
10
  }
9
11
  }
@@ -5,8 +5,10 @@ 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'
11
+ import { FlowDoNothing } from './flow-do-nothing'
10
12
  import { FlowGoToFlow } from './flow-go-to-flow'
11
13
  import { FlowHandoff } from './flow-handoff'
12
14
  import { FlowImage } from './flow-image'
@@ -33,9 +35,11 @@ export {
33
35
  FlowCaptureUserInput,
34
36
  FlowCarousel,
35
37
  FlowChannelConditional,
38
+ FlowContactReasonsConditional,
36
39
  FlowCountryConditional,
37
40
  FlowCustomConditional,
38
41
  FlowCustomConditionalV2,
42
+ FlowDoNothing,
39
43
  FlowGoToFlow,
40
44
  FlowHandoff,
41
45
  FlowImage,
@@ -72,3 +76,5 @@ export type FlowContent =
72
76
  | FlowCaptureUserInput
73
77
  | FlowAiAgentRouter
74
78
  | FlowCustomConditionalV2
79
+ | FlowDoNothing
80
+ | FlowContactReasonsConditional
@@ -7,10 +7,12 @@ import {
7
7
  FlowBotAction,
8
8
  FlowCarousel,
9
9
  FlowChannelConditional,
10
+ FlowContactReasonsConditional,
10
11
  type FlowContent,
11
12
  FlowCountryConditional,
12
13
  FlowCustomConditional,
13
14
  FlowCustomConditionalV2,
15
+ FlowDoNothing,
14
16
  FlowGoToFlow,
15
17
  FlowHandoff,
16
18
  FlowImage,
@@ -118,6 +120,15 @@ export class FlowFactory {
118
120
  this.botContext
119
121
  )
120
122
 
123
+ case HtNodeWithContentType.DO_NOTHING:
124
+ return FlowDoNothing.fromHubtypeCMS(hubtypeContent)
125
+
126
+ case HtNodeWithContentType.CONTACT_REASONS_CONDITION:
127
+ return FlowContactReasonsConditional.fromHubtypeCMS(
128
+ hubtypeContent,
129
+ this.botContext
130
+ )
131
+
121
132
  default:
122
133
  return undefined
123
134
  }
package/src/index.ts CHANGED
@@ -1,24 +1,16 @@
1
- import {
2
- INPUT,
3
- type Plugin,
4
- type PluginPreRequest,
5
- PROVIDER,
6
- type ResolvedPlugins,
7
- type Session,
1
+ import type {
2
+ BotContext,
3
+ Plugin,
4
+ ResolvedPlugins,
5
+ Session,
8
6
  } from '@botonic/core'
9
- import type { ActionRequest } from '@botonic/react'
10
7
  import { v7 as uuidv7 } from 'uuid'
11
8
 
12
9
  import { FlowBuilderApi } from './api'
13
- import {
14
- EMPTY_PAYLOAD,
15
- FLOW_BUILDER_API_URL_PROD,
16
- SEPARATOR,
17
- SOURCE_INFO_SEPARATOR,
18
- } from './constants'
10
+ import { BotContextManager } from './bot-context-manager'
11
+ import { FLOW_BUILDER_API_URL_PROD, SEPARATOR } from './constants'
19
12
  import type { FlowContent } from './content-fields'
20
13
  import {
21
- type HtBotActionNode,
22
14
  type HtFlowBuilderData,
23
15
  type HtNodeWithContent,
24
16
  HtNodeWithContentType,
@@ -35,10 +27,8 @@ import {
35
27
  type RatingSubmittedInfo,
36
28
  type TrackEventFunction,
37
29
  } from './types'
38
- import { getNextPayloadByUserInput } from './user-input'
39
30
  import type { SmartIntentsInferenceConfig } from './user-input/smart-intent'
40
31
  import { resolveGetAccessToken } from './utils/authentication'
41
- import { inputHasTextOrTranscript } from './utils/input'
42
32
 
43
33
  // TODO: Create a proper service to wrap all calls and allow api versioning
44
34
 
@@ -46,7 +36,7 @@ export default class BotonicPluginFlowBuilder implements Plugin {
46
36
  public cmsApi: FlowBuilderApi
47
37
  private flow?: HtFlowBuilderData
48
38
  private functions: Record<any, any>
49
- private currentRequest: PluginPreRequest
39
+ private botContext: BotContext
50
40
  public getAccessToken: (session: Session) => string
51
41
  public trackEvent?: TrackEventFunction
52
42
  public getAiAgentResponse?: AiAgentFunction
@@ -85,85 +75,33 @@ export default class BotonicPluginFlowBuilder implements Plugin {
85
75
  options.disableAIAgentInFirstInteraction || false
86
76
  }
87
77
 
88
- resolveFlowUrl(request: PluginPreRequest): string {
89
- if (request.session.is_test_integration) {
78
+ resolveFlowUrl(botContext: BotContext): string {
79
+ if (botContext.session.is_test_integration) {
90
80
  return `${this.apiUrl}/v1/bot_flows/{bot_id}/versions/${FlowBuilderJSONVersion.DRAFT}/`
91
81
  }
92
82
  return `${this.apiUrl}/v1/bot_flows/{bot_id}/versions/${this.jsonVersion}/`
93
83
  }
94
84
 
95
- async pre(request: PluginPreRequest): Promise<void> {
96
- // When AI Agent is executed in Whatsapp, button payloads come as referral and must be converted to text being processed by the agent.
97
- this.convertWhatsappAiAgentEmptyPayloads(request)
98
-
99
- this.currentRequest = request
85
+ async pre(botContext: BotContext): Promise<void> {
86
+ this.botContext = botContext
100
87
  this.cmsApi = await FlowBuilderApi.create({
101
- flowUrl: this.resolveFlowUrl(request),
88
+ flowUrl: this.resolveFlowUrl(botContext),
102
89
  url: this.apiUrl,
103
90
  flow: this.flow,
104
- accessToken: this.getAccessToken(request.session),
105
- request: this.currentRequest,
91
+ accessToken: this.getAccessToken(botContext.session),
92
+ botContext: this.botContext,
106
93
  })
107
94
 
108
- const checkUserTextInput =
109
- inputHasTextOrTranscript(request.input) && !request.input.payload
110
-
111
- if (checkUserTextInput) {
112
- const resolvedLocale = this.cmsApi.getResolvedLocale()
113
- const nextPayload = await getNextPayloadByUserInput(
114
- this.cmsApi,
115
- resolvedLocale,
116
- request as unknown as ActionRequest,
117
- this.smartIntentsConfig
118
- )
119
- request.input.payload = nextPayload
120
- }
121
-
122
- await this.updateRequestBeforeRoutes(request)
123
- }
124
-
125
- private convertWhatsappAiAgentEmptyPayloads(request: PluginPreRequest): void {
126
- if (request.session.user.provider === PROVIDER.WHATSAPP) {
127
- const shouldUseReferral =
128
- request.input.referral &&
129
- request.input.payload?.startsWith(EMPTY_PAYLOAD)
130
-
131
- if (shouldUseReferral) {
132
- request.input.type = INPUT.TEXT
133
- request.input.data = request.input.referral
134
- }
135
- }
136
- }
137
-
138
- private async updateRequestBeforeRoutes(
139
- request: PluginPreRequest
140
- ): Promise<void> {
141
- this.cmsApi.removeCaptureUserInputId()
142
- if (request.input.payload) {
143
- request.input.payload = this.removeSourceSuffix(request.input.payload)
144
-
145
- if (this.cmsApi.isBotAction(request.input.payload)) {
146
- const cmsBotAction = this.cmsApi.getNodeById<HtBotActionNode>(
147
- request.input.payload
148
- )
149
-
150
- request.input.payload =
151
- this.cmsApi.createPayloadWithParams(cmsBotAction)
152
-
153
- // Re-execute convertWhatsappAiAgentEmptyPayloads function to handle
154
- // the case that a BotAction has a payload equals to EMPTY_PAYLOAD
155
- this.convertWhatsappAiAgentEmptyPayloads(request)
156
- }
157
- }
158
- }
159
-
160
- private removeSourceSuffix(payload: string): string {
161
- return payload.split(SOURCE_INFO_SEPARATOR)[0]
95
+ const botContextManager = new BotContextManager(
96
+ this.cmsApi,
97
+ this.smartIntentsConfig
98
+ )
99
+ await botContextManager.prepareInputForRoutes(botContext)
162
100
  }
163
101
 
164
- post(request: PluginPreRequest): void {
165
- request.input.nluResolution = undefined
166
- delete request.session.user.system_locale_updated
102
+ post(botContext: BotContext): void {
103
+ botContext.input.nluResolution = undefined
104
+ delete botContext.session.user.system_locale_updated
167
105
  }
168
106
 
169
107
  async getContentsByContentID(
@@ -189,7 +127,7 @@ export default class BotonicPluginFlowBuilder implements Plugin {
189
127
 
190
128
  async getStartContents(): Promise<FlowContent[]> {
191
129
  const startNode = this.cmsApi.getStartNode()
192
- this.currentRequest.session.flow_thread_id = uuidv7()
130
+ this.botContext.session.flow_thread_id = uuidv7()
193
131
  return await this.getContentsByNode(startNode)
194
132
  }
195
133
 
@@ -206,7 +144,7 @@ export default class BotonicPluginFlowBuilder implements Plugin {
206
144
  ) {
207
145
  const customFunctionResolver = new CustomFunction(
208
146
  this.functions,
209
- this.currentRequest,
147
+ this.botContext,
210
148
  resolvedLocale
211
149
  )
212
150
  const targetId = await customFunctionResolver.call(node)
@@ -214,7 +152,7 @@ export default class BotonicPluginFlowBuilder implements Plugin {
214
152
  }
215
153
 
216
154
  const flowFactory = new FlowFactory(
217
- this.currentRequest,
155
+ this.botContext,
218
156
  this.cmsApi,
219
157
  resolvedLocale
220
158
  )
package/src/types.ts CHANGED
@@ -3,7 +3,6 @@ import type {
3
3
  BotContext,
4
4
  GuardrailRule,
5
5
  InferenceResponse,
6
- PluginPreRequest,
7
6
  ResolvedPlugins,
8
7
  } from '@botonic/core'
9
8
  import type { FlowContent } from './content-fields'
@@ -67,7 +66,7 @@ export interface FlowBuilderApiOptions {
67
66
  flowUrl: string
68
67
  flow?: HtFlowBuilderData
69
68
  accessToken: string
70
- request: PluginPreRequest
69
+ botContext: BotContext
71
70
  }
72
71
 
73
72
  export enum ProcessEnvNodeEnvs {
@@ -6,6 +6,7 @@ import { FlowImage } from '../content-fields/flow-image'
6
6
  import { FlowText } from '../content-fields/flow-text'
7
7
  import { FlowVideo } from '../content-fields/flow-video'
8
8
  import { FlowWhatsappCtaUrlButtonNode } from '../content-fields/flow-whatsapp-cta-url-button'
9
+ import { FlowWhatsappRequestContactInfoNode } from '../content-fields/flow-whatsapp-request-contact-info'
9
10
  import { FlowWhatsappTemplate } from '../content-fields/flow-whatsapp-template'
10
11
  import { HtButtonStyle } from '../content-fields/hubtype-fields'
11
12
  import { FlowWhatsappButtonList } from '../content-fields/whatsapp-button-list/flow-whatsapp-button-list'
@@ -34,6 +35,9 @@ export class HubtypeAssistantContent {
34
35
  if (content instanceof FlowWhatsappTemplate) {
35
36
  return HubtypeAssistantContent.formatWhatsappTemplateContent(content)
36
37
  }
38
+ if (content instanceof FlowWhatsappRequestContactInfoNode) {
39
+ return HubtypeAssistantContent.formatRequestContactInfoContent(content)
40
+ }
37
41
  return ''
38
42
  }
39
43
 
@@ -205,4 +209,14 @@ export class HubtypeAssistantContent {
205
209
  const { name, language } = template.htWhatsappTemplate
206
210
  return `WhatsApp Template: ${name} (${language})`
207
211
  }
212
+
213
+ private static formatRequestContactInfoContent(
214
+ requestContactInfo: FlowWhatsappRequestContactInfoNode
215
+ ): string {
216
+ const body = requestContactInfo.text?.trim() ?? ''
217
+ if (!body) {
218
+ return '[WhatsApp Request Contact Info]'
219
+ }
220
+ return requestContactInfo.text
221
+ }
208
222
  }