2
0
Files
bot/apps/builder/src/features/whatsapp/startWhatsAppPreview.ts
2023-09-27 18:46:53 +02:00

166 lines
4.9 KiB
TypeScript

import { authenticatedProcedure } from '@/helpers/server/trpc'
import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { startSession } from '@typebot.io/bot-engine/startSession'
import { env } from '@typebot.io/env'
import { HTTPError } from 'got'
import prisma from '@typebot.io/lib/prisma'
import { saveStateToDatabase } from '@typebot.io/bot-engine/saveStateToDatabase'
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'
export const startWhatsAppPreview = authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/typebots/{typebotId}/whatsapp/start-preview',
summary: 'Start preview',
tags: ['WhatsApp'],
protect: true,
},
})
.input(
z.object({
to: z
.string()
.min(1)
.transform((value) => value.replace(/\s/g, '').replace(/\+/g, '')),
typebotId: z.string(),
startGroupId: z.string().optional(),
})
)
.output(
z.object({
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',
})
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' })
const sessionId = `wa-${to}-preview`
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
const { newSessionState, messages, input, clientSideActions, logs } =
await startSession({
startParams: {
isOnlyRegistering: !canSendDirectMessagesToUser,
typebot: typebotId,
isPreview: true,
startGroupId,
},
userId: user.id,
})
if (canSendDirectMessagesToUser) {
await sendChatReplyToWhatsApp({
to,
typingEmulation: newSessionState.typingEmulation,
messages,
input,
clientSideActions,
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,
currentBlock: !input ? undefined : newSessionState.currentBlock,
},
},
})
} else {
await restartSession({
state: {
...newSessionState,
whatsApp: (existingSession?.state as SessionState | undefined)
?.whatsApp,
},
id: `wa-${to}-preview`,
})
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',
}
}
)