Skip to content

Commit 47d56f9

Browse files
Merge pull request #18 from judsonjuniorr/v2-is-on-whatsapp-cache
V2 is on whatsapp cache
2 parents ed5ff08 + 41342f3 commit 47d56f9

File tree

8 files changed

+191
-7
lines changed

8 files changed

+191
-7
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ DATABASE_SAVE_DATA_CONTACTS=true
3737
DATABASE_SAVE_DATA_CHATS=true
3838
DATABASE_SAVE_DATA_LABELS=true
3939
DATABASE_SAVE_DATA_HISTORIC=true
40+
DATABASE_SAVE_IS_ON_WHATSAPP=true
41+
DATABASE_SAVE_IS_ON_WHATSAPP_DAYS=7
4042

4143
# RabbitMQ - Environment variables
4244
RABBITMQ_ENABLED=false
@@ -172,6 +174,7 @@ DIFY_ENABLED=false
172174
# Redis Cache enabled
173175
CACHE_REDIS_ENABLED=true
174176
CACHE_REDIS_URI=redis://localhost:6379/6
177+
CACHE_REDIS_TTL=604800
175178
# Prefix serves to differentiate data from one installation to another that are using the same redis
176179
CACHE_REDIS_PREFIX_KEY=evolution
177180
# Enabling this variable will save the connection information in Redis and not in the database.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- CreateTable
2+
CREATE TABLE "is_on_whatsapp" (
3+
"id" TEXT NOT NULL,
4+
"remote_jid" VARCHAR(100) NOT NULL,
5+
"name" TEXT,
6+
"jid_options" TEXT NOT NULL,
7+
"created_at" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
8+
"updated_at" TIMESTAMP NOT NULL,
9+
10+
CONSTRAINT "is_on_whatsapp_pkey" PRIMARY KEY ("id")
11+
);
12+
13+
-- CreateIndex
14+
CREATE UNIQUE INDEX "is_on_whatsapp_remote_jid_key" ON "is_on_whatsapp"("remote_jid");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `name` on the `is_on_whatsapp` table. All the data in the column will be lost.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE "is_on_whatsapp" DROP COLUMN "name";

prisma/postgresql-schema.prisma

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,3 +673,13 @@ model FlowiseSetting {
673673
674674
@@map("flowise_settings")
675675
}
676+
677+
model IsOnWhatsapp {
678+
id String @id @default(cuid())
679+
remoteJid String @unique @map("remote_jid") @db.VarChar(100)
680+
jidOptions String @map("jid_options")
681+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp
682+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp
683+
684+
@@map("is_on_whatsapp")
685+
}

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import ffmpegPath from '@ffmpeg-installer/ffmpeg';
7171
import { Boom } from '@hapi/boom';
7272
import { Instance } from '@prisma/client';
7373
import { makeProxyAgent } from '@utils/makeProxyAgent';
74+
import { getOnWhatsappCache, saveOnWhatsappCache } from '@utils/onWhatsappCache';
7475
import useMultiFileAuthStatePrisma from '@utils/use-multi-file-auth-state-prisma';
7576
import { AuthStateProvider } from '@utils/use-multi-file-auth-state-provider-files';
7677
import { useMultiFileAuthStateRedisDb } from '@utils/use-multi-file-auth-state-redis-db';
@@ -682,14 +683,19 @@ export class BaileysStartupService extends ChannelStartupService {
682683
instanceId: this.instanceId,
683684
}));
684685

685-
if (contactsRaw.length > 0) this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw);
686-
687686
if (contactsRaw.length > 0) {
687+
this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw);
688+
688689
if (this.configService.get<Database>('DATABASE').SAVE_DATA.CONTACTS)
689690
await this.prismaRepository.contact.createMany({
690691
data: contactsRaw,
691692
skipDuplicates: true,
692693
});
694+
695+
const usersContacts = contactsRaw.filter((c) => c.remoteJid.includes('@s.whatsapp'));
696+
if (usersContacts) {
697+
await saveOnWhatsappCache(usersContacts.map((c) => ({ remoteJid: c.remoteJid })));
698+
}
693699
}
694700

695701
if (
@@ -717,9 +723,13 @@ export class BaileysStartupService extends ChannelStartupService {
717723
})),
718724
);
719725

720-
if (updatedContacts.length > 0) this.sendDataWebhook(Events.CONTACTS_UPDATE, updatedContacts);
721-
722726
if (updatedContacts.length > 0) {
727+
const usersContacts = updatedContacts.filter((c) => c.remoteJid.includes('@s.whatsapp'));
728+
if (usersContacts) {
729+
await saveOnWhatsappCache(usersContacts.map((c) => ({ remoteJid: c.remoteJid })));
730+
}
731+
732+
this.sendDataWebhook(Events.CONTACTS_UPDATE, updatedContacts);
723733
await Promise.all(
724734
updatedContacts.map(async (contact) => {
725735
const update = this.prismaRepository.contact.updateMany({
@@ -778,6 +788,11 @@ export class BaileysStartupService extends ChannelStartupService {
778788
}),
779789
);
780790
await this.prismaRepository.$transaction(updateTransactions);
791+
792+
const usersContacts = contactsRaw.filter((c) => c.remoteJid.includes('@s.whatsapp'));
793+
if (usersContacts) {
794+
await saveOnWhatsappCache(usersContacts.map((c) => ({ remoteJid: c.remoteJid })));
795+
}
781796
},
782797
};
783798

@@ -1225,6 +1240,10 @@ export class BaileysStartupService extends ChannelStartupService {
12251240
update: contactRaw,
12261241
create: contactRaw,
12271242
});
1243+
1244+
if (contactRaw.remoteJid.includes('@s.whatsapp')) {
1245+
await saveOnWhatsappCache([{ remoteJid: contactRaw.remoteJid }]);
1246+
}
12281247
}
12291248
} catch (error) {
12301249
this.logger.error('line 1318');
@@ -2686,11 +2705,27 @@ export class BaileysStartupService extends ChannelStartupService {
26862705
});
26872706

26882707
const numbersToVerify = jids.users.map(({ jid }) => jid.replace('+', ''));
2689-
const verify = await this.client.onWhatsApp(...numbersToVerify);
2708+
2709+
const cachedNumbers = await getOnWhatsappCache(numbersToVerify);
2710+
const filteredNumbers = numbersToVerify.filter(
2711+
(jid) => !cachedNumbers.some((cached) => cached.jidOptions.includes(jid)),
2712+
);
2713+
2714+
const verify = await this.client.onWhatsApp(...filteredNumbers);
26902715
const users: OnWhatsAppDto[] = await Promise.all(
26912716
jids.users.map(async (user) => {
26922717
let numberVerified: (typeof verify)[0] | null = null;
26932718

2719+
const cached = cachedNumbers.find((cached) => cached.jidOptions.includes(user.jid.replace('+', '')));
2720+
if (cached) {
2721+
return {
2722+
exists: true,
2723+
jid: cached.remoteJid,
2724+
name: contacts.find((c) => c.remoteJid === cached.remoteJid)?.pushName,
2725+
number: cached.number,
2726+
};
2727+
}
2728+
26942729
// Brazilian numbers
26952730
if (user.number.startsWith('55')) {
26962731
const numberWithDigit =
@@ -2733,6 +2768,7 @@ export class BaileysStartupService extends ChannelStartupService {
27332768
}
27342769

27352770
const numberJid = numberVerified?.jid || user.jid;
2771+
27362772
return {
27372773
exists: !!numberVerified?.exists,
27382774
jid: numberJid,
@@ -2742,6 +2778,8 @@ export class BaileysStartupService extends ChannelStartupService {
27422778
}),
27432779
);
27442780

2781+
await saveOnWhatsappCache(users.filter((user) => user.exists).map((user) => ({ remoteJid: user.jid })));
2782+
27452783
onWhatsapp.push(...users);
27462784

27472785
return onWhatsapp;
@@ -3525,8 +3563,15 @@ export class BaileysStartupService extends ChannelStartupService {
35253563
imgUrl: participant.imgUrl ?? contact?.profilePicUrl,
35263564
};
35273565
});
3566+
3567+
const usersContacts = parsedParticipants.filter((c) => c.id.includes('@s.whatsapp'));
3568+
if (usersContacts) {
3569+
await saveOnWhatsappCache(usersContacts.map((c) => ({ remoteJid: c.id })));
3570+
}
3571+
35283572
return { participants: parsedParticipants };
35293573
} catch (error) {
3574+
console.error(error);
35303575
this.logger.error('line 3583');
35313576
throw new NotFoundException('No participants', error.toString());
35323577
}

src/api/services/cache.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ export class CacheService {
3838
}
3939
}
4040

41-
async set(key: string, value: any) {
41+
async set(key: string, value: any, ttl?: number) {
4242
if (!this.cache) {
4343
return;
4444
}
45-
this.cache.set(key, value);
45+
this.cache.set(key, value, ttl);
4646
}
4747

4848
public async hSet(key: string, field: string, value: any) {

src/config/env.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ export type SaveData = {
4343
CONTACTS: boolean;
4444
CHATS: boolean;
4545
LABELS: boolean;
46+
IS_ON_WHATSAPP: boolean;
47+
IS_ON_WHATSAPP_DAYS: number;
4648
};
4749

4850
export type DBConnection = {
@@ -295,6 +297,8 @@ export class ConfigService {
295297
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
296298
HISTORIC: process.env?.DATABASE_SAVE_DATA_HISTORIC === 'true',
297299
LABELS: process.env?.DATABASE_SAVE_DATA_LABELS === 'true',
300+
IS_ON_WHATSAPP: process.env?.DATABASE_SAVE_IS_ON_WHATSAPP === 'true',
301+
IS_ON_WHATSAPP_DAYS: Number.parseInt(process.env?.DATABASE_SAVE_IS_ON_WHATSAPP_DAYS ?? '7'),
298302
},
299303
},
300304
RABBITMQ: {

src/utils/onWhatsappCache.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { prismaRepository } from '@api/server.module';
2+
import { configService, Database } from '@config/env.config';
3+
import dayjs from 'dayjs';
4+
5+
function getAvailableNumbers(remoteJid: string) {
6+
const numbersAvailable: string[] = [];
7+
8+
if (remoteJid.startsWith('+')) {
9+
remoteJid = remoteJid.slice(1);
10+
}
11+
12+
const [number, domain] = remoteJid.split('@');
13+
14+
// Brazilian numbers
15+
if (remoteJid.startsWith('55')) {
16+
const numberWithDigit =
17+
number.slice(4, 5) === '9' && number.length === 13 ? number : `${number.slice(0, 4)}9${number.slice(4)}`;
18+
const numberWithoutDigit = number.length === 12 ? number : number.slice(0, 4) + number.slice(5);
19+
20+
numbersAvailable.push(numberWithDigit);
21+
numbersAvailable.push(numberWithoutDigit);
22+
}
23+
24+
// Mexican/Argentina numbers
25+
// Ref: https://faq.whatsapp.com/1294841057948784
26+
else if (number.startsWith('52') || number.startsWith('54')) {
27+
let prefix = '';
28+
if (number.startsWith('52')) {
29+
prefix = '1';
30+
}
31+
if (number.startsWith('54')) {
32+
prefix = '9';
33+
}
34+
35+
const numberWithDigit =
36+
number.slice(2, 3) === prefix && number.length === 13
37+
? number
38+
: `${number.slice(0, 2)}${prefix}${number.slice(2)}`;
39+
const numberWithoutDigit = number.length === 12 ? number : number.slice(0, 2) + number.slice(3);
40+
41+
numbersAvailable.push(numberWithDigit);
42+
numbersAvailable.push(numberWithoutDigit);
43+
}
44+
45+
// Other countries
46+
else {
47+
numbersAvailable.push(remoteJid);
48+
}
49+
50+
return numbersAvailable.map((number) => `${number}@${domain}`);
51+
}
52+
53+
interface ISaveOnWhatsappCacheParams {
54+
remoteJid: string;
55+
}
56+
export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) {
57+
if (configService.get<Database>('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) {
58+
const upsertsQuery = data.map((item) => {
59+
const remoteJid = item.remoteJid.startsWith('+') ? item.remoteJid.slice(1) : item.remoteJid;
60+
const numbersAvailable = getAvailableNumbers(remoteJid);
61+
62+
return prismaRepository.isOnWhatsapp.upsert({
63+
create: { remoteJid: remoteJid, jidOptions: numbersAvailable.join(',') },
64+
update: { jidOptions: numbersAvailable.join(',') },
65+
where: { remoteJid: remoteJid },
66+
});
67+
});
68+
69+
await prismaRepository.$transaction(upsertsQuery);
70+
}
71+
}
72+
73+
export async function getOnWhatsappCache(remoteJids: string[]) {
74+
let results: {
75+
remoteJid: string;
76+
number: string;
77+
jidOptions: string[];
78+
}[] = [];
79+
80+
if (configService.get<Database>('DATABASE').SAVE_DATA.IS_ON_WHATSAPP) {
81+
const remoteJidsWithoutPlus = remoteJids.map((remoteJid) => getAvailableNumbers(remoteJid)).flat();
82+
83+
const onWhatsappCache = await prismaRepository.isOnWhatsapp.findMany({
84+
where: {
85+
OR: remoteJidsWithoutPlus.map((remoteJid) => ({ jidOptions: { contains: remoteJid } })),
86+
updatedAt: {
87+
gte: dayjs().subtract(configService.get<Database>('DATABASE').SAVE_DATA.IS_ON_WHATSAPP_DAYS, 'days').toDate(),
88+
},
89+
},
90+
});
91+
92+
results = onWhatsappCache.map((item) => ({
93+
remoteJid: item.remoteJid,
94+
number: item.remoteJid.split('@')[0],
95+
jidOptions: item.jidOptions.split(','),
96+
}));
97+
}
98+
99+
return results;
100+
}

0 commit comments

Comments
 (0)