@omnizap-system/omnizap 2.6.2 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +24 -0
- package/app/config/index.js +4 -0
- package/app/configParts/adminIdentity.js +29 -0
- package/app/configParts/baileysConfig.js +116 -0
- package/app/configParts/groupUtils.js +221 -0
- package/app/configParts/loggerConfig.js +185 -0
- package/app/configParts/messagePersistenceService.js +169 -7
- package/app/configParts/sessionConfig.js +85 -0
- package/app/connection/baileysCompatibility.test.js +9 -0
- package/app/connection/baileysDbAuthState.js +205 -9
- package/app/connection/baileysLibsignalPatch.js +210 -0
- package/app/connection/groupOwnerWriteStateResolver.js +53 -21
- package/app/connection/socketController.js +95 -25
- package/app/connection/socketController.multiSession.test.js +20 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +17 -3
- package/app/controllers/messageProcessingPipeline.js +2 -0
- package/app/controllers/messageProcessingPipeline.test.js +15 -13
- package/app/services/multiSession/assignmentBalancerService.js +1 -6
- package/app/services/multiSession/groupOwnershipRepository.js +9 -44
- package/app/services/multiSession/groupOwnershipService.js +9 -90
- package/app/services/multiSession/groupOwnershipService.test.js +12 -4
- package/app/services/multiSession/sessionRegistryService.js +6 -60
- package/app/utils/antiLink/antiLinkModule.js +54 -24
- package/docs/security/omnizap-static-security-headers.conf +3 -3
- package/package.json +3 -2
- package/public/comandos/commands-catalog.json +1 -1
- package/public/css/payments-react.css +478 -0
- package/public/js/apps/homeReactApp.js +2 -2
- package/public/js/apps/paymentsCancelReactApp.js +45 -0
- package/public/js/apps/paymentsReactApp.js +399 -0
- package/public/js/apps/paymentsSuccessReactApp.js +148 -0
- package/public/pages/pagamentos-cancelado.html +21 -0
- package/public/pages/pagamentos-sucesso.html +21 -0
- package/public/pages/pagamentos.html +30 -0
- package/scripts/deploy.sh +3 -0
- package/scripts/new-whatsapp-session.sh +247 -0
- package/server/controllers/admin/systemAdminController.js +4 -17
- package/server/controllers/payments/paymentsController.js +731 -0
- package/server/controllers/system/systemController.js +4 -30
- package/server/email/emailAutomationRuntime.js +36 -1
- package/server/email/emailAutomationService.js +42 -1
- package/server/email/emailTemplateService.js +137 -31
- package/server/http/httpRequestUtils.js +18 -14
- package/server/middleware/securityHeaders.js +15 -2
- package/server/routes/indexRouter.js +27 -7
- package/server/routes/payments/paymentsRouter.js +47 -0
- package/server/routes/static/staticPageRouter.js +3 -0
- package/vite.config.mjs +3 -0
package/.env.example
CHANGED
|
@@ -105,6 +105,7 @@ BAILEYS_GROUP_METADATA_CACHE_CHECKPERIOD_SECONDS=60
|
|
|
105
105
|
BAILEYS_SEND_RETRY_ATTEMPTS=2
|
|
106
106
|
BAILEYS_SEND_RETRY_BASE_DELAY_MS=600
|
|
107
107
|
BAILEYS_SEND_MEDIA_UPLOAD_TIMEOUT_MS=0
|
|
108
|
+
BAILEYS_SEND_PREFER_PN_FOR_LID=true
|
|
108
109
|
BAILEYS_VERSION=
|
|
109
110
|
BAILEYS_FETCH_LATEST_VERSION=false
|
|
110
111
|
BAILEYS_LOGGER_MODE=child
|
|
@@ -128,11 +129,13 @@ BAILEYS_AUTH_SESSION_ID=default
|
|
|
128
129
|
BAILEYS_SESSION_IDS=default
|
|
129
130
|
BAILEYS_PRIMARY_SESSION_ID=default
|
|
130
131
|
BAILEYS_SESSION_WEIGHTS=default=1
|
|
132
|
+
BAILEYS_AUTH_KEYS_CACHE_ENABLED=true
|
|
131
133
|
BAILEYS_AUTH_BOOTSTRAP_FROM_FILES=true
|
|
132
134
|
BAILEYS_SINGLE_WRITER_LOCK_ENABLED=true
|
|
133
135
|
BAILEYS_SINGLE_WRITER_LOCK_NAME=
|
|
134
136
|
BAILEYS_SINGLE_WRITER_LOCK_TIMEOUT_SECONDS=2
|
|
135
137
|
BAILEYS_SINGLE_WRITER_LOCK_RETRY_DELAY_MS=15000
|
|
138
|
+
BAILEYS_LIBSIGNAL_RUNTIME_PATCH_ENABLED=true
|
|
136
139
|
GROUP_OWNER_ENFORCEMENT_MODE=off
|
|
137
140
|
GROUP_OWNER_LEASE_MS=120000
|
|
138
141
|
GROUP_OWNER_HEARTBEAT_MS=30000
|
|
@@ -322,6 +325,7 @@ WEB_USER_PASSWORD_RECOVERY_HASH_SECRET=
|
|
|
322
325
|
WEB_URL=https://omnizap.shop
|
|
323
326
|
WEB_VISITOR_COOKIE_TTL_SECONDS=31536000
|
|
324
327
|
WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN=true
|
|
328
|
+
WHATSAPP_ALLOW_SELF_COMMANDS_ON_APPEND=true
|
|
325
329
|
WHATSAPP_GOOGLE_LINK_CHECK_CACHE_TTL_MS=60000
|
|
326
330
|
WHATSAPP_LOGIN_LINK_TTL_SECONDS=900
|
|
327
331
|
WHATSAPP_LOGIN_REQUIRE_SIGNATURE=true
|
|
@@ -905,6 +909,26 @@ WIKI_SYNC_SOURCE_DIR=./docs/wiki
|
|
|
905
909
|
WIKI_SYNC_TMP_DIR=/tmp/omnizap-wiki-sync
|
|
906
910
|
STACK_NAME=omnizap
|
|
907
911
|
|
|
912
|
+
# ==============================
|
|
913
|
+
# PAYMENTS (STRIPE CHECKOUT + WEBHOOK)
|
|
914
|
+
# ==============================
|
|
915
|
+
STRIPE_PAYMENTS_ENABLED=true
|
|
916
|
+
PAYMENTS_API_BASE_PATH=/api/payments
|
|
917
|
+
PAYMENTS_WEB_PATH=/pagamentos
|
|
918
|
+
STRIPE_SECRET_KEY=
|
|
919
|
+
STRIPE_WEBHOOK_SECRET=
|
|
920
|
+
STRIPE_PRICE_ID=
|
|
921
|
+
STRIPE_CHECKOUT_MODE=subscription
|
|
922
|
+
STRIPE_PLAN_NAME=Plano Premium
|
|
923
|
+
STRIPE_PLAN_PRICE_LABEL=Assinatura recorrente
|
|
924
|
+
STRIPE_CHECKOUT_SUCCESS_URL=https://omnizap.shop/pagamentos/sucesso?session_id={CHECKOUT_SESSION_ID}
|
|
925
|
+
STRIPE_CHECKOUT_CANCEL_URL=https://omnizap.shop/pagamentos/cancelado
|
|
926
|
+
STRIPE_API_BASE_URL=https://api.stripe.com/v1
|
|
927
|
+
STRIPE_API_TIMEOUT_MS=10000
|
|
928
|
+
STRIPE_ALLOW_PROMOTION_CODES=true
|
|
929
|
+
STRIPE_WEBHOOK_TOLERANCE_SECONDS=300
|
|
930
|
+
STRIPE_AUTO_REVOKE_ON_CANCELLATION=false
|
|
931
|
+
|
|
908
932
|
# ==============================
|
|
909
933
|
# EMAIL AUTOMATION (SMTP/OUTBOX)
|
|
910
934
|
# ==============================
|
package/app/config/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel de configuração da aplicação.
|
|
3
|
+
* Reexporta utilitários de Baileys, grupos, identidade admin, logger e sessão.
|
|
4
|
+
*/
|
|
1
5
|
export * from '../configParts/baileysConfig.js';
|
|
2
6
|
export * from '../configParts/groupUtils.js';
|
|
3
7
|
export * from '../configParts/adminIdentity.js';
|
|
@@ -2,8 +2,17 @@ import { encodeJid, getJidUser, isSameJidUser, normalizeJid } from './baileysCon
|
|
|
2
2
|
import { extractUserIdInfo, resolveUserId, resolveUserIdCached } from './baileysConfig.js';
|
|
3
3
|
import { normalizePhoneDigits, resolveAdminIdentityRawFromEnv, resolveAdminPhoneFromEnv } from '../../utils/whatsapp/contactEnv.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Retorna o valor bruto configurado para identidade de admin.
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
5
9
|
export const getAdminRawValue = () => resolveAdminIdentityRawFromEnv();
|
|
6
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Resolve o JID do administrador com base no valor de ambiente.
|
|
13
|
+
* Aceita JID completo ou telefone numérico.
|
|
14
|
+
* @returns {string|null}
|
|
15
|
+
*/
|
|
7
16
|
export const getAdminJid = () => {
|
|
8
17
|
const raw = getAdminRawValue();
|
|
9
18
|
if (!raw) return null;
|
|
@@ -24,6 +33,11 @@ export const getAdminJid = () => {
|
|
|
24
33
|
return normalizedResolved || candidate;
|
|
25
34
|
};
|
|
26
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Resolve o telefone do administrador.
|
|
38
|
+
* Prioriza `ADMIN_PHONE` explícito e faz fallback para JID/identidade.
|
|
39
|
+
* @returns {string|null}
|
|
40
|
+
*/
|
|
27
41
|
export const getAdminPhone = () => {
|
|
28
42
|
const explicitAdminPhone = resolveAdminPhoneFromEnv({ fallback: '' });
|
|
29
43
|
if (explicitAdminPhone) return explicitAdminPhone;
|
|
@@ -38,6 +52,10 @@ export const getAdminPhone = () => {
|
|
|
38
52
|
return digits || null;
|
|
39
53
|
};
|
|
40
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Resolve o JID do admin consultando reconciliação LID/JID quando disponível.
|
|
57
|
+
* @returns {Promise<string|null>}
|
|
58
|
+
*/
|
|
41
59
|
export const resolveAdminJid = async () => {
|
|
42
60
|
const cached = getAdminJid();
|
|
43
61
|
if (!cached) return null;
|
|
@@ -50,6 +68,11 @@ export const resolveAdminJid = async () => {
|
|
|
50
68
|
}
|
|
51
69
|
};
|
|
52
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Verifica se um JID de remetente corresponde ao administrador.
|
|
73
|
+
* @param {string|null|undefined} senderJid
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
*/
|
|
53
76
|
export const isAdminSender = (senderJid) => {
|
|
54
77
|
const adminJid = getAdminJid();
|
|
55
78
|
if (!adminJid || !senderJid) return false;
|
|
@@ -60,6 +83,12 @@ export const isAdminSender = (senderJid) => {
|
|
|
60
83
|
return isSameJidUser(normalizedSender, adminJid) || normalizedSender === adminJid;
|
|
61
84
|
};
|
|
62
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Verifica se a identidade do remetente corresponde ao administrador.
|
|
88
|
+
* Considera candidatos `jid`, `lid`, `participantAlt` e resolução assíncrona.
|
|
89
|
+
* @param {unknown} senderIdentity
|
|
90
|
+
* @returns {Promise<boolean>}
|
|
91
|
+
*/
|
|
63
92
|
export const isAdminSenderAsync = async (senderIdentity) => {
|
|
64
93
|
const senderInfo = extractUserIdInfo(senderIdentity);
|
|
65
94
|
if (!senderInfo.raw && !senderInfo.jid && !senderInfo.lid && !senderInfo.participantAlt) return false;
|
|
@@ -78,15 +78,28 @@ import { getMultiSessionRuntimeConfig } from './sessionConfig.js';
|
|
|
78
78
|
* }} SenderInfo
|
|
79
79
|
*/
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Versão local de fallback do Baileys.
|
|
83
|
+
* @type {number[]}
|
|
84
|
+
*/
|
|
81
85
|
const DEFAULT_BAILEYS_VERSION = [7, 0, 0];
|
|
82
86
|
const multiSessionRuntimeConfig = getMultiSessionRuntimeConfig();
|
|
83
87
|
const PRIMARY_BAILEYS_SESSION_ID = String(multiSessionRuntimeConfig?.primarySessionId || process.env.BAILEYS_AUTH_SESSION_ID || 'default').trim() || 'default';
|
|
84
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Normaliza ID de sessão com fallback para sessão primária.
|
|
91
|
+
* @param {string|null|undefined} sessionId
|
|
92
|
+
* @returns {string}
|
|
93
|
+
*/
|
|
85
94
|
const normalizeSessionId = (sessionId) => {
|
|
86
95
|
const normalized = String(sessionId || '').trim();
|
|
87
96
|
return normalized || PRIMARY_BAILEYS_SESSION_ID;
|
|
88
97
|
};
|
|
89
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Mapa de sockets ativos por sessão.
|
|
101
|
+
* @type {Map<string, BaileysSocket>}
|
|
102
|
+
*/
|
|
90
103
|
const sessionSocketMap = new Map();
|
|
91
104
|
let activeSocket = null;
|
|
92
105
|
|
|
@@ -265,6 +278,10 @@ export const WHATSAPP_USER_JID_SERVERS = new Set(['s.whatsapp.net', 'c.us', 'hos
|
|
|
265
278
|
*/
|
|
266
279
|
export const LID_USER_JID_SERVERS = new Set(['lid', 'hosted.lid']);
|
|
267
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Decode de JID com cache simples do último valor.
|
|
283
|
+
* @type {(jid: string) => ({user?: string, server?: string, domainType?: number, device?: number}|null)}
|
|
284
|
+
*/
|
|
268
285
|
const decodeJidParts = (() => {
|
|
269
286
|
let lastJid = null;
|
|
270
287
|
let lastDecoded = null;
|
|
@@ -341,10 +358,20 @@ export const MEDIA_TYPE_MAPPING = {
|
|
|
341
358
|
*/
|
|
342
359
|
export const BINARY_MEDIA_TYPES = new Set(['image', 'video', 'videoNote', 'audio', 'voice', 'document', 'sticker']);
|
|
343
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Aplica normalização nativa do Baileys no payload de mensagem.
|
|
363
|
+
* @param {Record<string, any>} message
|
|
364
|
+
* @returns {Record<string, any>}
|
|
365
|
+
*/
|
|
344
366
|
const normalizeMessage = (message) => normalizeMessageContent(message) || message;
|
|
345
367
|
|
|
346
368
|
const MESSAGE_CONTENT_WRAPPER_KEYS = ['ephemeralMessage', 'viewOnceMessage', 'viewOnceMessageV2', 'viewOnceMessageV2Extension', 'deviceSentMessage', 'documentWithCaptionMessage', 'botInvokeMessage', 'editedMessage', 'keepInChatMessage'];
|
|
347
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Resolve wrapper de mensagem de um nó com chave única.
|
|
372
|
+
* @param {Record<string, any>} node
|
|
373
|
+
* @returns {Record<string, any>|null}
|
|
374
|
+
*/
|
|
348
375
|
const resolveSingleWrapperMessage = (node) => {
|
|
349
376
|
if (!node || typeof node !== 'object') return null;
|
|
350
377
|
|
|
@@ -359,6 +386,12 @@ const resolveSingleWrapperMessage = (node) => {
|
|
|
359
386
|
return null;
|
|
360
387
|
};
|
|
361
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Remove camadas de wrappers (`ephemeral`, `viewOnce`, etc.) até o payload real.
|
|
391
|
+
* @param {Record<string, any>} message
|
|
392
|
+
* @param {number} [maxDepth=8]
|
|
393
|
+
* @returns {Record<string, any>}
|
|
394
|
+
*/
|
|
362
395
|
const unwrapMessageContent = (message, maxDepth = 8) => {
|
|
363
396
|
let current = normalizeMessage(message);
|
|
364
397
|
const visited = new Set();
|
|
@@ -392,6 +425,11 @@ const unwrapMessageContent = (message, maxDepth = 8) => {
|
|
|
392
425
|
return current || message;
|
|
393
426
|
};
|
|
394
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Verifica se uma mediaKey está presente e não vazia.
|
|
430
|
+
* @param {unknown} mediaKey
|
|
431
|
+
* @returns {boolean}
|
|
432
|
+
*/
|
|
395
433
|
const hasNonEmptyMediaKey = (mediaKey) => {
|
|
396
434
|
if (!mediaKey) return false;
|
|
397
435
|
|
|
@@ -417,6 +455,15 @@ const hasNonEmptyMediaKey = (mediaKey) => {
|
|
|
417
455
|
return Boolean(mediaKey);
|
|
418
456
|
};
|
|
419
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Constrói entrada padronizada de mídia detectada.
|
|
460
|
+
* @param {string} mediaType
|
|
461
|
+
* @param {string} messageKey
|
|
462
|
+
* @param {Record<string, any>} value
|
|
463
|
+
* @param {boolean} isQuoted
|
|
464
|
+
* @param {Partial<MediaEntry>} [overrides={}]
|
|
465
|
+
* @returns {MediaEntry}
|
|
466
|
+
*/
|
|
420
467
|
const buildMediaEntry = (mediaType, messageKey, value, isQuoted, overrides = {}) => ({
|
|
421
468
|
mediaType,
|
|
422
469
|
mediaKey: value,
|
|
@@ -434,6 +481,12 @@ const buildMediaEntry = (mediaType, messageKey, value, isQuoted, overrides = {})
|
|
|
434
481
|
...overrides,
|
|
435
482
|
});
|
|
436
483
|
|
|
484
|
+
/**
|
|
485
|
+
* Coleta mídias do corpo principal e, opcionalmente, de mensagem citada.
|
|
486
|
+
* @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message
|
|
487
|
+
* @param {{includeQuoted?: boolean}} [options]
|
|
488
|
+
* @returns {MediaEntry[]}
|
|
489
|
+
*/
|
|
437
490
|
const collectMediaFromMessage = (message, { includeQuoted = true } = {}) => {
|
|
438
491
|
if (!message || !message.message) {
|
|
439
492
|
return [];
|
|
@@ -452,6 +505,12 @@ const collectMediaFromMessage = (message, { includeQuoted = true } = {}) => {
|
|
|
452
505
|
return allMedia;
|
|
453
506
|
};
|
|
454
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Filtra lista de mídia por tipo binário e inclusão de tipos desconhecidos.
|
|
510
|
+
* @param {MediaEntry[]} media
|
|
511
|
+
* @param {{includeAllTypes?: boolean, includeUnknown?: boolean}} [options]
|
|
512
|
+
* @returns {MediaEntry[]}
|
|
513
|
+
*/
|
|
455
514
|
const filterMedia = (media, { includeAllTypes = false, includeUnknown = false } = {}) => {
|
|
456
515
|
let filtered = media;
|
|
457
516
|
|
|
@@ -466,6 +525,11 @@ const filterMedia = (media, { includeAllTypes = false, includeUnknown = false }
|
|
|
466
525
|
return filtered;
|
|
467
526
|
};
|
|
468
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Procura o campo `contextInfo.expiration` de forma recursiva no payload.
|
|
530
|
+
* @param {Record<string, any>} root
|
|
531
|
+
* @returns {number|null}
|
|
532
|
+
*/
|
|
469
533
|
const findExpiration = (root) => {
|
|
470
534
|
if (!root || typeof root !== 'object') return null;
|
|
471
535
|
|
|
@@ -491,6 +555,11 @@ const findExpiration = (root) => {
|
|
|
491
555
|
return null;
|
|
492
556
|
};
|
|
493
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Resolve extensão padrão por tipo de mídia.
|
|
560
|
+
* @param {string} type
|
|
561
|
+
* @returns {string}
|
|
562
|
+
*/
|
|
494
563
|
const getMediaExtension = (type) => {
|
|
495
564
|
if (type === 'image') return 'jpeg';
|
|
496
565
|
if (type === 'video') return 'mp4';
|
|
@@ -498,6 +567,11 @@ const getMediaExtension = (type) => {
|
|
|
498
567
|
return 'bin';
|
|
499
568
|
};
|
|
500
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Detecta erros de decrypt inválido (OpenSSL).
|
|
572
|
+
* @param {any} error
|
|
573
|
+
* @returns {boolean}
|
|
574
|
+
*/
|
|
501
575
|
const isBadDecryptError = (error) => {
|
|
502
576
|
if (!error || typeof error !== 'object') return false;
|
|
503
577
|
if (error.code === 'ERR_OSSL_BAD_DECRYPT') return true;
|
|
@@ -1189,6 +1263,10 @@ const authReverseLidCache = new Map();
|
|
|
1189
1263
|
|
|
1190
1264
|
let backfillPromise = null;
|
|
1191
1265
|
|
|
1266
|
+
/**
|
|
1267
|
+
* Atualiza métrica de profundidade da fila `lid_map`.
|
|
1268
|
+
* @returns {void}
|
|
1269
|
+
*/
|
|
1192
1270
|
const updateLidQueueMetric = () => {
|
|
1193
1271
|
setQueueDepth('lid_map', lidWriteBuffer.size);
|
|
1194
1272
|
};
|
|
@@ -1199,20 +1277,40 @@ const updateLidQueueMetric = () => {
|
|
|
1199
1277
|
*/
|
|
1200
1278
|
const now = () => __timeNowMs();
|
|
1201
1279
|
|
|
1280
|
+
/**
|
|
1281
|
+
* Normaliza um identificador LID.
|
|
1282
|
+
* @param {string|null|undefined} lid
|
|
1283
|
+
* @returns {string|null}
|
|
1284
|
+
*/
|
|
1202
1285
|
const normalizeLid = (lid) => {
|
|
1203
1286
|
if (!lid || !isLidJid(lid)) return null;
|
|
1204
1287
|
const normalized = normalizeJid(lid);
|
|
1205
1288
|
return normalized || null;
|
|
1206
1289
|
};
|
|
1207
1290
|
|
|
1291
|
+
/**
|
|
1292
|
+
* Normaliza JID de usuário WhatsApp.
|
|
1293
|
+
* @param {string|null|undefined} jid
|
|
1294
|
+
* @returns {string|null}
|
|
1295
|
+
*/
|
|
1208
1296
|
const normalizeWhatsAppJid = (jid) => {
|
|
1209
1297
|
if (!jid || !isWhatsAppJid(jid)) return null;
|
|
1210
1298
|
const normalized = normalizeJid(jid);
|
|
1211
1299
|
return normalized || null;
|
|
1212
1300
|
};
|
|
1213
1301
|
|
|
1302
|
+
/**
|
|
1303
|
+
* Mantém apenas dígitos de um valor.
|
|
1304
|
+
* @param {unknown} value
|
|
1305
|
+
* @returns {string}
|
|
1306
|
+
*/
|
|
1214
1307
|
const toDigits = (value) => String(value || '').replace(/\D+/g, '');
|
|
1215
1308
|
|
|
1309
|
+
/**
|
|
1310
|
+
* Extrai telefone (dígitos) de payload reverso LID.
|
|
1311
|
+
* @param {unknown} content
|
|
1312
|
+
* @returns {string}
|
|
1313
|
+
*/
|
|
1216
1314
|
const parseReverseMappingPhoneDigits = (content) => {
|
|
1217
1315
|
const raw = String(content || '').trim();
|
|
1218
1316
|
if (!raw) return '';
|
|
@@ -1228,6 +1326,11 @@ const parseReverseMappingPhoneDigits = (content) => {
|
|
|
1228
1326
|
return digits.length >= 10 && digits.length <= 15 ? digits : '';
|
|
1229
1327
|
};
|
|
1230
1328
|
|
|
1329
|
+
/**
|
|
1330
|
+
* Resolve JID a partir de LID consultando auth state (MySQL/arquivos).
|
|
1331
|
+
* @param {string} lid
|
|
1332
|
+
* @returns {Promise<string|null>}
|
|
1333
|
+
*/
|
|
1231
1334
|
const resolveAuthStoreJidByLid = async (lid) => {
|
|
1232
1335
|
const normalizedLid = normalizeLid(lid);
|
|
1233
1336
|
if (!normalizedLid) return null;
|
|
@@ -1519,6 +1622,11 @@ const resolveIdentityCandidates = ({ lid, jid, participantAlt } = {}) => {
|
|
|
1519
1622
|
};
|
|
1520
1623
|
};
|
|
1521
1624
|
|
|
1625
|
+
/**
|
|
1626
|
+
* Monta SQL de upsert para lote do `lid_map`.
|
|
1627
|
+
* @param {number} rows
|
|
1628
|
+
* @returns {string}
|
|
1629
|
+
*/
|
|
1522
1630
|
const buildLidUpsertSql = (rows) => {
|
|
1523
1631
|
const placeholders = buildRowPlaceholders(rows, '(?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?)');
|
|
1524
1632
|
return `
|
|
@@ -1768,6 +1876,10 @@ export const reconcileLidToJid = async ({ lid, jid, source = 'map' } = {}) => {
|
|
|
1768
1876
|
return { updated };
|
|
1769
1877
|
};
|
|
1770
1878
|
|
|
1879
|
+
/**
|
|
1880
|
+
* Executa flush em lote do buffer de atualizações LID->JID.
|
|
1881
|
+
* @returns {Promise<void>}
|
|
1882
|
+
*/
|
|
1771
1883
|
const flushLidQueueCore = async () => {
|
|
1772
1884
|
if (lidWriteBuffer.size === 0) return;
|
|
1773
1885
|
const entries = Array.from(lidWriteBuffer.values());
|
|
@@ -1861,6 +1973,10 @@ export const extractUserIdInfo = (value) => {
|
|
|
1861
1973
|
};
|
|
1862
1974
|
}
|
|
1863
1975
|
|
|
1976
|
+
/**
|
|
1977
|
+
* @param {unknown} entry
|
|
1978
|
+
* @returns {string|null}
|
|
1979
|
+
*/
|
|
1864
1980
|
const readJid = (entry) => (typeof entry === 'string' ? normalizeJid(entry) || null : null);
|
|
1865
1981
|
|
|
1866
1982
|
const participantAlt = readJid(value.participantAlt);
|