import { z } from 'zod' import { googleAnalyticsOptionsSchema, paymentInputRuntimeOptionsSchema, redirectOptionsSchema, } from './blocks' import { publicTypebotSchema } from './publicTypebot' import { logSchema, resultSchema } from './result' import { listVariableValue, typebotSchema } from './typebot' import { textBubbleContentSchema, imageBubbleContentSchema, videoBubbleContentSchema, audioBubbleContentSchema, embedBubbleContentSchema, } from './blocks/bubbles' import { answerSchema } from './answer' import { BubbleBlockType } from './blocks/bubbles/enums' import { inputBlockSchema } from './blocks/schemas' const typebotInSessionStateSchema = publicTypebotSchema.pick({ id: true, groups: true, edges: true, variables: true, }) const dynamicThemeSchema = z.object({ hostAvatarUrl: z.string().optional(), guestAvatarUrl: z.string().optional(), }) const answerInSessionStateSchema = answerSchema.pick({ content: true, blockId: true, variableId: true, }) const resultInSessionStateSchema = resultSchema .pick({ variables: true, }) .merge( z.object({ answers: z.array(answerInSessionStateSchema), id: z.string().optional(), }) ) export const sessionStateSchema = z.object({ typebot: typebotInSessionStateSchema, dynamicTheme: dynamicThemeSchema.optional(), linkedTypebots: z.object({ typebots: z.array(typebotInSessionStateSchema), queue: z.array(z.object({ edgeId: z.string(), typebotId: z.string() })), }), currentTypebotId: z.string(), result: resultInSessionStateSchema, currentBlock: z .object({ blockId: z.string(), groupId: z.string(), }) .optional(), }) const chatSessionSchema = z.object({ id: z.string(), createdAt: z.date(), updatedAt: z.date(), state: sessionStateSchema, }) const textMessageSchema = z.object({ type: z.literal(BubbleBlockType.TEXT), content: textBubbleContentSchema, }) const imageMessageSchema = z.object({ type: z.enum([BubbleBlockType.IMAGE]), content: imageBubbleContentSchema, }) const videoMessageSchema = z.object({ type: z.enum([BubbleBlockType.VIDEO]), content: videoBubbleContentSchema, }) const audioMessageSchema = z.object({ type: z.enum([BubbleBlockType.AUDIO]), content: audioBubbleContentSchema, }) const embedMessageSchema = z.object({ type: z.enum([BubbleBlockType.EMBED]), content: embedBubbleContentSchema .omit({ height: true, }) .merge(z.object({ height: z.number().optional() })), }) const chatMessageSchema = z .object({ id: z.string() }) .and( z.discriminatedUnion('type', [ textMessageSchema, imageMessageSchema, videoMessageSchema, audioMessageSchema, embedMessageSchema, ]) ) const scriptToExecuteSchema = z.object({ content: z.string(), args: z.array( z.object({ id: z.string(), value: z .string() .or(z.number()) .or(z.boolean()) .or(listVariableValue) .nullish(), }) ), }) const startTypebotSchema = typebotSchema.pick({ id: true, groups: true, edges: true, variables: true, settings: true, theme: true, }) const startParamsSchema = z.object({ typebot: startTypebotSchema .or(z.string()) .describe( 'Either a Typebot ID or a Typebot object. If you provide a Typebot object, it will be executed in preview mode. ([How can I find my typebot ID?](https://docs.typebot.io/api#how-to-find-my-typebotid)).' ), isPreview: z .boolean() .optional() .describe( "If set to `true`, it will start a Preview session with the unpublished bot and it won't be saved in the Results tab. You need to be authenticated for this to work." ), resultId: z .string() .optional() .describe("Provide it if you'd like to overwrite an existing result."), startGroupId: z .string() .optional() .describe('Start chat from a specific group.'), prefilledVariables: z .record(z.unknown()) .optional() .describe( '[More info about prefilled variables.](https://docs.typebot.io/editor/variables#prefilled-variables)' ), }) export const sendMessageInputSchema = z.object({ message: z .string() .optional() .describe( 'The answer to the previous chat input. Do not provide it if you are starting a new chat.' ), sessionId: z .string() .optional() .describe( 'Session ID that you get from the initial chat request to a bot. If not provided, it will create a new session.' ), startParams: startParamsSchema.optional(), }) const runtimeOptionsSchema = paymentInputRuntimeOptionsSchema.optional() const replyLogSchema = logSchema .pick({ status: true, description: true, }) .merge(z.object({ details: z.unknown().optional() })) const clientSideActionSchema = z .object({ lastBubbleBlockId: z.string().optional(), }) .and( z .object({ scriptToExecute: scriptToExecuteSchema, }) .or( z.object({ redirect: redirectOptionsSchema, }) ) .or( z.object({ chatwoot: z.object({ scriptToExecute: scriptToExecuteSchema }), }) ) .or( z.object({ googleAnalytics: googleAnalyticsOptionsSchema, }) ) .or( z.object({ wait: z.object({ secondsToWaitFor: z.number(), }), }) ) ) export const chatReplySchema = z.object({ messages: z.array(chatMessageSchema), input: inputBlockSchema .and( z.object({ prefilledValue: z.string().optional(), runtimeOptions: runtimeOptionsSchema.optional(), }) ) .optional(), clientSideActions: z.array(clientSideActionSchema).optional(), sessionId: z.string().optional(), typebot: typebotSchema .pick({ id: true, theme: true, settings: true }) .optional(), resultId: z.string().optional(), dynamicTheme: dynamicThemeSchema.optional(), logs: z.array(replyLogSchema).optional(), }) export type ChatSession = z.infer export type SessionState = z.infer export type TypebotInSession = z.infer export type ResultInSession = z.infer export type ChatReply = z.infer export type ChatMessage = z.infer export type SendMessageInput = z.infer export type ScriptToExecute = z.infer export type StartParams = z.infer export type RuntimeOptions = z.infer export type StartTypebot = z.infer export type ReplyLog = z.infer