CognxSafeTrack commited on
Commit Β·
c4487e4
1
Parent(s): fed10ef
fix: restore missing loops and fix syntax in whatsapp route
Browse files- apps/api/src/routes/whatsapp.ts +66 -48
apps/api/src/routes/whatsapp.ts
CHANGED
|
@@ -101,6 +101,7 @@ export async function whatsappRoutes(fastify: FastifyInstance) {
|
|
| 101 |
// ββ POST /webhook β Incoming Meta events ββββββββββββββββββββββββββββββββ
|
| 102 |
fastify.post('/webhook', async (request, reply) => {
|
| 103 |
// ββ 1. HMAC Signature Verification ββββββββββββββββββββββββββββββββββ
|
|
|
|
| 104 |
const appSecret = process.env.WHATSAPP_APP_SECRET;
|
| 105 |
|
| 106 |
if (appSecret) {
|
|
@@ -173,9 +174,10 @@ export async function whatsappRoutes(fastify: FastifyInstance) {
|
|
| 173 |
}
|
| 174 |
|
| 175 |
// ββ 4. Background Processing (enqueue to Worker) ββ
|
|
|
|
| 176 |
setImmediate(async () => {
|
| 177 |
try {
|
| 178 |
-
const parsed = WebhookPayloadSchema.safeParse(body);
|
| 179 |
if (!parsed.success) {
|
| 180 |
fastify.log.warn(`[WEBHOOK] Failed to parse webhook payload: ${parsed.error.message}`);
|
| 181 |
return;
|
|
@@ -184,54 +186,70 @@ export async function whatsappRoutes(fastify: FastifyInstance) {
|
|
| 184 |
const { scheduleInboundMessage } = await import('../services/queue');
|
| 185 |
const payload = parsed.data;
|
| 186 |
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
}
|
| 190 |
-
if (text) await scheduleInboundMessage({ phone, text, messageId });
|
| 191 |
-
|
| 192 |
-
} else if (message.type === 'audio' && message.audio) {
|
| 193 |
-
// Existing audio logic (queues download-media)
|
| 194 |
-
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN || undefined;
|
| 195 |
-
const { Queue } = await import('bullmq');
|
| 196 |
-
const Redis = (await import('ioredis')).default;
|
| 197 |
-
const conn = process.env.REDIS_URL
|
| 198 |
-
? new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })
|
| 199 |
-
: new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), maxRetriesPerRequest: null });
|
| 200 |
-
const q = new Queue('whatsapp-queue', { connection: conn as any });
|
| 201 |
-
|
| 202 |
-
await q.add('download-media', {
|
| 203 |
-
mediaId: message.audio.id,
|
| 204 |
-
mimeType: message.audio.mime_type || 'audio/ogg',
|
| 205 |
-
phone,
|
| 206 |
-
...(accessToken ? { accessToken } : {})
|
| 207 |
-
}, { priority: 1, attempts: 3, backoff: { type: 'exponential', delay: 2000 } });
|
| 208 |
-
|
| 209 |
-
await q.add('send-message-direct', {
|
| 210 |
-
phone,
|
| 211 |
-
text: "β³ J'analyse ton audio..."
|
| 212 |
-
});
|
| 213 |
-
} else if (message.type === 'image' && message.image) {
|
| 214 |
-
console.log(`[IMAGE-FLOW] Image detected! ID: ${message.image.id}`);
|
| 215 |
-
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN || undefined;
|
| 216 |
-
const { Queue } = await import('bullmq');
|
| 217 |
-
const Redis = (await import('ioredis')).default;
|
| 218 |
-
const conn = process.env.REDIS_URL
|
| 219 |
-
? new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })
|
| 220 |
-
: new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), maxRetriesPerRequest: null });
|
| 221 |
-
const q = new Queue('whatsapp-queue', { connection: conn as any });
|
| 222 |
-
|
| 223 |
-
console.log(`[IMAGE-FLOW] Enqueuing for download...`);
|
| 224 |
-
await q.add('download-media', {
|
| 225 |
-
mediaId: message.image.id,
|
| 226 |
-
mimeType: 'image/jpeg',
|
| 227 |
-
phone,
|
| 228 |
-
...(accessToken ? { accessToken } : {})
|
| 229 |
-
}, { priority: 1, attempts: 3, backoff: { type: 'exponential', delay: 2000 } });
|
| 230 |
-
|
| 231 |
-
await q.add('send-message-direct', {
|
| 232 |
-
phone,
|
| 233 |
-
text: "β³ J'analyse ton image..."
|
| 234 |
-
});
|
| 235 |
}
|
| 236 |
}
|
| 237 |
}
|
|
|
|
| 101 |
// ββ POST /webhook β Incoming Meta events ββββββββββββββββββββββββββββββββ
|
| 102 |
fastify.post('/webhook', async (request, reply) => {
|
| 103 |
// ββ 1. HMAC Signature Verification ββββββββββββββββββββββββββββββββββ
|
| 104 |
+
console.log("[RAW-WHATSAPP-PAYLOAD]", JSON.stringify(request.body, null, 2));
|
| 105 |
const appSecret = process.env.WHATSAPP_APP_SECRET;
|
| 106 |
|
| 107 |
if (appSecret) {
|
|
|
|
| 174 |
}
|
| 175 |
|
| 176 |
// ββ 4. Background Processing (enqueue to Worker) ββ
|
| 177 |
+
const body = request.body;
|
| 178 |
setImmediate(async () => {
|
| 179 |
try {
|
| 180 |
+
const parsed = WebhookPayloadSchema.safeParse(body);
|
| 181 |
if (!parsed.success) {
|
| 182 |
fastify.log.warn(`[WEBHOOK] Failed to parse webhook payload: ${parsed.error.message}`);
|
| 183 |
return;
|
|
|
|
| 186 |
const { scheduleInboundMessage } = await import('../services/queue');
|
| 187 |
const payload = parsed.data;
|
| 188 |
|
| 189 |
+
for (const entry of payload.entry || []) {
|
| 190 |
+
for (const change of entry.changes || []) {
|
| 191 |
+
if (change.value?.messages) {
|
| 192 |
+
for (const message of change.value.messages) {
|
| 193 |
+
const phone = message.from;
|
| 194 |
+
const messageId = message.id;
|
| 195 |
+
console.log("[WEBHOOK-TRACE] Processing message for phone:", phone);
|
| 196 |
+
|
| 197 |
+
let text: string | undefined;
|
| 198 |
+
if (message.type === 'text' && message.text) {
|
| 199 |
+
text = message.text.body;
|
| 200 |
+
} else if (message.type === 'interactive' && message.interactive) {
|
| 201 |
+
if (message.interactive.type === 'button_reply' && message.interactive.button_reply) {
|
| 202 |
+
text = message.interactive.button_reply.id;
|
| 203 |
+
} else if (message.interactive.type === 'list_reply' && message.interactive.list_reply) {
|
| 204 |
+
text = message.interactive.list_reply.id;
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
if (text) {
|
| 209 |
+
await scheduleInboundMessage({ phone, text, messageId });
|
| 210 |
+
} else if (message.type === 'audio' && message.audio) {
|
| 211 |
+
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN || undefined;
|
| 212 |
+
const { Queue } = await import('bullmq');
|
| 213 |
+
const Redis = (await import('ioredis')).default;
|
| 214 |
+
const conn = process.env.REDIS_URL
|
| 215 |
+
? new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })
|
| 216 |
+
: new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), maxRetriesPerRequest: null });
|
| 217 |
+
const q = new Queue('whatsapp-queue', { connection: conn as any });
|
| 218 |
+
|
| 219 |
+
await q.add('download-media', {
|
| 220 |
+
mediaId: message.audio.id,
|
| 221 |
+
mimeType: message.audio.mime_type || 'audio/ogg',
|
| 222 |
+
phone,
|
| 223 |
+
...(accessToken ? { accessToken } : {})
|
| 224 |
+
}, { priority: 1, attempts: 3, backoff: { type: 'exponential', delay: 2000 } });
|
| 225 |
+
|
| 226 |
+
await q.add('send-message-direct', {
|
| 227 |
+
phone,
|
| 228 |
+
text: "β³ J'analyse ton audio..."
|
| 229 |
+
});
|
| 230 |
+
} else if (message.type === 'image' && message.image) {
|
| 231 |
+
console.log(`[IMAGE-FLOW] Image detected! ID: ${message.image.id}`);
|
| 232 |
+
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN || undefined;
|
| 233 |
+
const { Queue } = await import('bullmq');
|
| 234 |
+
const Redis = (await import('ioredis')).default;
|
| 235 |
+
const conn = process.env.REDIS_URL
|
| 236 |
+
? new Redis(process.env.REDIS_URL, { maxRetriesPerRequest: null })
|
| 237 |
+
: new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), maxRetriesPerRequest: null });
|
| 238 |
+
const q = new Queue('whatsapp-queue', { connection: conn as any });
|
| 239 |
+
|
| 240 |
+
console.log(`[IMAGE-FLOW] Enqueuing for download...`);
|
| 241 |
+
await q.add('download-media', {
|
| 242 |
+
mediaId: message.image.id,
|
| 243 |
+
mimeType: 'image/jpeg',
|
| 244 |
+
phone,
|
| 245 |
+
...(accessToken ? { accessToken } : {})
|
| 246 |
+
}, { priority: 1, attempts: 3, backoff: { type: 'exponential', delay: 2000 } });
|
| 247 |
+
|
| 248 |
+
await q.add('send-message-direct', {
|
| 249 |
+
phone,
|
| 250 |
+
text: "β³ J'analyse ton image..."
|
| 251 |
+
});
|
| 252 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
}
|
| 254 |
}
|
| 255 |
}
|