From afc2927837e9475488059d2846d6d36b5c70c94f Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 13 Jun 2025 11:52:32 -0300 Subject: [PATCH] feat(IsOnWhatsapp): add optional lid field and update related logic - Introduced a new optional `lid` field in the IsOnWhatsapp model to enhance data tracking. - Updated migration script to add the `lid` column to the database. - Modified OnWhatsAppDto to include the `lid` property for better integration with WhatsApp user data. - Enhanced the WhatsApp Baileys service to handle `lid` numbers separately and improve user verification logic. - Updated cache handling functions to support the new `lid` field for consistent data management. --- .../migration.sql | 2 + prisma/postgresql-schema.prisma | 1 + src/api/dto/chat.dto.ts | 1 + .../whatsapp/whatsapp.baileys.service.ts | 176 +++++++++++------- src/api/routes/chat.router.ts | 19 +- src/utils/onWhatsappCache.ts | 15 +- 6 files changed, 138 insertions(+), 76 deletions(-) create mode 100644 prisma/postgresql-migrations/20250613143000_add_lid_column_to_is_onwhatsapp/migration.sql diff --git a/prisma/postgresql-migrations/20250613143000_add_lid_column_to_is_onwhatsapp/migration.sql b/prisma/postgresql-migrations/20250613143000_add_lid_column_to_is_onwhatsapp/migration.sql new file mode 100644 index 00000000..c9510fcb --- /dev/null +++ b/prisma/postgresql-migrations/20250613143000_add_lid_column_to_is_onwhatsapp/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "IsOnWhatsapp" ADD COLUMN "lid" VARCHAR(100); diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index 86f5ae6c..7d2410e9 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -648,6 +648,7 @@ model IsOnWhatsapp { id String @id @default(cuid()) remoteJid String @unique @db.VarChar(100) jidOptions String + lid String? @db.VarChar(100) createdAt DateTime @default(now()) @db.Timestamp updatedAt DateTime @updatedAt @db.Timestamp } diff --git a/src/api/dto/chat.dto.ts b/src/api/dto/chat.dto.ts index 00da7fdd..b11f32b0 100644 --- a/src/api/dto/chat.dto.ts +++ b/src/api/dto/chat.dto.ts @@ -13,6 +13,7 @@ export class OnWhatsAppDto { public readonly exists: boolean, public readonly number: string, public readonly name?: string, + public readonly lid?: string, ) {} } diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index aad5ffc8..79cc9d7d 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1170,7 +1170,7 @@ export class BaileysStartupService extends ChannelStartupService { ) { const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, - { instanceName: this.instance.name, instanceId: this.instance.id }, + { instanceName: this.instance.name, instanceId: this.instanceId }, messageRaw, ); @@ -3131,10 +3131,10 @@ export class BaileysStartupService extends ChannelStartupService { const group = await this.findGroup({ groupJid: jid }, 'inner'); if (!group) { - new OnWhatsAppDto(jid, false, number); + return new OnWhatsAppDto(jid, false, number); } - return new OnWhatsAppDto(group.id, !!group?.id, number, group?.subject); + return new OnWhatsAppDto(group.id, true, number, group?.subject); }), ); onWhatsapp.push(...groups); @@ -3144,84 +3144,126 @@ export class BaileysStartupService extends ChannelStartupService { where: { instanceId: this.instanceId, remoteJid: { in: jids.users.map(({ jid }) => jid) } }, }); - const numbersToVerify = jids.users.map(({ jid }) => jid.replace('+', '')); + // Separate @lid numbers from normal numbers + const lidUsers = jids.users.filter(({ jid }) => jid.includes('@lid')); + const normalUsers = jids.users.filter(({ jid }) => !jid.includes('@lid')); - const cachedNumbers = await getOnWhatsappCache(numbersToVerify); - const filteredNumbers = numbersToVerify.filter( - (jid) => !cachedNumbers.some((cached) => cached.jidOptions.includes(jid)), - ); + // For normal numbers, use traditional Baileys verification + let normalVerifiedUsers: OnWhatsAppDto[] = []; + if (normalUsers.length > 0) { + console.log('normalUsers', normalUsers); + const numbersToVerify = normalUsers.map(({ jid }) => jid.replace('+', '')); + console.log('numbersToVerify', numbersToVerify); - const verify = await this.client.onWhatsApp(...filteredNumbers); - const users: OnWhatsAppDto[] = await Promise.all( - jids.users.map(async (user) => { - let numberVerified: (typeof verify)[0] | null = null; + const cachedNumbers = await getOnWhatsappCache(numbersToVerify); + console.log('cachedNumbers', cachedNumbers); - const cached = cachedNumbers.find((cached) => cached.jidOptions.includes(user.jid.replace('+', ''))); - if (cached) { - return { - exists: true, - jid: cached.remoteJid, - name: contacts.find((c) => c.remoteJid === cached.remoteJid)?.pushName, - number: user.number, - }; - } + const filteredNumbers = numbersToVerify.filter( + (jid) => !cachedNumbers.some((cached) => cached.jidOptions.includes(jid)), + ); + console.log('filteredNumbers', filteredNumbers); - // Brazilian numbers - if (user.number.startsWith('55')) { - const numberWithDigit = - user.number.slice(4, 5) === '9' && user.number.length === 13 - ? user.number - : `${user.number.slice(0, 4)}9${user.number.slice(4)}`; - const numberWithoutDigit = - user.number.length === 12 ? user.number : user.number.slice(0, 4) + user.number.slice(5); + const verify = await this.client.onWhatsApp(...filteredNumbers); + console.log('verify', verify); + normalVerifiedUsers = await Promise.all( + normalUsers.map(async (user) => { + let numberVerified: (typeof verify)[0] | null = null; - numberVerified = verify.find( - (v) => v.jid === `${numberWithDigit}@s.whatsapp.net` || v.jid === `${numberWithoutDigit}@s.whatsapp.net`, - ); - } - - // Mexican/Argentina numbers - // Ref: https://faq.whatsapp.com/1294841057948784 - if (!numberVerified && (user.number.startsWith('52') || user.number.startsWith('54'))) { - let prefix = ''; - if (user.number.startsWith('52')) { - prefix = ''; - } - if (user.number.startsWith('54')) { - prefix = '9'; + const cached = cachedNumbers.find((cached) => cached.jidOptions.includes(user.jid.replace('+', ''))); + if (cached) { + return new OnWhatsAppDto( + cached.remoteJid, + true, + user.number, + contacts.find((c) => c.remoteJid === cached.remoteJid)?.pushName, + cached.lid || (cached.remoteJid.includes('@lid') ? cached.remoteJid.split('@')[1] : undefined), + ); } - const numberWithDigit = - user.number.slice(2, 3) === prefix && user.number.length === 13 - ? user.number - : `${user.number.slice(0, 2)}${prefix}${user.number.slice(2)}`; - const numberWithoutDigit = - user.number.length === 12 ? user.number : user.number.slice(0, 2) + user.number.slice(3); + // Brazilian numbers + if (user.number.startsWith('55')) { + const numberWithDigit = + user.number.slice(4, 5) === '9' && user.number.length === 13 + ? user.number + : `${user.number.slice(0, 4)}9${user.number.slice(4)}`; + const numberWithoutDigit = + user.number.length === 12 ? user.number : user.number.slice(0, 4) + user.number.slice(5); - numberVerified = verify.find( - (v) => v.jid === `${numberWithDigit}@s.whatsapp.net` || v.jid === `${numberWithoutDigit}@s.whatsapp.net`, + numberVerified = verify.find( + (v) => v.jid === `${numberWithDigit}@s.whatsapp.net` || v.jid === `${numberWithoutDigit}@s.whatsapp.net`, + ); + } + + // Mexican/Argentina numbers + // Ref: https://faq.whatsapp.com/1294841057948784 + if (!numberVerified && (user.number.startsWith('52') || user.number.startsWith('54'))) { + let prefix = ''; + if (user.number.startsWith('52')) { + prefix = '1'; + } + if (user.number.startsWith('54')) { + prefix = '9'; + } + + const numberWithDigit = + user.number.slice(2, 3) === prefix && user.number.length === 13 + ? user.number + : `${user.number.slice(0, 2)}${prefix}${user.number.slice(2)}`; + const numberWithoutDigit = + user.number.length === 12 ? user.number : user.number.slice(0, 2) + user.number.slice(3); + + numberVerified = verify.find( + (v) => v.jid === `${numberWithDigit}@s.whatsapp.net` || v.jid === `${numberWithoutDigit}@s.whatsapp.net`, + ); + } + + if (!numberVerified) { + numberVerified = verify.find((v) => v.jid === user.jid); + } + + const numberJid = numberVerified?.jid || user.jid; + const lid = + typeof numberVerified?.lid === 'string' + ? numberVerified.lid + : numberJid.includes('@lid') + ? numberJid.split('@')[1] + : undefined; + return new OnWhatsAppDto( + numberJid, + !!numberVerified?.exists, + user.number, + contacts.find((c) => c.remoteJid === numberJid)?.pushName, + lid, ); - } + }), + ); + } - if (!numberVerified) { - numberVerified = verify.find((v) => v.jid === user.jid); - } + // For @lid numbers, always consider them as valid + const lidVerifiedUsers: OnWhatsAppDto[] = lidUsers.map((user) => { + return new OnWhatsAppDto( + user.jid, + true, + user.number, + contacts.find((c) => c.remoteJid === user.jid)?.pushName, + user.jid.split('@')[1], + ); + }); - const numberJid = numberVerified?.jid || user.jid; + // Combine results + onWhatsapp.push(...normalVerifiedUsers, ...lidVerifiedUsers); - return { - exists: !!numberVerified?.exists, - jid: numberJid, - name: contacts.find((c) => c.remoteJid === numberJid)?.pushName, - number: user.number, - }; - }), + // Save to cache only valid numbers + await saveOnWhatsappCache( + onWhatsapp + .filter((user) => user.exists) + .map((user) => ({ + remoteJid: user.jid, + jidOptions: user.jid.replace('+', ''), + lid: user.lid, + })), ); - await saveOnWhatsappCache(users.filter((user) => user.exists).map((user) => ({ remoteJid: user.jid }))); - - onWhatsapp.push(...users); - return onWhatsapp; } diff --git a/src/api/routes/chat.router.ts b/src/api/routes/chat.router.ts index 5c556705..a1094b5e 100644 --- a/src/api/routes/chat.router.ts +++ b/src/api/routes/chat.router.ts @@ -46,14 +46,19 @@ export class ChatRouter extends RouterBroker { super(); this.router .post(this.routerPath('whatsappNumbers'), ...guards, async (req, res) => { - const response = await this.dataValidate({ - request: req, - schema: whatsappNumberSchema, - ClassRef: WhatsAppNumberDto, - execute: (instance, data) => chatController.whatsappNumber(instance, data), - }); + try { + const response = await this.dataValidate({ + request: req, + schema: whatsappNumberSchema, + ClassRef: WhatsAppNumberDto, + execute: (instance, data) => chatController.whatsappNumber(instance, data), + }); - return res.status(HttpStatus.OK).json(response); + return res.status(HttpStatus.OK).json(response); + } catch (error) { + console.log(error); + return res.status(HttpStatus.BAD_REQUEST).json(error); + } }) .post(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => { const response = await this.dataValidate({ diff --git a/src/utils/onWhatsappCache.ts b/src/utils/onWhatsappCache.ts index a77ac396..68f88ba4 100644 --- a/src/utils/onWhatsappCache.ts +++ b/src/utils/onWhatsappCache.ts @@ -52,7 +52,9 @@ function getAvailableNumbers(remoteJid: string) { interface ISaveOnWhatsappCacheParams { remoteJid: string; + lid?: string; } + export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { if (configService.get('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) { const upsertsQuery = data.map((item) => { @@ -60,8 +62,15 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { const numbersAvailable = getAvailableNumbers(remoteJid); return prismaRepository.isOnWhatsapp.upsert({ - create: { remoteJid: remoteJid, jidOptions: numbersAvailable.join(',') }, - update: { jidOptions: numbersAvailable.join(',') }, + create: { + remoteJid: remoteJid, + jidOptions: numbersAvailable.join(','), + lid: item.lid, + }, + update: { + jidOptions: numbersAvailable.join(','), + lid: item.lid, + }, where: { remoteJid: remoteJid }, }); }); @@ -75,6 +84,7 @@ export async function getOnWhatsappCache(remoteJids: string[]) { remoteJid: string; number: string; jidOptions: string[]; + lid?: string; }[] = []; if (configService.get('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) { @@ -93,6 +103,7 @@ export async function getOnWhatsappCache(remoteJids: string[]) { remoteJid: item.remoteJid, number: item.remoteJid.split('@')[0], jidOptions: item.jidOptions.split(','), + lid: item.lid, })); }