@botonic/plugin-flow-builder 0.58.0-alpha.1 → 0.58.0-alpha.3

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 (270) hide show
  1. package/lib/cjs/action/contents-result.d.ts +4 -0
  2. package/lib/cjs/action/contents-result.js +3 -0
  3. package/lib/cjs/action/contents-result.js.map +1 -0
  4. package/lib/cjs/action/first-interaction.js +1 -1
  5. package/lib/cjs/action/first-interaction.js.map +1 -1
  6. package/lib/cjs/action/get-contents.d.ts +3 -2
  7. package/lib/cjs/action/get-contents.js +12 -6
  8. package/lib/cjs/action/get-contents.js.map +1 -1
  9. package/lib/cjs/action/index.d.ts +1 -0
  10. package/lib/cjs/action/index.js +20 -6
  11. package/lib/cjs/action/index.js.map +1 -1
  12. package/lib/cjs/action/payload.d.ts +2 -2
  13. package/lib/cjs/action/payload.js +12 -28
  14. package/lib/cjs/action/payload.js.map +1 -1
  15. package/lib/cjs/action/push-flow-resolver.d.ts +19 -0
  16. package/lib/cjs/action/push-flow-resolver.js +51 -0
  17. package/lib/cjs/action/push-flow-resolver.js.map +1 -0
  18. package/lib/cjs/api.d.ts +1 -2
  19. package/lib/cjs/api.js +7 -5
  20. package/lib/cjs/api.js.map +1 -1
  21. package/lib/cjs/bot-context-manager.d.ts +3 -1
  22. package/lib/cjs/bot-context-manager.js +4 -2
  23. package/lib/cjs/bot-context-manager.js.map +1 -1
  24. package/lib/cjs/campaign-execution.d.ts +12 -0
  25. package/lib/cjs/campaign-execution.js +37 -0
  26. package/lib/cjs/campaign-execution.js.map +1 -0
  27. package/lib/cjs/content-fields/content-fields-base.d.ts +1 -0
  28. package/lib/cjs/content-fields/content-fields-base.js.map +1 -1
  29. package/lib/cjs/content-fields/custom-conditional/flow-custom-condition-v2.d.ts +1 -0
  30. package/lib/cjs/content-fields/custom-conditional/flow-custom-condition-v2.js +3 -0
  31. package/lib/cjs/content-fields/custom-conditional/flow-custom-condition-v2.js.map +1 -1
  32. package/lib/cjs/content-fields/flow-ai-agent-base.d.ts +1 -0
  33. package/lib/cjs/content-fields/flow-ai-agent-base.js +3 -0
  34. package/lib/cjs/content-fields/flow-ai-agent-base.js.map +1 -1
  35. package/lib/cjs/content-fields/flow-bot-action.d.ts +1 -0
  36. package/lib/cjs/content-fields/flow-bot-action.js +3 -0
  37. package/lib/cjs/content-fields/flow-bot-action.js.map +1 -1
  38. package/lib/cjs/content-fields/flow-button.d.ts +1 -0
  39. package/lib/cjs/content-fields/flow-button.js +3 -0
  40. package/lib/cjs/content-fields/flow-button.js.map +1 -1
  41. package/lib/cjs/content-fields/flow-capture-user-input.d.ts +1 -0
  42. package/lib/cjs/content-fields/flow-capture-user-input.js +3 -0
  43. package/lib/cjs/content-fields/flow-capture-user-input.js.map +1 -1
  44. package/lib/cjs/content-fields/flow-carousel.d.ts +1 -0
  45. package/lib/cjs/content-fields/flow-carousel.js +3 -0
  46. package/lib/cjs/content-fields/flow-carousel.js.map +1 -1
  47. package/lib/cjs/content-fields/flow-channel-conditional.d.ts +1 -0
  48. package/lib/cjs/content-fields/flow-channel-conditional.js +3 -0
  49. package/lib/cjs/content-fields/flow-channel-conditional.js.map +1 -1
  50. package/lib/cjs/content-fields/flow-contact-reasons-conditional.d.ts +1 -0
  51. package/lib/cjs/content-fields/flow-contact-reasons-conditional.js +3 -0
  52. package/lib/cjs/content-fields/flow-contact-reasons-conditional.js.map +1 -1
  53. package/lib/cjs/content-fields/flow-country-conditional.d.ts +1 -0
  54. package/lib/cjs/content-fields/flow-country-conditional.js +3 -0
  55. package/lib/cjs/content-fields/flow-country-conditional.js.map +1 -1
  56. package/lib/cjs/content-fields/flow-custom-conditional.d.ts +1 -0
  57. package/lib/cjs/content-fields/flow-custom-conditional.js +3 -0
  58. package/lib/cjs/content-fields/flow-custom-conditional.js.map +1 -1
  59. package/lib/cjs/content-fields/flow-do-nothing.d.ts +2 -0
  60. package/lib/cjs/content-fields/flow-do-nothing.js +20 -1
  61. package/lib/cjs/content-fields/flow-do-nothing.js.map +1 -1
  62. package/lib/cjs/content-fields/flow-element.d.ts +1 -0
  63. package/lib/cjs/content-fields/flow-element.js +3 -0
  64. package/lib/cjs/content-fields/flow-element.js.map +1 -1
  65. package/lib/cjs/content-fields/flow-go-to-flow.d.ts +1 -0
  66. package/lib/cjs/content-fields/flow-go-to-flow.js +3 -0
  67. package/lib/cjs/content-fields/flow-go-to-flow.js.map +1 -1
  68. package/lib/cjs/content-fields/flow-handoff.d.ts +1 -0
  69. package/lib/cjs/content-fields/flow-handoff.js +3 -0
  70. package/lib/cjs/content-fields/flow-handoff.js.map +1 -1
  71. package/lib/cjs/content-fields/flow-image.d.ts +1 -0
  72. package/lib/cjs/content-fields/flow-image.js +3 -0
  73. package/lib/cjs/content-fields/flow-image.js.map +1 -1
  74. package/lib/cjs/content-fields/flow-queue-status-conditional.d.ts +1 -0
  75. package/lib/cjs/content-fields/flow-queue-status-conditional.js +3 -0
  76. package/lib/cjs/content-fields/flow-queue-status-conditional.js.map +1 -1
  77. package/lib/cjs/content-fields/flow-rating.d.ts +1 -0
  78. package/lib/cjs/content-fields/flow-rating.js +3 -0
  79. package/lib/cjs/content-fields/flow-rating.js.map +1 -1
  80. package/lib/cjs/content-fields/flow-text.d.ts +1 -0
  81. package/lib/cjs/content-fields/flow-text.js +3 -0
  82. package/lib/cjs/content-fields/flow-text.js.map +1 -1
  83. package/lib/cjs/content-fields/flow-update-variables.d.ts +1 -0
  84. package/lib/cjs/content-fields/flow-update-variables.js +8 -9
  85. package/lib/cjs/content-fields/flow-update-variables.js.map +1 -1
  86. package/lib/cjs/content-fields/flow-video.d.ts +1 -0
  87. package/lib/cjs/content-fields/flow-video.js +3 -0
  88. package/lib/cjs/content-fields/flow-video.js.map +1 -1
  89. package/lib/cjs/content-fields/flow-webview.d.ts +1 -0
  90. package/lib/cjs/content-fields/flow-webview.js +3 -0
  91. package/lib/cjs/content-fields/flow-webview.js.map +1 -1
  92. package/lib/cjs/content-fields/flow-whatsapp-cta-url-button.d.ts +1 -0
  93. package/lib/cjs/content-fields/flow-whatsapp-cta-url-button.js +3 -0
  94. package/lib/cjs/content-fields/flow-whatsapp-cta-url-button.js.map +1 -1
  95. package/lib/cjs/content-fields/flow-whatsapp-request-contact-info.d.ts +1 -0
  96. package/lib/cjs/content-fields/flow-whatsapp-request-contact-info.js +3 -0
  97. package/lib/cjs/content-fields/flow-whatsapp-request-contact-info.js.map +1 -1
  98. package/lib/cjs/content-fields/flow-whatsapp-template.d.ts +1 -0
  99. package/lib/cjs/content-fields/flow-whatsapp-template.js +3 -0
  100. package/lib/cjs/content-fields/flow-whatsapp-template.js.map +1 -1
  101. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.d.ts +1 -0
  102. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.js +3 -0
  103. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.js.map +1 -1
  104. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.d.ts +1 -0
  105. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.js +3 -0
  106. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.js.map +1 -1
  107. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list.d.ts +1 -0
  108. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list.js +3 -0
  109. package/lib/cjs/content-fields/whatsapp-button-list/flow-whatsapp-button-list.js.map +1 -1
  110. package/lib/cjs/index.d.ts +2 -0
  111. package/lib/cjs/index.js +3 -1
  112. package/lib/cjs/index.js.map +1 -1
  113. package/lib/cjs/services/campaigns-v2-api.d.ts +14 -0
  114. package/lib/cjs/services/campaigns-v2-api.js +30 -0
  115. package/lib/cjs/services/campaigns-v2-api.js.map +1 -0
  116. package/lib/esm/action/contents-result.d.ts +4 -0
  117. package/lib/esm/action/contents-result.js +3 -0
  118. package/lib/esm/action/contents-result.js.map +1 -0
  119. package/lib/esm/action/first-interaction.js +1 -1
  120. package/lib/esm/action/first-interaction.js.map +1 -1
  121. package/lib/esm/action/get-contents.d.ts +3 -2
  122. package/lib/esm/action/get-contents.js +12 -6
  123. package/lib/esm/action/get-contents.js.map +1 -1
  124. package/lib/esm/action/index.d.ts +1 -0
  125. package/lib/esm/action/index.js +20 -6
  126. package/lib/esm/action/index.js.map +1 -1
  127. package/lib/esm/action/payload.d.ts +2 -2
  128. package/lib/esm/action/payload.js +12 -28
  129. package/lib/esm/action/payload.js.map +1 -1
  130. package/lib/esm/action/push-flow-resolver.d.ts +19 -0
  131. package/lib/esm/action/push-flow-resolver.js +51 -0
  132. package/lib/esm/action/push-flow-resolver.js.map +1 -0
  133. package/lib/esm/api.d.ts +1 -2
  134. package/lib/esm/api.js +7 -5
  135. package/lib/esm/api.js.map +1 -1
  136. package/lib/esm/bot-context-manager.d.ts +3 -1
  137. package/lib/esm/bot-context-manager.js +4 -2
  138. package/lib/esm/bot-context-manager.js.map +1 -1
  139. package/lib/esm/campaign-execution.d.ts +12 -0
  140. package/lib/esm/campaign-execution.js +37 -0
  141. package/lib/esm/campaign-execution.js.map +1 -0
  142. package/lib/esm/content-fields/content-fields-base.d.ts +1 -0
  143. package/lib/esm/content-fields/content-fields-base.js.map +1 -1
  144. package/lib/esm/content-fields/custom-conditional/flow-custom-condition-v2.d.ts +1 -0
  145. package/lib/esm/content-fields/custom-conditional/flow-custom-condition-v2.js +3 -0
  146. package/lib/esm/content-fields/custom-conditional/flow-custom-condition-v2.js.map +1 -1
  147. package/lib/esm/content-fields/flow-ai-agent-base.d.ts +1 -0
  148. package/lib/esm/content-fields/flow-ai-agent-base.js +3 -0
  149. package/lib/esm/content-fields/flow-ai-agent-base.js.map +1 -1
  150. package/lib/esm/content-fields/flow-bot-action.d.ts +1 -0
  151. package/lib/esm/content-fields/flow-bot-action.js +3 -0
  152. package/lib/esm/content-fields/flow-bot-action.js.map +1 -1
  153. package/lib/esm/content-fields/flow-button.d.ts +1 -0
  154. package/lib/esm/content-fields/flow-button.js +3 -0
  155. package/lib/esm/content-fields/flow-button.js.map +1 -1
  156. package/lib/esm/content-fields/flow-capture-user-input.d.ts +1 -0
  157. package/lib/esm/content-fields/flow-capture-user-input.js +3 -0
  158. package/lib/esm/content-fields/flow-capture-user-input.js.map +1 -1
  159. package/lib/esm/content-fields/flow-carousel.d.ts +1 -0
  160. package/lib/esm/content-fields/flow-carousel.js +3 -0
  161. package/lib/esm/content-fields/flow-carousel.js.map +1 -1
  162. package/lib/esm/content-fields/flow-channel-conditional.d.ts +1 -0
  163. package/lib/esm/content-fields/flow-channel-conditional.js +3 -0
  164. package/lib/esm/content-fields/flow-channel-conditional.js.map +1 -1
  165. package/lib/esm/content-fields/flow-contact-reasons-conditional.d.ts +1 -0
  166. package/lib/esm/content-fields/flow-contact-reasons-conditional.js +3 -0
  167. package/lib/esm/content-fields/flow-contact-reasons-conditional.js.map +1 -1
  168. package/lib/esm/content-fields/flow-country-conditional.d.ts +1 -0
  169. package/lib/esm/content-fields/flow-country-conditional.js +3 -0
  170. package/lib/esm/content-fields/flow-country-conditional.js.map +1 -1
  171. package/lib/esm/content-fields/flow-custom-conditional.d.ts +1 -0
  172. package/lib/esm/content-fields/flow-custom-conditional.js +3 -0
  173. package/lib/esm/content-fields/flow-custom-conditional.js.map +1 -1
  174. package/lib/esm/content-fields/flow-do-nothing.d.ts +2 -0
  175. package/lib/esm/content-fields/flow-do-nothing.js +20 -1
  176. package/lib/esm/content-fields/flow-do-nothing.js.map +1 -1
  177. package/lib/esm/content-fields/flow-element.d.ts +1 -0
  178. package/lib/esm/content-fields/flow-element.js +3 -0
  179. package/lib/esm/content-fields/flow-element.js.map +1 -1
  180. package/lib/esm/content-fields/flow-go-to-flow.d.ts +1 -0
  181. package/lib/esm/content-fields/flow-go-to-flow.js +3 -0
  182. package/lib/esm/content-fields/flow-go-to-flow.js.map +1 -1
  183. package/lib/esm/content-fields/flow-handoff.d.ts +1 -0
  184. package/lib/esm/content-fields/flow-handoff.js +3 -0
  185. package/lib/esm/content-fields/flow-handoff.js.map +1 -1
  186. package/lib/esm/content-fields/flow-image.d.ts +1 -0
  187. package/lib/esm/content-fields/flow-image.js +3 -0
  188. package/lib/esm/content-fields/flow-image.js.map +1 -1
  189. package/lib/esm/content-fields/flow-queue-status-conditional.d.ts +1 -0
  190. package/lib/esm/content-fields/flow-queue-status-conditional.js +3 -0
  191. package/lib/esm/content-fields/flow-queue-status-conditional.js.map +1 -1
  192. package/lib/esm/content-fields/flow-rating.d.ts +1 -0
  193. package/lib/esm/content-fields/flow-rating.js +3 -0
  194. package/lib/esm/content-fields/flow-rating.js.map +1 -1
  195. package/lib/esm/content-fields/flow-text.d.ts +1 -0
  196. package/lib/esm/content-fields/flow-text.js +3 -0
  197. package/lib/esm/content-fields/flow-text.js.map +1 -1
  198. package/lib/esm/content-fields/flow-update-variables.d.ts +1 -0
  199. package/lib/esm/content-fields/flow-update-variables.js +8 -9
  200. package/lib/esm/content-fields/flow-update-variables.js.map +1 -1
  201. package/lib/esm/content-fields/flow-video.d.ts +1 -0
  202. package/lib/esm/content-fields/flow-video.js +3 -0
  203. package/lib/esm/content-fields/flow-video.js.map +1 -1
  204. package/lib/esm/content-fields/flow-webview.d.ts +1 -0
  205. package/lib/esm/content-fields/flow-webview.js +3 -0
  206. package/lib/esm/content-fields/flow-webview.js.map +1 -1
  207. package/lib/esm/content-fields/flow-whatsapp-cta-url-button.d.ts +1 -0
  208. package/lib/esm/content-fields/flow-whatsapp-cta-url-button.js +3 -0
  209. package/lib/esm/content-fields/flow-whatsapp-cta-url-button.js.map +1 -1
  210. package/lib/esm/content-fields/flow-whatsapp-request-contact-info.d.ts +1 -0
  211. package/lib/esm/content-fields/flow-whatsapp-request-contact-info.js +3 -0
  212. package/lib/esm/content-fields/flow-whatsapp-request-contact-info.js.map +1 -1
  213. package/lib/esm/content-fields/flow-whatsapp-template.d.ts +1 -0
  214. package/lib/esm/content-fields/flow-whatsapp-template.js +3 -0
  215. package/lib/esm/content-fields/flow-whatsapp-template.js.map +1 -1
  216. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.d.ts +1 -0
  217. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.js +3 -0
  218. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.js.map +1 -1
  219. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.d.ts +1 -0
  220. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.js +3 -0
  221. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.js.map +1 -1
  222. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list.d.ts +1 -0
  223. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list.js +3 -0
  224. package/lib/esm/content-fields/whatsapp-button-list/flow-whatsapp-button-list.js.map +1 -1
  225. package/lib/esm/index.d.ts +2 -0
  226. package/lib/esm/index.js +3 -1
  227. package/lib/esm/index.js.map +1 -1
  228. package/lib/esm/services/campaigns-v2-api.d.ts +14 -0
  229. package/lib/esm/services/campaigns-v2-api.js +30 -0
  230. package/lib/esm/services/campaigns-v2-api.js.map +1 -0
  231. package/package.json +2 -2
  232. package/src/action/contents-result.ts +5 -0
  233. package/src/action/first-interaction.ts +7 -6
  234. package/src/action/get-contents.ts +16 -8
  235. package/src/action/index.tsx +36 -6
  236. package/src/action/payload.ts +16 -46
  237. package/src/action/push-flow-resolver.ts +89 -0
  238. package/src/api.ts +14 -13
  239. package/src/bot-context-manager.ts +4 -1
  240. package/src/campaign-execution.ts +51 -0
  241. package/src/content-fields/content-fields-base.ts +2 -0
  242. package/src/content-fields/custom-conditional/flow-custom-condition-v2.tsx +4 -0
  243. package/src/content-fields/flow-ai-agent-base.tsx +4 -0
  244. package/src/content-fields/flow-bot-action.tsx +4 -0
  245. package/src/content-fields/flow-button.tsx +4 -0
  246. package/src/content-fields/flow-capture-user-input.tsx +4 -0
  247. package/src/content-fields/flow-carousel.tsx +4 -0
  248. package/src/content-fields/flow-channel-conditional.tsx +4 -0
  249. package/src/content-fields/flow-contact-reasons-conditional.tsx +4 -0
  250. package/src/content-fields/flow-country-conditional.tsx +4 -0
  251. package/src/content-fields/flow-custom-conditional.tsx +4 -0
  252. package/src/content-fields/flow-do-nothing.tsx +25 -1
  253. package/src/content-fields/flow-element.tsx +4 -0
  254. package/src/content-fields/flow-go-to-flow.tsx +4 -0
  255. package/src/content-fields/flow-handoff.tsx +4 -0
  256. package/src/content-fields/flow-image.tsx +4 -0
  257. package/src/content-fields/flow-queue-status-conditional.tsx +4 -0
  258. package/src/content-fields/flow-rating.tsx +4 -0
  259. package/src/content-fields/flow-text.tsx +4 -0
  260. package/src/content-fields/flow-update-variables.tsx +14 -8
  261. package/src/content-fields/flow-video.tsx +4 -0
  262. package/src/content-fields/flow-webview.tsx +4 -0
  263. package/src/content-fields/flow-whatsapp-cta-url-button.tsx +4 -0
  264. package/src/content-fields/flow-whatsapp-request-contact-info.tsx +4 -0
  265. package/src/content-fields/flow-whatsapp-template.tsx +4 -0
  266. package/src/content-fields/whatsapp-button-list/flow-whatsapp-button-list-row.tsx +4 -0
  267. package/src/content-fields/whatsapp-button-list/flow-whatsapp-button-list-section.tsx +4 -0
  268. package/src/content-fields/whatsapp-button-list/flow-whatsapp-button-list.tsx +4 -0
  269. package/src/index.ts +4 -0
  270. package/src/services/campaigns-v2-api.ts +44 -0
@@ -1,5 +1,6 @@
1
1
  import { type BotContext, isDev, isWebchat } from '@botonic/core'
2
2
  import {
3
+ CampaignMessageAttributesProvider,
3
4
  Multichannel,
4
5
  RequestContext,
5
6
  WebchatSettings,
@@ -46,31 +47,38 @@ export class FlowBuilderAction extends React.Component<FlowBuilderActionProps> {
46
47
  botContext: BotContext,
47
48
  contentID?: string
48
49
  ): Promise<FlowBuilderActionProps> {
49
- const contents = await getContents(botContext, contentID)
50
+ const { contents } = await getContents(botContext, contentID)
50
51
 
51
52
  const contentsToRender = await FlowBuilderAction.prepareContentsToRender(
52
53
  botContext,
53
54
  contents
54
55
  )
55
56
 
56
- return { contents: contentsToRender }
57
+ return {
58
+ contents: contentsToRender,
59
+ }
57
60
  }
58
61
 
59
62
  static async prepareContentsToRender(
60
63
  botContext: BotContext,
61
64
  contents: FlowContent[]
62
65
  ) {
66
+ const flowBuilderPlugin = getFlowBuilderPlugin(botContext.plugins)
67
+ const { campaignExecution, cmsApi } = flowBuilderPlugin
68
+
63
69
  for (const content of contents) {
64
70
  if (FlowBuilderAction.isAiAgentContent(content)) {
65
71
  await FlowBuilderAction.processAiAgentContent(botContext, contents)
72
+ campaignExecution.updateHasBotResponse(content)
66
73
  continue
67
74
  }
68
75
 
69
76
  await content.processContent(botContext)
70
77
 
78
+ campaignExecution.updateHasBotResponse(content)
79
+
71
80
  if (content instanceof FlowWhatsappRequestContactInfoNode) {
72
- const flowBuilderPlugin = getFlowBuilderPlugin(botContext.plugins)
73
- flowBuilderPlugin.cmsApi.setWhatsappRequestContactId(content.id)
81
+ cmsApi.setWhatsappRequestContactId(content.id)
74
82
  }
75
83
  }
76
84
 
@@ -147,10 +155,32 @@ export class FlowBuilderAction extends React.Component<FlowBuilderActionProps> {
147
155
  {shouldSendWebchatSettings && (
148
156
  <WebchatSettings {...webchatSettingsParams} />
149
157
  )}
150
- {contents.map(content => content.toBotonic(botContext))}
158
+ {FlowBuilderAction.renderContents(contents, botContext)}
151
159
  </>
152
160
  )
153
161
  }
162
+
163
+ protected static renderContents(
164
+ contents: FlowContent[],
165
+ botContext: BotContext
166
+ ): JSX.Element | JSX.Element[] {
167
+ const flowBuilderPlugin = getFlowBuilderPlugin(botContext.plugins)
168
+ const { campaignExecution } = flowBuilderPlugin
169
+
170
+ const rendered = contents.map(content => content.toBotonic(botContext))
171
+
172
+ if (!campaignExecution.isActive() || !campaignExecution.campaignId) {
173
+ return rendered
174
+ }
175
+
176
+ return (
177
+ <CampaignMessageAttributesProvider
178
+ campaignId={campaignExecution.campaignId}
179
+ >
180
+ {rendered}
181
+ </CampaignMessageAttributesProvider>
182
+ )
183
+ }
154
184
  }
155
185
 
156
186
  export class FlowBuilderMultichannelAction extends FlowBuilderAction {
@@ -165,7 +195,7 @@ export class FlowBuilderMultichannelAction extends FlowBuilderAction {
165
195
  {shouldSendWebchatSettings && (
166
196
  <WebchatSettings {...webchatSettingsParams} />
167
197
  )}
168
- {contents.map(content => content.toBotonic(botContext))}
198
+ {FlowBuilderAction.renderContents(contents, botContext)}
169
199
  </Multichannel>
170
200
  )
171
201
  }
@@ -1,25 +1,22 @@
1
1
  import { EventAction, type EventFeedback, storeCaseRating } from '@botonic/core'
2
2
  import { v7 as uuid } from 'uuid'
3
-
4
- import {
5
- AGENT_RATING_PAYLOAD,
6
- PUSH_FLOW_PAYLOAD,
7
- SEPARATOR,
8
- } from '../constants'
9
- import type { FlowContent } from '../content-fields'
3
+ import { AGENT_RATING_PAYLOAD, PUSH_FLOW_PAYLOAD } from '../constants'
10
4
  import type { HtNodeWithContent } from '../content-fields/hubtype-fields'
11
5
  import { trackEvent } from '../tracking'
6
+ import type { FlowBuilderContentsResult } from './contents-result'
12
7
  import type { FlowBuilderContext } from './context'
8
+ import { PushFlowResolver } from './push-flow-resolver'
13
9
 
14
10
  export async function getContentsByPayload(
15
11
  context: FlowBuilderContext
16
- ): Promise<FlowContent[]> {
12
+ ): Promise<FlowBuilderContentsResult> {
17
13
  const { cmsApi, flowBuilderPlugin, request, contentID } = context
18
14
  if (request.input.payload?.startsWith(AGENT_RATING_PAYLOAD)) {
19
15
  return await resolveRatingPayload(context)
20
16
  }
21
17
  if (request.input.payload?.startsWith(PUSH_FLOW_PAYLOAD)) {
22
- return await resolvePushFlowPayload(context)
18
+ const pushFlow = new PushFlowResolver(context)
19
+ return await pushFlow.getContents()
23
20
  }
24
21
 
25
22
  const id = contentID
@@ -28,19 +25,21 @@ export async function getContentsByPayload(
28
25
  const targetNode = id ? cmsApi.getNodeById<HtNodeWithContent>(id) : undefined
29
26
 
30
27
  if (targetNode) {
31
- return await flowBuilderPlugin.getContentsByNode(targetNode)
28
+ return {
29
+ contents: await flowBuilderPlugin.getContentsByNode(targetNode),
30
+ }
32
31
  }
33
32
 
34
- return []
33
+ return { contents: [] }
35
34
  }
36
35
 
37
36
  async function resolveRatingPayload(
38
37
  context: FlowBuilderContext
39
- ): Promise<FlowContent[]> {
38
+ ): Promise<FlowBuilderContentsResult> {
40
39
  const { cmsApi, flowBuilderPlugin, request } = context
41
40
 
42
41
  if (!request.input.payload) {
43
- return []
42
+ return { contents: [] }
44
43
  }
45
44
 
46
45
  const { target, text, value, possibleOptions, possibleValues } =
@@ -66,39 +65,10 @@ async function resolveRatingPayload(
66
65
  : undefined
67
66
 
68
67
  if (targetNode) {
69
- return await flowBuilderPlugin.getContentsByNode(targetNode)
70
- }
71
-
72
- return []
73
- }
74
-
75
- async function resolvePushFlowPayload(
76
- context: FlowBuilderContext
77
- ): Promise<FlowContent[]> {
78
- const { cmsApi, flowBuilderPlugin, request } = context
79
-
80
- if (!request.input.payload) {
81
- return []
82
- }
83
-
84
- const parts = request.input.payload.split(SEPARATOR)
85
- const pushFlowId = parts.length > 1 ? parts[1] : ''
86
-
87
- if (!pushFlowId) {
88
- return []
89
- }
90
-
91
- try {
92
- const pushFlowNode =
93
- cmsApi.getNodeByCampaignId<HtNodeWithContent>(pushFlowId)
94
-
95
- if (!pushFlowNode) {
96
- return []
68
+ return {
69
+ contents: await flowBuilderPlugin.getContentsByNode(targetNode),
97
70
  }
98
-
99
- return await flowBuilderPlugin.getContentsByNode(pushFlowNode)
100
- } catch {
101
- // Campaign not found - return empty to trigger fallback
102
- return []
103
71
  }
72
+
73
+ return { contents: [] }
104
74
  }
@@ -0,0 +1,89 @@
1
+ import {
2
+ type BotContext,
3
+ BotonicInputContextType,
4
+ getInputContextOfType,
5
+ } from '@botonic/core'
6
+ import type { FlowBuilderApi } from '../api'
7
+ import type { HtNodeWithContent } from '../content-fields/hubtype-fields'
8
+ import type BotonicPluginFlowBuilder from '../index'
9
+ import type { FlowBuilderContentsResult } from './contents-result'
10
+ import type { FlowBuilderContext } from './context'
11
+
12
+ export enum PushFlowErrorCode {
13
+ NoCampaignContext = 'NoCampaignContext',
14
+ NoCampaignStartNode = 'NoCampaignStartNode',
15
+ NoCampaignFollowUpNode = 'NoCampaignFollowUpNode',
16
+ }
17
+
18
+ export class PushFlowError extends Error {
19
+ readonly code: PushFlowErrorCode
20
+
21
+ constructor(code: PushFlowErrorCode, reason: string) {
22
+ super(`push-flow failed: ${reason}`)
23
+ this.code = code
24
+ this.name = 'PushFlowError'
25
+ }
26
+ }
27
+
28
+ export class PushFlowResolver {
29
+ private botContext: BotContext
30
+ private cmsApi: FlowBuilderApi
31
+ private flowBuilderPlugin: BotonicPluginFlowBuilder
32
+
33
+ constructor(context: FlowBuilderContext) {
34
+ this.botContext = context.request
35
+ this.cmsApi = context.cmsApi
36
+ this.flowBuilderPlugin = context.flowBuilderPlugin
37
+ }
38
+
39
+ async getContents(): Promise<FlowBuilderContentsResult> {
40
+ const campaignContext = getInputContextOfType(
41
+ this.botContext.input.context,
42
+ BotonicInputContextType.PushFlow
43
+ )
44
+
45
+ if (!campaignContext) {
46
+ throw new PushFlowError(
47
+ PushFlowErrorCode.NoCampaignContext,
48
+ 'No campaign context found in input context'
49
+ )
50
+ }
51
+
52
+ const {
53
+ campaign_flow_id: campaignFlowId,
54
+ campaign_follow_up_id: campaignFollowUpId,
55
+ } = campaignContext
56
+
57
+ const campaignStartNode = this.getCampaignStartNode(campaignFlowId)
58
+ const followUpNode = campaignFollowUpId
59
+ ? this.cmsApi.getNodeById<HtNodeWithContent>(campaignFollowUpId)
60
+ : undefined
61
+
62
+ if (campaignFollowUpId && !followUpNode) {
63
+ throw new PushFlowError(
64
+ PushFlowErrorCode.NoCampaignFollowUpNode,
65
+ 'No campaign follow up node found'
66
+ )
67
+ }
68
+
69
+ return {
70
+ contents: await this.flowBuilderPlugin.getContentsByNode(
71
+ followUpNode ?? campaignStartNode
72
+ ),
73
+ }
74
+ }
75
+
76
+ private getCampaignStartNode(campaignId: string): HtNodeWithContent {
77
+ const campaignStartNode =
78
+ this.cmsApi.getNodeByCampaignId<HtNodeWithContent>(campaignId)
79
+
80
+ if (!campaignStartNode) {
81
+ throw new PushFlowError(
82
+ PushFlowErrorCode.NoCampaignStartNode,
83
+ 'No campaign start node found'
84
+ )
85
+ }
86
+
87
+ return campaignStartNode
88
+ }
89
+ }
package/src/api.ts CHANGED
@@ -1,12 +1,7 @@
1
1
  import type { BotContext } from '@botonic/core'
2
2
  import axios from 'axios'
3
3
 
4
- import {
5
- AI_AGENTS_FLOW_NAME,
6
- PUSH_FLOW_PAYLOAD,
7
- SEPARATOR,
8
- UUID_REGEXP,
9
- } from './constants'
4
+ import { AI_AGENTS_FLOW_NAME, SEPARATOR, UUID_REGEXP } from './constants'
10
5
  import {
11
6
  type HtBotActionNode,
12
7
  type HtCaptureUserInputNode,
@@ -82,12 +77,22 @@ export class FlowBuilderApi {
82
77
  return this.getNodeById<HtNodeWithContent>(subFlow.start_node_id)
83
78
  }
84
79
 
85
- getNodeByCampaignId<T extends HtNodeComponent>(id: string): T {
80
+ getNodeByCampaignId<T extends HtNodeComponent>(id: string): T | undefined {
86
81
  const campaign = this.flow.campaigns.find(campaign => campaign.id === id)
87
82
  if (!campaign) {
88
- throw Error(`Campaign with id: '${id}' not found`)
83
+ return undefined
84
+ }
85
+
86
+ const node = this.flow.nodes.find(
87
+ flowNode => flowNode.id === campaign.start_node_id
88
+ )
89
+
90
+ if (!node) {
91
+ console.error(`Node with id: '${campaign.start_node_id}' not found`)
92
+ return undefined
89
93
  }
90
- return this.getNodeById<T>(campaign.start_node_id)
94
+
95
+ return node as T
91
96
  }
92
97
 
93
98
  getNodeById<T extends HtNodeComponent>(id: string): T {
@@ -330,8 +335,4 @@ export class FlowBuilderApi {
330
335
  defaultLocaleCode
331
336
  ).resolve()
332
337
  }
333
-
334
- isPushFlowPayload(payload?: string): boolean {
335
- return payload?.startsWith(PUSH_FLOW_PAYLOAD) ?? false
336
- }
337
338
  }
@@ -6,6 +6,7 @@ import {
6
6
  } from '@botonic/core'
7
7
 
8
8
  import type { FlowBuilderApi } from './api'
9
+ import type { CampaignExecution } from './campaign-execution'
9
10
  import {
10
11
  EMPTY_PAYLOAD,
11
12
  ON_CLOSE_HANDOFF_PAYLOAD,
@@ -24,6 +25,7 @@ import { inputHasTextOrTranscript } from './utils/input'
24
25
 
25
26
  export class BotContextManager {
26
27
  constructor(
28
+ private readonly campaignExecution: CampaignExecution,
27
29
  private readonly cmsApi: FlowBuilderApi,
28
30
  private readonly smartIntentsConfig: SmartIntentsInferenceConfig
29
31
  ) {}
@@ -48,7 +50,8 @@ export class BotContextManager {
48
50
  private async updateFirstInteractionOnPushFlowPayload(
49
51
  botContext: BotContext
50
52
  ): Promise<void> {
51
- if (this.cmsApi.isPushFlowPayload(botContext.input.payload)) {
53
+ const campaignExecution = this.campaignExecution
54
+ if (campaignExecution.isPushFlowPayload()) {
52
55
  botContext.session.is_first_interaction = false
53
56
  }
54
57
  }
@@ -0,0 +1,51 @@
1
+ import {
2
+ type BotContext,
3
+ BotonicInputContextType,
4
+ getInputContextOfType,
5
+ type PushFlowContext,
6
+ } from '@botonic/core'
7
+
8
+ import { PUSH_FLOW_PAYLOAD } from './constants'
9
+ import type { FlowContent } from './content-fields'
10
+
11
+ export class CampaignExecution {
12
+ public hasBotResponse = false
13
+ private botContext: BotContext
14
+
15
+ constructor(botContext: BotContext) {
16
+ this.botContext = botContext
17
+ }
18
+
19
+ isActive(): boolean {
20
+ if (!this.isPushFlowPayload()) {
21
+ return false
22
+ }
23
+
24
+ return this.getPushFlowContext() !== undefined
25
+ }
26
+
27
+ get campaignId(): string | undefined {
28
+ return this.getPushFlowContext()?.campaign_id
29
+ }
30
+
31
+ updateHasBotResponse(content: FlowContent): void {
32
+ if (!this.isActive() || this.hasBotResponse) {
33
+ return
34
+ }
35
+ if (content.producesBotResponse()) {
36
+ this.hasBotResponse = true
37
+ }
38
+ }
39
+
40
+ isPushFlowPayload(): boolean {
41
+ const payload = this.botContext.input.payload
42
+ return payload?.startsWith(PUSH_FLOW_PAYLOAD) ?? false
43
+ }
44
+
45
+ private getPushFlowContext(): PushFlowContext | undefined {
46
+ return getInputContextOfType(
47
+ this.botContext.input.context,
48
+ BotonicInputContextType.PushFlow
49
+ )
50
+ }
51
+ }
@@ -22,6 +22,8 @@ export abstract class ContentFieldsBase {
22
22
 
23
23
  abstract trackFlow(botContext: BotContext): Promise<void>
24
24
 
25
+ abstract producesBotResponse(): boolean
26
+
25
27
  abstract processContent(botContext: BotContext): Promise<void>
26
28
 
27
29
  async filterContent(
@@ -124,6 +124,10 @@ export class FlowCustomConditionalV2 extends ContentFieldsBase {
124
124
  await trackEvent(botContext, action, eventArgs)
125
125
  }
126
126
 
127
+ producesBotResponse(): boolean {
128
+ return false
129
+ }
130
+
127
131
  async processContent(botContext: BotContext): Promise<void> {
128
132
  await this.filterContent(botContext, this)
129
133
  await this.trackFlow(botContext)
@@ -114,6 +114,10 @@ export abstract class FlowAiAgentBase extends ContentFieldsBase {
114
114
  return
115
115
  }
116
116
 
117
+ producesBotResponse(): boolean {
118
+ return this.jsxElements.length > 0
119
+ }
120
+
117
121
  async processContent(
118
122
  botContext: BotContext,
119
123
  previousContents?: FlowContent[]
@@ -51,6 +51,10 @@ export class FlowBotAction extends ContentFieldsBase {
51
51
  botContext.session._botonic_action = `${BotonicAction.Redirect}:${this.payload}`
52
52
  }
53
53
 
54
+ producesBotResponse(): boolean {
55
+ return false
56
+ }
57
+
54
58
  async processContent(botContext: BotContext): Promise<void> {
55
59
  await this.filterContent(botContext, this)
56
60
  this.doBotAction(botContext)
@@ -98,6 +98,10 @@ export class FlowButton extends ContentFieldsBase {
98
98
  }
99
99
  }
100
100
 
101
+ producesBotResponse(): boolean {
102
+ return false
103
+ }
104
+
101
105
  async processContent(): Promise<void> {
102
106
  return
103
107
  }
@@ -34,6 +34,10 @@ export class FlowCaptureUserInput extends ContentFieldsBase {
34
34
  return
35
35
  }
36
36
 
37
+ producesBotResponse(): boolean {
38
+ return false
39
+ }
40
+
37
41
  async processContent(): Promise<void> {
38
42
  return
39
43
  }
@@ -203,6 +203,10 @@ export class FlowCarousel extends ContentFieldsBase {
203
203
  }
204
204
  }
205
205
 
206
+ producesBotResponse(): boolean {
207
+ return true
208
+ }
209
+
206
210
  async processContent(botContext: BotContext): Promise<void> {
207
211
  await this.filterContent(botContext, this)
208
212
  await this.trackFlow(botContext)
@@ -62,6 +62,10 @@ export class FlowChannelConditional extends ContentFieldsBase {
62
62
  await trackEvent(botContext, action, eventArgs)
63
63
  }
64
64
 
65
+ producesBotResponse(): boolean {
66
+ return false
67
+ }
68
+
65
69
  async processContent(botContext: BotContext): Promise<void> {
66
70
  await this.filterContent(botContext, this)
67
71
  await this.trackFlow(botContext)
@@ -107,6 +107,10 @@ export class FlowContactReasonsConditional extends ContentFieldsBase {
107
107
  return
108
108
  }
109
109
 
110
+ producesBotResponse(): boolean {
111
+ return false
112
+ }
113
+
110
114
  async processContent(botContext: BotContext): Promise<void> {
111
115
  await this.filterContent(botContext, this)
112
116
  await this.trackFlow(botContext)
@@ -64,6 +64,10 @@ export class FlowCountryConditional extends ContentFieldsBase {
64
64
  await trackEvent(botContext, action, eventArgs)
65
65
  }
66
66
 
67
+ producesBotResponse(): boolean {
68
+ return false
69
+ }
70
+
67
71
  async processContent(botContext: BotContext): Promise<void> {
68
72
  await this.filterContent(botContext, this)
69
73
  await this.trackFlow(botContext)
@@ -115,6 +115,10 @@ export class FlowCustomConditional extends ContentFieldsBase {
115
115
  )
116
116
  }
117
117
 
118
+ producesBotResponse(): boolean {
119
+ return false
120
+ }
121
+
118
122
  async processContent(botContext: BotContext): Promise<void> {
119
123
  await this.filterContent(botContext, this)
120
124
  await this.trackFlow(botContext)
@@ -1,6 +1,8 @@
1
1
  import type { BotContext } from '@botonic/core'
2
2
 
3
+ import { CampaignsV2Api } from '../services/campaigns-v2-api'
3
4
  import { trackOneContent } from '../tracking'
5
+ import { getFlowBuilderPlugin } from '../utils/get-flow-builder-plugin'
4
6
  import { ContentFieldsBase } from './content-fields-base'
5
7
  import type { HtDoNothingNode } from './hubtype-fields'
6
8
 
@@ -8,18 +10,40 @@ export class FlowDoNothing extends ContentFieldsBase {
8
10
  static fromHubtypeCMS(cmsDoNothing: HtDoNothingNode): FlowDoNothing {
9
11
  const newDoNothing = new FlowDoNothing(cmsDoNothing.id)
10
12
  newDoNothing.code = cmsDoNothing.code
11
- newDoNothing.followUp = cmsDoNothing.follow_up
13
+ newDoNothing.followUp = undefined
12
14
 
13
15
  return newDoNothing
14
16
  }
15
17
 
18
+ private async skipCampaignIfNoBotResponse(
19
+ botContext: BotContext
20
+ ): Promise<void> {
21
+ const flowBuilderPlugin = getFlowBuilderPlugin(botContext.plugins)
22
+ const campaignExecution = flowBuilderPlugin.campaignExecution
23
+ if (!campaignExecution.isActive() || campaignExecution.hasBotResponse) {
24
+ return
25
+ }
26
+ const campaignId = campaignExecution.campaignId
27
+ if (!campaignId) {
28
+ return
29
+ }
30
+
31
+ const campaignsV2Api = new CampaignsV2Api(botContext)
32
+ await campaignsV2Api.skipEmptyBotResponse(campaignId)
33
+ }
34
+
16
35
  async trackFlow(botContext: BotContext): Promise<void> {
17
36
  await trackOneContent(botContext, this)
18
37
  }
19
38
 
39
+ producesBotResponse(): boolean {
40
+ return false
41
+ }
42
+
20
43
  async processContent(botContext: BotContext): Promise<void> {
21
44
  await this.filterContent(botContext, this)
22
45
  await this.trackFlow(botContext)
46
+ await this.skipCampaignIfNoBotResponse(botContext)
23
47
  return
24
48
  }
25
49
 
@@ -62,6 +62,10 @@ export class FlowElement extends ContentFieldsBase {
62
62
  }
63
63
  }
64
64
 
65
+ producesBotResponse(): boolean {
66
+ return false
67
+ }
68
+
65
69
  async processContent(botContext: BotContext): Promise<void> {
66
70
  await this.trackFlow(botContext)
67
71
  return
@@ -50,6 +50,10 @@ export class FlowGoToFlow extends ContentFieldsBase {
50
50
  await trackEvent(botContext, action, eventArgs)
51
51
  }
52
52
 
53
+ producesBotResponse(): boolean {
54
+ return false
55
+ }
56
+
53
57
  async processContent(botContext: BotContext): Promise<void> {
54
58
  await this.filterContent(botContext, this)
55
59
  await this.trackFlow(botContext)
@@ -142,6 +142,10 @@ export class FlowHandoff extends ContentFieldsBase {
142
142
  // TODO: Not apply for this content because backend track handoff success event
143
143
  }
144
144
 
145
+ producesBotResponse(): boolean {
146
+ return false
147
+ }
148
+
145
149
  async processContent(botContext: BotContext): Promise<void> {
146
150
  await this.filterContent(botContext, this)
147
151
  await this.doHandoff(botContext)
@@ -21,6 +21,10 @@ export class FlowImage extends ContentFieldsBase {
21
21
  await trackOneContent(botContext, this)
22
22
  }
23
23
 
24
+ producesBotResponse(): boolean {
25
+ return true
26
+ }
27
+
24
28
  async processContent(botContext: BotContext): Promise<void> {
25
29
  await this.filterContent(botContext, this)
26
30
  await this.trackFlow(botContext)
@@ -127,6 +127,10 @@ export class FlowQueueStatusConditional extends ContentFieldsBase {
127
127
  await trackEvent(botContext, action, eventArgs)
128
128
  }
129
129
 
130
+ producesBotResponse(): boolean {
131
+ return false
132
+ }
133
+
130
134
  async processContent(botContext: BotContext): Promise<void> {
131
135
  await this.filterContent(botContext, this)
132
136
  await this.trackFlow(botContext)
@@ -39,6 +39,10 @@ export class FlowRating extends ContentFieldsBase {
39
39
  await trackOneContent(botContext, this)
40
40
  }
41
41
 
42
+ producesBotResponse(): boolean {
43
+ return true
44
+ }
45
+
42
46
  async processContent(botContext: BotContext): Promise<void> {
43
47
  await this.filterContent(botContext, this)
44
48
  await this.trackFlow(botContext)
@@ -70,6 +70,10 @@ export class FlowText extends ContentFieldsBase {
70
70
  )
71
71
  }
72
72
 
73
+ producesBotResponse(): boolean {
74
+ return true
75
+ }
76
+
73
77
  async processContent(botContext: BotContext): Promise<void> {
74
78
  await this.filterContent(botContext, this)
75
79
  await this.trackFlow(botContext)