FEAT: Parser de eventos do webhook Meta (messages e statuses)
This commit is contained in:
parent
c681814cca
commit
79b55be7ef
@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Delete, Param, ForbiddenException, Query, RawBody } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, Delete, Param, ForbiddenException, Query } from '@nestjs/common';
|
||||
import { ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { WhatsappService } from './whatsapp.service';
|
||||
import { AttendanceAssignmentService } from '../attendance/attendance-assignment.service';
|
||||
@ -291,7 +291,7 @@ export class WhatsappController {
|
||||
@ApiOperation({ summary: 'Recebe eventos do webhook Meta' })
|
||||
@Post('webhook')
|
||||
async receiveWebhook(@Body() body: any) {
|
||||
console.log('Webhook Meta:', JSON.stringify(body, null, 2));
|
||||
await this.whatsappService.handleMetaWebhookEvent(body);
|
||||
return { status: 'ok' };
|
||||
}
|
||||
|
||||
|
||||
@ -578,4 +578,78 @@ export class WhatsappService implements OnModuleInit {
|
||||
return `*Atendente: ${cleanSenderName}*\n\n${cleanMessage}`;
|
||||
}
|
||||
|
||||
async handleMetaWebhookEvent(body: any): Promise<void> {
|
||||
if (body?.object !== 'whatsapp_business_account') return;
|
||||
|
||||
for (const entry of body?.entry ?? []) {
|
||||
for (const change of entry?.changes ?? []) {
|
||||
if (change.field !== 'messages') continue;
|
||||
|
||||
const value = change.value;
|
||||
|
||||
if (value?.messages?.length) {
|
||||
for (const message of value.messages) {
|
||||
await this.handleMetaIncomingMessage(message, value.contacts ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
if (value?.statuses?.length) {
|
||||
for (const status of value.statuses) {
|
||||
this.logger.log(`[Meta] Status de mensagem: ${status.id} → ${status.status} (destinatário: ${status.recipient_id})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handleMetaIncomingMessage(message: any, contacts: any[]): Promise<void> {
|
||||
const from: string = message.from;
|
||||
const messageId: string = message.id;
|
||||
const timestamp: number = parseInt(message.timestamp, 10);
|
||||
const messageBody: string = message.text?.body?.trim() ?? '';
|
||||
|
||||
const contact = contacts.find((c) => c.wa_id === from);
|
||||
const contactName: string = contact?.profile?.name || from;
|
||||
|
||||
if (this.processedIncomingMessages.has(messageId)) return;
|
||||
this.processedIncomingMessages.add(messageId);
|
||||
if (this.processedIncomingMessages.size > 1000) {
|
||||
const [oldest] = this.processedIncomingMessages;
|
||||
this.processedIncomingMessages.delete(oldest);
|
||||
}
|
||||
|
||||
this.logger.log(`[Meta] Mensagem recebida de ${from} (${contactName}): ${messageBody || '[mídia]'}`);
|
||||
|
||||
this.gateway.emitNewMessage({
|
||||
from,
|
||||
body: messageBody,
|
||||
timestamp,
|
||||
isGroupMsg: false,
|
||||
id: messageId,
|
||||
fromMe: false,
|
||||
notifyName: contactName,
|
||||
hasMedia: message.type !== 'text',
|
||||
media: null,
|
||||
});
|
||||
|
||||
if (messageBody) {
|
||||
await this.assignmentService.markCustomerReplied(from);
|
||||
}
|
||||
|
||||
if (!messageBody) {
|
||||
this.logger.log(`[Meta] Triagem ignorada para ${from}: mensagem sem texto.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const fakeMsg = {
|
||||
reply: async (text: string) => {
|
||||
// TODO Fase 2: enviar via Meta Graph API
|
||||
this.logger.warn(`[Meta] Bot reply pendente de implementação. Mensagem para ${from}: "${text}"`);
|
||||
},
|
||||
_data: { notifyName: contactName },
|
||||
};
|
||||
|
||||
await this.enqueueIncomingRoute(from, fakeMsg, messageBody, messageId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user