2
0

Add Meta Pixel block

Closes #582
This commit is contained in:
Baptiste Arnaud
2023-06-28 09:52:03 +02:00
parent 92f7f3cbe2
commit 033f8f99dd
39 changed files with 826 additions and 38 deletions

View File

@@ -1,23 +1,21 @@
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { isNotEmpty } from '@typebot.io/lib'
import { GoogleAnalyticsBlock, SessionState } from '@typebot.io/schemas'
export const executeGoogleAnalyticsBlock = (
{ typebot: { variables } }: SessionState,
{ typebot: { variables }, result }: SessionState,
block: GoogleAnalyticsBlock
): ExecuteIntegrationResponse => {
const googleAnalytics = deepParseVariables(variables)(block.options)
if (!result) return { outgoingEdgeId: block.outgoingEdgeId }
const googleAnalytics = deepParseVariables(variables, {
guessCorrectTypes: true,
removeEmptyStrings: true,
})(block.options)
return {
outgoingEdgeId: block.outgoingEdgeId,
clientSideActions: [
{
googleAnalytics: {
...googleAnalytics,
value: isNotEmpty(googleAnalytics.value as string)
? Number(googleAnalytics.value)
: undefined,
},
googleAnalytics,
},
],
}

View File

@@ -0,0 +1,25 @@
import { ExecuteIntegrationResponse } from '@/features/chat/types'
import { deepParseVariables } from '@/features/variables/deepParseVariable'
import { PixelBlock, SessionState } from '@typebot.io/schemas'
export const executePixelBlock = (
{ typebot: { variables }, result }: SessionState,
block: PixelBlock
): ExecuteIntegrationResponse => {
if (!result) return { outgoingEdgeId: block.outgoingEdgeId }
const pixel = deepParseVariables(variables, {
guessCorrectTypes: true,
removeEmptyStrings: true,
})(block.options)
return {
outgoingEdgeId: block.outgoingEdgeId,
clientSideActions: [
{
pixel: {
...pixel,
pixelId: block.options.pixelId,
},
},
],
}
}

View File

@@ -6,6 +6,9 @@ import {
ChatReply,
chatReplySchema,
ChatSession,
GoogleAnalyticsBlock,
IntegrationBlockType,
PixelBlock,
ResultInSession,
sendMessageInputSchema,
SessionState,
@@ -180,10 +183,40 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
'setVariable' in action || 'streamOpenAiChatCompletion' in action
)
const startClientSideAction = clientSideActions ?? []
const parsedStartPropsActions = parseStartClientSideAction(typebot)
const startLogs = logs ?? []
if (isDefined(parsedStartPropsActions)) {
if (!result) {
if ('startPropsToInject' in parsedStartPropsActions) {
const { customHeadCode, googleAnalyticsId, pixelId, gtmId } =
parsedStartPropsActions.startPropsToInject
let toolsList = ''
if (customHeadCode) toolsList += 'Custom head code, '
if (googleAnalyticsId) toolsList += 'Google Analytics, '
if (pixelId) toolsList += 'Pixel, '
if (gtmId) toolsList += 'Google Tag Manager, '
toolsList = toolsList.slice(0, -2)
startLogs.push({
description: `${toolsList} ${
toolsList.includes(',') ? 'are not' : 'is not'
} enabled in Preview mode`,
status: 'info',
})
}
} else {
startClientSideAction.push(parsedStartPropsActions)
}
}
if (!input && !clientSideActionsNeedSessionId)
return {
messages,
clientSideActions,
clientSideActions:
startClientSideAction.length > 0 ? startClientSideAction : undefined,
typebot: {
id: typebot.id,
settings: deepParseVariables(newSessionState.typebot.variables)(
@@ -194,7 +227,7 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
),
},
dynamicTheme: parseDynamicThemeReply(newSessionState),
logs,
logs: startLogs.length > 0 ? startLogs : undefined,
}
const session = (await prisma.chatSession.create({
@@ -217,9 +250,10 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
},
messages,
input,
clientSideActions,
clientSideActions:
startClientSideAction.length > 0 ? startClientSideAction : undefined,
dynamicTheme: parseDynamicThemeReply(newSessionState),
logs,
logs: startLogs.length > 0 ? startLogs : undefined,
} satisfies ChatReply
}
@@ -403,3 +437,38 @@ const parseDynamicThemeReply = (
),
}
}
const parseStartClientSideAction = (
typebot: StartTypebot
): NonNullable<ChatReply['clientSideActions']>[number] | undefined => {
const blocks = typebot.groups.flatMap((group) => group.blocks)
const startPropsToInject = {
customHeadCode: typebot.settings.metadata.customHeadCode,
gtmId: typebot.settings.metadata.googleTagManagerId,
googleAnalyticsId: (
blocks.find(
(block) =>
block.type === IntegrationBlockType.GOOGLE_ANALYTICS &&
block.options.trackingId
) as GoogleAnalyticsBlock | undefined
)?.options.trackingId,
pixelId: (
blocks.find(
(block) =>
block.type === IntegrationBlockType.PIXEL && block.options.pixelId
) as PixelBlock | undefined
)?.options.pixelId,
}
if (
!startPropsToInject.customHeadCode &&
!startPropsToInject.gtmId &&
!startPropsToInject.googleAnalyticsId &&
!startPropsToInject.pixelId
)
return
return {
startPropsToInject,
}
}

View File

@@ -165,7 +165,11 @@ const parseBubbleBlock =
(block: BubbleBlock): ChatReply['messages'][0] => {
switch (block.type) {
case BubbleBlockType.TEXT:
return deepParseVariables(variables, { takeLatestIfList: true })(block)
return deepParseVariables(
variables,
{},
{ takeLatestIfList: true }
)(block)
case BubbleBlockType.EMBED: {
const message = deepParseVariables(variables)(block)
return {

View File

@@ -4,6 +4,7 @@ import { executeWebhookBlock } from '@/features/blocks/integrations/webhook/exec
import { executeChatwootBlock } from '@/features/blocks/integrations/chatwoot/executeChatwootBlock'
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/executeGoogleAnalyticsBlock'
import { executeGoogleSheetBlock } from '@/features/blocks/integrations/googleSheets/executeGoogleSheetBlock'
import { executePixelBlock } from '@/features/blocks/integrations/pixel/executePixelBlock'
import {
IntegrationBlock,
IntegrationBlockType,
@@ -30,5 +31,7 @@ export const executeIntegration =
return executeWebhookBlock(state, block)
case IntegrationBlockType.OPEN_AI:
return executeOpenAIBlock(state, block)
case IntegrationBlockType.PIXEL:
return executePixelBlock(state, block)
}
}

View File

@@ -4,21 +4,38 @@ import {
parseVariables,
ParseVariablesOptions,
} from './parseVariables'
import { parseGuessedTypeFromString } from './parseGuessedTypeFromString'
type DeepParseOptions = {
guessCorrectTypes?: boolean
removeEmptyStrings?: boolean
}
export const deepParseVariables =
(
variables: Variable[],
options: ParseVariablesOptions = defaultParseVariablesOptions
deepParseOptions: DeepParseOptions = {
guessCorrectTypes: false,
removeEmptyStrings: false,
},
parseVariablesOptions: ParseVariablesOptions = defaultParseVariablesOptions
) =>
<T extends Record<string, unknown>>(object: T): T =>
Object.keys(object).reduce<T>((newObj, key) => {
const currentValue = object[key]
if (typeof currentValue === 'string') {
const parsedVariable = parseVariables(variables, options)(currentValue)
const parsedVariable = parseVariables(
variables,
parseVariablesOptions
)(currentValue)
if (deepParseOptions.removeEmptyStrings && parsedVariable === '')
return newObj
return {
...newObj,
[key]: parsedVariable,
[key]: deepParseOptions.guessCorrectTypes
? parseGuessedTypeFromString(parsedVariable)
: parsedVariable,
}
}
@@ -27,14 +44,21 @@ export const deepParseVariables =
...newObj,
[key]: deepParseVariables(
variables,
options
deepParseOptions,
parseVariablesOptions
)(currentValue as Record<string, unknown>),
}
if (currentValue instanceof Array)
return {
...newObj,
[key]: currentValue.map(deepParseVariables(variables, options)),
[key]: currentValue.map(
deepParseVariables(
variables,
deepParseOptions,
parseVariablesOptions
)
),
}
return { ...newObj, [key]: currentValue }

View File

@@ -0,0 +1,12 @@
export const parseGuessedTypeFromString = (value: string): unknown => {
if (value === 'undefined') return undefined
return safeJsonParse(value)
}
const safeJsonParse = (value: string): unknown => {
try {
return JSON.parse(value)
} catch {
return value
}
}

View File

@@ -28,7 +28,7 @@ export const parseVariables =
return text.replace(
pattern,
(_full, nameInCurlyBraces, _dollarSign, nameInTemplateLitteral) => {
const dollarSign = _dollarSign ?? ''
const dollarSign = (_dollarSign ?? '') as string
const matchedVarName = nameInCurlyBraces ?? nameInTemplateLitteral
const variable = variables.find((variable) => {
return (