@@ -1,5 +1,8 @@
|
||||
import { publicProcedure } from '@/helpers/server/trpc'
|
||||
import { continueChatResponseSchema } from '@typebot.io/schemas/features/chat/schema'
|
||||
import {
|
||||
continueChatResponseSchema,
|
||||
messageSchema,
|
||||
} from '@typebot.io/schemas/features/chat/schema'
|
||||
import { z } from 'zod'
|
||||
import { continueChat as continueChatFn } from '@typebot.io/bot-engine/apiHandlers/continueChat'
|
||||
|
||||
@@ -13,7 +16,7 @@ export const continueChat = publicProcedure
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
message: z.string().optional(),
|
||||
message: messageSchema.optional(),
|
||||
sessionId: z
|
||||
.string()
|
||||
.describe(
|
||||
|
||||
@@ -91,7 +91,9 @@ export const sendMessageV1 = publicProcedure
|
||||
typeof startParams.typebot === 'string'
|
||||
? undefined
|
||||
: startParams.typebot,
|
||||
message,
|
||||
message: message
|
||||
? { type: 'text', text: message }
|
||||
: undefined,
|
||||
userId: user?.id,
|
||||
textBubbleContentFormat: 'richText',
|
||||
}
|
||||
@@ -102,10 +104,11 @@ export const sendMessageV1 = publicProcedure
|
||||
publicId: startParams.typebot,
|
||||
prefilledVariables: startParams.prefilledVariables,
|
||||
resultId: startParams.resultId,
|
||||
message,
|
||||
message: message
|
||||
? { type: 'text', text: message }
|
||||
: undefined,
|
||||
textBubbleContentFormat: 'richText',
|
||||
},
|
||||
message,
|
||||
})
|
||||
|
||||
if (startParams.isPreview || typeof startParams.typebot !== 'string') {
|
||||
@@ -185,11 +188,14 @@ export const sendMessageV1 = publicProcedure
|
||||
lastMessageNewFormat,
|
||||
visitedEdges,
|
||||
setVariableHistory,
|
||||
} = await continueBotFlow(message, {
|
||||
version: 1,
|
||||
state: session.state,
|
||||
textBubbleContentFormat: 'richText',
|
||||
})
|
||||
} = await continueBotFlow(
|
||||
message ? { type: 'text', text: message } : undefined,
|
||||
{
|
||||
version: 1,
|
||||
state: session.state,
|
||||
textBubbleContentFormat: 'richText',
|
||||
}
|
||||
)
|
||||
|
||||
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
||||
|
||||
|
||||
@@ -91,7 +91,9 @@ export const sendMessageV2 = publicProcedure
|
||||
typeof startParams.typebot === 'string'
|
||||
? undefined
|
||||
: startParams.typebot,
|
||||
message,
|
||||
message: message
|
||||
? { type: 'text', text: message }
|
||||
: undefined,
|
||||
userId: user?.id,
|
||||
textBubbleContentFormat: 'richText',
|
||||
}
|
||||
@@ -102,10 +104,11 @@ export const sendMessageV2 = publicProcedure
|
||||
publicId: startParams.typebot,
|
||||
prefilledVariables: startParams.prefilledVariables,
|
||||
resultId: startParams.resultId,
|
||||
message,
|
||||
message: message
|
||||
? { type: 'text', text: message }
|
||||
: undefined,
|
||||
textBubbleContentFormat: 'richText',
|
||||
},
|
||||
message,
|
||||
})
|
||||
|
||||
if (startParams.isPreview || typeof startParams.typebot !== 'string') {
|
||||
@@ -184,11 +187,14 @@ export const sendMessageV2 = publicProcedure
|
||||
lastMessageNewFormat,
|
||||
visitedEdges,
|
||||
setVariableHistory,
|
||||
} = await continueBotFlow(message, {
|
||||
version: 2,
|
||||
state: session.state,
|
||||
textBubbleContentFormat: 'richText',
|
||||
})
|
||||
} = await continueBotFlow(
|
||||
message ? { type: 'text', text: message } : undefined,
|
||||
{
|
||||
version: 2,
|
||||
state: session.state,
|
||||
textBubbleContentFormat: 'richText',
|
||||
}
|
||||
)
|
||||
|
||||
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
||||
|
||||
|
||||
@@ -5,9 +5,14 @@ import { generatePresignedPostPolicy } from '@typebot.io/lib/s3/generatePresigne
|
||||
import { env } from '@typebot.io/env'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { getSession } from '@typebot.io/bot-engine/queries/getSession'
|
||||
import { parseGroups } from '@typebot.io/schemas'
|
||||
import {
|
||||
FileInputBlock,
|
||||
parseGroups,
|
||||
TextInputBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { getBlockById } from '@typebot.io/schemas/helpers'
|
||||
import { PublicTypebot } from '@typebot.io/prisma'
|
||||
|
||||
export const generateUploadUrl = publicProcedure
|
||||
.meta({
|
||||
@@ -50,27 +55,18 @@ export const generateUploadUrl = publicProcedure
|
||||
|
||||
const typebotId = session.state.typebotsQueue[0].typebot.id
|
||||
|
||||
const publicTypebot = await prisma.publicTypebot.findFirst({
|
||||
where: {
|
||||
typebotId,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
groups: true,
|
||||
typebot: {
|
||||
select: {
|
||||
workspaceId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const isPreview = session.state.typebotsQueue[0].resultId
|
||||
|
||||
const workspaceId = publicTypebot?.typebot.workspaceId
|
||||
const typebot = session.state.typebotsQueue[0].resultId
|
||||
? await getAndParsePublicTypebot(
|
||||
session.state.typebotsQueue[0].typebot.id
|
||||
)
|
||||
: session.state.typebotsQueue[0].typebot
|
||||
|
||||
if (!workspaceId)
|
||||
if (!typebot?.version)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: "Can't find workspaceId",
|
||||
message: "Can't find typebot",
|
||||
})
|
||||
|
||||
if (session.state.currentBlockId === undefined)
|
||||
@@ -79,42 +75,93 @@ export const generateUploadUrl = publicProcedure
|
||||
message: "Can't find currentBlockId in session state",
|
||||
})
|
||||
|
||||
const { block: fileUploadBlock } = getBlockById(
|
||||
const { block } = getBlockById(
|
||||
session.state.currentBlockId,
|
||||
parseGroups(publicTypebot.groups, {
|
||||
typebotVersion: publicTypebot.version,
|
||||
parseGroups(typebot.groups, {
|
||||
typebotVersion: typebot.version,
|
||||
})
|
||||
)
|
||||
|
||||
if (fileUploadBlock?.type !== InputBlockType.FILE)
|
||||
if (
|
||||
block?.type !== InputBlockType.FILE &&
|
||||
(block.type !== InputBlockType.TEXT ||
|
||||
!block.options?.attachments?.isEnabled)
|
||||
)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: "Can't find file upload block",
|
||||
message: 'Current block does not expect file upload',
|
||||
})
|
||||
|
||||
const { visibility, maxFileSize } = parseFileUploadParams(block)
|
||||
|
||||
const resultId = session.state.typebotsQueue[0].resultId
|
||||
|
||||
const filePath = `${
|
||||
fileUploadBlock.options?.visibility === 'Private' ? 'private' : 'public'
|
||||
}/workspaces/${workspaceId}/typebots/${typebotId}/results/${resultId}/${fileName}`
|
||||
const filePath =
|
||||
'workspaceId' in typebot && typebot.workspaceId
|
||||
? `${visibility === 'Private' ? 'private' : 'public'}/workspaces/${
|
||||
typebot.workspaceId
|
||||
}/typebots/${typebotId}/results/${resultId}/${fileName}`
|
||||
: `/public/tmp/${typebotId}/${fileName}`
|
||||
|
||||
const presignedPostPolicy = await generatePresignedPostPolicy({
|
||||
fileType,
|
||||
filePath,
|
||||
maxFileSize:
|
||||
fileUploadBlock.options && 'sizeLimit' in fileUploadBlock.options
|
||||
? (fileUploadBlock.options.sizeLimit as number)
|
||||
: env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
maxFileSize,
|
||||
})
|
||||
|
||||
return {
|
||||
presignedUrl: presignedPostPolicy.postURL,
|
||||
formData: presignedPostPolicy.formData,
|
||||
fileUrl:
|
||||
fileUploadBlock.options?.visibility === 'Private'
|
||||
visibility === 'Private' && !isPreview
|
||||
? `${env.NEXTAUTH_URL}/api/typebots/${typebotId}/results/${resultId}/${fileName}`
|
||||
: env.S3_PUBLIC_CUSTOM_DOMAIN
|
||||
? `${env.S3_PUBLIC_CUSTOM_DOMAIN}/${filePath}`
|
||||
: `${presignedPostPolicy.postURL}/${presignedPostPolicy.formData.key}`,
|
||||
}
|
||||
})
|
||||
|
||||
const getAndParsePublicTypebot = async (typebotId: string) => {
|
||||
const publicTypebot = (await prisma.publicTypebot.findFirst({
|
||||
where: {
|
||||
typebotId,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
groups: true,
|
||||
typebot: {
|
||||
select: {
|
||||
workspaceId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})) as (PublicTypebot & { typebot: { workspaceId: string } }) | null
|
||||
|
||||
return {
|
||||
...publicTypebot,
|
||||
workspaceId: publicTypebot?.typebot.workspaceId,
|
||||
}
|
||||
}
|
||||
|
||||
const parseFileUploadParams = (
|
||||
block: FileInputBlock | TextInputBlock
|
||||
): { visibility: 'Public' | 'Private'; maxFileSize: number | undefined } => {
|
||||
if (block.type === InputBlockType.FILE) {
|
||||
return {
|
||||
visibility:
|
||||
block.options?.visibility === 'Private' ? 'Private' : 'Public',
|
||||
maxFileSize:
|
||||
block.options && 'sizeLimit' in block.options
|
||||
? (block.options.sizeLimit as number)
|
||||
: env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
visibility:
|
||||
block.options?.attachments?.visibility === 'Private'
|
||||
? 'Private'
|
||||
: 'Public',
|
||||
maxFileSize: env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { StartChatInput, StartPreviewChatInput } from '@typebot.io/schemas'
|
||||
|
||||
test.afterEach(async () => {
|
||||
await deleteWebhooks(['chat-webhook-id'])
|
||||
await deleteTypebots(['chat-sub-bot'])
|
||||
await deleteTypebots(['chat-sub-bot', 'starting-with-input'])
|
||||
})
|
||||
|
||||
test('API chat execution should work on preview bot', async ({ request }) => {
|
||||
@@ -108,6 +108,13 @@ test('API chat execution should work on published bot', async ({ request }) => {
|
||||
id: 'chat-sub-bot',
|
||||
publicId: 'chat-sub-bot-public',
|
||||
})
|
||||
await importTypebotInDatabase(
|
||||
getTestAsset('typebots/chat/startingWithInput.json'),
|
||||
{
|
||||
id: 'starting-with-input',
|
||||
publicId: 'starting-with-input-public',
|
||||
}
|
||||
)
|
||||
await createWebhook(typebotId, {
|
||||
id: 'chat-webhook-id',
|
||||
method: HttpMethod.GET,
|
||||
@@ -296,11 +303,12 @@ test('API chat execution should work on published bot', async ({ request }) => {
|
||||
expect(messages[2].content.richText.length).toBeGreaterThan(0)
|
||||
})
|
||||
await test.step('Starting with a message when typebot starts with input should proceed', async () => {
|
||||
const { messages } = await (
|
||||
const response = await (
|
||||
await request.post(
|
||||
`/api/v1/typebots/starting-with-input-public/startChat`,
|
||||
{
|
||||
data: {
|
||||
//@ts-expect-error We want to test if message is correctly preprocessed by zod
|
||||
message: 'Hey',
|
||||
isStreamEnabled: false,
|
||||
isOnlyRegistering: false,
|
||||
@@ -309,7 +317,7 @@ test('API chat execution should work on published bot', async ({ request }) => {
|
||||
}
|
||||
)
|
||||
).json()
|
||||
expect(messages[0].content.richText).toStrictEqual([
|
||||
expect(response.messages[0].content.richText).toStrictEqual([
|
||||
{
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -14,11 +14,11 @@ test('Transcript set variable should be correctly computed', async ({
|
||||
|
||||
await page.goto(`/${typebotId}-public`)
|
||||
await page.getByPlaceholder('Type your answer...').fill('hey')
|
||||
await page.getByRole('button').click()
|
||||
await page.getByLabel('Send').click()
|
||||
await page.getByPlaceholder('Type your answer...').fill('hey 2')
|
||||
await page.getByRole('button').click()
|
||||
await page.getByLabel('Send').click()
|
||||
await page.getByPlaceholder('Type your answer...').fill('hey 3')
|
||||
await page.getByRole('button').click()
|
||||
await page.getByLabel('Send').click()
|
||||
await expect(
|
||||
page.getByText('Assistant: "How are you? You said "')
|
||||
).toBeVisible()
|
||||
|
||||
@@ -31,8 +31,8 @@ test.beforeAll(async () => {
|
||||
|
||||
test('should work as expected', async ({ page }) => {
|
||||
await page.goto(`/${typebotId}-public`)
|
||||
await page.locator('input').fill('Hello there!')
|
||||
await page.locator('input').press('Enter')
|
||||
await page.getByPlaceholder('Type your answer...').fill('Hello there!')
|
||||
await page.getByPlaceholder('Type your answer...').press('Enter')
|
||||
await expect(page.getByText('Cheers!')).toBeVisible()
|
||||
await page.goto(`${env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||
await expect(page.locator('text=Hello there!')).toBeVisible()
|
||||
@@ -41,8 +41,8 @@ test('should work as expected', async ({ page }) => {
|
||||
test.describe('Merge disabled', () => {
|
||||
test('should work as expected', async ({ page }) => {
|
||||
await page.goto(`/${typebotWithMergeDisabledId}-public`)
|
||||
await page.locator('input').fill('Hello there!')
|
||||
await page.locator('input').press('Enter')
|
||||
await page.getByPlaceholder('Type your answer...').fill('Hello there!')
|
||||
await page.getByPlaceholder('Type your answer...').press('Enter')
|
||||
await expect(page.getByText('Cheers!')).toBeVisible()
|
||||
await page.goto(
|
||||
`${process.env.NEXTAUTH_URL}/typebots/${typebotWithMergeDisabledId}/results`
|
||||
|
||||
Reference in New Issue
Block a user