⚡ (chat) Improve chat API compatibility with preview mode
This commit is contained in:
@ -1,33 +1,22 @@
|
||||
import { getInitialChatReplyQuery } from '@/queries/getInitialChatReplyQuery'
|
||||
import {
|
||||
getExistingResultFromSession,
|
||||
setResultInSession,
|
||||
} from '@/utils/sessionStorage'
|
||||
import { Standard } from '@typebot.io/react'
|
||||
import { BackgroundType, InitialChatReply, Typebot } from 'models'
|
||||
import { BackgroundType, Typebot } from 'models'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ErrorPage } from './ErrorPage'
|
||||
import { SEO } from './Seo'
|
||||
|
||||
export type TypebotPageV2Props = {
|
||||
url: string
|
||||
typebot: Pick<
|
||||
Typebot,
|
||||
'settings' | 'theme' | 'id' | 'name' | 'isClosed' | 'isArchived'
|
||||
'settings' | 'theme' | 'name' | 'isClosed' | 'isArchived' | 'publicId'
|
||||
>
|
||||
}
|
||||
|
||||
let hasInitializedChat = false
|
||||
|
||||
export const TypebotPageV2 = ({ url, typebot }: TypebotPageV2Props) => {
|
||||
const { asPath, push } = useRouter()
|
||||
const [initialChatReply, setInitialChatReply] = useState<InitialChatReply>()
|
||||
const [error, setError] = useState<Error | undefined>(undefined)
|
||||
|
||||
const background = typebot.theme.general.background
|
||||
|
||||
const clearQueryParamsIfNecessary = useCallback(() => {
|
||||
const clearQueryParamsIfNecessary = () => {
|
||||
const hasQueryParams = asPath.includes('?')
|
||||
if (
|
||||
!hasQueryParams ||
|
||||
@ -35,44 +24,8 @@ export const TypebotPageV2 = ({ url, typebot }: TypebotPageV2Props) => {
|
||||
)
|
||||
return
|
||||
push(asPath.split('?')[0], undefined, { shallow: true })
|
||||
}, [asPath, push, typebot.settings.general.isHideQueryParamsEnabled])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(open)
|
||||
clearQueryParamsIfNecessary()
|
||||
}, [clearQueryParamsIfNecessary])
|
||||
|
||||
useEffect(() => {
|
||||
if (hasInitializedChat) return
|
||||
hasInitializedChat = true
|
||||
const prefilledVariables = extractPrefilledVariables()
|
||||
const existingResultId = getExistingResultFromSession() ?? undefined
|
||||
|
||||
getInitialChatReplyQuery({
|
||||
typebotId: typebot.id,
|
||||
resultId:
|
||||
typebot.settings.general.isNewResultOnRefreshEnabled ?? false
|
||||
? undefined
|
||||
: existingResultId,
|
||||
prefilledVariables,
|
||||
}).then(({ data, error }) => {
|
||||
if (error && 'code' in error && error.code === 'FORBIDDEN') {
|
||||
setError(new Error('This bot is now closed.'))
|
||||
return
|
||||
}
|
||||
if (!data) return setError(new Error("Couldn't initiate the chat"))
|
||||
setInitialChatReply(data)
|
||||
setResultInSession(data.resultId)
|
||||
})
|
||||
}, [
|
||||
initialChatReply,
|
||||
typebot.id,
|
||||
typebot.settings.general.isNewResultOnRefreshEnabled,
|
||||
])
|
||||
|
||||
if (error) {
|
||||
return <ErrorPage error={error} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@ -89,20 +42,12 @@ export const TypebotPageV2 = ({ url, typebot }: TypebotPageV2Props) => {
|
||||
typebotName={typebot.name}
|
||||
metadata={typebot.settings.metadata}
|
||||
/>
|
||||
{initialChatReply && (
|
||||
<Standard typebotId={typebot.id} initialChatReply={initialChatReply} />
|
||||
{typebot.publicId && (
|
||||
<Standard
|
||||
typebot={typebot.publicId}
|
||||
onInit={clearQueryParamsIfNecessary}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const extractPrefilledVariables = () => {
|
||||
const urlParams = new URLSearchParams(location.search)
|
||||
|
||||
const prefilledVariables: { [key: string]: string } = {}
|
||||
urlParams.forEach((value, key) => {
|
||||
prefilledVariables[key] = value
|
||||
})
|
||||
|
||||
return prefilledVariables
|
||||
}
|
||||
|
@ -3,4 +3,9 @@ import { ChoiceInputBlock } from 'models'
|
||||
export const validateButtonInput = (
|
||||
buttonBlock: ChoiceInputBlock,
|
||||
input: string
|
||||
) => buttonBlock.items.some((item) => item.content === input)
|
||||
) =>
|
||||
input
|
||||
.split(',')
|
||||
.every((value) =>
|
||||
buttonBlock.items.some((item) => item.content === value.trim())
|
||||
)
|
||||
|
@ -2,6 +2,7 @@ import { checkChatsUsage } from '@/features/usage'
|
||||
import {
|
||||
parsePrefilledVariables,
|
||||
deepParseVariable,
|
||||
parseVariables,
|
||||
} from '@/features/variables'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { publicProcedure } from '@/utils/server/trpc'
|
||||
@ -11,15 +12,17 @@ import {
|
||||
ChatReply,
|
||||
chatReplySchema,
|
||||
ChatSession,
|
||||
PublicTypebot,
|
||||
Result,
|
||||
sendMessageInputSchema,
|
||||
SessionState,
|
||||
StartParams,
|
||||
StartTypebot,
|
||||
Theme,
|
||||
Typebot,
|
||||
Variable,
|
||||
} from 'models'
|
||||
import { continueBotFlow, getSession, startBotFlow } from '../utils'
|
||||
import { omit } from 'utils'
|
||||
|
||||
export const sendMessageProcedure = publicProcedure
|
||||
.meta({
|
||||
@ -37,12 +40,13 @@ export const sendMessageProcedure = publicProcedure
|
||||
const session = sessionId ? await getSession(sessionId) : null
|
||||
|
||||
if (!session) {
|
||||
const { sessionId, typebot, messages, input, resultId } =
|
||||
const { sessionId, typebot, messages, input, resultId, dynamicTheme } =
|
||||
await startSession(startParams)
|
||||
return {
|
||||
sessionId,
|
||||
typebot: typebot
|
||||
? {
|
||||
id: typebot.id,
|
||||
theme: typebot.theme,
|
||||
settings: typebot.settings,
|
||||
}
|
||||
@ -50,6 +54,7 @@ export const sendMessageProcedure = publicProcedure
|
||||
messages,
|
||||
input,
|
||||
resultId,
|
||||
dynamicTheme,
|
||||
}
|
||||
} else {
|
||||
const { messages, input, logic, newSessionState, integrations } =
|
||||
@ -67,89 +72,35 @@ export const sendMessageProcedure = publicProcedure
|
||||
input,
|
||||
logic,
|
||||
integrations,
|
||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const startSession = async (startParams?: StartParams) => {
|
||||
if (!startParams?.typebotId)
|
||||
if (!startParams?.typebot)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'No typebotId provided in startParams',
|
||||
})
|
||||
const typebotQuery = startParams.isPreview
|
||||
? await prisma.typebot.findUnique({
|
||||
where: { id: startParams.typebotId },
|
||||
select: {
|
||||
groups: true,
|
||||
edges: true,
|
||||
settings: true,
|
||||
theme: true,
|
||||
variables: true,
|
||||
isArchived: true,
|
||||
},
|
||||
})
|
||||
: await prisma.typebot.findUnique({
|
||||
where: { id: startParams.typebotId },
|
||||
select: {
|
||||
publishedTypebot: {
|
||||
select: {
|
||||
groups: true,
|
||||
edges: true,
|
||||
settings: true,
|
||||
theme: true,
|
||||
variables: true,
|
||||
},
|
||||
},
|
||||
name: true,
|
||||
isClosed: true,
|
||||
isArchived: true,
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
|
||||
const typebot =
|
||||
typebotQuery && 'publishedTypebot' in typebotQuery
|
||||
? (typebotQuery.publishedTypebot as Pick<
|
||||
PublicTypebot,
|
||||
'groups' | 'edges' | 'settings' | 'theme' | 'variables'
|
||||
>)
|
||||
: (typebotQuery as Pick<
|
||||
Typebot,
|
||||
'groups' | 'edges' | 'settings' | 'theme' | 'variables' | 'isArchived'
|
||||
>)
|
||||
|
||||
if (!typebot)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Typebot not found',
|
||||
message: 'No typebot provided in startParams',
|
||||
})
|
||||
|
||||
if ('isClosed' in typebot && typebot.isClosed)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Typebot is closed',
|
||||
})
|
||||
|
||||
const hasReachedLimit = !startParams.isPreview
|
||||
? await checkChatsUsage(startParams.typebotId)
|
||||
: false
|
||||
|
||||
if (hasReachedLimit)
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'Your workspace reached its chat limit',
|
||||
})
|
||||
const typebot = await getTypebot(startParams)
|
||||
|
||||
const startVariables = startParams.prefilledVariables
|
||||
? parsePrefilledVariables(typebot.variables, startParams.prefilledVariables)
|
||||
: typebot.variables
|
||||
|
||||
const result = await getResult({ ...startParams, startVariables })
|
||||
const result = await getResult({
|
||||
...startParams,
|
||||
typebot: typebot.id,
|
||||
startVariables,
|
||||
isNewResultOnRefreshEnabled:
|
||||
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
|
||||
})
|
||||
|
||||
const initialState: SessionState = {
|
||||
typebot: {
|
||||
id: startParams.typebotId,
|
||||
id: typebot.id,
|
||||
groups: typebot.groups,
|
||||
edges: typebot.edges,
|
||||
variables: startVariables,
|
||||
@ -161,8 +112,9 @@ const startSession = async (startParams?: StartParams) => {
|
||||
result: result
|
||||
? { id: result.id, variables: result.variables, hasStarted: false }
|
||||
: undefined,
|
||||
isPreview: false,
|
||||
currentTypebotId: startParams.typebotId,
|
||||
isPreview: startParams.isPreview || typeof startParams.typebot !== 'string',
|
||||
currentTypebotId: typebot.id,
|
||||
dynamicTheme: parseDynamicThemeInState(typebot.theme),
|
||||
}
|
||||
|
||||
const {
|
||||
@ -170,16 +122,26 @@ const startSession = async (startParams?: StartParams) => {
|
||||
input,
|
||||
logic,
|
||||
newSessionState: newInitialState,
|
||||
} = await startBotFlow(initialState)
|
||||
} = await startBotFlow(initialState, startParams.startGroupId)
|
||||
|
||||
if (!input)
|
||||
return {
|
||||
messages,
|
||||
logic,
|
||||
typebot: {
|
||||
id: typebot.id,
|
||||
settings: deepParseVariable(newInitialState.typebot.variables)(
|
||||
typebot.settings
|
||||
),
|
||||
theme: deepParseVariable(newInitialState.typebot.variables)(
|
||||
typebot.theme
|
||||
),
|
||||
},
|
||||
dynamicTheme: parseDynamicThemeReply(newInitialState),
|
||||
}
|
||||
|
||||
const sessionState: ChatSession['state'] = {
|
||||
...(newInitialState ?? initialState),
|
||||
...newInitialState,
|
||||
currentBlock: {
|
||||
groupId: input.groupId,
|
||||
blockId: input.id,
|
||||
@ -196,27 +158,122 @@ const startSession = async (startParams?: StartParams) => {
|
||||
resultId: result?.id,
|
||||
sessionId: session.id,
|
||||
typebot: {
|
||||
settings: deepParseVariable(typebot.variables)(typebot.settings),
|
||||
theme: deepParseVariable(typebot.variables)(typebot.theme),
|
||||
id: typebot.id,
|
||||
settings: deepParseVariable(newInitialState.typebot.variables)(
|
||||
typebot.settings
|
||||
),
|
||||
theme: deepParseVariable(newInitialState.typebot.variables)(
|
||||
typebot.theme
|
||||
),
|
||||
},
|
||||
messages,
|
||||
input,
|
||||
logic,
|
||||
dynamicTheme: parseDynamicThemeReply(newInitialState),
|
||||
} satisfies ChatReply
|
||||
}
|
||||
|
||||
const getTypebot = async ({
|
||||
typebot,
|
||||
isPreview,
|
||||
}: Pick<StartParams, 'typebot' | 'isPreview'>): Promise<StartTypebot> => {
|
||||
if (typeof typebot !== 'string') return typebot
|
||||
const typebotQuery = isPreview
|
||||
? await prisma.typebot.findUnique({
|
||||
where: { id: typebot },
|
||||
select: {
|
||||
id: true,
|
||||
groups: true,
|
||||
edges: true,
|
||||
settings: true,
|
||||
theme: true,
|
||||
variables: true,
|
||||
isArchived: true,
|
||||
},
|
||||
})
|
||||
: await prisma.publicTypebot.findFirst({
|
||||
where: { typebot: { publicId: typebot } },
|
||||
select: {
|
||||
groups: true,
|
||||
edges: true,
|
||||
settings: true,
|
||||
theme: true,
|
||||
variables: true,
|
||||
typebotId: true,
|
||||
typebot: {
|
||||
select: {
|
||||
isArchived: true,
|
||||
isClosed: true,
|
||||
workspace: {
|
||||
select: {
|
||||
id: true,
|
||||
plan: true,
|
||||
additionalChatsIndex: true,
|
||||
chatsLimitFirstEmailSentAt: true,
|
||||
chatsLimitSecondEmailSentAt: true,
|
||||
customChatsLimit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const parsedTypebot =
|
||||
typebotQuery && 'typebot' in typebotQuery
|
||||
? ({
|
||||
id: typebotQuery.typebotId,
|
||||
...omit(typebotQuery.typebot, 'workspace'),
|
||||
...omit(typebotQuery, 'typebot', 'typebotId'),
|
||||
} as StartTypebot & Pick<Typebot, 'isArchived' | 'isClosed'>)
|
||||
: (typebotQuery as StartTypebot & Pick<Typebot, 'isArchived'>)
|
||||
|
||||
if (
|
||||
!parsedTypebot ||
|
||||
('isArchived' in parsedTypebot && parsedTypebot.isArchived)
|
||||
)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Typebot not found',
|
||||
})
|
||||
|
||||
if ('isClosed' in parsedTypebot && parsedTypebot.isClosed)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Typebot is closed',
|
||||
})
|
||||
|
||||
const hasReachedLimit =
|
||||
typebotQuery && 'typebot' in typebotQuery
|
||||
? await checkChatsUsage({
|
||||
typebotId: parsedTypebot.id,
|
||||
workspace: typebotQuery.typebot.workspace,
|
||||
})
|
||||
: false
|
||||
|
||||
if (hasReachedLimit)
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'You have reached your chats limit',
|
||||
})
|
||||
|
||||
return parsedTypebot
|
||||
}
|
||||
|
||||
const getResult = async ({
|
||||
typebotId,
|
||||
typebot,
|
||||
isPreview,
|
||||
resultId,
|
||||
startVariables,
|
||||
}: Pick<StartParams, 'isPreview' | 'resultId' | 'typebotId'> & {
|
||||
isNewResultOnRefreshEnabled,
|
||||
}: Pick<StartParams, 'isPreview' | 'resultId' | 'typebot'> & {
|
||||
startVariables: Variable[]
|
||||
isNewResultOnRefreshEnabled: boolean
|
||||
}) => {
|
||||
if (isPreview) return undefined
|
||||
if (isPreview || typeof typebot !== 'string') return undefined
|
||||
const data = {
|
||||
isCompleted: false,
|
||||
typebotId: typebotId,
|
||||
typebotId: typebot,
|
||||
variables: { set: startVariables.filter((variable) => variable.value) },
|
||||
} satisfies Prisma.ResultUncheckedCreateInput
|
||||
const select = {
|
||||
@ -225,7 +282,7 @@ const getResult = async ({
|
||||
hasStarted: true,
|
||||
} satisfies Prisma.ResultSelect
|
||||
return (
|
||||
resultId
|
||||
resultId && !isNewResultOnRefreshEnabled
|
||||
? await prisma.result.update({
|
||||
where: { id: resultId },
|
||||
data,
|
||||
@ -237,3 +294,36 @@ const getResult = async ({
|
||||
})
|
||||
) as Pick<Result, 'id' | 'variables' | 'hasStarted'>
|
||||
}
|
||||
|
||||
const parseDynamicThemeInState = (theme: Theme) => {
|
||||
const hostAvatarUrl =
|
||||
theme.chat.hostAvatar?.isEnabled ?? true
|
||||
? theme.chat.hostAvatar?.url
|
||||
: undefined
|
||||
const guestAvatarUrl =
|
||||
theme.chat.guestAvatar?.isEnabled ?? false
|
||||
? theme.chat.guestAvatar?.url
|
||||
: undefined
|
||||
if (!hostAvatarUrl?.startsWith('{{') && !guestAvatarUrl?.startsWith('{{'))
|
||||
return
|
||||
return {
|
||||
hostAvatarUrl: hostAvatarUrl?.startsWith('{{') ? hostAvatarUrl : undefined,
|
||||
guestAvatarUrl: guestAvatarUrl?.startsWith('{{')
|
||||
? guestAvatarUrl
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
const parseDynamicThemeReply = (
|
||||
state: SessionState | undefined
|
||||
): ChatReply['dynamicTheme'] => {
|
||||
if (!state?.dynamicTheme) return
|
||||
return {
|
||||
hostAvatarUrl: parseVariables(state?.typebot.variables)(
|
||||
state.dynamicTheme.hostAvatarUrl
|
||||
),
|
||||
guestAvatarUrl: parseVariables(state?.typebot.variables)(
|
||||
state.dynamicTheme.guestAvatarUrl
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export const executeGroup =
|
||||
(state: SessionState, currentReply?: ChatReply) =>
|
||||
async (
|
||||
group: Group
|
||||
): Promise<ChatReply & { newSessionState?: SessionState }> => {
|
||||
): Promise<ChatReply & { newSessionState: SessionState }> => {
|
||||
const messages: ChatReply['messages'] = currentReply?.messages ?? []
|
||||
let logic: ChatReply['logic'] = currentReply?.logic
|
||||
let integrations: ChatReply['integrations'] = currentReply?.integrations
|
||||
@ -72,8 +72,10 @@ export const executeGroup =
|
||||
integrations = executionResponse.integrations
|
||||
if (executionResponse.newSessionState)
|
||||
newSessionState = executionResponse.newSessionState
|
||||
if (executionResponse.outgoingEdgeId)
|
||||
if (executionResponse.outgoingEdgeId) {
|
||||
nextEdgeId = executionResponse.outgoingEdgeId
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextEdgeId) return { messages, newSessionState, logic, integrations }
|
||||
|
@ -1,13 +1,26 @@
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { ChatReply, SessionState } from 'models'
|
||||
import { executeGroup } from './executeGroup'
|
||||
import { getNextGroup } from './getNextGroup'
|
||||
|
||||
export const startBotFlow = async (
|
||||
state: SessionState
|
||||
): Promise<ChatReply & { newSessionState?: SessionState }> => {
|
||||
state: SessionState,
|
||||
startGroupId?: string
|
||||
): Promise<ChatReply & { newSessionState: SessionState }> => {
|
||||
if (startGroupId) {
|
||||
const group = state.typebot.groups.find(
|
||||
(group) => group.id === startGroupId
|
||||
)
|
||||
if (!group)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: "startGroupId doesn't exist",
|
||||
})
|
||||
return executeGroup(state)(group)
|
||||
}
|
||||
const firstEdgeId = state.typebot.groups[0].blocks[0].outgoingEdgeId
|
||||
if (!firstEdgeId) return { messages: [] }
|
||||
if (!firstEdgeId) return { messages: [], newSessionState: state }
|
||||
const nextGroup = getNextGroup(state)(firstEdgeId)
|
||||
if (!nextGroup) return { messages: [] }
|
||||
if (!nextGroup) return { messages: [], newSessionState: state }
|
||||
return executeGroup(state)(nextGroup.group)
|
||||
}
|
||||
|
@ -37,11 +37,10 @@ test('API chat execution should work on preview bot', async ({ request }) => {
|
||||
await request.post(`/api/v1/sendMessage`, {
|
||||
data: {
|
||||
startParams: {
|
||||
typebotId,
|
||||
typebot: typebotId,
|
||||
isPreview: true,
|
||||
},
|
||||
// TODO: replace with satisfies once compatible with playwright
|
||||
} as SendMessageInput,
|
||||
} satisfies SendMessageInput,
|
||||
})
|
||||
).json()
|
||||
expect(resultId).toBeUndefined()
|
||||
@ -75,10 +74,9 @@ test('API chat execution should work on published bot', async ({ request }) => {
|
||||
await request.post(`/api/v1/sendMessage`, {
|
||||
data: {
|
||||
startParams: {
|
||||
typebotId,
|
||||
typebot: publicId,
|
||||
},
|
||||
// TODO: replace with satisfies once compatible with playwright
|
||||
} as SendMessageInput,
|
||||
} satisfies SendMessageInput,
|
||||
})
|
||||
).json()
|
||||
chatSessionId = sessionId
|
||||
|
@ -27,8 +27,8 @@ test('Result should be overwritten on page refresh', async ({ page }) => {
|
||||
])
|
||||
const { resultId } = await response.json()
|
||||
expect(resultId).toBeDefined()
|
||||
|
||||
await expect(page.getByRole('textbox')).toBeVisible()
|
||||
|
||||
const [, secondResponse] = await Promise.all([
|
||||
page.reload(),
|
||||
page.waitForResponse(/sendMessage/),
|
||||
|
@ -4,30 +4,44 @@ import {
|
||||
sendAlmostReachedChatsLimitEmail,
|
||||
sendReachedChatsLimitEmail,
|
||||
} from 'emails'
|
||||
import { Workspace } from 'models'
|
||||
import { env, getChatsLimit, isDefined } from 'utils'
|
||||
|
||||
const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8
|
||||
|
||||
export const checkChatsUsage = async (typebotId: string) => {
|
||||
const typebot = await prisma.typebot.findUnique({
|
||||
where: {
|
||||
id: typebotId,
|
||||
},
|
||||
include: {
|
||||
workspace: {
|
||||
select: {
|
||||
id: true,
|
||||
plan: true,
|
||||
additionalChatsIndex: true,
|
||||
chatsLimitFirstEmailSentAt: true,
|
||||
chatsLimitSecondEmailSentAt: true,
|
||||
customChatsLimit: true,
|
||||
export const checkChatsUsage = async (props: {
|
||||
typebotId: string
|
||||
workspace?: Pick<
|
||||
Workspace,
|
||||
| 'id'
|
||||
| 'plan'
|
||||
| 'additionalChatsIndex'
|
||||
| 'chatsLimitFirstEmailSentAt'
|
||||
| 'chatsLimitSecondEmailSentAt'
|
||||
| 'customChatsLimit'
|
||||
>
|
||||
}) => {
|
||||
const typebot = props.workspace
|
||||
? null
|
||||
: await prisma.typebot.findUnique({
|
||||
where: {
|
||||
id: props.typebotId,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
include: {
|
||||
workspace: {
|
||||
select: {
|
||||
id: true,
|
||||
plan: true,
|
||||
additionalChatsIndex: true,
|
||||
chatsLimitFirstEmailSentAt: true,
|
||||
chatsLimitSecondEmailSentAt: true,
|
||||
customChatsLimit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const workspace = typebot?.workspace
|
||||
const workspace = props.workspace || typebot?.workspace
|
||||
|
||||
if (!workspace) return false
|
||||
|
||||
|
@ -26,7 +26,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
}
|
||||
if (req.method === 'POST') {
|
||||
const typebotId = req.query.typebotId as string
|
||||
const hasReachedLimit = await checkChatsUsage(typebotId)
|
||||
const hasReachedLimit = await checkChatsUsage({ typebotId })
|
||||
if (hasReachedLimit) return res.send({ result: null, hasReachedLimit })
|
||||
const result = await prisma.result.create({
|
||||
data: {
|
||||
|
@ -56,12 +56,12 @@ const getTypebotFromPublicId = async (
|
||||
const typebot = (await prisma.typebot.findUnique({
|
||||
where: { publicId },
|
||||
select: {
|
||||
id: true,
|
||||
theme: true,
|
||||
name: true,
|
||||
settings: true,
|
||||
isArchived: true,
|
||||
isClosed: true,
|
||||
publicId: true,
|
||||
},
|
||||
})) as TypebotPageV2Props['typebot'] | null
|
||||
if (isNotDefined(typebot)) return null
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { InitialChatReply, SendMessageInput } from 'models'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
type Props = {
|
||||
typebotId: string
|
||||
resultId?: string
|
||||
prefilledVariables?: Record<string, string>
|
||||
}
|
||||
export async function getInitialChatReplyQuery({
|
||||
typebotId,
|
||||
resultId,
|
||||
prefilledVariables,
|
||||
}: Props) {
|
||||
if (!typebotId)
|
||||
throw new Error('Typebot ID is required to get initial messages')
|
||||
|
||||
return sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `/api/v1/sendMessage`,
|
||||
body: {
|
||||
startParams: {
|
||||
typebotId,
|
||||
resultId,
|
||||
prefilledVariables,
|
||||
},
|
||||
} satisfies SendMessageInput,
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user