@@ -2,7 +2,7 @@ import { ExecuteLogicResponse } from '@/features/chat/types'
|
||||
import { extractVariablesFromText } from '@/features/variables/extractVariablesFromText'
|
||||
import { parseGuessedValueType } from '@/features/variables/parseGuessedValueType'
|
||||
import { parseVariables } from '@/features/variables/parseVariables'
|
||||
import { ScriptBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { ScriptBlock, SessionState, Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const executeScript = (
|
||||
{ typebot: { variables } }: SessionState,
|
||||
@@ -11,26 +11,37 @@ export const executeScript = (
|
||||
): ExecuteLogicResponse => {
|
||||
if (!block.options.content) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||
|
||||
const content = parseVariables(variables, { fieldToParse: 'id' })(
|
||||
const scriptToExecute = parseScriptToExecuteClientSideAction(
|
||||
variables,
|
||||
block.options.content
|
||||
)
|
||||
const args = extractVariablesFromText(variables)(block.options.content).map(
|
||||
(variable) => ({
|
||||
id: variable.id,
|
||||
value: parseGuessedValueType(variable.value),
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
clientSideActions: [
|
||||
{
|
||||
scriptToExecute: {
|
||||
content,
|
||||
args,
|
||||
},
|
||||
scriptToExecute: scriptToExecute,
|
||||
lastBubbleBlockId,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export const parseScriptToExecuteClientSideAction = (
|
||||
variables: Variable[],
|
||||
contentToEvaluate: string
|
||||
) => {
|
||||
const content = parseVariables(variables, { fieldToParse: 'id' })(
|
||||
contentToEvaluate
|
||||
)
|
||||
const args = extractVariablesFromText(variables)(contentToEvaluate).map(
|
||||
(variable) => ({
|
||||
id: variable.id,
|
||||
value: parseGuessedValueType(variable.value),
|
||||
})
|
||||
)
|
||||
return {
|
||||
content,
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,35 @@ import { ExecuteLogicResponse } from '@/features/chat/types'
|
||||
import { updateVariables } from '@/features/variables/updateVariables'
|
||||
import { parseVariables } from '@/features/variables/parseVariables'
|
||||
import { parseGuessedValueType } from '@/features/variables/parseGuessedValueType'
|
||||
import { parseScriptToExecuteClientSideAction } from '../script/executeScript'
|
||||
|
||||
export const executeSetVariable = async (
|
||||
state: SessionState,
|
||||
block: SetVariableBlock
|
||||
block: SetVariableBlock,
|
||||
lastBubbleBlockId?: string
|
||||
): Promise<ExecuteLogicResponse> => {
|
||||
const { variables } = state.typebot
|
||||
if (!block.options?.variableId)
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
}
|
||||
if (block.options.isExecutedOnClient && block.options.expressionToEvaluate) {
|
||||
const scriptToExecute = parseScriptToExecuteClientSideAction(
|
||||
state.typebot.variables,
|
||||
block.options.expressionToEvaluate
|
||||
)
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
clientSideActions: [
|
||||
{
|
||||
setVariable: {
|
||||
scriptToExecute,
|
||||
},
|
||||
lastBubbleBlockId,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
const evaluatedExpression = block.options.expressionToEvaluate
|
||||
? evaluateSetVariableExpression(variables)(
|
||||
block.options.expressionToEvaluate
|
||||
|
||||
@@ -84,8 +84,13 @@ export const sendMessage = publicProcedure
|
||||
},
|
||||
})
|
||||
|
||||
const containsSetVariableClientSideAction = clientSideActions?.some(
|
||||
(action) => 'setVariable' in action
|
||||
)
|
||||
|
||||
if (
|
||||
!input &&
|
||||
!containsSetVariableClientSideAction &&
|
||||
session.state.result.answers.length > 0 &&
|
||||
session.state.result.id
|
||||
)
|
||||
@@ -149,42 +154,33 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
dynamicTheme: parseDynamicThemeInState(typebot.theme),
|
||||
}
|
||||
|
||||
const {
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
newSessionState: newInitialState,
|
||||
logs,
|
||||
} = await startBotFlow(initialState, startParams.startGroupId)
|
||||
const { messages, input, clientSideActions, newSessionState, logs } =
|
||||
await startBotFlow(initialState, startParams.startGroupId)
|
||||
|
||||
if (!input)
|
||||
const containsSetVariableClientSideAction = clientSideActions?.some(
|
||||
(action) => 'setVariable' in action
|
||||
)
|
||||
|
||||
if (!input && !containsSetVariableClientSideAction)
|
||||
return {
|
||||
messages,
|
||||
clientSideActions,
|
||||
typebot: {
|
||||
id: typebot.id,
|
||||
settings: deepParseVariables(newInitialState.typebot.variables)(
|
||||
settings: deepParseVariables(newSessionState.typebot.variables)(
|
||||
typebot.settings
|
||||
),
|
||||
theme: deepParseVariables(newInitialState.typebot.variables)(
|
||||
theme: deepParseVariables(newSessionState.typebot.variables)(
|
||||
typebot.theme
|
||||
),
|
||||
},
|
||||
dynamicTheme: parseDynamicThemeReply(newInitialState),
|
||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||
logs,
|
||||
}
|
||||
|
||||
const sessionState: ChatSession['state'] = {
|
||||
...newInitialState,
|
||||
currentBlock: {
|
||||
groupId: input.groupId,
|
||||
blockId: input.id,
|
||||
},
|
||||
}
|
||||
|
||||
const session = (await prisma.chatSession.create({
|
||||
data: {
|
||||
state: sessionState,
|
||||
state: newSessionState,
|
||||
},
|
||||
})) as ChatSession
|
||||
|
||||
@@ -193,17 +189,17 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
sessionId: session.id,
|
||||
typebot: {
|
||||
id: typebot.id,
|
||||
settings: deepParseVariables(newInitialState.typebot.variables)(
|
||||
settings: deepParseVariables(newSessionState.typebot.variables)(
|
||||
typebot.settings
|
||||
),
|
||||
theme: deepParseVariables(newInitialState.typebot.variables)(
|
||||
theme: deepParseVariables(newSessionState.typebot.variables)(
|
||||
typebot.theme
|
||||
),
|
||||
},
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
dynamicTheme: parseDynamicThemeReply(newInitialState),
|
||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||
logs,
|
||||
} satisfies ChatReply
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import {
|
||||
ChatReply,
|
||||
InputBlock,
|
||||
InputBlockType,
|
||||
LogicBlockType,
|
||||
ResultInSession,
|
||||
SessionState,
|
||||
SetVariableBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { isInputBlock, isNotDefined } from '@typebot.io/lib'
|
||||
import { isInputBlock, isNotDefined, byId, isDefined } from '@typebot.io/lib'
|
||||
import { executeGroup } from './executeGroup'
|
||||
import { getNextGroup } from './getNextGroup'
|
||||
import { validateEmail } from '@/features/blocks/inputs/email/validateEmail'
|
||||
@@ -27,6 +29,7 @@ export const continueBotFlow =
|
||||
async (
|
||||
reply?: string
|
||||
): Promise<ChatReply & { newSessionState?: SessionState }> => {
|
||||
let newSessionState = { ...state }
|
||||
const group = state.typebot.groups.find(
|
||||
(group) => group.id === state.currentBlock?.groupId
|
||||
)
|
||||
@@ -43,25 +46,37 @@ export const continueBotFlow =
|
||||
message: 'Current block not found',
|
||||
})
|
||||
|
||||
if (!isInputBlock(block))
|
||||
if (block.type === LogicBlockType.SET_VARIABLE && isDefined(reply)) {
|
||||
const existingVariable = state.typebot.variables.find(
|
||||
byId(block.options.variableId)
|
||||
)
|
||||
if (existingVariable) {
|
||||
const newVariable = {
|
||||
...existingVariable,
|
||||
value: reply,
|
||||
}
|
||||
newSessionState = await updateVariables(state)([newVariable])
|
||||
}
|
||||
} else if (!isInputBlock(block))
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Current block is not an input block',
|
||||
})
|
||||
|
||||
if (reply && !isReplyValid(reply, block)) return parseRetryMessage(block)
|
||||
let formattedReply = null
|
||||
|
||||
const formattedReply = formatReply(reply, block.type)
|
||||
if (isInputBlock(block)) {
|
||||
if (reply && !isReplyValid(reply, block)) return parseRetryMessage(block)
|
||||
|
||||
if (!formattedReply && !canSkip(block.type)) {
|
||||
return parseRetryMessage(block)
|
||||
formattedReply = formatReply(reply, block.type)
|
||||
|
||||
if (!formattedReply && !canSkip(block.type)) {
|
||||
return parseRetryMessage(block)
|
||||
}
|
||||
|
||||
newSessionState = await processAndSaveAnswer(state, block)(formattedReply)
|
||||
}
|
||||
|
||||
const newSessionState = await processAndSaveAnswer(
|
||||
state,
|
||||
block
|
||||
)(formattedReply)
|
||||
|
||||
const groupHasMoreBlocks = blockIndex < group.blocks.length - 1
|
||||
|
||||
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
|
||||
@@ -221,7 +236,7 @@ const computeStorageUsed = async (reply: string) => {
|
||||
|
||||
const getOutgoingEdgeId =
|
||||
({ typebot: { variables } }: Pick<SessionState, 'typebot'>) =>
|
||||
(block: InputBlock, reply: string | null) => {
|
||||
(block: InputBlock | SetVariableBlock, reply: string | null) => {
|
||||
if (
|
||||
block.type === InputBlockType.CHOICE &&
|
||||
!block.options.isMultipleChoice &&
|
||||
|
||||
@@ -75,11 +75,31 @@ export const executeGroup =
|
||||
if (
|
||||
'clientSideActions' in executionResponse &&
|
||||
executionResponse.clientSideActions
|
||||
)
|
||||
) {
|
||||
clientSideActions = [
|
||||
...(clientSideActions ?? []),
|
||||
...executionResponse.clientSideActions,
|
||||
]
|
||||
if (
|
||||
executionResponse.clientSideActions?.find(
|
||||
(action) => 'setVariable' in action
|
||||
)
|
||||
) {
|
||||
return {
|
||||
messages,
|
||||
newSessionState: {
|
||||
...newSessionState,
|
||||
currentBlock: {
|
||||
groupId: group.id,
|
||||
blockId: block.id,
|
||||
},
|
||||
},
|
||||
clientSideActions,
|
||||
logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (executionResponse.logs)
|
||||
logs = [...(logs ?? []), ...executionResponse.logs]
|
||||
if (executionResponse.newSessionState)
|
||||
|
||||
@@ -13,7 +13,7 @@ export const executeLogic =
|
||||
async (block: LogicBlock): Promise<ExecuteLogicResponse> => {
|
||||
switch (block.type) {
|
||||
case LogicBlockType.SET_VARIABLE:
|
||||
return executeSetVariable(state, block)
|
||||
return executeSetVariable(state, block, lastBubbleBlockId)
|
||||
case LogicBlockType.CONDITION:
|
||||
return executeCondition(state, block)
|
||||
case LogicBlockType.REDIRECT:
|
||||
|
||||
Reference in New Issue
Block a user