2
0

🐛 New sendMessage version for the new parser

Make sure old client still communicate with old parser
This commit is contained in:
Baptiste Arnaud
2023-10-06 10:14:26 +02:00
parent 6f3e9e9251
commit 3838ac9c3f
35 changed files with 710 additions and 416 deletions

View File

@ -31,128 +31,102 @@ import { updateVariablesInSession } from './variables/updateVariablesInSession'
import { startBotFlow } from './startBotFlow'
import { TRPCError } from '@trpc/server'
export const continueBotFlow =
(state: SessionState) =>
async (
reply?: string
): Promise<ChatReply & { newSessionState: SessionState }> => {
let newSessionState = { ...state }
type Params = {
version: 1 | 2
state: SessionState
}
export const continueBotFlow = async (
reply: string | undefined,
{ state, version }: Params
): Promise<ChatReply & { newSessionState: SessionState }> => {
let newSessionState = { ...state }
if (!newSessionState.currentBlock) return startBotFlow(state)
if (!newSessionState.currentBlock) return startBotFlow({ state, version })
const group = state.typebotsQueue[0].typebot.groups.find(
(group) => group.id === state.currentBlock?.groupId
const group = state.typebotsQueue[0].typebot.groups.find(
(group) => group.id === state.currentBlock?.groupId
)
const blockIndex =
group?.blocks.findIndex(
(block) => block.id === state.currentBlock?.blockId
) ?? -1
const block = blockIndex >= 0 ? group?.blocks[blockIndex ?? 0] : null
if (!block || !group)
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Group / block not found',
})
if (block.type === LogicBlockType.SET_VARIABLE) {
const existingVariable = state.typebotsQueue[0].typebot.variables.find(
byId(block.options.variableId)
)
const blockIndex =
group?.blocks.findIndex(
(block) => block.id === state.currentBlock?.blockId
) ?? -1
const block = blockIndex >= 0 ? group?.blocks[blockIndex ?? 0] : null
if (!block || !group)
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Group / block not found',
})
if (block.type === LogicBlockType.SET_VARIABLE) {
const existingVariable = state.typebotsQueue[0].typebot.variables.find(
byId(block.options.variableId)
)
if (existingVariable && reply) {
const newVariable = {
...existingVariable,
value: safeJsonParse(reply),
}
newSessionState = updateVariablesInSession(state)([newVariable])
if (existingVariable && reply) {
const newVariable = {
...existingVariable,
value: safeJsonParse(reply),
}
} else if (reply && block.type === IntegrationBlockType.WEBHOOK) {
const result = resumeWebhookExecution({
state,
block,
response: JSON.parse(reply),
})
if (result.newSessionState) newSessionState = result.newSessionState
} else if (
block.type === IntegrationBlockType.OPEN_AI &&
block.options.task === 'Create chat completion'
) {
if (reply) {
const result = await resumeChatCompletion(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})(reply)
newSessionState = result.newSessionState
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 (
block.type === IntegrationBlockType.OPEN_AI &&
block.options.task === 'Create chat completion'
) {
if (reply) {
const result = await resumeChatCompletion(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})(reply)
newSessionState = result.newSessionState
}
}
let formattedReply: string | undefined
if (isInputBlock(block)) {
const parsedReplyResult = parseReply(newSessionState)(reply, block)
if (parsedReplyResult.status === 'fail')
return {
...(await parseRetryMessage(newSessionState)(block)),
newSessionState,
}
}
let formattedReply: string | undefined
if (isInputBlock(block)) {
const parsedReplyResult = parseReply(newSessionState)(reply, block)
if (parsedReplyResult.status === 'fail')
return {
...(await parseRetryMessage(newSessionState)(block)),
newSessionState,
}
formattedReply =
'reply' in parsedReplyResult ? parsedReplyResult.reply : undefined
const nextEdgeId = getOutgoingEdgeId(newSessionState)(
block,
formattedReply
)
const itemId = nextEdgeId
? newSessionState.typebotsQueue[0].typebot.edges.find(byId(nextEdgeId))
?.from.itemId
: undefined
newSessionState = await processAndSaveAnswer(
state,
block,
itemId
)(formattedReply)
}
const groupHasMoreBlocks = blockIndex < group.blocks.length - 1
formattedReply =
'reply' in parsedReplyResult ? parsedReplyResult.reply : undefined
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
const itemId = nextEdgeId
? newSessionState.typebotsQueue[0].typebot.edges.find(byId(nextEdgeId))
?.from.itemId
: undefined
newSessionState = await processAndSaveAnswer(
state,
block,
itemId
)(formattedReply)
}
if (groupHasMoreBlocks && !nextEdgeId) {
const chatReply = await executeGroup(newSessionState)({
const groupHasMoreBlocks = blockIndex < group.blocks.length - 1
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
if (groupHasMoreBlocks && !nextEdgeId) {
const chatReply = await executeGroup(
{
...group,
blocks: group.blocks.slice(blockIndex + 1),
})
return {
...chatReply,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
}
if (!nextEdgeId && state.typebotsQueue.length === 1)
return {
messages: [],
newSessionState,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
newSessionState = nextGroup.newSessionState
if (!nextGroup.group)
return {
messages: [],
newSessionState,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
const chatReply = await executeGroup(newSessionState)(nextGroup.group)
},
{ version, state: newSessionState }
)
return {
...chatReply,
lastMessageNewFormat:
@ -160,6 +134,37 @@ export const continueBotFlow =
}
}
if (!nextEdgeId && state.typebotsQueue.length === 1)
return {
messages: [],
newSessionState,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
newSessionState = nextGroup.newSessionState
if (!nextGroup.group)
return {
messages: [],
newSessionState,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
const chatReply = await executeGroup(nextGroup.group, {
version,
state: newSessionState,
})
return {
...chatReply,
lastMessageNewFormat: formattedReply !== reply ? formattedReply : undefined,
}
}
const processAndSaveAnswer =
(state: SessionState, block: InputBlock, itemId?: string) =>
async (reply: string | undefined): Promise<SessionState> => {

View File

@ -1,17 +1,13 @@
import {
BubbleBlock,
BubbleBlockType,
ChatReply,
Group,
InputBlock,
InputBlockType,
RuntimeOptions,
SessionState,
Variable,
} from '@typebot.io/schemas'
import {
isBubbleBlock,
isEmpty,
isInputBlock,
isIntegrationBlock,
isLogicBlock,
@ -26,49 +22,85 @@ import { injectVariableValuesInPictureChoiceBlock } from './blocks/inputs/pictur
import { getPrefilledInputValue } from './getPrefilledValue'
import { parseDateInput } from './blocks/inputs/date/parseDateInput'
import { deepParseVariables } from './variables/deepParseVariables'
import { parseVideoUrl } from '@typebot.io/lib/parseVideoUrl'
import { TDescendant, createPlateEditor } from '@udecode/plate-common'
import {
createDeserializeMdPlugin,
deserializeMd,
} from '@udecode/plate-serializer-md'
import { getVariablesToParseInfoInText } from './variables/parseVariables'
import { parseBubbleBlock } from './parseBubbleBlock'
export const executeGroup =
(
state: SessionState,
currentReply?: ChatReply,
currentLastBubbleId?: string
) =>
async (
group: Group
): Promise<ChatReply & { newSessionState: SessionState }> => {
const messages: ChatReply['messages'] = currentReply?.messages ?? []
let clientSideActions: ChatReply['clientSideActions'] =
currentReply?.clientSideActions
let logs: ChatReply['logs'] = currentReply?.logs
let nextEdgeId = null
let lastBubbleBlockId: string | undefined = currentLastBubbleId
type ContextProps = {
version: 1 | 2
state: SessionState
currentReply?: ChatReply
currentLastBubbleId?: string
}
let newSessionState = state
export const executeGroup = async (
group: Group,
{ version, state, currentReply, currentLastBubbleId }: ContextProps
): Promise<ChatReply & { newSessionState: SessionState }> => {
const messages: ChatReply['messages'] = currentReply?.messages ?? []
let clientSideActions: ChatReply['clientSideActions'] =
currentReply?.clientSideActions
let logs: ChatReply['logs'] = currentReply?.logs
let nextEdgeId = null
let lastBubbleBlockId: string | undefined = currentLastBubbleId
for (const block of group.blocks) {
nextEdgeId = block.outgoingEdgeId
let newSessionState = state
if (isBubbleBlock(block)) {
messages.push(
parseBubbleBlock(newSessionState.typebotsQueue[0].typebot.variables)(
block
)
)
lastBubbleBlockId = block.id
continue
for (const block of group.blocks) {
nextEdgeId = block.outgoingEdgeId
if (isBubbleBlock(block)) {
messages.push(
parseBubbleBlock(block, {
version,
variables: newSessionState.typebotsQueue[0].typebot.variables,
})
)
lastBubbleBlockId = block.id
continue
}
if (isInputBlock(block))
return {
messages,
input: await parseInput(newSessionState)(block),
newSessionState: {
...newSessionState,
currentBlock: {
groupId: group.id,
blockId: block.id,
},
},
clientSideActions,
logs,
}
const executionResponse = isLogicBlock(block)
? await executeLogic(newSessionState)(block)
: isIntegrationBlock(block)
? await executeIntegration(newSessionState)(block)
: null
if (isInputBlock(block))
if (!executionResponse) continue
if (executionResponse.logs)
logs = [...(logs ?? []), ...executionResponse.logs]
if (executionResponse.newSessionState)
newSessionState = executionResponse.newSessionState
if (
'clientSideActions' in executionResponse &&
executionResponse.clientSideActions
) {
clientSideActions = [
...(clientSideActions ?? []),
...executionResponse.clientSideActions.map((action) => ({
...action,
lastBubbleBlockId,
})),
]
if (
executionResponse.clientSideActions?.find(
(action) => action.expectsDedicatedReply
)
) {
return {
messages,
input: await parseInput(newSessionState)(block),
newSessionState: {
...newSessionState,
currentBlock: {
@ -79,78 +111,38 @@ export const executeGroup =
clientSideActions,
logs,
}
const executionResponse = isLogicBlock(block)
? await executeLogic(newSessionState)(block)
: isIntegrationBlock(block)
? await executeIntegration(newSessionState)(block)
: null
if (!executionResponse) continue
if (executionResponse.logs)
logs = [...(logs ?? []), ...executionResponse.logs]
if (executionResponse.newSessionState)
newSessionState = executionResponse.newSessionState
if (
'clientSideActions' in executionResponse &&
executionResponse.clientSideActions
) {
clientSideActions = [
...(clientSideActions ?? []),
...executionResponse.clientSideActions.map((action) => ({
...action,
lastBubbleBlockId,
})),
]
if (
executionResponse.clientSideActions?.find(
(action) => action.expectsDedicatedReply
)
) {
return {
messages,
newSessionState: {
...newSessionState,
currentBlock: {
groupId: group.id,
blockId: block.id,
},
},
clientSideActions,
logs,
}
}
}
if (executionResponse.outgoingEdgeId) {
nextEdgeId = executionResponse.outgoingEdgeId
break
}
}
if (!nextEdgeId && newSessionState.typebotsQueue.length === 1)
return { messages, newSessionState, clientSideActions, logs }
const nextGroup = await getNextGroup(newSessionState)(
nextEdgeId ?? undefined
)
newSessionState = nextGroup.newSessionState
if (!nextGroup.group) {
return { messages, newSessionState, clientSideActions, logs }
if (executionResponse.outgoingEdgeId) {
nextEdgeId = executionResponse.outgoingEdgeId
break
}
return executeGroup(
newSessionState,
{
messages,
clientSideActions,
logs,
},
lastBubbleBlockId
)(nextGroup.group)
}
if (!nextEdgeId && newSessionState.typebotsQueue.length === 1)
return { messages, newSessionState, clientSideActions, logs }
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId ?? undefined)
newSessionState = nextGroup.newSessionState
if (!nextGroup.group) {
return { messages, newSessionState, clientSideActions, logs }
}
return executeGroup(nextGroup.group, {
version,
state: newSessionState,
currentReply: {
messages,
clientSideActions,
logs,
},
currentLastBubbleId: lastBubbleBlockId,
})
}
const computeRuntimeOptions =
(state: SessionState) =>
(block: InputBlock): Promise<RuntimeOptions> | undefined => {
@ -161,136 +153,6 @@ const computeRuntimeOptions =
}
}
const parseBubbleBlock =
(variables: Variable[]) =>
(block: BubbleBlock): ChatReply['messages'][0] => {
switch (block.type) {
case BubbleBlockType.TEXT: {
return {
...block,
content: {
...block.content,
richText: parseVariablesInRichText(
block.content.richText,
variables
),
},
}
}
case BubbleBlockType.EMBED: {
const message = deepParseVariables(variables)(block)
return {
...message,
content: {
...message.content,
height:
typeof message.content.height === 'string'
? parseFloat(message.content.height)
: message.content.height,
},
}
}
case BubbleBlockType.VIDEO: {
const parsedContent = deepParseVariables(variables)(block.content)
return {
...block,
content: parsedContent.url ? parseVideoUrl(parsedContent.url) : {},
}
}
default:
return deepParseVariables(variables)(block)
}
}
const parseVariablesInRichText = (
elements: TDescendant[],
variables: Variable[]
): TDescendant[] => {
const parsedElements: TDescendant[] = []
for (const element of elements) {
if ('text' in element) {
const text = element.text as string
if (isEmpty(text)) {
parsedElements.push(element)
continue
}
const variablesInText = getVariablesToParseInfoInText(text, variables)
if (variablesInText.length === 0) {
parsedElements.push(element)
continue
}
for (const variableInText of variablesInText) {
const textBeforeVariable = text.substring(0, variableInText.startIndex)
const textAfterVariable = text.substring(variableInText.endIndex)
const isStandaloneElement =
isEmpty(textBeforeVariable) && isEmpty(textAfterVariable)
const variableElements = convertMarkdownToRichText(
isStandaloneElement
? variableInText.value
: variableInText.value.replace(/[\n]+/g, ' ')
)
const variableElementsWithStyling = variableElements.map(
(variableElement) => ({
...variableElement,
children: [
...(variableElement.children as TDescendant[]).map((child) => ({
...element,
...child,
})),
],
})
)
if (isStandaloneElement) {
parsedElements.push(...variableElementsWithStyling)
continue
}
const children: TDescendant[] = []
if (isNotEmpty(textBeforeVariable))
children.push({
...element,
text: textBeforeVariable,
})
children.push({
type: 'inline-variable',
children: variableElementsWithStyling,
})
if (isNotEmpty(textAfterVariable))
children.push({
...element,
text: textAfterVariable,
})
parsedElements.push(...children)
}
continue
}
const type =
element.children.length === 1 &&
'text' in element.children[0] &&
(element.children[0].text as string).startsWith('{{') &&
(element.children[0].text as string).endsWith('}}')
? 'variable'
: element.type
parsedElements.push({
...element,
type,
children: parseVariablesInRichText(
element.children as TDescendant[],
variables
),
})
}
return parsedElements
}
const convertMarkdownToRichText = (text: string): TDescendant[] => {
const plugins = [createDeserializeMdPlugin()]
//@ts-ignore
return deserializeMd(createPlateEditor({ plugins }), text)
}
export const parseInput =
(state: SessionState) =>
async (block: InputBlock): Promise<ChatReply['input']> => {

View File

@ -0,0 +1,186 @@
import { parseVideoUrl } from '@typebot.io/lib/parseVideoUrl'
import {
BubbleBlock,
Variable,
ChatReply,
BubbleBlockType,
} from '@typebot.io/schemas'
import { deepParseVariables } from './variables/deepParseVariables'
import { isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
import { getVariablesToParseInfoInText } from './variables/parseVariables'
import { TDescendant, createPlateEditor } from '@udecode/plate-common'
import {
createDeserializeMdPlugin,
deserializeMd,
} from '@udecode/plate-serializer-md'
type Params = {
version: 1 | 2
variables: Variable[]
}
export const parseBubbleBlock = (
block: BubbleBlock,
{ version, variables }: Params
): ChatReply['messages'][0] => {
switch (block.type) {
case BubbleBlockType.TEXT: {
if (version === 1)
return deepParseVariables(
variables,
{},
{ takeLatestIfList: true }
)(block)
return {
...block,
content: {
...block.content,
richText: parseVariablesInRichText(block.content.richText, {
variables,
takeLatestIfList: true,
}),
},
}
}
case BubbleBlockType.EMBED: {
const message = deepParseVariables(variables)(block)
return {
...message,
content: {
...message.content,
height:
typeof message.content.height === 'string'
? parseFloat(message.content.height)
: message.content.height,
},
}
}
case BubbleBlockType.VIDEO: {
const parsedContent = deepParseVariables(variables)(block.content)
return {
...block,
content: parsedContent.url ? parseVideoUrl(parsedContent.url) : {},
}
}
default:
return deepParseVariables(variables)(block)
}
}
const parseVariablesInRichText = (
elements: TDescendant[],
{
variables,
takeLatestIfList,
}: { variables: Variable[]; takeLatestIfList?: boolean }
): TDescendant[] => {
const parsedElements: TDescendant[] = []
for (const element of elements) {
if ('text' in element) {
const text = element.text as string
if (isEmpty(text)) {
parsedElements.push(element)
continue
}
const variablesInText = getVariablesToParseInfoInText(text, {
variables,
takeLatestIfList,
})
if (variablesInText.length === 0) {
parsedElements.push(element)
continue
}
let lastTextEndIndex = 0
let index = -1
for (const variableInText of variablesInText) {
index += 1
const textBeforeVariable = text.substring(
lastTextEndIndex,
variableInText.startIndex
)
const textAfterVariable =
index === variablesInText.length - 1
? text.substring(variableInText.endIndex)
: undefined
lastTextEndIndex = variableInText.endIndex
const isStandaloneElement =
isEmpty(textBeforeVariable) && isEmpty(textAfterVariable)
const variableElements = convertMarkdownToRichText(
isStandaloneElement
? variableInText.value
: variableInText.value.replace(/[\n]+/g, ' ')
)
const variableElementsWithStyling = applyElementStyleToDescendants(
variableElements,
{
bold: element.bold,
italic: element.italic,
underline: element.underline,
}
)
if (isStandaloneElement) {
parsedElements.push(...variableElementsWithStyling)
continue
}
const children: TDescendant[] = []
if (isNotEmpty(textBeforeVariable))
children.push({
...element,
text: textBeforeVariable,
})
children.push({
type: 'inline-variable',
children: variableElementsWithStyling,
})
if (isNotEmpty(textAfterVariable))
children.push({
...element,
text: textAfterVariable,
})
parsedElements.push(...children)
}
continue
}
const type =
element.children.length === 1 &&
'text' in element.children[0] &&
(element.children[0].text as string).startsWith('{{') &&
(element.children[0].text as string).endsWith('}}')
? 'variable'
: element.type
parsedElements.push({
...element,
type,
children: parseVariablesInRichText(element.children as TDescendant[], {
variables,
takeLatestIfList,
}),
})
}
return parsedElements
}
const applyElementStyleToDescendants = (
variableElements: TDescendant[],
styles: { bold: unknown; italic: unknown; underline: unknown }
): TDescendant[] =>
variableElements.map((variableElement) => {
if ('text' in variableElement) return { ...styles, ...variableElement }
return {
...variableElement,
children: applyElementStyleToDescendants(
variableElement.children,
styles
),
}
})
const convertMarkdownToRichText = (text: string): TDescendant[] => {
const plugins = [createDeserializeMdPlugin()]
return deserializeMd(createPlateEditor({ plugins }) as unknown as any, text)
}

View File

@ -3,10 +3,17 @@ import { ChatReply, SessionState } from '@typebot.io/schemas'
import { executeGroup } from './executeGroup'
import { getNextGroup } from './getNextGroup'
export const startBotFlow = async (
state: SessionState,
type Props = {
version: 1 | 2
state: SessionState
startGroupId?: string
): Promise<ChatReply & { newSessionState: SessionState }> => {
}
export const startBotFlow = async ({
version,
state,
startGroupId,
}: Props): Promise<ChatReply & { newSessionState: SessionState }> => {
let newSessionState = state
if (startGroupId) {
const group = state.typebotsQueue[0].typebot.groups.find(
@ -17,7 +24,7 @@ export const startBotFlow = async (
code: 'BAD_REQUEST',
message: "startGroupId doesn't exist",
})
return executeGroup(newSessionState)(group)
return executeGroup(group, { version, state: newSessionState })
}
const firstEdgeId =
newSessionState.typebotsQueue[0].typebot.groups[0].blocks[0].outgoingEdgeId
@ -25,5 +32,5 @@ export const startBotFlow = async (
const nextGroup = await getNextGroup(newSessionState)(firstEdgeId)
newSessionState = nextGroup.newSessionState
if (!nextGroup.group) return { messages: [], newSessionState }
return executeGroup(newSessionState)(nextGroup.group)
return executeGroup(nextGroup.group, { version, state: newSessionState })
}

View File

@ -31,6 +31,7 @@ import { upsertResult } from './queries/upsertResult'
import { continueBotFlow } from './continueBotFlow'
type Props = {
version: 1 | 2
message: string | undefined
startParams: StartParams
userId: string | undefined
@ -38,6 +39,7 @@ type Props = {
}
export const startSession = async ({
version,
message,
startParams,
userId,
@ -135,7 +137,11 @@ export const startSession = async ({
}
}
let chatReply = await startBotFlow(initialState, startParams.startGroupId)
let chatReply = await startBotFlow({
version,
state: initialState,
startGroupId: startParams.startGroupId,
})
// If params has message and first block is an input block, we can directly continue the bot flow
if (message) {
@ -154,10 +160,13 @@ export const startSession = async ({
resultId,
typebot: newSessionState.typebotsQueue[0].typebot,
})
chatReply = await continueBotFlow({
...newSessionState,
currentBlock: { groupId: firstBlock.groupId, blockId: firstBlock.id },
})(message)
chatReply = await continueBotFlow(message, {
version,
state: {
...newSessionState,
currentBlock: { groupId: firstBlock.groupId, blockId: firstBlock.id },
},
})
}
}

View File

@ -64,7 +64,10 @@ type VariableToParseInformation = {
export const getVariablesToParseInfoInText = (
text: string,
variables: Variable[]
{
variables,
takeLatestIfList,
}: { variables: Variable[]; takeLatestIfList?: boolean }
): VariableToParseInformation[] => {
const pattern = /\{\{([^{}]+)\}\}|(\$)\{\{([^{}]+)\}\}/g
const variablesToParseInfo: VariableToParseInformation[] = []
@ -78,7 +81,12 @@ export const getVariablesToParseInfoInText = (
startIndex: match.index,
endIndex: match.index + match[0].length,
textToReplace: match[0],
value: safeStringify(variable?.value) ?? '',
value:
safeStringify(
takeLatestIfList && Array.isArray(variable?.value)
? variable?.value[variable?.value.length - 1]
: variable?.value
) ?? '',
})
}
return variablesToParseInfo

View File

@ -64,9 +64,10 @@ export const resumeWhatsAppFlow = async ({
const resumeResponse =
session && !isSessionExpired
? await continueBotFlow({ ...session.state, whatsApp: { contact } })(
messageContent
)
? await continueBotFlow(messageContent, {
version: 2,
state: { ...session.state, whatsApp: { contact } },
})
: workspaceId
? await startWhatsAppSession({
incomingMessage: messageContent,

View File

@ -51,7 +51,7 @@ export const sendChatReplyToWhatsApp = async ({
const result = await executeClientSideAction({ to, credentials })(action)
if (!result) continue
const { input, newSessionState, messages, clientSideActions } =
await continueBotFlow(state)(result.replyToSend)
await continueBotFlow(result.replyToSend, { version: 2, state })
return sendChatReplyToWhatsApp({
to,
@ -95,7 +95,7 @@ export const sendChatReplyToWhatsApp = async ({
)
if (!result) continue
const { input, newSessionState, messages, clientSideActions } =
await continueBotFlow(state)(result.replyToSend)
await continueBotFlow(result.replyToSend, { version: 2, state })
return sendChatReplyToWhatsApp({
to,

View File

@ -78,6 +78,7 @@ export const startWhatsAppSession = async ({
defaultSessionExpiryTimeout
return startSession({
version: 2,
message: incomingMessage,
startParams: {
typebot: publicTypebot.typebot.publicId as string,

View File

@ -16,7 +16,7 @@ npm install @typebot.io/js
```
<script type="module">
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js'
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js'
Typebot.initStandard({
typebot: 'my-typebot',
@ -34,7 +34,7 @@ There, you can change the container dimensions. Here is a code example:
```html
<script type="module">
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js'
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js'
Typebot.initStandard({
typebot: 'my-typebot',
@ -54,7 +54,7 @@ Here is an example:
```html
<script type="module">
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js'
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js'
Typebot.initPopup({
typebot: 'my-typebot',
@ -96,7 +96,7 @@ Here is an example:
```html
<script type="module">
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js'
import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js'
Typebot.initBubble({
typebot: 'my-typebot',

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.1.34",
"version": "0.2.0",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",

View File

@ -32,7 +32,7 @@ export async function getInitialChatReplyQuery({
if (paymentInProgressState) removePaymentInProgressFromStorage()
const { data, error } = await sendRequest<InitialChatReply>({
method: 'POST',
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sendMessage`,
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v2/sendMessage`,
body: {
startParams: paymentInProgressState
? undefined

View File

@ -8,6 +8,6 @@ export const sendMessageQuery = ({
}: SendMessageInput & { apiHost?: string }) =>
sendRequest<ChatReply>({
method: 'POST',
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sendMessage`,
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v2/sendMessage`,
body,
})

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/nextjs",
"version": "0.1.34",
"version": "0.2.0",
"description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.1.34",
"version": "0.2.0",
"description": "Convenient library to display typebots on your React app",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -40,7 +40,7 @@ class Typebot_Public
function typebot_script()
{
echo '<script type="module">import Typebot from "https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js";';
echo '<script type="module">import Typebot from "https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js";';
if (
get_option('excluded_pages') !== null &&
get_option('excluded_pages') !== ''
@ -91,7 +91,7 @@ class Typebot_Public
public function add_typebot_container($attributes = [])
{
$lib_url = "https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js";
$lib_url = "https://cdn.jsdelivr.net/npm/@typebot.io/js@0.2/dist/web.js";
$width = '100%';
$height = '500px';
$api_host = 'https://viewer.typebot.io';