@botonic/plugin-flow-builder
Version:
Use Flow Builder to show your contents
116 lines (100 loc) • 3.39 kB
text/typescript
import { NluType } from '@botonic/core'
import { ActionRequest } from '@botonic/react'
import { FlowBuilderApi } from '../api'
import { REG_EXP_PATTERN } from '../constants'
import {
HtKeywordNode,
HtNodeWithContent,
} from '../content-fields/hubtype-fields'
import { EventAction, trackEvent } from '../tracking'
interface KeywordProps {
cmsApi: FlowBuilderApi
locale: string
request: ActionRequest
}
export class KeywordMatcher {
public cmsApi: FlowBuilderApi
public locale: string
public request: ActionRequest
public isRegExp: boolean
public matchedKeyword?: string
public keywordNodeId?: string
public flowId?: string
constructor({ cmsApi, locale, request }: KeywordProps) {
this.cmsApi = cmsApi
this.locale = locale
this.request = request
this.isRegExp = false
}
async getNodeByInput(userInput: string): Promise<HtKeywordNode | undefined> {
const keywordNodes = this.cmsApi.getKeywordNodes()
const keywordNode = this.getNodeByKeyword(userInput, keywordNodes)
if (!keywordNode || !this.matchedKeyword) {
return undefined
}
const targetPayload = this.cmsApi.getPayload(keywordNode.target)
this.request.input.nluResolution = {
type: NluType.Keyword,
matchedValue: this.matchedKeyword,
payload: targetPayload,
}
await this.trackKeywordEvent()
return keywordNode
}
private getNodeByKeyword(
userInput: string,
keywordNodes: HtKeywordNode[]
): HtKeywordNode | undefined {
const matchedKeywordNode = keywordNodes.find(node =>
this.matchKeywords(userInput, node)
)
return matchedKeywordNode?.target ? matchedKeywordNode : undefined
}
private matchKeywords(userInput: string, node: HtKeywordNode): boolean {
const result = node.content.keywords.find(keywords => {
if (keywords.locale === this.locale) {
this.keywordNodeId = node.id
this.flowId = this.cmsApi.getNodeById<HtNodeWithContent>(
node.id
).flow_id
return this.inputMatchesAnyKeyword(userInput, keywords.values)
}
return false
})
return Boolean(result)
}
private inputMatchesAnyKeyword(
userInput: string,
keywords: string[]
): boolean {
return keywords.some(keyword => {
const regExpMatchArray = keyword.match(REG_EXP_PATTERN)
if (regExpMatchArray) {
const keywordAsRegExp = this.resolveKeywordAsRegExp(regExpMatchArray)
const match = userInput.match(keywordAsRegExp)
this.isRegExp = true
this.matchedKeyword = match ? match[0] : undefined
} else {
this.isRegExp = false
this.matchedKeyword = userInput.includes(keyword) ? keyword : undefined
}
return this.matchedKeyword !== undefined
})
}
private resolveKeywordAsRegExp(regExpMatchArray: RegExpMatchArray): RegExp {
const [, pattern, flags] = regExpMatchArray
return new RegExp(pattern, flags)
}
private async trackKeywordEvent() {
const eventArgs = {
nluKeywordName: this.matchedKeyword,
nluKeywordIsRegex: this.isRegExp,
nluKeywordMessageId: this.request.input.message_id,
userInput: this.request.input.data,
flowThreadId: this.request.session.flow_thread_id,
flowId: this.flowId,
flowNodeId: this.keywordNodeId,
}
await trackEvent(this.request, EventAction.Keyword, eventArgs)
}
}