Skip to content

Commit faa8d42

Browse files
committed
♻️ Add whatsapp api endpoints with waituntil
1 parent 87f5d85 commit faa8d42

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

apps/builder/src/features/whatsapp/receiveMessagePreview.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { isNotDefined } from '@typebot.io/lib'
66
import { TRPCError } from '@trpc/server'
77
import { env } from '@typebot.io/env'
88

9+
// TODO: Delete file, replaced by /api/v1/whatsapp/preview/webhook.ts
910
export const receiveMessagePreview = publicProcedure
1011
.meta({
1112
openapi: {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
WhatsAppWebhookRequestBody,
3+
whatsAppWebhookRequestBodySchema,
4+
} from '@typebot.io/schemas/features/whatsapp'
5+
import { resumeWhatsAppFlow } from '@typebot.io/bot-engine/whatsapp/resumeWhatsAppFlow'
6+
import { isNotDefined } from '@typebot.io/lib'
7+
import { env } from '@typebot.io/env'
8+
import { NextApiRequest, NextApiResponse } from 'next'
9+
10+
export default async function handler(
11+
req: NextApiRequest,
12+
res: NextApiResponse
13+
) {
14+
if (!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID)
15+
return res
16+
.status(500)
17+
.send('WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID is not defined')
18+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
19+
const { entry } = whatsAppWebhookRequestBodySchema.parse(body)
20+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
21+
// @ts-ignore
22+
const ctx = globalThis[Symbol.for('@vercel/request-context')]
23+
if (ctx?.get?.().waitUntil) {
24+
ctx.get().waitUntil(() => processWhatsAppReply(entry))
25+
return res.status(200).json({ message: 'Message is being processed.' })
26+
}
27+
console.log('Processing message')
28+
const { message } = await processWhatsAppReply(entry)
29+
return res.status(200).json({ message })
30+
}
31+
32+
const processWhatsAppReply = async (
33+
entry: WhatsAppWebhookRequestBody['entry']
34+
) => {
35+
const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0)
36+
if (isNotDefined(receivedMessage)) return { message: 'No message found' }
37+
const contactName =
38+
entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ''
39+
const contactPhoneNumber =
40+
entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ''
41+
return resumeWhatsAppFlow({
42+
receivedMessage,
43+
sessionId: `wa-preview-${receivedMessage.from}`,
44+
contact: {
45+
name: contactName,
46+
phoneNumber: contactPhoneNumber,
47+
},
48+
})
49+
}

apps/viewer/src/features/whatsapp/api/receiveMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from 'zod'
44
import { isNotDefined } from '@typebot.io/lib'
55
import { resumeWhatsAppFlow } from '@typebot.io/bot-engine/whatsapp/resumeWhatsAppFlow'
66

7+
// TODO: Delete file, replaced by /api/v1/workspaces/[workspaceId]/whatsapp/[credentialsId]/webhook.ts
78
export const receiveMessage = publicProcedure
89
.meta({
910
openapi: {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
WhatsAppWebhookRequestBody,
3+
whatsAppWebhookRequestBodySchema,
4+
} from '@typebot.io/schemas/features/whatsapp'
5+
import { resumeWhatsAppFlow } from '@typebot.io/bot-engine/whatsapp/resumeWhatsAppFlow'
6+
import { isNotDefined } from '@typebot.io/lib'
7+
import { NextApiRequest, NextApiResponse } from 'next'
8+
9+
type Props = {
10+
entry: WhatsAppWebhookRequestBody['entry']
11+
workspaceId: string
12+
credentialsId: string
13+
}
14+
15+
export const config = {
16+
supportsResponseStreaming: true,
17+
}
18+
19+
export default async function handler(
20+
req: NextApiRequest,
21+
res: NextApiResponse
22+
) {
23+
const workspaceId = req.query.workspaceId as string
24+
const credentialsId = req.query.credentialsId as string
25+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
26+
const { entry } = whatsAppWebhookRequestBodySchema.parse(body)
27+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
28+
// @ts-ignore
29+
const ctx = globalThis[Symbol.for('@vercel/request-context')]
30+
if (ctx?.get?.().waitUntil) {
31+
ctx
32+
.get()
33+
.waitUntil(() =>
34+
processWhatsAppReply({ entry, workspaceId, credentialsId })
35+
)
36+
return res.status(200).json({ message: 'Message is being processed.' })
37+
}
38+
const { message } = await processWhatsAppReply({
39+
entry,
40+
workspaceId,
41+
credentialsId,
42+
})
43+
return res.status(200).json({ message })
44+
}
45+
46+
const processWhatsAppReply = async ({
47+
entry,
48+
workspaceId,
49+
credentialsId,
50+
}: Props) => {
51+
const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0)
52+
if (isNotDefined(receivedMessage)) return { message: 'No message found' }
53+
const contactName =
54+
entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ''
55+
const contactPhoneNumber =
56+
entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ''
57+
const phoneNumberId = entry.at(0)?.changes.at(0)?.value
58+
.metadata.phone_number_id
59+
if (!phoneNumberId) return { message: 'No phone number id found' }
60+
return resumeWhatsAppFlow({
61+
receivedMessage,
62+
sessionId: `wa-${phoneNumberId}-${receivedMessage.from}`,
63+
phoneNumberId,
64+
credentialsId,
65+
workspaceId,
66+
contact: {
67+
name: contactName,
68+
phoneNumber: contactPhoneNumber,
69+
},
70+
})
71+
}

0 commit comments

Comments
 (0)