✨ Introducing The Forge (#1072)
The Forge allows anyone to easily create their own Typebot Block. Closes #380
This commit is contained in:
@@ -5,9 +5,9 @@ import {
|
||||
} from '@typebot.io/schemas'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { filterChoiceItems } from './filterChoiceItems'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { transformStringVariablesToList } from '../../../variables/transformVariablesToList'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import { transformVariablesToList } from '@typebot.io/variables/transformVariablesToList'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
|
||||
export const injectVariableValuesInButtonsInputBlock =
|
||||
(state: SessionState) =>
|
||||
@@ -38,7 +38,7 @@ const getVariableValue =
|
||||
(variable: VariableWithValue): (string | null)[] => {
|
||||
if (!Array.isArray(variable.value)) {
|
||||
const { variables } = state.typebotsQueue[0].typebot
|
||||
const [transformedVariable] = transformStringVariablesToList(variables)([
|
||||
const [transformedVariable] = transformVariablesToList(variables)([
|
||||
variable.id,
|
||||
])
|
||||
updateVariablesInSession(state)([transformedVariable])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getPrefilledInputValue } from '../../../getPrefilledValue'
|
||||
import { DateInputBlock, SessionState, Variable } from '@typebot.io/schemas'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const parseDateInput =
|
||||
(state: SessionState) => (block: DateInputBlock) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isNotDefined } from '@typebot.io/lib'
|
||||
import { NumberInputBlock, Variable } from '@typebot.io/schemas'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const validateNumber = (
|
||||
inputValue: string,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '@typebot.io/schemas'
|
||||
import Stripe from 'stripe'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { defaultPaymentInputOptions } from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from '@typebot.io/schemas'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { filterPictureChoiceItems } from './filterPictureChoiceItems'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
|
||||
export const injectVariableValuesInPictureChoiceBlock =
|
||||
(variables: Variable[]) =>
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { ChatwootBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { extractVariablesFromText } from '../../../variables/extractVariablesFromText'
|
||||
import { parseGuessedValueType } from '../../../variables/parseGuessedValueType'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { extractVariablesFromText } from '@typebot.io/variables/extractVariablesFromText'
|
||||
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { defaultChatwootOptions } from '@typebot.io/schemas/features/blocks/integrations/chatwoot/constants'
|
||||
|
||||
const parseSetUserCode = (
|
||||
|
||||
@@ -8,8 +8,8 @@ import { isNotEmpty, byId, isDefined } from '@typebot.io/lib'
|
||||
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { matchFilter } from './helpers/matchFilter'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
|
||||
export const getRow = async (
|
||||
state: SessionState,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Variable, Cell } from '@typebot.io/schemas'
|
||||
import { parseVariables } from '../../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const parseCellValues =
|
||||
(variables: Variable[]) =>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { parseCellValues } from './helpers/parseCellValues'
|
||||
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { matchFilter } from './helpers/matchFilter'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
|
||||
export const updateRow = async (
|
||||
state: SessionState,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { ExecuteIntegrationResponse } from '../../../../types'
|
||||
import { GoogleAnalyticsBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
|
||||
export const executeGoogleAnalyticsBlock = (
|
||||
state: SessionState,
|
||||
@@ -7,12 +7,12 @@ import { isNotEmpty } from '@typebot.io/lib'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { defaultOpenAIOptions } from '@typebot.io/schemas/features/blocks/integrations/openai/constants'
|
||||
import { ExecuteIntegrationResponse } from '../../../../types'
|
||||
import { ExecuteIntegrationResponse } from '../../../../../types'
|
||||
import OpenAI, { ClientOptions } from 'openai'
|
||||
import { uploadFileToBucket } from '@typebot.io/lib/s3/uploadFileToBucket'
|
||||
import { updateVariablesInSession } from '../../../../variables/updateVariablesInSession'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { parseVariables } from '../../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const createSpeechOpenAI = async (
|
||||
state: SessionState,
|
||||
@@ -15,9 +15,9 @@ import { parseChatCompletionMessages } from './parseChatCompletionMessages'
|
||||
import { executeChatCompletionOpenAIRequest } from './executeChatCompletionOpenAIRequest'
|
||||
import { isPlaneteScale } from '@typebot.io/lib/isPlanetScale'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { parseVariableNumber } from '../../../variables/parseVariableNumber'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { ExecuteIntegrationResponse } from '../../../../types'
|
||||
import { parseVariableNumber } from '@typebot.io/variables/parseVariableNumber'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
import {
|
||||
chatCompletionMessageRoles,
|
||||
defaultOpenAIOptions,
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SessionState } from '@typebot.io/schemas'
|
||||
import { OpenAIBlock } from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||
import { createChatCompletionOpenAI } from './createChatCompletionOpenAI'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { ExecuteIntegrationResponse } from '../../../../types'
|
||||
import { createSpeechOpenAI } from './audio/createSpeechOpenAI'
|
||||
|
||||
export const executeOpenAIBlock = async (
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||
import { SessionState } from '@typebot.io/schemas/features/chat/sessionState'
|
||||
import { OpenAIStream } from 'ai'
|
||||
import { parseVariableNumber } from '../../../variables/parseVariableNumber'
|
||||
import { parseVariableNumber } from '@typebot.io/variables/parseVariableNumber'
|
||||
import { ClientOptions, OpenAI } from 'openai'
|
||||
import { defaultOpenAIOptions } from '@typebot.io/schemas/features/blocks/integrations/openai/constants'
|
||||
|
||||
@@ -2,8 +2,8 @@ import { byId, isNotEmpty } from '@typebot.io/lib'
|
||||
import { Variable, VariableWithValue } from '@typebot.io/schemas'
|
||||
import { ChatCompletionOpenAIOptions } from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||
import type { OpenAI } from 'openai'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { transformStringVariablesToList } from '../../../variables/transformVariablesToList'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { transformVariablesToList } from '@typebot.io/variables/transformVariablesToList'
|
||||
|
||||
export const parseChatCompletionMessages =
|
||||
(variables: Variable[]) =>
|
||||
@@ -24,7 +24,7 @@ export const parseChatCompletionMessages =
|
||||
)
|
||||
return
|
||||
variablesTransformedToList.push(
|
||||
...transformStringVariablesToList(variables)([
|
||||
...transformVariablesToList(variables)([
|
||||
message.content.assistantMessagesVariableId,
|
||||
message.content.userMessagesVariableId,
|
||||
])
|
||||
@@ -2,7 +2,7 @@ import { byId, isDefined } from '@typebot.io/lib'
|
||||
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'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
|
||||
export const resumeChatCompletion =
|
||||
(
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PixelBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { deepParseVariables } from '../../../variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
|
||||
export const executePixelBlock = (
|
||||
state: SessionState,
|
||||
|
||||
@@ -14,11 +14,11 @@ import { byId, isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
||||
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||
import { defaultFrom, defaultTransportOptions } from './constants'
|
||||
import { findUniqueVariableValue } from '../../../variables/findUniqueVariableValue'
|
||||
import { findUniqueVariableValue } from '@typebot.io/variables/findUniqueVariableValue'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { defaultSendEmailOptions } from '@typebot.io/schemas/features/blocks/integrations/sendEmail/constants'
|
||||
|
||||
export const executeSendEmailBlock = async (
|
||||
|
||||
@@ -18,7 +18,7 @@ import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||
import got, { Method, HTTPError, OptionsInit } from 'got'
|
||||
import { resumeWebhookExecution } from './resumeWebhookExecution'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import {
|
||||
HttpMethod,
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
} from '@typebot.io/schemas'
|
||||
import { SessionState } from '@typebot.io/schemas/features/chat/sessionState'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
|
||||
type Props = {
|
||||
state: SessionState
|
||||
|
||||
@@ -10,7 +10,7 @@ import { byId, isDefined, isEmpty } from '@typebot.io/lib'
|
||||
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
|
||||
const URL = 'https://api.zemantic.ai/v1/search-documents'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isNotDefined, isDefined } from '@typebot.io/lib'
|
||||
import { Comparison, Condition, Variable } from '@typebot.io/schemas'
|
||||
import { findUniqueVariableValue } from '../../../variables/findUniqueVariableValue'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { findUniqueVariableValue } from '@typebot.io/variables/findUniqueVariableValue'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import {
|
||||
LogicalOperator,
|
||||
ComparisonOperators,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RedirectBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { sanitizeUrl } from '@typebot.io/lib'
|
||||
import { ExecuteLogicResponse } from '../../../types'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const executeRedirect = (
|
||||
state: SessionState,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ExecuteLogicResponse } from '../../../types'
|
||||
import { ScriptBlock, SessionState, Variable } from '@typebot.io/schemas'
|
||||
import { extractVariablesFromText } from '../../../variables/extractVariablesFromText'
|
||||
import { parseGuessedValueType } from '../../../variables/parseGuessedValueType'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { extractVariablesFromText } from '@typebot.io/variables/extractVariablesFromText'
|
||||
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const executeScript = (
|
||||
state: SessionState,
|
||||
|
||||
@@ -2,9 +2,9 @@ import { SessionState, SetVariableBlock, Variable } from '@typebot.io/schemas'
|
||||
import { byId } from '@typebot.io/lib'
|
||||
import { ExecuteLogicResponse } from '../../../types'
|
||||
import { parseScriptToExecuteClientSideAction } from '../script/executeScript'
|
||||
import { parseGuessedValueType } from '../../../variables/parseGuessedValueType'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { updateVariablesInSession } from '../../../variables/updateVariablesInSession'
|
||||
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
|
||||
export const executeSetVariable = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExecuteLogicResponse } from '../../../types'
|
||||
import { SessionState, WaitBlock } from '@typebot.io/schemas'
|
||||
import { parseVariables } from '../../../variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { isNotDefined } from '@typebot.io/lib'
|
||||
|
||||
export const executeWait = (
|
||||
|
||||
@@ -12,7 +12,6 @@ import { getNextGroup } from './getNextGroup'
|
||||
import { validateEmail } from './blocks/inputs/email/validateEmail'
|
||||
import { formatPhoneNumber } from './blocks/inputs/phone/formatPhoneNumber'
|
||||
import { validateUrl } from './blocks/inputs/url/validateUrl'
|
||||
import { resumeChatCompletion } from './blocks/integrations/openai/resumeChatCompletion'
|
||||
import { resumeWebhookExecution } from './blocks/integrations/webhook/resumeWebhookExecution'
|
||||
import { upsertAnswer } from './queries/upsertAnswer'
|
||||
import { parseButtonsReply } from './blocks/inputs/buttons/parseButtonsReply'
|
||||
@@ -21,8 +20,8 @@ import { validateNumber } from './blocks/inputs/number/validateNumber'
|
||||
import { parseDateReply } from './blocks/inputs/date/parseDateReply'
|
||||
import { validateRatingReply } from './blocks/inputs/rating/validateRatingReply'
|
||||
import { parsePictureChoicesReply } from './blocks/inputs/pictureChoice/parsePictureChoicesReply'
|
||||
import { parseVariables } from './variables/parseVariables'
|
||||
import { updateVariablesInSession } from './variables/updateVariablesInSession'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
import { startBotFlow } from './startBotFlow'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { parseNumber } from './blocks/inputs/number/parseNumber'
|
||||
@@ -37,6 +36,9 @@ import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks
|
||||
import { defaultFileInputOptions } from '@typebot.io/schemas/features/blocks/inputs/file/constants'
|
||||
import { VisitedEdge } from '@typebot.io/prisma'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
import { ForgedBlock, forgedBlocks } from '@typebot.io/forge-schemas'
|
||||
import { enabledBlocks } from '@typebot.io/forge-repository'
|
||||
import { resumeChatCompletion } from './blocks/integrations/legacy/openai/resumeChatCompletion'
|
||||
|
||||
type Params = {
|
||||
version: 1 | 2
|
||||
@@ -80,14 +82,9 @@ export const continueBotFlow = async (
|
||||
}
|
||||
newSessionState = updateVariablesInSession(state)([newVariable])
|
||||
}
|
||||
} else if (reply && block.type === IntegrationBlockType.WEBHOOK) {
|
||||
const result = resumeWebhookExecution({
|
||||
state,
|
||||
block,
|
||||
response: JSON.parse(reply),
|
||||
})
|
||||
if (result.newSessionState) newSessionState = result.newSessionState
|
||||
} else if (
|
||||
}
|
||||
// Legacy
|
||||
else if (
|
||||
block.type === IntegrationBlockType.OPEN_AI &&
|
||||
block.options?.task === 'Create chat completion'
|
||||
) {
|
||||
@@ -99,6 +96,58 @@ export const continueBotFlow = async (
|
||||
})(reply)
|
||||
newSessionState = result.newSessionState
|
||||
}
|
||||
} else if (reply && block.type === IntegrationBlockType.WEBHOOK) {
|
||||
const result = resumeWebhookExecution({
|
||||
state,
|
||||
block,
|
||||
response: JSON.parse(reply),
|
||||
})
|
||||
if (result.newSessionState) newSessionState = result.newSessionState
|
||||
} else if (
|
||||
enabledBlocks.includes(block.type as (typeof enabledBlocks)[number])
|
||||
) {
|
||||
if (reply) {
|
||||
const options = (block as ForgedBlock).options
|
||||
const action = forgedBlocks
|
||||
.find((b) => b.id === block.type)
|
||||
?.actions.find((a) => a.name === options?.action)
|
||||
if (action) {
|
||||
if (action.run?.stream?.getStreamVariableId) {
|
||||
firstBubbleWasStreamed = true
|
||||
const variableToUpdate =
|
||||
state.typebotsQueue[0].typebot.variables.find(
|
||||
(v) => v.id === action?.run?.stream?.getStreamVariableId(options)
|
||||
)
|
||||
if (variableToUpdate)
|
||||
newSessionState = updateVariablesInSession(state)([
|
||||
{
|
||||
...variableToUpdate,
|
||||
value: reply,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
if (
|
||||
action.run?.web?.displayEmbedBubble?.waitForEvent?.getSaveVariableId
|
||||
) {
|
||||
const variableToUpdate =
|
||||
state.typebotsQueue[0].typebot.variables.find(
|
||||
(v) =>
|
||||
v.id ===
|
||||
action?.run?.web?.displayEmbedBubble?.waitForEvent?.getSaveVariableId?.(
|
||||
options
|
||||
)
|
||||
)
|
||||
if (variableToUpdate)
|
||||
newSessionState = updateVariablesInSession(state)([
|
||||
{
|
||||
...variableToUpdate,
|
||||
value: reply,
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let formattedReply: string | undefined
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
SessionState,
|
||||
} from '@typebot.io/schemas'
|
||||
import {
|
||||
createId,
|
||||
isBubbleBlock,
|
||||
isInputBlock,
|
||||
isIntegrationBlock,
|
||||
@@ -20,7 +21,7 @@ import { injectVariableValuesInButtonsInputBlock } from './blocks/inputs/buttons
|
||||
import { injectVariableValuesInPictureChoiceBlock } from './blocks/inputs/pictureChoice/injectVariableValuesInPictureChoiceBlock'
|
||||
import { getPrefilledInputValue } from './getPrefilledValue'
|
||||
import { parseDateInput } from './blocks/inputs/date/parseDateInput'
|
||||
import { deepParseVariables } from './variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import {
|
||||
BubbleBlockWithDefinedContent,
|
||||
parseBubbleBlock,
|
||||
@@ -139,10 +140,21 @@ export const executeGroup = async (
|
||||
lastBubbleBlockId,
|
||||
})),
|
||||
]
|
||||
if (
|
||||
'customEmbedBubble' in executionResponse &&
|
||||
executionResponse.customEmbedBubble
|
||||
) {
|
||||
messages.push({
|
||||
id: createId(),
|
||||
...executionResponse.customEmbedBubble,
|
||||
})
|
||||
}
|
||||
if (
|
||||
executionResponse.clientSideActions?.find(
|
||||
(action) => action.expectsDedicatedReply
|
||||
)
|
||||
) ||
|
||||
('customEmbedBubble' in executionResponse &&
|
||||
executionResponse.customEmbedBubble)
|
||||
) {
|
||||
return {
|
||||
messages,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { executeOpenAIBlock } from './blocks/integrations/openai/executeOpenAIBlock'
|
||||
import { executeSendEmailBlock } from './blocks/integrations/sendEmail/executeSendEmailBlock'
|
||||
import { executeWebhookBlock } from './blocks/integrations/webhook/executeWebhookBlock'
|
||||
import { executeChatwootBlock } from './blocks/integrations/chatwoot/executeChatwootBlock'
|
||||
import { executeGoogleAnalyticsBlock } from './blocks/integrations/googleAnalytics/executeGoogleAnalyticsBlock'
|
||||
import { executeGoogleAnalyticsBlock } from './blocks/integrations/legacy/googleAnalytics/executeGoogleAnalyticsBlock'
|
||||
import { executeGoogleSheetBlock } from './blocks/integrations/googleSheets/executeGoogleSheetBlock'
|
||||
import { executePixelBlock } from './blocks/integrations/pixel/executePixelBlock'
|
||||
import { executeZemanticAiBlock } from './blocks/integrations/zemanticAi/executeZemanticAiBlock'
|
||||
import { IntegrationBlock, SessionState } from '@typebot.io/schemas'
|
||||
import { ExecuteIntegrationResponse } from './types'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
import { executeOpenAIBlock } from './blocks/integrations/legacy/openai/executeOpenAIBlock'
|
||||
import { executeForgedBlock } from './forge/executeForgedBlock'
|
||||
|
||||
export const executeIntegration =
|
||||
(state: SessionState) =>
|
||||
@@ -33,5 +34,7 @@ export const executeIntegration =
|
||||
return executePixelBlock(state, block)
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return executeZemanticAiBlock(state, block)
|
||||
default:
|
||||
return executeForgedBlock(state, block)
|
||||
}
|
||||
}
|
||||
|
||||
207
packages/bot-engine/forge/executeForgedBlock.ts
Normal file
207
packages/bot-engine/forge/executeForgedBlock.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
import { VariableStore, LogsStore } from '@typebot.io/forge'
|
||||
import { ForgedBlock, forgedBlocks } from '@typebot.io/forge-schemas'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||
import { isPlaneteScale } from '@typebot.io/lib/isPlanetScale'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import {
|
||||
SessionState,
|
||||
ContinueChatResponse,
|
||||
Block,
|
||||
TypebotInSession,
|
||||
} from '@typebot.io/schemas'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import {
|
||||
ParseVariablesOptions,
|
||||
parseVariables,
|
||||
} from '@typebot.io/variables/parseVariables'
|
||||
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
|
||||
import { ExecuteIntegrationResponse } from '../types'
|
||||
import { byId } from '@typebot.io/lib'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
|
||||
export const executeForgedBlock = async (
|
||||
state: SessionState,
|
||||
block: ForgedBlock
|
||||
): Promise<ExecuteIntegrationResponse> => {
|
||||
const blockDef = forgedBlocks.find((b) => b.id === block.type)
|
||||
if (!blockDef) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||
const action = blockDef.actions.find((a) => a.name === block.options.action)
|
||||
const noCredentialsError = {
|
||||
status: 'error',
|
||||
description: 'Credentials not provided for integration',
|
||||
}
|
||||
|
||||
let credentials: { data: string; iv: string } | null = null
|
||||
if (blockDef.auth) {
|
||||
if (!block.options.credentialsId) {
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
logs: [noCredentialsError],
|
||||
}
|
||||
}
|
||||
credentials = await prisma.credentials.findUnique({
|
||||
where: {
|
||||
id: block.options.credentialsId,
|
||||
},
|
||||
})
|
||||
if (!credentials) {
|
||||
console.error('Could not find credentials in database')
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
logs: [noCredentialsError],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typebot = state.typebotsQueue[0].typebot
|
||||
if (
|
||||
action?.run?.stream &&
|
||||
isPlaneteScale() &&
|
||||
credentials &&
|
||||
isCredentialsV2(credentials) &&
|
||||
state.isStreamEnabled &&
|
||||
!state.whatsApp &&
|
||||
isNextBubbleTextWithStreamingVar(typebot)(
|
||||
block.id,
|
||||
action.run.stream.getStreamVariableId(block.options)
|
||||
)
|
||||
) {
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
clientSideActions: [
|
||||
{
|
||||
expectsDedicatedReply: true,
|
||||
stream: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
let newSessionState = state
|
||||
|
||||
const variables: VariableStore = {
|
||||
get: (id: string) => {
|
||||
const variable = newSessionState.typebotsQueue[0].typebot.variables.find(
|
||||
(variable) => variable.id === id
|
||||
)
|
||||
return variable?.value
|
||||
},
|
||||
set: (id: string, value: unknown) => {
|
||||
const variable = newSessionState.typebotsQueue[0].typebot.variables.find(
|
||||
(variable) => variable.id === id
|
||||
)
|
||||
if (!variable) return
|
||||
newSessionState = updateVariablesInSession(newSessionState)([
|
||||
{ ...variable, value },
|
||||
])
|
||||
},
|
||||
parse: (text: string, params?: ParseVariablesOptions) =>
|
||||
parseVariables(
|
||||
newSessionState.typebotsQueue[0].typebot.variables,
|
||||
params
|
||||
)(text),
|
||||
}
|
||||
let logs: NonNullable<ContinueChatResponse['logs']> = []
|
||||
const logsStore: LogsStore = {
|
||||
add: (log) => {
|
||||
if (typeof log === 'string') {
|
||||
logs.push({
|
||||
status: 'error',
|
||||
description: log,
|
||||
})
|
||||
return
|
||||
}
|
||||
logs.push(log)
|
||||
},
|
||||
}
|
||||
const credentialsData = credentials
|
||||
? await decrypt(credentials.data, credentials.iv)
|
||||
: undefined
|
||||
|
||||
const parsedOptions = deepParseVariables(
|
||||
state.typebotsQueue[0].typebot.variables
|
||||
)(block.options)
|
||||
await action?.run?.server?.({
|
||||
credentials: credentialsData ?? {},
|
||||
options: parsedOptions,
|
||||
variables,
|
||||
logs: logsStore,
|
||||
})
|
||||
|
||||
const clientSideActions: ExecuteIntegrationResponse['clientSideActions'] = []
|
||||
|
||||
if (
|
||||
action?.run?.web?.parseFunction &&
|
||||
(state.typebotsQueue[0].resultId || !blockDef.isDisabledInPreview)
|
||||
) {
|
||||
clientSideActions.push({
|
||||
codeToExecute: action?.run?.web?.parseFunction({
|
||||
options: parsedOptions,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
newSessionState,
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
logs,
|
||||
clientSideActions,
|
||||
customEmbedBubble: action?.run?.web?.displayEmbedBubble
|
||||
? {
|
||||
type: 'custom-embed',
|
||||
content: {
|
||||
initFunction: action.run.web.displayEmbedBubble.parseInitFunction({
|
||||
options: parsedOptions,
|
||||
}),
|
||||
waitForEventFunction:
|
||||
action.run.web.displayEmbedBubble.waitForEvent?.parseFunction?.({
|
||||
options: parsedOptions,
|
||||
}),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
const isNextBubbleTextWithStreamingVar =
|
||||
(typebot: TypebotInSession) =>
|
||||
(blockId: string, streamVariableId?: string): boolean => {
|
||||
const streamVariable = typebot.variables.find(
|
||||
(variable) => variable.id === streamVariableId
|
||||
)
|
||||
if (!streamVariable) return false
|
||||
const nextBlock = getNextBlock(typebot)(blockId)
|
||||
if (!nextBlock) return false
|
||||
return (
|
||||
nextBlock.type === BubbleBlockType.TEXT &&
|
||||
(nextBlock.content?.richText?.length ?? 0) > 0 &&
|
||||
nextBlock.content?.richText?.at(0)?.children.at(0).text ===
|
||||
`{{${streamVariable.name}}}`
|
||||
)
|
||||
}
|
||||
|
||||
const getNextBlock =
|
||||
(typebot: TypebotInSession) =>
|
||||
(blockId: string): Block | undefined => {
|
||||
const group = typebot.groups.find((group) =>
|
||||
group.blocks.find(byId(blockId))
|
||||
)
|
||||
if (!group) return
|
||||
const blockIndex = group.blocks.findIndex(byId(blockId))
|
||||
const nextBlockInGroup = group.blocks.at(blockIndex + 1)
|
||||
if (nextBlockInGroup) return nextBlockInGroup
|
||||
const outgoingEdgeId = group.blocks.at(blockIndex)?.outgoingEdgeId
|
||||
if (!outgoingEdgeId) return
|
||||
const outgoingEdge = typebot.edges.find(byId(outgoingEdgeId))
|
||||
if (!outgoingEdge) return
|
||||
const connectedGroup = typebot.groups.find(byId(outgoingEdge?.to.groupId))
|
||||
if (!connectedGroup) return
|
||||
return outgoingEdge.to.blockId
|
||||
? connectedGroup.blocks.find(
|
||||
(block) => block.id === outgoingEdge.to.blockId
|
||||
)
|
||||
: connectedGroup?.blocks.at(0)
|
||||
}
|
||||
|
||||
const isCredentialsV2 = (credentials: { iv: string }) =>
|
||||
credentials.iv.length === 24
|
||||
@@ -16,6 +16,7 @@
|
||||
"@typebot.io/prisma": "workspace:*",
|
||||
"@typebot.io/schemas": "workspace:*",
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"@typebot.io/variables": "workspace:*",
|
||||
"@udecode/plate-common": "21.1.5",
|
||||
"@udecode/plate-serializer-md": "24.4.0",
|
||||
"ai": "2.2.24",
|
||||
@@ -34,6 +35,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/nodemailer": "6.4.8",
|
||||
"@types/qs": "6.9.7"
|
||||
"@types/qs": "6.9.7",
|
||||
"@typebot.io/forge-schemas": "workspace:*",
|
||||
"@typebot.io/forge": "workspace:*",
|
||||
"@typebot.io/forge-repository": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import {
|
||||
ContinueChatResponse,
|
||||
Typebot,
|
||||
} from '@typebot.io/schemas'
|
||||
import { deepParseVariables } from './variables/deepParseVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import { isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
|
||||
import {
|
||||
getVariablesToParseInfoInText,
|
||||
parseVariables,
|
||||
} from './variables/parseVariables'
|
||||
} from '@typebot.io/variables/parseVariables'
|
||||
import { TDescendant, createPlateEditor } from '@udecode/plate-common'
|
||||
import {
|
||||
createDeserializeMdPlugin,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SessionState, ContinueChatResponse } from '@typebot.io/schemas'
|
||||
import { parseVariables } from './variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
|
||||
export const parseDynamicTheme = (
|
||||
state: SessionState | undefined
|
||||
|
||||
@@ -24,18 +24,20 @@ import { findTypebot } from './queries/findTypebot'
|
||||
import { findPublicTypebot } from './queries/findPublicTypebot'
|
||||
import { findResult } from './queries/findResult'
|
||||
import { startBotFlow } from './startBotFlow'
|
||||
import { prefillVariables } from './variables/prefillVariables'
|
||||
import { deepParseVariables } from './variables/deepParseVariables'
|
||||
import { injectVariablesFromExistingResult } from './variables/injectVariablesFromExistingResult'
|
||||
import { prefillVariables } from '@typebot.io/variables/prefillVariables'
|
||||
import { deepParseVariables } from '@typebot.io/variables/deepParseVariables'
|
||||
import { injectVariablesFromExistingResult } from '@typebot.io/variables/injectVariablesFromExistingResult'
|
||||
import { getNextGroup } from './getNextGroup'
|
||||
import { upsertResult } from './queries/upsertResult'
|
||||
import { continueBotFlow } from './continueBotFlow'
|
||||
import { parseVariables } from './variables/parseVariables'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
import { VisitedEdge } from '@typebot.io/prisma'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { forgedBlocks } from '@typebot.io/forge-schemas'
|
||||
import { FunctionToExecute } from '@typebot.io/forge'
|
||||
|
||||
type StartParams =
|
||||
| ({
|
||||
@@ -425,9 +427,7 @@ const parseStartClientSideAction = (
|
||||
)
|
||||
return
|
||||
|
||||
return {
|
||||
startPropsToInject,
|
||||
}
|
||||
return { startPropsToInject }
|
||||
}
|
||||
|
||||
const sanitizeAndParseTheme = (
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ContinueChatResponse, SessionState } from '@typebot.io/schemas'
|
||||
import {
|
||||
ContinueChatResponse,
|
||||
CustomEmbedBubble,
|
||||
SessionState,
|
||||
} from '@typebot.io/schemas'
|
||||
|
||||
export type EdgeId = string
|
||||
|
||||
@@ -11,6 +15,7 @@ export type ExecuteIntegrationResponse = {
|
||||
outgoingEdgeId: EdgeId | undefined
|
||||
newSessionState?: SessionState
|
||||
startTimeShouldBeUpdated?: boolean
|
||||
customEmbedBubble?: CustomEmbedBubble
|
||||
} & Pick<ContinueChatResponse, 'clientSideActions' | 'logs'>
|
||||
|
||||
export type ParsedReply =
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import {
|
||||
defaultParseVariablesOptions,
|
||||
parseVariables,
|
||||
ParseVariablesOptions,
|
||||
} from './parseVariables'
|
||||
import { parseGuessedTypeFromString } from './parseGuessedTypeFromString'
|
||||
|
||||
type DeepParseOptions = {
|
||||
guessCorrectTypes?: boolean
|
||||
removeEmptyStrings?: boolean
|
||||
}
|
||||
|
||||
export const deepParseVariables =
|
||||
(
|
||||
variables: Variable[],
|
||||
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,
|
||||
parseVariablesOptions
|
||||
)(currentValue)
|
||||
if (deepParseOptions.removeEmptyStrings && parsedVariable === '')
|
||||
return newObj
|
||||
return {
|
||||
...newObj,
|
||||
[key]: deepParseOptions.guessCorrectTypes
|
||||
? parseGuessedTypeFromString(parsedVariable)
|
||||
: parsedVariable,
|
||||
}
|
||||
}
|
||||
|
||||
if (currentValue instanceof Object && currentValue.constructor === Object)
|
||||
return {
|
||||
...newObj,
|
||||
[key]: deepParseVariables(
|
||||
variables,
|
||||
deepParseOptions,
|
||||
parseVariablesOptions
|
||||
)(currentValue as Record<string, unknown>),
|
||||
}
|
||||
|
||||
if (currentValue instanceof Array)
|
||||
return {
|
||||
...newObj,
|
||||
[key]: currentValue.map(
|
||||
deepParseVariables(
|
||||
variables,
|
||||
deepParseOptions,
|
||||
parseVariablesOptions
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
return { ...newObj, [key]: currentValue }
|
||||
}, {} as T)
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const extractVariablesFromText =
|
||||
(variables: Variable[]) =>
|
||||
(text: string): Variable[] => {
|
||||
const matches = [...text.matchAll(/\{\{(.*?)\}\}/g)]
|
||||
return matches.reduce<Variable[]>((acc, match) => {
|
||||
const variableName = match[1]
|
||||
const variable = variables.find(
|
||||
(variable) => variable.name === variableName
|
||||
)
|
||||
if (
|
||||
!variable ||
|
||||
acc.find((accVariable) => accVariable.id === variable.id)
|
||||
)
|
||||
return acc
|
||||
return [...acc, variable]
|
||||
}, [])
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const findUniqueVariableValue =
|
||||
(variables: Variable[]) =>
|
||||
(value: string | undefined): Variable['value'] => {
|
||||
if (!value || !value.startsWith('{{') || !value.endsWith('}}')) return null
|
||||
const variableName = value.slice(2, -2)
|
||||
const variable = variables.find(
|
||||
(variable) => variable.name === variableName
|
||||
)
|
||||
return variable?.value ?? null
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export const hasVariable = (str: string): boolean => /\{\{(.*?)\}\}/g.test(str)
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Result, Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const injectVariablesFromExistingResult = (
|
||||
variables: Variable[],
|
||||
resultVariables: Result['variables']
|
||||
): Variable[] =>
|
||||
variables.map((variable) => {
|
||||
const resultVariable = resultVariables.find(
|
||||
(resultVariable) =>
|
||||
resultVariable.name === variable.name && !variable.value
|
||||
)
|
||||
if (!resultVariable) return variable
|
||||
return {
|
||||
...variable,
|
||||
value: resultVariable.value,
|
||||
}
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const parseGuessedValueType = (
|
||||
value: Variable['value']
|
||||
): string | (string | null)[] | boolean | number | null | undefined => {
|
||||
if (value === null) return null
|
||||
if (value === undefined) return undefined
|
||||
if (typeof value !== 'string') return value
|
||||
const isStartingWithZero =
|
||||
value.startsWith('0') && !value.startsWith('0.') && value.length > 1
|
||||
if (typeof value === 'string' && isStartingWithZero) return value
|
||||
const isStartingWithPlus = value.startsWith('+')
|
||||
if (typeof value === 'string' && isStartingWithPlus) return value
|
||||
if (typeof value === 'number') return value
|
||||
if (value === 'true') return true
|
||||
if (value === 'false') return false
|
||||
if (value === 'null') return null
|
||||
if (value === 'undefined') return undefined
|
||||
// isNaN works with strings
|
||||
if (isNaN(value as unknown as number)) return value
|
||||
return Number(value)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import { parseGuessedValueType } from './parseGuessedValueType'
|
||||
import { parseVariables } from './parseVariables'
|
||||
|
||||
export const parseVariableNumber =
|
||||
(variables: Variable[]) =>
|
||||
(input: number | `{{${string}}}` | undefined): number | undefined => {
|
||||
if (typeof input === 'number' || input === undefined) return input
|
||||
const parsedInput = parseGuessedValueType(parseVariables(variables)(input))
|
||||
if (typeof parsedInput !== 'number') return undefined
|
||||
return parsedInput
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
import { safeStringify } from '@typebot.io/lib/safeStringify'
|
||||
import { isDefined, isNotDefined } from '@typebot.io/lib/utils'
|
||||
import { Variable, VariableWithValue } from '@typebot.io/schemas'
|
||||
import { parseGuessedValueType } from './parseGuessedValueType'
|
||||
|
||||
export type ParseVariablesOptions = {
|
||||
fieldToParse?: 'value' | 'id'
|
||||
isInsideJson?: boolean
|
||||
takeLatestIfList?: boolean
|
||||
isInsideHtml?: boolean
|
||||
}
|
||||
|
||||
export const defaultParseVariablesOptions: ParseVariablesOptions = {
|
||||
fieldToParse: 'value',
|
||||
isInsideJson: false,
|
||||
takeLatestIfList: false,
|
||||
isInsideHtml: false,
|
||||
}
|
||||
|
||||
// {{= inline code =}}
|
||||
const inlineCodeRegex = /\{\{=(.+?)=\}\}/g
|
||||
|
||||
// {{variable}} and ${{{variable}}}
|
||||
const variableRegex = /\{\{([^{}]+)\}\}|(\$)\{\{([^{}]+)\}\}/g
|
||||
|
||||
export const parseVariables =
|
||||
(
|
||||
variables: Variable[],
|
||||
options: ParseVariablesOptions = defaultParseVariablesOptions
|
||||
) =>
|
||||
(text: string | undefined): string => {
|
||||
if (!text || text === '') return ''
|
||||
const textWithInlineCodeParsed = text.replace(
|
||||
inlineCodeRegex,
|
||||
(_full, inlineCodeToEvaluate) =>
|
||||
evaluateInlineCode(inlineCodeToEvaluate, { variables })
|
||||
)
|
||||
|
||||
return textWithInlineCodeParsed.replace(
|
||||
variableRegex,
|
||||
(_full, nameInCurlyBraces, _dollarSign, nameInTemplateLitteral) => {
|
||||
const dollarSign = (_dollarSign ?? '') as string
|
||||
const matchedVarName = nameInCurlyBraces ?? nameInTemplateLitteral
|
||||
const variable = variables.find((variable) => {
|
||||
return (
|
||||
matchedVarName === variable.name &&
|
||||
(options.fieldToParse === 'id' || isDefined(variable.value))
|
||||
)
|
||||
}) as VariableWithValue | undefined
|
||||
if (!variable) return dollarSign + ''
|
||||
if (options.fieldToParse === 'id') return dollarSign + variable.id
|
||||
const { value } = variable
|
||||
if (options.isInsideJson)
|
||||
return dollarSign + parseVariableValueInJson(value)
|
||||
const parsedValue =
|
||||
dollarSign +
|
||||
safeStringify(
|
||||
options.takeLatestIfList && Array.isArray(value)
|
||||
? value[value.length - 1]
|
||||
: value
|
||||
)
|
||||
if (!parsedValue) return dollarSign + ''
|
||||
if (options.isInsideHtml) return parseVariableValueInHtml(parsedValue)
|
||||
return parsedValue
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const evaluateInlineCode = (
|
||||
code: string,
|
||||
{ variables }: { variables: Variable[] }
|
||||
) => {
|
||||
const evaluating = parseVariables(variables, { fieldToParse: 'id' })(
|
||||
code.includes('return ') ? code : `return ${code}`
|
||||
)
|
||||
try {
|
||||
const func = Function(...variables.map((v) => v.id), evaluating)
|
||||
return func(...variables.map((v) => parseGuessedValueType(v.value)))
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return parseVariables(variables)(code)
|
||||
}
|
||||
}
|
||||
|
||||
type VariableToParseInformation = {
|
||||
startIndex: number
|
||||
endIndex: number
|
||||
textToReplace: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export const getVariablesToParseInfoInText = (
|
||||
text: string,
|
||||
{
|
||||
variables,
|
||||
takeLatestIfList,
|
||||
}: { variables: Variable[]; takeLatestIfList?: boolean }
|
||||
): VariableToParseInformation[] => {
|
||||
const variablesToParseInfo: VariableToParseInformation[] = []
|
||||
const inlineCodeMatches = [...text.matchAll(inlineCodeRegex)]
|
||||
inlineCodeMatches.forEach((match) => {
|
||||
if (isNotDefined(match.index) || !match[0].length) return
|
||||
const inlineCodeToEvaluate = match[1]
|
||||
const evaluatedValue = evaluateInlineCode(inlineCodeToEvaluate, {
|
||||
variables,
|
||||
})
|
||||
variablesToParseInfo.push({
|
||||
startIndex: match.index,
|
||||
endIndex: match.index + match[0].length,
|
||||
textToReplace: match[0],
|
||||
value:
|
||||
safeStringify(
|
||||
takeLatestIfList && Array.isArray(evaluatedValue)
|
||||
? evaluatedValue[evaluatedValue.length - 1]
|
||||
: evaluatedValue
|
||||
) ?? '',
|
||||
})
|
||||
})
|
||||
const textWithInlineCodeParsed = text.replace(
|
||||
inlineCodeRegex,
|
||||
(_full, inlineCodeToEvaluate) =>
|
||||
evaluateInlineCode(inlineCodeToEvaluate, { variables })
|
||||
)
|
||||
const variableMatches = [...textWithInlineCodeParsed.matchAll(variableRegex)]
|
||||
variableMatches.forEach((match) => {
|
||||
if (isNotDefined(match.index) || !match[0].length) return
|
||||
const matchedVarName = match[1] ?? match[3]
|
||||
const variable = variables.find((variable) => {
|
||||
return matchedVarName === variable.name && isDefined(variable.value)
|
||||
}) as VariableWithValue | undefined
|
||||
variablesToParseInfo.push({
|
||||
startIndex: match.index,
|
||||
endIndex: match.index + match[0].length,
|
||||
textToReplace: match[0],
|
||||
value:
|
||||
safeStringify(
|
||||
takeLatestIfList && Array.isArray(variable?.value)
|
||||
? variable?.value[variable?.value.length - 1]
|
||||
: variable?.value
|
||||
) ?? '',
|
||||
})
|
||||
})
|
||||
return variablesToParseInfo.sort((a, b) => a.startIndex - b.startIndex)
|
||||
}
|
||||
|
||||
const parseVariableValueInJson = (value: VariableWithValue['value']) => {
|
||||
const stringifiedValue = JSON.stringify(value)
|
||||
if (typeof value === 'string') return stringifiedValue.slice(1, -1)
|
||||
return stringifiedValue
|
||||
}
|
||||
|
||||
const parseVariableValueInHtml = (
|
||||
value: VariableWithValue['value']
|
||||
): string => {
|
||||
if (typeof value === 'string')
|
||||
return value.replace(/</g, '<').replace(/>/g, '>')
|
||||
return JSON.stringify(value).replace(/</g, '<').replace(/>/g, '>')
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { safeStringify } from '@typebot.io/lib/safeStringify'
|
||||
import { StartChatInput, Variable } from '@typebot.io/schemas'
|
||||
|
||||
export const prefillVariables = (
|
||||
variables: Variable[],
|
||||
prefilledVariables: NonNullable<StartChatInput['prefilledVariables']>
|
||||
): Variable[] =>
|
||||
variables.map((variable) => {
|
||||
const prefilledVariable = prefilledVariables[variable.name]
|
||||
if (!prefilledVariable) return variable
|
||||
return {
|
||||
...variable,
|
||||
value: safeStringify(prefilledVariable),
|
||||
}
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
import { isNotDefined } from '@typebot.io/lib/utils'
|
||||
import { Variable, VariableWithValue } from '@typebot.io/schemas'
|
||||
|
||||
export const transformStringVariablesToList =
|
||||
(variables: Variable[]) =>
|
||||
(variableIds: string[]): VariableWithValue[] => {
|
||||
const newVariables = variables.reduce<VariableWithValue[]>(
|
||||
(variables, variable) => {
|
||||
if (
|
||||
!variableIds.includes(variable.id) ||
|
||||
isNotDefined(variable.value) ||
|
||||
typeof variable.value !== 'string'
|
||||
)
|
||||
return variables
|
||||
return [
|
||||
...variables,
|
||||
{
|
||||
...variable,
|
||||
value: [variable.value],
|
||||
},
|
||||
]
|
||||
},
|
||||
[]
|
||||
)
|
||||
return newVariables
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { safeStringify } from '@typebot.io/lib/safeStringify'
|
||||
import {
|
||||
SessionState,
|
||||
VariableWithUnknowValue,
|
||||
Variable,
|
||||
} from '@typebot.io/schemas'
|
||||
|
||||
export const updateVariablesInSession =
|
||||
(state: SessionState) =>
|
||||
(newVariables: VariableWithUnknowValue[]): SessionState => ({
|
||||
...state,
|
||||
typebotsQueue: state.typebotsQueue.map((typebotInQueue, index) =>
|
||||
index === 0
|
||||
? {
|
||||
...typebotInQueue,
|
||||
typebot: {
|
||||
...typebotInQueue.typebot,
|
||||
variables: updateTypebotVariables(typebotInQueue.typebot)(
|
||||
newVariables
|
||||
),
|
||||
},
|
||||
}
|
||||
: typebotInQueue
|
||||
),
|
||||
})
|
||||
|
||||
const updateTypebotVariables =
|
||||
(typebot: { variables: Variable[] }) =>
|
||||
(newVariables: VariableWithUnknowValue[]): Variable[] => {
|
||||
const serializedNewVariables = newVariables.map((variable) => ({
|
||||
...variable,
|
||||
value: Array.isArray(variable.value)
|
||||
? variable.value.map(safeStringify)
|
||||
: safeStringify(variable.value),
|
||||
}))
|
||||
|
||||
return [
|
||||
...typebot.variables.filter((existingVariable) =>
|
||||
serializedNewVariables.every(
|
||||
(newVariable) => existingVariable.id !== newVariable.id
|
||||
)
|
||||
),
|
||||
...serializedNewVariables,
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user