Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin-hubtype-analytics: event flow #2828

6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/botonic-core/src/models/legacy-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export interface Session {
_shadowing?: boolean
external?: any
zendesk_ticket_id?: string
flow_thread_id?: string
}

export type InputMatcher = (input: Input) => boolean
Expand Down
78 changes: 54 additions & 24 deletions packages/botonic-plugin-flow-builder/src/action/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import { FlowContent, FlowHandoff } from '../content-fields'
import { HtNodeWithContent } from '../content-fields/hubtype-fields'
import { getFlowBuilderPlugin } from '../helpers'
import { EventName, trackEvent } from '../tracking'
import BotonicPluginFlowBuilder from '../index'
import { EventAction, getEventArgs, trackEvent } from '../tracking'

Check warning on line 9 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L9

Added line #L9 was not covered by tests
import { createNodeFromKnowledgeBase } from './knowledge-bases'

export type FlowBuilderActionProps = {
Expand All @@ -18,15 +19,7 @@
static async botonicInit(
request: ActionRequest
): Promise<FlowBuilderActionProps> {
const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
const locale = flowBuilderPlugin.getLocale(request.session)

const targetNode = await getTargetNode(flowBuilderPlugin.cmsApi, request)

const contents = await flowBuilderPlugin.getContentsByNode(
targetNode,
locale
)
const contents = await getContents(request)

Check warning on line 22 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L22

Added line #L22 was not covered by tests

const handoffContent = contents.find(
content => content instanceof FlowHandoff
Expand Down Expand Up @@ -55,26 +48,64 @@
}
}

async function getTargetNode(cmsApi: FlowBuilderApi, request: ActionRequest) {
async function getContents(request: ActionRequest): Promise<FlowContent[]> {
const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
const cmsApi = flowBuilderPlugin.cmsApi
const locale = flowBuilderPlugin.getLocale(request.session)
const resolvedLocale = flowBuilderPlugin.cmsApi.getResolvedLocale(locale)
const context = {

Check warning on line 56 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L52-L56

Added lines #L52 - L56 were not covered by tests
cmsApi,
flowBuilderPlugin,
request,
resolvedLocale,
}

if (request.session.is_first_interaction) {
const startNode = cmsApi.getStartNode()
await trackEvent(request, EventName.botStart)
return startNode
return await flowBuilderPlugin.getStartContents(resolvedLocale)

Check warning on line 64 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L64

Added line #L64 was not covered by tests
}

if (request.input.payload) {
return await getContentsByPayload(context)

Check warning on line 68 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L68

Added line #L68 was not covered by tests
}
const contentId = request.input.payload

const targetNode = contentId
? cmsApi.getNodeById<HtNodeWithContent>(contentId)
return await getContentsByFallback(context)

Check warning on line 71 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L71

Added line #L71 was not covered by tests
}

interface FlowBuilderContext {
cmsApi: FlowBuilderApi
flowBuilderPlugin: BotonicPluginFlowBuilder
request: ActionRequest
resolvedLocale: string
}

async function getContentsByPayload({
cmsApi,
flowBuilderPlugin,
request,
resolvedLocale,
}: FlowBuilderContext): Promise<FlowContent[]> {
const targetNode = request.input.payload
? cmsApi.getNodeById<HtNodeWithContent>(request.input.payload)
: undefined

if (targetNode) {
const eventArgs = {
faq_name: targetNode.code,
}
await trackEvent(request, EventName.botFaq, eventArgs)
return targetNode
const eventArgs = getEventArgs(request, targetNode)
await trackEvent(request, EventAction.flowNode, eventArgs)
return await flowBuilderPlugin.getContentsByNode(targetNode, resolvedLocale)

Check warning on line 94 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L92-L94

Added lines #L92 - L94 were not covered by tests
}
return await getFallbackNode(cmsApi, request)
return []

Check warning on line 96 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L96

Added line #L96 was not covered by tests
}

async function getContentsByFallback({
cmsApi,
flowBuilderPlugin,
request,
resolvedLocale,
}: FlowBuilderContext): Promise<FlowContent[]> {
const fallbackNode = await getFallbackNode(cmsApi, request)
const eventArgs = getEventArgs(request, fallbackNode)
await trackEvent(request, EventAction.flowNode, eventArgs)
return await flowBuilderPlugin.getContentsByNode(fallbackNode, resolvedLocale)

Check warning on line 108 in packages/botonic-plugin-flow-builder/src/action/index.tsx

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/action/index.tsx#L105-L108

Added lines #L105 - L108 were not covered by tests
}

async function getFallbackNode(cmsApi: FlowBuilderApi, request: ActionRequest) {
Expand All @@ -95,6 +126,5 @@
const fallbackNode = cmsApi.getFallbackNode(isFirstFallbackOption)
request.session.user.extra_data.isFirstFallbackOption = !isFirstFallbackOption

await trackEvent(request, EventName.fallback)
return fallbackNode
}
5 changes: 5 additions & 0 deletions packages/botonic-plugin-flow-builder/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@
}
}

getFlowName(flowId: string): string {
const flow = this.flow.flows.find(flow => flow.id === flowId)

Check warning on line 254 in packages/botonic-plugin-flow-builder/src/api.ts

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/api.ts#L254

Added line #L254 was not covered by tests
return flow ? flow.name : ''
}

getResolvedLocale(locale: string): string {
if (this.flow.locales.find(flowLocale => flowLocale === locale)) {
return locale
Expand Down
21 changes: 19 additions & 2 deletions packages/botonic-plugin-flow-builder/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Plugin, PluginPreRequest, Session } from '@botonic/core'
import { ActionRequest } from '@botonic/react'
import { v4 as uuid } from 'uuid'

import { FlowBuilderApi } from './api'
import {
Expand Down Expand Up @@ -29,6 +30,7 @@ import {
HtNodeWithContentType,
} from './content-fields/hubtype-fields'
import { DEFAULT_FUNCTIONS } from './functions'
import { EventAction, getEventArgs, trackEvent } from './tracking'
import {
BotonicPluginFlowBuilderOptions,
FlowBuilderJSONVersion,
Expand All @@ -48,7 +50,7 @@ export default class BotonicPluginFlowBuilder implements Plugin {
public getLocale: (session: Session) => string
public trackEvent?: (
request: ActionRequest,
eventName: string,
eventAction: string,
args?: Record<string, any>
) => Promise<void>
public getKnowledgeBaseResponse?: (
Expand Down Expand Up @@ -140,8 +142,19 @@ export default class BotonicPluginFlowBuilder implements Plugin {
}

async getStartContents(locale: string): Promise<FlowContent[]> {
const resolvedLocale = this.cmsApi.getResolvedLocale(locale)
const startNode = this.cmsApi.getStartNode()
return await this.getContentsByNode(startNode, locale)
this.currentRequest.session.flow_thread_id = uuid()
const eventArgs = getEventArgs(
this.currentRequest as unknown as ActionRequest,
startNode
)
await trackEvent(
this.currentRequest as unknown as ActionRequest,
EventAction.flowNode,
eventArgs
)
return await this.getContentsByNode(startNode, resolvedLocale)
}

async getContentsByNode(
Expand Down Expand Up @@ -257,6 +270,10 @@ export default class BotonicPluginFlowBuilder implements Plugin {
const payloadParams = JSON.parse(payload.split(SEPARATOR)[1] || '{}')
return payloadParams
}

getFlowName(flowId: string): string {
return this.cmsApi.getFlowName(flowId)
}
}

export * from './action'
Expand Down
48 changes: 31 additions & 17 deletions packages/botonic-plugin-flow-builder/src/tracking.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
import { ActionRequest } from '@botonic/react'

import { HtNodeWithContent } from './content-fields/hubtype-fields'
import { getFlowBuilderPlugin } from './helpers'

export async function trackEvent(
request: ActionRequest,
eventName: EventName,
eventAction: EventAction,
args?: Record<string, any>
): Promise<void> {
const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
if (flowBuilderPlugin.trackEvent) {
await flowBuilderPlugin.trackEvent(request, eventName, args)
await flowBuilderPlugin.trackEvent(request, eventAction, args)

Check warning on line 13 in packages/botonic-plugin-flow-builder/src/tracking.ts

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/tracking.ts#L13

Added line #L13 was not covered by tests
}
return
}

export enum EventName {
botAgentRating = 'bot_agent_rating',
botChannelRating = 'bot_channel_rating',
botFaqUseful = 'bot_faq_useful',
botRating = 'bot_rating',
botFaq = 'bot_faq',
botStart = 'bot_start',
botOpen = 'bot_open',
botAiModel = 'bot_ai_model',
botAiKnowledgeBase = 'bot_ai_knowledge_base',
botKeywordsModel = 'bot_keywords_model',
fallback = 'fallback',
handoffOption = 'handoff_option',
handoffSuccess = 'handoff_success',
handoffFail = 'handoff_fail',
export function getEventArgs(request: ActionRequest, node: HtNodeWithContent) {
const flowBuilderPlugin = getFlowBuilderPlugin(request.plugins)
return {

Check warning on line 20 in packages/botonic-plugin-flow-builder/src/tracking.ts

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/tracking.ts#L18-L20

Added lines #L18 - L20 were not covered by tests
flowThreadId: request.session.flow_thread_id,
flowId: node.flow_id,
flowName: flowBuilderPlugin.getFlowName(node.flow_id),
flowNodeId: node.id,
flowNodeContentId: node.code,
flowNodeIsMeaningful: undefined, //node?.isMeaningful,
}
}

export enum EventAction {
flowNode = 'flow_node',

Check warning on line 31 in packages/botonic-plugin-flow-builder/src/tracking.ts

View check run for this annotation

Codecov / codecov/patch

packages/botonic-plugin-flow-builder/src/tracking.ts#L31

Added line #L31 was not covered by tests
}

// export enum EventName {
// feedback = 'feedback',
// flow = 'botevent',

// botOpen = 'bot_open',
// botAiModel = 'bot_ai_model',
// botAiKnowledgeBase = 'bot_ai_knowledge_base',
// botKeywordsModel = 'bot_keywords_model',
// fallback = 'fallback',
// handoffOption = 'handoff_option',
// handoffSuccess = 'handoff_success',
// handoffFail = 'handoff_fail',
// }
2 changes: 1 addition & 1 deletion packages/botonic-plugin-flow-builder/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface BotonicPluginFlowBuilderOptions {
getAccessToken: () => string
trackEvent?: (
request: ActionRequest,
eventName: string,
eventAction: string,
args?: Record<string, any>
) => Promise<void>
getKnowledgeBaseResponse?: (
Expand Down
43 changes: 38 additions & 5 deletions packages/botonic-plugin-hubtype-analytics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,52 @@ By default if you do not define these functions it will use the language defined

## Use

You can use it in your actions for example an event to check that a faq has been displayed in the bot:
You can use it in your actions.

- For example an event to save a feedback given by the user:

```
const hubtypeAnalyticsPlugin = request.plugins.hubtypeAnalytics
const eventBotFaq = {
event_type: EventName.botFaq
event_data: { enduser_language: 'en', faq_name: 'orders_and_deliveries' }
const eventProps = {
action: FeedbackAction.case,
data: {
possibleOptions: ['*', '**', '***', '****', '*****'],
possibleValues: [1, 2, 3, 4, 5],
option: '**',
value: 2,
}
}

try {
const response = await hubtypeAnalyticsPlugin.trackEvent(request, event)
console.log(response)
} catch(error) {
console.error(error)
}

```

- For example an event to check that a flow content has been displayed in the bot:
flowThreadId -> This value is managed by the plugin-flow-builder, stored in the session and updated every time the content connected to the conversation start is displayed

```
const hubtypeAnalyticsPlugin = request.plugins.hubtypeAnalytics
const eventProps = {
action: FlowAction.flowNode,
data: {
flowThreadId: request.session.flow_thread_id,
flowId: '8d527e7d-ea6d-5422-b810-5b4c8be7657b',
flowName: 'Main',
flowNodeId: 'WELCOME_MSG',
flowNodeContentId: '607205c9-6814-45ba-9aeb-2dd08d0cb529',
}
}

try {
const response = await hubtypeAnalyticsPlugin.trackEvent(request, event)
console.log(response)
} catch(error) {
console.log(error)
console.error(error)
}

```
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EventFeedback, FeedbackAction, RequestData } from '../types'
import { EventFeedback, EventName, FeedbackAction, RequestData } from '../types'
import { HtEvent } from './ht-event'

export interface EventDataFeedback {
Expand All @@ -17,8 +17,7 @@ export class HtEventFeedback extends HtEvent {

constructor(event: EventFeedback, requestData: RequestData) {
super(event, requestData)
this.data = {} as EventDataFeedback
this.data.action = event.data.action
this.type = EventName.feedback
this.data.message_generated_by = event.data.messageGeneratedBy // ?? nomes te valor quan action es message. Si es message de knowledge base => sources y chunks. Quan es un node de flow builder => content_id
this.data.feedback_target_id = event.data.feedbackTargetId // ?? case_id, message_id, conversation_id ???, webview_name
this.data.feedback_group_id = event.data.feedbackGroupId // ??
Expand Down
Loading
Loading