@@ -4,8 +4,8 @@ import * as Sentry from '@sentry/nextjs'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getServerSession } from 'next-auth'
|
||||
import { mockedUser } from '../mockedUser'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { mockedUser } from '@typebot.io/lib/mockedUser'
|
||||
|
||||
export const getAuthenticatedUser = async (
|
||||
req: NextApiRequest,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { User } from '@typebot.io/prisma'
|
||||
|
||||
export const mockedUser: User = {
|
||||
id: 'userId',
|
||||
name: 'John Doe',
|
||||
email: 'user@email.com',
|
||||
company: null,
|
||||
createdAt: new Date('2022-01-01'),
|
||||
emailVerified: null,
|
||||
graphNavigation: 'TRACKPAD',
|
||||
preferredAppAppearance: null,
|
||||
image: 'https://avatars.githubusercontent.com/u/16015833?v=4',
|
||||
lastActivityAt: new Date('2022-01-01'),
|
||||
onboardingCategories: [],
|
||||
updatedAt: new Date('2022-01-01'),
|
||||
}
|
||||
@@ -12,8 +12,6 @@ export const AudioBubbleNode = ({ url }: Props) => {
|
||||
return isDefined(url) ? (
|
||||
<audio src={url} controls />
|
||||
) : (
|
||||
<Text color={'gray.500'}>
|
||||
{t('editor.blocks.bubbles.audio.node.clickToEdit.text')}
|
||||
</Text>
|
||||
<Text color={'gray.500'}>{t('clickToEdit')}</Text>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@ type Props = {
|
||||
export const EmbedBubbleContent = ({ block }: Props) => {
|
||||
const { t } = useTranslate()
|
||||
if (!block.content?.url)
|
||||
return (
|
||||
<Text color="gray.500">
|
||||
{t('editor.blocks.bubbles.embed.node.clickToEdit.text')}
|
||||
</Text>
|
||||
)
|
||||
return <Text color="gray.500">{t('clickToEdit')}</Text>
|
||||
return <Text>{t('editor.blocks.bubbles.embed.node.show.text')}</Text>
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ export const ImageBubbleContent = ({ block }: Props) => {
|
||||
const containsVariables =
|
||||
block.content?.url?.includes('{{') && block.content.url.includes('}}')
|
||||
return !block.content?.url ? (
|
||||
<Text color={'gray.500'}>
|
||||
{t('editor.blocks.bubbles.image.node.clickToEdit.text')}
|
||||
</Text>
|
||||
<Text color={'gray.500'}>{t('clickToEdit')}</Text>
|
||||
) : (
|
||||
<Box w="full">
|
||||
<Image
|
||||
|
||||
@@ -13,11 +13,7 @@ type Props = {
|
||||
export const VideoBubbleContent = ({ block }: Props) => {
|
||||
const { t } = useTranslate()
|
||||
if (!block.content?.url || !block.content.type)
|
||||
return (
|
||||
<Text color="gray.500">
|
||||
{t('editor.blocks.bubbles.video.node.clickToEdit.text')}
|
||||
</Text>
|
||||
)
|
||||
return <Text color="gray.500">{t('clickToEdit')}</Text>
|
||||
const containsVariables =
|
||||
block.content?.url?.includes('{{') && block.content.url.includes('}}')
|
||||
switch (block.content.type) {
|
||||
|
||||
@@ -32,7 +32,7 @@ test('should be configurable', async ({ page }) => {
|
||||
await expect(page.getByTestId('selected-item-label').first()).toHaveText(
|
||||
'My link typebot 2'
|
||||
)
|
||||
await page.click('input[placeholder="Select a block"]')
|
||||
await page.click('input[placeholder="Select a group"]')
|
||||
await page.click('text=Group #2')
|
||||
|
||||
await page.click('text=Preview')
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { useGraph } from '@/features/graph/providers/GraphProvider'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { Standard } from '@typebot.io/nextjs'
|
||||
import { ChatReply } from '@typebot.io/schemas'
|
||||
import { ContinueChatResponse } from '@typebot.io/schemas'
|
||||
|
||||
export const WebPreview = () => {
|
||||
const { typebot } = useTypebot()
|
||||
@@ -13,7 +13,7 @@ export const WebPreview = () => {
|
||||
|
||||
const { showToast } = useToast()
|
||||
|
||||
const handleNewLogs = (logs: ChatReply['logs']) => {
|
||||
const handleNewLogs = (logs: ContinueChatResponse['logs']) => {
|
||||
logs?.forEach((log) => {
|
||||
showToast({
|
||||
icon: <WebhookIcon />,
|
||||
@@ -40,8 +40,13 @@ export const WebPreview = () => {
|
||||
<Standard
|
||||
key={`web-preview${startPreviewAtGroup ?? ''}`}
|
||||
typebot={typebot}
|
||||
startGroupId={startPreviewAtGroup}
|
||||
startEventId={startPreviewAtEvent}
|
||||
startFrom={
|
||||
startPreviewAtGroup
|
||||
? { type: 'group', groupId: startPreviewAtGroup }
|
||||
: startPreviewAtEvent
|
||||
? { type: 'event', eventId: startPreviewAtEvent }
|
||||
: undefined
|
||||
}
|
||||
onNewInputBlock={(block) =>
|
||||
setPreviewingBlock({
|
||||
id: block.id,
|
||||
|
||||
@@ -24,7 +24,7 @@ import { BuoyIcon, ExternalLinkIcon } from '@/components/icons'
|
||||
|
||||
export const WhatsAppPreviewInstructions = (props: StackProps) => {
|
||||
const { typebot, save } = useTypebot()
|
||||
const { startPreviewAtGroup } = useEditor()
|
||||
const { startPreviewAtGroup, startPreviewAtEvent } = useEditor()
|
||||
const [phoneNumber, setPhoneNumber] = useState(
|
||||
getPhoneNumberFromLocalStorage() ?? ''
|
||||
)
|
||||
@@ -56,7 +56,11 @@ export const WhatsAppPreviewInstructions = (props: StackProps) => {
|
||||
mutate({
|
||||
to: phoneNumber,
|
||||
typebotId: typebot.id,
|
||||
startGroupId: startPreviewAtGroup,
|
||||
startFrom: startPreviewAtGroup
|
||||
? { type: 'group', groupId: startPreviewAtGroup }
|
||||
: startPreviewAtEvent
|
||||
? { type: 'event', eventId: startPreviewAtEvent }
|
||||
: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { restartSession } from '@typebot.io/bot-engine/queries/restartSession'
|
||||
import { sendChatReplyToWhatsApp } from '@typebot.io/bot-engine/whatsapp/sendChatReplyToWhatsApp'
|
||||
import { sendWhatsAppMessage } from '@typebot.io/bot-engine/whatsapp/sendWhatsAppMessage'
|
||||
import { isReadTypebotForbidden } from '../typebot/helpers/isReadTypebotForbidden'
|
||||
import { SessionState } from '@typebot.io/schemas'
|
||||
import { SessionState, startFromSchema } from '@typebot.io/schemas'
|
||||
|
||||
export const startWhatsAppPreview = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -31,7 +31,7 @@ export const startWhatsAppPreview = authenticatedProcedure
|
||||
value.replace(/\s/g, '').replace(/\+/g, '').replace(/-/g, '')
|
||||
),
|
||||
typebotId: z.string(),
|
||||
startGroupId: z.string().optional(),
|
||||
startFrom: startFromSchema.optional(),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
@@ -39,135 +39,133 @@ export const startWhatsAppPreview = authenticatedProcedure
|
||||
message: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(
|
||||
async ({ input: { to, typebotId, startGroupId }, ctx: { user } }) => {
|
||||
if (
|
||||
!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID ||
|
||||
!env.META_SYSTEM_USER_TOKEN ||
|
||||
!env.WHATSAPP_PREVIEW_TEMPLATE_NAME
|
||||
)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message:
|
||||
'Missing WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID or META_SYSTEM_USER_TOKEN or WHATSAPP_PREVIEW_TEMPLATE_NAME env variables',
|
||||
})
|
||||
.mutation(async ({ input: { to, typebotId, startFrom }, ctx: { user } }) => {
|
||||
if (
|
||||
!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID ||
|
||||
!env.META_SYSTEM_USER_TOKEN ||
|
||||
!env.WHATSAPP_PREVIEW_TEMPLATE_NAME
|
||||
)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message:
|
||||
'Missing WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID or META_SYSTEM_USER_TOKEN or WHATSAPP_PREVIEW_TEMPLATE_NAME env variables',
|
||||
})
|
||||
|
||||
const existingTypebot = await prisma.typebot.findFirst({
|
||||
where: {
|
||||
id: typebotId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
workspaceId: true,
|
||||
collaborators: {
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
const existingTypebot = await prisma.typebot.findFirst({
|
||||
where: {
|
||||
id: typebotId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
workspaceId: true,
|
||||
collaborators: {
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
if (
|
||||
!existingTypebot?.id ||
|
||||
(await isReadTypebotForbidden(existingTypebot, user))
|
||||
)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
|
||||
},
|
||||
})
|
||||
if (
|
||||
!existingTypebot?.id ||
|
||||
(await isReadTypebotForbidden(existingTypebot, user))
|
||||
)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
|
||||
|
||||
const sessionId = `wa-preview-${to}`
|
||||
const sessionId = `wa-preview-${to}`
|
||||
|
||||
const existingSession = await prisma.chatSession.findFirst({
|
||||
where: {
|
||||
id: sessionId,
|
||||
},
|
||||
select: {
|
||||
updatedAt: true,
|
||||
state: true,
|
||||
},
|
||||
})
|
||||
const existingSession = await prisma.chatSession.findFirst({
|
||||
where: {
|
||||
id: sessionId,
|
||||
},
|
||||
select: {
|
||||
updatedAt: true,
|
||||
state: true,
|
||||
},
|
||||
})
|
||||
|
||||
// For users that did not interact with the bot in the last 24 hours, we need to send a template message.
|
||||
const canSendDirectMessagesToUser =
|
||||
(existingSession?.updatedAt.getTime() ?? 0) >
|
||||
Date.now() - 24 * 60 * 60 * 1000
|
||||
// For users that did not interact with the bot in the last 24 hours, we need to send a template message.
|
||||
const canSendDirectMessagesToUser =
|
||||
(existingSession?.updatedAt.getTime() ?? 0) >
|
||||
Date.now() - 24 * 60 * 60 * 1000
|
||||
|
||||
const {
|
||||
newSessionState,
|
||||
const {
|
||||
newSessionState,
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
logs,
|
||||
visitedEdges,
|
||||
} = await startSession({
|
||||
version: 2,
|
||||
message: undefined,
|
||||
startParams: {
|
||||
isOnlyRegistering: !canSendDirectMessagesToUser,
|
||||
type: 'preview',
|
||||
typebotId,
|
||||
startFrom,
|
||||
userId: user.id,
|
||||
},
|
||||
initialSessionState: {
|
||||
whatsApp: (existingSession?.state as SessionState | undefined)
|
||||
?.whatsApp,
|
||||
},
|
||||
})
|
||||
|
||||
if (canSendDirectMessagesToUser) {
|
||||
await sendChatReplyToWhatsApp({
|
||||
to,
|
||||
typingEmulation: newSessionState.typingEmulation,
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
logs,
|
||||
visitedEdges,
|
||||
} = await startSession({
|
||||
version: 2,
|
||||
message: undefined,
|
||||
startParams: {
|
||||
isOnlyRegistering: !canSendDirectMessagesToUser,
|
||||
typebot: typebotId,
|
||||
isPreview: true,
|
||||
startGroupId,
|
||||
},
|
||||
userId: user.id,
|
||||
initialSessionState: {
|
||||
whatsApp: (existingSession?.state as SessionState | undefined)
|
||||
?.whatsApp,
|
||||
credentials: {
|
||||
phoneNumberId: env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID,
|
||||
systemUserAccessToken: env.META_SYSTEM_USER_TOKEN,
|
||||
},
|
||||
state: newSessionState,
|
||||
})
|
||||
|
||||
if (canSendDirectMessagesToUser) {
|
||||
await sendChatReplyToWhatsApp({
|
||||
await saveStateToDatabase({
|
||||
clientSideActions: [],
|
||||
input,
|
||||
logs,
|
||||
session: {
|
||||
id: sessionId,
|
||||
state: newSessionState,
|
||||
},
|
||||
visitedEdges,
|
||||
})
|
||||
} else {
|
||||
await restartSession({
|
||||
state: newSessionState,
|
||||
id: sessionId,
|
||||
})
|
||||
try {
|
||||
await sendWhatsAppMessage({
|
||||
to,
|
||||
typingEmulation: newSessionState.typingEmulation,
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
message: {
|
||||
type: 'template',
|
||||
template: {
|
||||
language: {
|
||||
code: env.WHATSAPP_PREVIEW_TEMPLATE_LANG,
|
||||
},
|
||||
name: env.WHATSAPP_PREVIEW_TEMPLATE_NAME,
|
||||
},
|
||||
},
|
||||
credentials: {
|
||||
phoneNumberId: env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID,
|
||||
systemUserAccessToken: env.META_SYSTEM_USER_TOKEN,
|
||||
},
|
||||
state: newSessionState,
|
||||
})
|
||||
await saveStateToDatabase({
|
||||
clientSideActions: [],
|
||||
input,
|
||||
logs,
|
||||
session: {
|
||||
id: sessionId,
|
||||
state: newSessionState,
|
||||
},
|
||||
visitedEdges,
|
||||
} catch (err) {
|
||||
if (err instanceof HTTPError) console.log(err.response.body)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Request to Meta to send preview message failed',
|
||||
cause: err,
|
||||
})
|
||||
} else {
|
||||
await restartSession({
|
||||
state: newSessionState,
|
||||
id: sessionId,
|
||||
})
|
||||
try {
|
||||
await sendWhatsAppMessage({
|
||||
to,
|
||||
message: {
|
||||
type: 'template',
|
||||
template: {
|
||||
language: {
|
||||
code: env.WHATSAPP_PREVIEW_TEMPLATE_LANG,
|
||||
},
|
||||
name: env.WHATSAPP_PREVIEW_TEMPLATE_NAME,
|
||||
},
|
||||
},
|
||||
credentials: {
|
||||
phoneNumberId: env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID,
|
||||
systemUserAccessToken: env.META_SYSTEM_USER_TOKEN,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof HTTPError) console.log(err.response.body)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Request to Meta to send preview message failed',
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
return {
|
||||
message: 'success',
|
||||
}
|
||||
}
|
||||
)
|
||||
return {
|
||||
message: 'success',
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { customAdapter } from '../../../features/auth/api/customAdapter'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { getAtPath, isDefined } from '@typebot.io/lib'
|
||||
import { mockedUser } from '@/features/auth/mockedUser'
|
||||
import { mockedUser } from '@typebot.io/lib/mockedUser'
|
||||
import { getNewUserInvitations } from '@/features/auth/helpers/getNewUserInvitations'
|
||||
import { sendVerificationRequest } from '@/features/auth/helpers/sendVerificationRequest'
|
||||
import { Ratelimit } from '@upstash/ratelimit'
|
||||
|
||||
Reference in New Issue
Block a user