2
0

🧑‍💻 (chat) Introduce startChat and continueChat endpoints

Closes #1030
This commit is contained in:
Baptiste Arnaud
2023-11-13 15:27:36 +01:00
parent 63233eb7ee
commit 084588a086
74 changed files with 28426 additions and 645 deletions

View File

@@ -2,7 +2,7 @@ import {
SessionState,
GoogleSheetsGetOptions,
VariableWithValue,
ReplyLog,
ChatLog,
} from '@typebot.io/schemas'
import { isNotEmpty, byId, isDefined } from '@typebot.io/lib'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
@@ -18,7 +18,7 @@ export const getRow = async (
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsGetOptions }
): Promise<ExecuteIntegrationResponse> => {
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const { variables } = state.typebotsQueue[0].typebot
const { sheetId, cellsToExtract, filter, ...parsedOptions } =
deepParseVariables(variables)(options)

View File

@@ -1,7 +1,7 @@
import {
SessionState,
GoogleSheetsInsertRowOptions,
ReplyLog,
ChatLog,
} from '@typebot.io/schemas'
import { parseCellValues } from './helpers/parseCellValues'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
@@ -17,7 +17,7 @@ export const insertRow = async (
const { variables } = state.typebotsQueue[0].typebot
if (!options.cellsToInsert || !options.sheetId) return { outgoingEdgeId }
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const doc = await getAuthenticatedGoogleDoc({
credentialsId: options.credentialsId,

View File

@@ -1,7 +1,7 @@
import {
SessionState,
GoogleSheetsUpdateRowOptions,
ReplyLog,
ChatLog,
} from '@typebot.io/schemas'
import { parseCellValues } from './helpers/parseCellValues'
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
@@ -28,7 +28,7 @@ export const updateRow = async (
if (!options.cellsToUpsert || !sheetId || (!referenceCell && !filter))
return { outgoingEdgeId }
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const doc = await getAuthenticatedGoogleDoc({
credentialsId: options.credentialsId,

View File

@@ -1,5 +1,5 @@
import { isNotEmpty } from '@typebot.io/lib/utils'
import { ChatReply } from '@typebot.io/schemas'
import { ContinueChatResponse } from '@typebot.io/schemas'
import { OpenAIBlock } from '@typebot.io/schemas/features/blocks/integrations/openai'
import { HTTPError } from 'got'
import { ClientOptions, OpenAI } from 'openai'
@@ -10,7 +10,7 @@ type Props = Pick<
> & {
apiKey: string
temperature: number | undefined
currentLogs?: ChatReply['logs']
currentLogs?: ContinueChatResponse['logs']
isRetrying?: boolean
} & Pick<NonNullable<OpenAIBlock['options']>, 'apiVersion' | 'baseUrl'>
@@ -25,9 +25,9 @@ export const executeChatCompletionOpenAIRequest = async ({
currentLogs = [],
}: Props): Promise<{
chatCompletion?: OpenAI.Chat.Completions.ChatCompletion
logs?: ChatReply['logs']
logs?: ContinueChatResponse['logs']
}> => {
const logs: ChatReply['logs'] = currentLogs
const logs: ContinueChatResponse['logs'] = currentLogs
if (messages.length === 0) return { logs }
try {
const config = {

View File

@@ -1,5 +1,5 @@
import { byId, isDefined } from '@typebot.io/lib'
import { ChatReply, SessionState } from '@typebot.io/schemas'
import { ContinueChatResponse, SessionState } from '@typebot.io/schemas'
import { ChatCompletionOpenAIOptions } from '@typebot.io/schemas/features/blocks/integrations/openai'
import { VariableWithUnknowValue } from '@typebot.io/schemas/features/typebot/variable'
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
@@ -14,7 +14,7 @@ export const resumeChatCompletion =
}: {
outgoingEdgeId?: string
options: ChatCompletionOpenAIOptions
logs?: ChatReply['logs']
logs?: ContinueChatResponse['logs']
}
) =>
async (message: string, totalTokens?: number) => {

View File

@@ -1,7 +1,7 @@
import { DefaultBotNotificationEmail, render } from '@typebot.io/emails'
import {
AnswerInSessionState,
ReplyLog,
ChatLog,
SendEmailBlock,
SessionState,
SmtpCredentials,
@@ -25,7 +25,7 @@ export const executeSendEmailBlock = async (
state: SessionState,
block: SendEmailBlock
): Promise<ExecuteIntegrationResponse> => {
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const { options } = block
const { typebot, resultId, answers } = state.typebotsQueue[0]
const isPreview = !resultId
@@ -114,8 +114,8 @@ const sendEmail = async ({
typebot: TypebotInSession
answers: AnswerInSessionState[]
fileUrls?: string | string[]
}): Promise<ReplyLog[] | undefined> => {
const logs: ReplyLog[] = []
}): Promise<ChatLog[] | undefined> => {
const logs: ChatLog[] = []
const { name: replyToName } = parseEmailRecipient(replyTo)
const { host, port, isTlsEnabled, username, password, from } =

View File

@@ -8,7 +8,7 @@ import {
Variable,
WebhookResponse,
KeyValue,
ReplyLog,
ChatLog,
ExecutableWebhook,
AnswerInSessionState,
} from '@typebot.io/schemas'
@@ -34,7 +34,7 @@ export const executeWebhookBlock = async (
state: SessionState,
block: WebhookBlock | ZapierBlock | MakeComBlock | PabblyConnectBlock
): Promise<ExecuteIntegrationResponse> => {
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const webhook =
block.options?.webhook ??
('webhookId' in block
@@ -142,8 +142,8 @@ const parseWebhookAttributes =
export const executeWebhook = async (
webhook: ParsedWebhook
): Promise<{ response: WebhookResponse; logs?: ReplyLog[] }> => {
const logs: ReplyLog[] = []
): Promise<{ response: WebhookResponse; logs?: ChatLog[] }> => {
const logs: ChatLog[] = []
const { headers, url, method, basicAuth, body, isJson } = webhook
const contentType = headers ? headers['Content-Type'] : undefined

View File

@@ -2,7 +2,7 @@ import { byId } from '@typebot.io/lib'
import {
MakeComBlock,
PabblyConnectBlock,
ReplyLog,
ChatLog,
VariableWithUnknowValue,
WebhookBlock,
ZapierBlock,
@@ -15,7 +15,7 @@ import { updateVariablesInSession } from '../../../variables/updateVariablesInSe
type Props = {
state: SessionState
block: WebhookBlock | ZapierBlock | MakeComBlock | PabblyConnectBlock
logs?: ReplyLog[]
logs?: ChatLog[]
response: {
statusCode: number
data?: unknown

View File

@@ -3,7 +3,7 @@ import {
TypebotLinkBlock,
SessionState,
Variable,
ReplyLog,
ChatLog,
Edge,
typebotInSessionStateSchema,
TypebotInSession,
@@ -19,7 +19,7 @@ export const executeTypebotLink = async (
state: SessionState,
block: TypebotLinkBlock
): Promise<ExecuteLogicResponse> => {
const logs: ReplyLog[] = []
const logs: ChatLog[] = []
const typebotId = block.options?.typebotId
if (!typebotId) {
logs.push({

View File

@@ -1,7 +1,7 @@
import {
AnswerInSessionState,
Block,
ChatReply,
ContinueChatResponse,
Group,
InputBlock,
SessionState,
@@ -46,7 +46,10 @@ export const continueBotFlow = async (
reply: string | undefined,
{ state, version }: Params
): Promise<
ChatReply & { newSessionState: SessionState; visitedEdges: VisitedEdge[] }
ContinueChatResponse & {
newSessionState: SessionState
visitedEdges: VisitedEdge[]
}
> => {
let firstBubbleWasStreamed = false
let newSessionState = { ...state }
@@ -202,7 +205,9 @@ const saveVariableValueIfAny =
const parseRetryMessage =
(state: SessionState) =>
async (block: InputBlock): Promise<Pick<ChatReply, 'messages' | 'input'>> => {
async (
block: InputBlock
): Promise<Pick<ContinueChatResponse, 'messages' | 'input'>> => {
const retryMessage =
block.options &&
'retryMessageContent' in block.options &&

View File

@@ -1,5 +1,5 @@
import {
ChatReply,
ContinueChatResponse,
Group,
InputBlock,
RuntimeOptions,
@@ -31,7 +31,7 @@ import { VisitedEdge } from '@typebot.io/prisma'
type ContextProps = {
version: 1 | 2
state: SessionState
currentReply?: ChatReply
currentReply?: ContinueChatResponse
currentLastBubbleId?: string
firstBubbleWasStreamed?: boolean
visitedEdges: VisitedEdge[]
@@ -48,12 +48,16 @@ export const executeGroup = async (
firstBubbleWasStreamed,
}: ContextProps
): Promise<
ChatReply & { newSessionState: SessionState; visitedEdges: VisitedEdge[] }
ContinueChatResponse & {
newSessionState: SessionState
visitedEdges: VisitedEdge[]
}
> => {
const messages: ChatReply['messages'] = currentReply?.messages ?? []
let clientSideActions: ChatReply['clientSideActions'] =
const messages: ContinueChatResponse['messages'] =
currentReply?.messages ?? []
let clientSideActions: ContinueChatResponse['clientSideActions'] =
currentReply?.clientSideActions
let logs: ChatReply['logs'] = currentReply?.logs
let logs: ContinueChatResponse['logs'] = currentReply?.logs
let nextEdgeId = null
let lastBubbleBlockId: string | undefined = currentLastBubbleId
@@ -173,7 +177,7 @@ const computeRuntimeOptions =
export const parseInput =
(state: SessionState) =>
async (block: InputBlock): Promise<ChatReply['input']> => {
async (block: InputBlock): Promise<ContinueChatResponse['input']> => {
switch (block.type) {
case InputBlockType.CHOICE: {
return injectVariableValuesInButtonsInputBlock(state)(block)

View File

@@ -1,5 +1,10 @@
import { parseVideoUrl } from '@typebot.io/lib/parseVideoUrl'
import { BubbleBlock, Variable, ChatReply, Typebot } from '@typebot.io/schemas'
import {
BubbleBlock,
Variable,
ContinueChatResponse,
Typebot,
} from '@typebot.io/schemas'
import { deepParseVariables } from './variables/deepParseVariables'
import { isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
import {
@@ -27,7 +32,7 @@ export type BubbleBlockWithDefinedContent = BubbleBlock & {
export const parseBubbleBlock = (
block: BubbleBlockWithDefinedContent,
{ version, variables, typebotVersion }: Params
): ChatReply['messages'][0] => {
): ContinueChatResponse['messages'][0] => {
switch (block.type) {
case BubbleBlockType.TEXT: {
if (version === 1)

View File

@@ -1,9 +1,9 @@
import { SessionState, ChatReply } from '@typebot.io/schemas'
import { SessionState, ContinueChatResponse } from '@typebot.io/schemas'
import { parseVariables } from './variables/parseVariables'
export const parseDynamicTheme = (
state: SessionState | undefined
): ChatReply['dynamicTheme'] => {
): ContinueChatResponse['dynamicTheme'] => {
if (!state?.dynamicTheme) return
return {
hostAvatarUrl: parseVariables(state?.typebotsQueue[0].typebot.variables)(

View File

@@ -1,4 +1,4 @@
import { ChatReply, ChatSession } from '@typebot.io/schemas'
import { ContinueChatResponse, ChatSession } from '@typebot.io/schemas'
import { upsertResult } from './queries/upsertResult'
import { saveLogs } from './queries/saveLogs'
import { updateSession } from './queries/updateSession'
@@ -11,9 +11,9 @@ import { VisitedEdge } from '@typebot.io/prisma'
type Props = {
session: Pick<ChatSession, 'state'> & { id?: string }
input: ChatReply['input']
logs: ChatReply['logs']
clientSideActions: ChatReply['clientSideActions']
input: ContinueChatResponse['input']
logs: ContinueChatResponse['logs']
clientSideActions: ContinueChatResponse['clientSideActions']
visitedEdges: VisitedEdge[]
forceCreateSession?: boolean
}

View File

@@ -1,5 +1,9 @@
import { TRPCError } from '@trpc/server'
import { ChatReply, SessionState, StartElementId } from '@typebot.io/schemas'
import {
ContinueChatResponse,
SessionState,
StartFrom,
} from '@typebot.io/schemas'
import { executeGroup } from './executeGroup'
import { getNextGroup } from './getNextGroup'
import { VisitedEdge } from '@typebot.io/prisma'
@@ -7,20 +11,24 @@ import { VisitedEdge } from '@typebot.io/prisma'
type Props = {
version: 1 | 2
state: SessionState
} & StartElementId
startFrom?: StartFrom
}
export const startBotFlow = async ({
version,
state,
...props
startFrom,
}: Props): Promise<
ChatReply & { newSessionState: SessionState; visitedEdges: VisitedEdge[] }
ContinueChatResponse & {
newSessionState: SessionState
visitedEdges: VisitedEdge[]
}
> => {
let newSessionState = state
const visitedEdges: VisitedEdge[] = []
if ('startGroupId' in props) {
if (startFrom?.type === 'group') {
const group = state.typebotsQueue[0].typebot.groups.find(
(group) => group.id === props.startGroupId
(group) => group.id === startFrom.groupId
)
if (!group)
throw new TRPCError({
@@ -35,7 +43,7 @@ export const startBotFlow = async ({
}
const firstEdgeId = getFirstEdgeId({
state: newSessionState,
startEventId: 'startEventId' in props ? props.startEventId : undefined,
startEventId: startFrom?.type === 'event' ? startFrom.eventId : undefined,
})
if (!firstEdgeId) return { messages: [], newSessionState, visitedEdges: [] }
const nextGroup = await getNextGroup(newSessionState)(firstEdgeId)

View File

@@ -12,13 +12,13 @@ import {
Block,
} from '@typebot.io/schemas'
import {
ChatReply,
StartParams,
StartChatInput,
StartChatResponse,
StartPreviewChatInput,
StartTypebot,
startTypebotSchema,
} from '@typebot.io/schemas/features/chat/schema'
import parse, { NodeType } from 'node-html-parser'
import { env } from '@typebot.io/env'
import { parseDynamicTheme } from './parseDynamicTheme'
import { findTypebot } from './queries/findTypebot'
import { findPublicTypebot } from './queries/findPublicTypebot'
@@ -36,11 +36,19 @@ import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integr
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { VisitedEdge } from '@typebot.io/prisma'
type StartParams =
| ({
type: 'preview'
userId: string
} & StartPreviewChatInput)
| ({
type: 'live'
} & StartChatInput)
type Props = {
version: 1 | 2
message: string | undefined
startParams: StartParams
userId: string | undefined
initialSessionState?: Pick<SessionState, 'whatsApp' | 'expiryTimeout'>
}
@@ -48,26 +56,24 @@ export const startSession = async ({
version,
message,
startParams,
userId,
initialSessionState,
}: Props): Promise<
ChatReply & { newSessionState: SessionState; visitedEdges: VisitedEdge[] }
Omit<StartChatResponse, 'resultId'> & {
newSessionState: SessionState
visitedEdges: VisitedEdge[]
resultId?: string
}
> => {
if (!startParams)
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'StartParams are missing',
})
const typebot = await getTypebot(startParams)
const typebot = await getTypebot(startParams, userId)
const prefilledVariables = startParams.prefilledVariables
? prefillVariables(typebot.variables, startParams.prefilledVariables)
: typebot.variables
const prefilledVariables =
startParams.type === 'live' && startParams.prefilledVariables
? prefillVariables(typebot.variables, startParams.prefilledVariables)
: typebot.variables
const result = await getResult({
...startParams,
isPreview: startParams.isPreview || typeof startParams.typebot !== 'string',
resultId: startParams.type === 'live' ? startParams.resultId : undefined,
isPreview: startParams.type === 'preview',
typebotId: typebot.id,
prefilledVariables,
isRememberUserEnabled:
@@ -148,11 +154,8 @@ export const startSession = async ({
let chatReply = await startBotFlow({
version,
state: initialState,
...('startGroupId' in startParams
? { startGroupId: startParams.startGroupId }
: 'startEventId' in startParams
? { startEventId: startParams.startEventId }
: {}),
startFrom:
startParams.type === 'preview' ? startParams.startFrom : undefined,
})
// If params has message and first block is an input block, we can directly continue the bot flow
@@ -266,20 +269,16 @@ export const startSession = async ({
}
}
const getTypebot = async (
{ typebot, isPreview }: Pick<StartParams, 'typebot' | 'isPreview'>,
userId?: string
): Promise<StartTypebot> => {
if (typeof typebot !== 'string') return typebot
if (isPreview && !userId && !env.NEXT_PUBLIC_E2E_TEST)
throw new TRPCError({
code: 'UNAUTHORIZED',
message:
'You need to authenticate the request to start a bot in preview mode.',
})
const typebotQuery = isPreview
? await findTypebot({ id: typebot, userId })
: await findPublicTypebot({ publicId: typebot })
const getTypebot = async (startParams: StartParams): Promise<StartTypebot> => {
if (startParams.type === 'preview' && startParams.typebot)
return startParams.typebot
const typebotQuery =
startParams.type === 'preview'
? await findTypebot({
id: startParams.typebotId,
userId: startParams.userId,
})
: await findPublicTypebot({ publicId: startParams.publicId })
const parsedTypebot =
typebotQuery && 'typebot' in typebotQuery
@@ -319,7 +318,9 @@ const getResult = async ({
resultId,
prefilledVariables,
isRememberUserEnabled,
}: Pick<StartParams, 'isPreview' | 'resultId'> & {
}: {
resultId: string | undefined
isPreview: boolean
typebotId: string
prefilledVariables: Variable[]
isRememberUserEnabled: boolean
@@ -375,7 +376,7 @@ const parseDynamicThemeInState = (theme: Theme) => {
const parseStartClientSideAction = (
typebot: StartTypebot
): NonNullable<ChatReply['clientSideActions']>[number] | undefined => {
): NonNullable<StartChatResponse['clientSideActions']>[number] | undefined => {
const blocks = typebot.groups.flatMap<Block>((group) => group.blocks)
const pixelBlocks = (
blocks.filter(

View File

@@ -1,16 +1,16 @@
import { ChatReply, SessionState } from '@typebot.io/schemas'
import { ContinueChatResponse, SessionState } from '@typebot.io/schemas'
export type EdgeId = string
export type ExecuteLogicResponse = {
outgoingEdgeId: EdgeId | undefined
newSessionState?: SessionState
} & Pick<ChatReply, 'clientSideActions' | 'logs'>
} & Pick<ContinueChatResponse, 'clientSideActions' | 'logs'>
export type ExecuteIntegrationResponse = {
outgoingEdgeId: EdgeId | undefined
newSessionState?: SessionState
} & Pick<ChatReply, 'clientSideActions' | 'logs'>
} & Pick<ContinueChatResponse, 'clientSideActions' | 'logs'>
export type ParsedReply =
| { status: 'success'; reply: string }

View File

@@ -1,9 +1,9 @@
import { safeStringify } from '@typebot.io/lib/safeStringify'
import { StartParams, Variable } from '@typebot.io/schemas'
import { StartChatInput, Variable } from '@typebot.io/schemas'
export const prefillVariables = (
variables: Variable[],
prefilledVariables: NonNullable<StartParams['prefilledVariables']>
prefilledVariables: NonNullable<StartChatInput['prefilledVariables']>
): Variable[] =>
variables.map((variable) => {
const prefilledVariable = prefilledVariables[variable.name]

View File

@@ -1,4 +1,4 @@
import { ButtonItem, ChatReply } from '@typebot.io/schemas'
import { ButtonItem, ContinueChatResponse } from '@typebot.io/schemas'
import { WhatsAppSendingMessage } from '@typebot.io/schemas/features/whatsapp'
import { convertRichTextToWhatsAppText } from './convertRichTextToWhatsAppText'
import { isDefined, isEmpty } from '@typebot.io/lib/utils'
@@ -8,8 +8,8 @@ import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks
import { defaultChoiceInputOptions } from '@typebot.io/schemas/features/blocks/inputs/choice/constants'
export const convertInputToWhatsAppMessages = (
input: NonNullable<ChatReply['input']>,
lastMessage: ChatReply['messages'][number] | undefined
input: NonNullable<ContinueChatResponse['input']>,
lastMessage: ContinueChatResponse['messages'][number] | undefined
): WhatsAppSendingMessage[] => {
const lastMessageText =
lastMessage?.type === BubbleBlockType.TEXT

View File

@@ -1,4 +1,4 @@
import { ChatReply } from '@typebot.io/schemas'
import { ContinueChatResponse } from '@typebot.io/schemas'
import { WhatsAppSendingMessage } from '@typebot.io/schemas/features/whatsapp'
import { convertRichTextToWhatsAppText } from './convertRichTextToWhatsAppText'
import { isSvgSrc } from '@typebot.io/lib/utils'
@@ -8,7 +8,7 @@ import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubb
const mp4HttpsUrlRegex = /^https:\/\/.*\.mp4$/
export const convertMessageToWhatsAppMessage = (
message: ChatReply['messages'][number]
message: ContinueChatResponse['messages'][number]
): WhatsAppSendingMessage | undefined => {
switch (message.type) {
case BubbleBlockType.TEXT: {

View File

@@ -1,4 +1,8 @@
import { ChatReply, SessionState, Settings } from '@typebot.io/schemas'
import {
ContinueChatResponse,
SessionState,
Settings,
} from '@typebot.io/schemas'
import {
WhatsAppCredentials,
WhatsAppSendingMessage,
@@ -21,7 +25,7 @@ type Props = {
typingEmulation: SessionState['typingEmulation']
credentials: WhatsAppCredentials['data']
state: SessionState
} & Pick<ChatReply, 'messages' | 'input' | 'clientSideActions'>
} & Pick<ContinueChatResponse, 'messages' | 'input' | 'clientSideActions'>
export const sendChatReplyToWhatsApp = async ({
to,
@@ -171,7 +175,9 @@ const getTypingDuration = ({
}
}
const isLastMessageIncludedInInput = (input: ChatReply['input']): boolean => {
const isLastMessageIncludedInInput = (
input: ContinueChatResponse['input']
): boolean => {
if (isNotDefined(input)) return false
return input.type === InputBlockType.CHOICE
}
@@ -179,7 +185,9 @@ const isLastMessageIncludedInInput = (input: ChatReply['input']): boolean => {
const executeClientSideAction =
(context: { to: string; credentials: WhatsAppCredentials['data'] }) =>
async (
clientSideAction: NonNullable<ChatReply['clientSideActions']>[number]
clientSideAction: NonNullable<
ContinueChatResponse['clientSideActions']
>[number]
): Promise<{ replyToSend: string | undefined } | void> => {
if ('wait' in clientSideAction) {
await new Promise((resolve) =>

View File

@@ -1,6 +1,6 @@
import prisma from '@typebot.io/lib/prisma'
import {
ChatReply,
ContinueChatResponse,
PublicTypebot,
SessionState,
Settings,
@@ -20,7 +20,7 @@ import { VisitedEdge } from '@typebot.io/prisma'
type Props = {
incomingMessage?: string
workspaceId?: string
workspaceId: string
credentials: WhatsAppCredentials['data'] & Pick<WhatsAppCredentials, 'id'>
contact: NonNullable<SessionState['whatsApp']>['contact']
}
@@ -31,7 +31,7 @@ export const startWhatsAppSession = async ({
credentials,
contact,
}: Props): Promise<
| (ChatReply & {
| (ContinueChatResponse & {
newSessionState: SessionState
visitedEdges: VisitedEdge[]
})
@@ -89,9 +89,10 @@ export const startWhatsAppSession = async ({
version: 2,
message: incomingMessage,
startParams: {
typebot: publicTypebot.typebot.publicId as string,
type: 'live',
publicId: publicTypebot.typebot.publicId as string,
isOnlyRegistering: false,
},
userId: undefined,
initialSessionState: {
whatsApp: {
contact,