Skip to content

Commit c45ea00

Browse files
committed
👷 Add vercel function to benefit from waitUntil method for WA webhook
1 parent 6e0388c commit c45ea00

File tree

6 files changed

+113
-5
lines changed

6 files changed

+113
-5
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ snapshots
3939

4040
.env
4141
.typebot-build
42+
.vercel
43+
.env*.local
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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 type { RequestContext } from '@vercel/edge'
9+
10+
const processWhatsAppReply = async (
11+
entry: WhatsAppWebhookRequestBody['entry']
12+
) => {
13+
const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0)
14+
if (isNotDefined(receivedMessage)) return { message: 'No message found' }
15+
const contactName =
16+
entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ''
17+
const contactPhoneNumber =
18+
entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ''
19+
return resumeWhatsAppFlow({
20+
receivedMessage,
21+
sessionId: `wa-preview-${receivedMessage.from}`,
22+
contact: {
23+
name: contactName,
24+
phoneNumber: contactPhoneNumber,
25+
},
26+
})
27+
}
28+
29+
export async function POST(request: Request, context: RequestContext) {
30+
if (!env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID)
31+
return new Response(
32+
'WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID is not defined',
33+
{
34+
status: 500,
35+
}
36+
)
37+
const body = await request.json()
38+
const { entry } = whatsAppWebhookRequestBodySchema.parse(body)
39+
context.waitUntil(processWhatsAppReply(entry))
40+
return new Response('Message is being processed.', { status: 200 })
41+
}

apps/builder/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@
6767
"framer-motion": "10.3.0",
6868
"google-auth-library": "8.9.0",
6969
"google-spreadsheet": "4.1.1",
70-
"ky": "1.2.3",
7170
"immer": "10.0.2",
7271
"jsonwebtoken": "9.0.1",
72+
"ky": "1.2.3",
7373
"libphonenumber-js": "1.10.37",
7474
"micro": "10.0.1",
7575
"micro-cors": "0.1.1",
@@ -122,6 +122,7 @@
122122
"@types/qs": "6.9.7",
123123
"@types/react": "18.2.15",
124124
"@types/tinycolor2": "1.4.3",
125+
"@vercel/edge": "1.1.1",
125126
"dotenv-cli": "7.2.1",
126127
"eslint": "8.44.0",
127128
"eslint-config-custom": "workspace:*",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 type { RequestContext } from '@vercel/edge'
8+
9+
type Props = {
10+
entry: WhatsAppWebhookRequestBody['entry']
11+
workspaceId: string
12+
credentialsId: string
13+
}
14+
15+
const processWhatsAppReply = async ({
16+
entry,
17+
workspaceId,
18+
credentialsId,
19+
}: Props) => {
20+
const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0)
21+
if (isNotDefined(receivedMessage)) return { message: 'No message found' }
22+
const contactName =
23+
entry.at(0)?.changes.at(0)?.value?.contacts?.at(0)?.profile?.name ?? ''
24+
const contactPhoneNumber =
25+
entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ''
26+
const phoneNumberId = entry.at(0)?.changes.at(0)?.value
27+
.metadata.phone_number_id
28+
if (!phoneNumberId) return { message: 'No phone number id found' }
29+
return resumeWhatsAppFlow({
30+
receivedMessage,
31+
sessionId: `wa-${phoneNumberId}-${receivedMessage.from}`,
32+
phoneNumberId,
33+
credentialsId,
34+
workspaceId,
35+
contact: {
36+
name: contactName,
37+
phoneNumber: contactPhoneNumber,
38+
},
39+
})
40+
}
41+
42+
export async function POST(request: Request, context: RequestContext) {
43+
const workspaceId = request.url.match(/\/workspaces\/([^/]+)\//)?.[1]
44+
const credentialsId = request.url.match(/\/whatsapp\/([^/]+)\//)?.[1]
45+
if (!workspaceId || !credentialsId) {
46+
console.error('No workspace or credentials id found')
47+
return { message: 'No workspace or credentials id found' }
48+
}
49+
const body = await request.json()
50+
const { entry } = whatsAppWebhookRequestBodySchema.parse(body)
51+
context.waitUntil(processWhatsAppReply({ entry, workspaceId, credentialsId }))
52+
return new Response('Message is being processed.', { status: 200 })
53+
}

apps/viewer/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
"stripe": "12.13.0"
3838
},
3939
"devDependencies": {
40-
"dotenv": "16.4.5",
4140
"@faire/mjml-react": "3.3.0",
4241
"@paralleldrive/cuid2": "2.2.1",
4342
"@playwright/test": "1.36.0",
@@ -46,6 +45,8 @@
4645
"@typebot.io/forge": "workspace:*",
4746
"@typebot.io/forge-repository": "workspace:*",
4847
"@typebot.io/lib": "workspace:*",
48+
"@typebot.io/playwright": "workspace:*",
49+
"@typebot.io/results": "workspace:*",
4950
"@typebot.io/schemas": "workspace:*",
5051
"@typebot.io/tsconfig": "workspace:*",
5152
"@typebot.io/variables": "workspace:*",
@@ -55,6 +56,8 @@
5556
"@types/papaparse": "5.3.7",
5657
"@types/qs": "6.9.7",
5758
"@types/react": "18.2.15",
59+
"@vercel/edge": "1.1.1",
60+
"dotenv": "16.4.5",
5861
"dotenv-cli": "7.2.1",
5962
"eslint": "8.44.0",
6063
"eslint-config-custom": "workspace:*",
@@ -63,8 +66,6 @@
6366
"papaparse": "5.4.1",
6467
"superjson": "1.12.4",
6568
"typescript": "5.3.2",
66-
"zod": "3.22.4",
67-
"@typebot.io/playwright": "workspace:*",
68-
"@typebot.io/results": "workspace:*"
69+
"zod": "3.22.4"
6970
}
7071
}

pnpm-lock.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)