Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 98 additions & 34 deletions src/api/integrations/channel/meta/meta.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,118 @@ export class MetaController extends ChannelController implements ChannelControll
integrationEnabled: boolean;

public async receiveWebhook(data: any) {
if (data.object === 'whatsapp_business_account') {
if (data.entry[0]?.changes[0]?.field === 'message_template_status_update') {
const template = await this.prismaRepository.template.findFirst({
where: { templateId: `${data.entry[0].changes[0].value.message_template_id}` },
});
if (data.object !== 'whatsapp_business_account') {
return {
status: 'success',
};
}

if (!template) {
console.log('template not found');
return;
}
const entries = data.entry ?? [];

const { webhookUrl } = template;
for (const entry of entries) {
const changes = entry.changes ?? [];

await axios.post(webhookUrl, data.entry[0].changes[0].value, {
headers: {
'Content-Type': 'application/json',
},
});
return;
}
for (const change of changes) {
if (change?.field === 'message_template_status_update') {
const templateId = change?.value?.message_template_id;

if (!templateId) {
this.logger.error('WebhookService -> receiveWebhookMeta -> templateId not found');
continue;
}

const template = await this.prismaRepository.template.findFirst({
where: {
templateId: String(templateId),
},
});

if (!template) {
this.logger.error(`WebhookService -> receiveWebhookMeta -> template not found: ${templateId}`);
continue;
}

if (!template.webhookUrl) {
this.logger.error(`WebhookService -> receiveWebhookMeta -> template webhookUrl not found: ${templateId}`);
continue;
}

try {
await axios.post(template.webhookUrl, change.value, {
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
this.logger.error(
`WebhookService -> receiveWebhookMeta -> error sending template webhook: ${
error instanceof Error ? error.message : String(error)
}`,
);
}

continue;
}

data.entry?.forEach(async (entry: any) => {
const numberId = entry.changes[0].value.metadata.phone_number_id;
const numberId = change?.value?.metadata?.phone_number_id;

if (!numberId) {
this.logger.error('WebhookService -> receiveWebhookMeta -> numberId not found');
return {
status: 'success',
};
continue;
}

const instance = await this.prismaRepository.instance.findFirst({
where: { number: numberId },
const instances = await this.prismaRepository.instance.findMany({
where: {
number: String(numberId),
},
});

if (!instance) {
this.logger.error('WebhookService -> receiveWebhookMeta -> instance not found');
return {
status: 'success',
};
if (!instances.length) {
this.logger.error(`WebhookService -> receiveWebhookMeta -> instances not found for numberId: ${numberId}`);
continue;
}

await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);

return {
status: 'success',
const webhookData = {
...data,
entry: [
{
...entry,
changes: [change],
},
],
};
});

const results = await Promise.allSettled(
instances.map(async (instance) => {
const waInstance = this.waMonitor.waInstances[instance.name];

if (!waInstance) {
throw new Error(`Instance not loaded: ${instance.name}`);
}

await waInstance.connectToWhatsapp(webhookData);

return instance.name;
}),
);

results.forEach((result, index) => {
const instanceName = instances[index].name;

if (result.status === 'rejected') {
this.logger.error(
`WebhookService -> receiveWebhookMeta -> error processing webhook for instance ${instanceName}: ${
result.reason instanceof Error ? result.reason.message : String(result.reason)
}`,
);
return;
}

this.logger.log(
`WebhookService -> receiveWebhookMeta -> webhook processed successfully for instance ${instanceName}`,
);
});
}
}

return {
Expand Down
40 changes: 32 additions & 8 deletions src/api/integrations/channel/meta/whatsapp.business.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,38 @@ export class BusinessStartupService extends ChannelStartupService {
public async connectToWhatsapp(data?: any): Promise<any> {
if (!data) return;

const content = data.entry[0].changes[0].value;
const content = data?.entry?.[0]?.changes?.[0]?.value;

if (!content) {
this.logger.error('ChannelStartupService -> connectToWhatsapp -> webhook content not found');
return;
}

try {
this.loadChatwoot();
const message = Array.isArray(content.messages) ? content.messages[0] : undefined;

const status = Array.isArray(content.statuses) ? content.statuses[0] : undefined;

this.eventHandler(content);
const messageEcho = Array.isArray(content.message_echoes) ? content.message_echoes[0] : undefined;

this.phoneNumber = createJid(content.messages ? content.messages[0].from : content.statuses[0]?.recipient_id);
const phoneNumber = message?.from ?? status?.recipient_id ?? messageEcho?.to;

if (!phoneNumber) {
this.logger.error(
'ChannelStartupService -> connectToWhatsapp -> phone number not found in messages, statuses or message_echoes',
);
return;
}

this.phoneNumber = createJid(phoneNumber);

this.loadChatwoot();

await this.eventHandler(content);
} catch (error) {
this.logger.error(error);
throw new InternalServerErrorException(error?.toString());

throw new InternalServerErrorException(error instanceof Error ? error.message : String(error));
}
}

Expand Down Expand Up @@ -387,7 +408,10 @@ export class BusinessStartupService extends ChannelStartupService {
let messageRaw: any;
let pushName: any;

if (received.contacts) pushName = received.contacts[0].profile.name;
const receivedContact = received.contacts[0];
const remoteJid = receivedContact.profile?.phone || receivedContact.wa_id;

if (received.contacts) pushName = receivedContact.profile?.name || '';
Comment on lines +411 to +414

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Guard against missing or empty received.contacts before indexing into it.

receivedContact = received.contacts[0] runs before the if (received.contacts) guard, so an undefined or empty contacts array will cause a runtime error. Consider guarding first, e.g.:

const receivedContact = Array.isArray(received.contacts) ? received.contacts[0] : undefined;
if (receivedContact) {
  const remoteJid = receivedContact.profile?.phone || receivedContact.wa_id;
  pushName = receivedContact.profile?.name || '';
}

This prevents crashes when contacts is missing or empty and keeps remoteJid aligned with the same presence checks as pushName.


if (received.messages) {
const message = received.messages[0]; // AΓ±adir esta lΓ­nea para definir message
Expand Down Expand Up @@ -702,7 +726,7 @@ export class BusinessStartupService extends ChannelStartupService {
});

const contactRaw: any = {
remoteJid: received.contacts[0].profile.phone,
remoteJid,
pushName,
// profilePicUrl: '',
instanceId: this.instanceId,
Expand All @@ -714,7 +738,7 @@ export class BusinessStartupService extends ChannelStartupService {

if (contact) {
const contactRaw: any = {
remoteJid: received.contacts[0].profile.phone,
remoteJid,
pushName,
// profilePicUrl: '',
instanceId: this.instanceId,
Expand Down