@omnizap-system/omnizap 2.6.1 → 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 +78 -9
- package/.github/workflows/ci.yml +3 -3
- package/.github/workflows/security-runner-hardening.yml +1 -1
- package/.github/workflows/security-zap-full-scan.yml +1 -0
- package/app/config/index.js +6 -0
- package/app/configParts/adminIdentity.js +36 -7
- package/app/configParts/baileysConfig.js +343 -56
- package/app/configParts/groupUtils.js +226 -0
- package/app/configParts/loggerConfig.js +185 -0
- package/app/configParts/messagePersistenceService.js +307 -5
- package/app/configParts/sessionConfig.js +242 -0
- package/app/connection/baileysCompatibility.test.js +10 -1
- package/app/connection/baileysDbAuthState.js +205 -9
- package/app/connection/baileysLibsignalPatch.js +210 -0
- package/app/connection/groupOwnerWriteStateResolver.js +141 -0
- package/app/connection/socketController.js +694 -123
- package/app/connection/socketController.multiSession.test.js +128 -0
- package/app/controllers/messageController.js +1 -1
- package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
- package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +96 -4
- package/app/controllers/messageProcessingPipeline.js +90 -9
- package/app/controllers/messageProcessingPipeline.test.js +202 -0
- package/app/modules/adminModule/AGENT.md +1 -1
- package/app/modules/adminModule/commandConfig.json +3318 -1347
- package/app/modules/adminModule/groupCommandHandlers.js +856 -14
- package/app/modules/adminModule/groupCommandHandlers.test.js +375 -9
- package/app/modules/adminModule/groupWarningRepository.js +152 -0
- package/app/modules/aiModule/AGENT.md +47 -30
- package/app/modules/aiModule/aiConfigRuntime.js +1 -0
- package/app/modules/aiModule/catCommand.js +132 -25
- package/app/modules/aiModule/commandConfig.json +114 -28
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
- package/app/modules/gameModule/AGENT.md +1 -1
- package/app/modules/gameModule/commandConfig.json +29 -0
- package/app/modules/menuModule/AGENT.md +1 -1
- package/app/modules/menuModule/commandConfig.json +45 -10
- package/app/modules/menuModule/menuCatalogService.js +190 -0
- package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
- package/app/modules/menuModule/menuDynamicService.js +511 -0
- package/app/modules/menuModule/menuDynamicService.test.js +141 -0
- package/app/modules/menuModule/menus.js +36 -5
- package/app/modules/playModule/AGENT.md +10 -5
- package/app/modules/playModule/commandConfig.json +74 -16
- package/app/modules/playModule/playCommandConstants.js +13 -7
- package/app/modules/playModule/playCommandCore.js +4 -6
- package/app/modules/playModule/{playCommandYtDlpClient.js → playCommandMediaClient.js} +684 -332
- package/app/modules/playModule/playConfigRuntime.js +5 -6
- package/app/modules/playModule/playModuleCriticalFlows.test.js +44 -59
- package/app/modules/quoteModule/AGENT.md +1 -1
- package/app/modules/quoteModule/commandConfig.json +29 -0
- package/app/modules/rpgPokemonModule/AGENT.md +1 -1
- package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
- package/app/modules/statsModule/AGENT.md +1 -1
- package/app/modules/statsModule/commandConfig.json +58 -0
- package/app/modules/stickerModule/AGENT.md +1 -1
- package/app/modules/stickerModule/commandConfig.json +145 -0
- package/app/modules/stickerPackModule/AGENT.md +1 -1
- package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
- package/app/modules/stickerPackModule/commandConfig.json +29 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +1 -1
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +78 -57
- package/app/modules/stickerPackModule/stickerPackService.js +13 -6
- package/app/modules/systemMetricsModule/AGENT.md +1 -1
- package/app/modules/systemMetricsModule/commandConfig.json +29 -0
- package/app/modules/tiktokModule/AGENT.md +1 -1
- package/app/modules/tiktokModule/commandConfig.json +29 -0
- package/app/modules/userModule/AGENT.md +1 -1
- package/app/modules/userModule/commandConfig.json +29 -0
- package/app/modules/waifuPicsModule/AGENT.md +57 -27
- package/app/modules/waifuPicsModule/commandConfig.json +87 -0
- package/app/observability/metrics.js +136 -0
- package/app/services/ai/commandConfigEnrichmentService.js +229 -47
- package/app/services/ai/geminiService.js +131 -7
- package/app/services/ai/geminiService.test.js +59 -2
- package/app/services/ai/moduleAiHelpCoreService.js +33 -4
- package/app/services/group/groupMetadataService.js +24 -1
- package/app/services/infra/dbWriteQueue.js +51 -21
- package/app/services/messaging/newsBroadcastService.js +843 -27
- package/app/services/multiSession/assignmentBalancerService.js +452 -0
- package/app/services/multiSession/groupOwnershipRepository.js +346 -0
- package/app/services/multiSession/groupOwnershipService.js +809 -0
- package/app/services/multiSession/groupOwnershipService.test.js +317 -0
- package/app/services/multiSession/sessionRegistryService.js +239 -0
- package/app/store/aiPromptStore.js +36 -19
- package/app/store/groupConfigStore.js +41 -5
- package/app/store/premiumUserStore.js +21 -7
- package/app/utils/antiLink/antiLinkModule.js +391 -25
- package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
- package/database/index.js +6 -0
- package/database/migrations/20260307_d0_hardening_down.sql +1 -1
- package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
- package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
- package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
- package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
- package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
- package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
- package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
- package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
- package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
- package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
- package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
- package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
- package/database/schema.sql +102 -1
- package/docker-compose.yml +4 -1
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
- package/docs/compliance/privacy-policy-2026-03-07.md +2 -2
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
- package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
- package/docs/security/omnizap-static-security-headers.conf +25 -0
- package/ecosystem.prod.config.cjs +31 -11
- package/index.js +52 -18
- package/observability/alert-rules.yml +20 -0
- package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
- package/observability/mysql-setup.sql +4 -4
- package/observability/system-admin-observability.md +26 -0
- package/package.json +14 -6
- package/public/comandos/commands-catalog.json +2253 -78
- package/public/css/payments-react.css +478 -0
- package/public/js/apps/commandsReactApp.js +267 -87
- package/public/js/apps/createPackApp.js +3 -3
- 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/js/apps/stickersApp.js +255 -103
- package/public/js/apps/termsReactApp.js +57 -8
- package/public/js/apps/userPasswordResetReactApp.js +406 -0
- package/public/js/apps/userReactApp.js +96 -47
- package/public/js/apps/userSystemAdmReactApp.js +1506 -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/public/pages/politica-de-privacidade.html +1 -1
- package/public/pages/stickers.html +5 -5
- package/public/pages/termos-de-uso-texto-integral.html +1 -1
- package/public/pages/termos-de-uso.html +1 -1
- package/public/pages/user-password-reset.html +3 -4
- package/public/pages/user-systemadm.html +8 -462
- package/public/pages/user.html +1 -1
- package/scripts/clear-whatsapp-session.sh +123 -0
- package/scripts/core-ai-mode.mjs +163 -0
- package/scripts/deploy.sh +13 -0
- package/scripts/enrich-command-config-ux-openai.mjs +492 -0
- package/scripts/generate-commands-catalog.mjs +155 -0
- package/scripts/new-whatsapp-session.sh +564 -0
- package/scripts/security-web-surface-check.mjs +218 -0
- package/server/controllers/admin/adminPanelHandlers.js +253 -3
- package/server/controllers/admin/systemAdminController.js +254 -0
- package/server/controllers/payments/paymentsController.js +731 -0
- package/server/controllers/sticker/stickerCatalogController.js +9 -23
- package/server/controllers/system/contactController.js +9 -17
- package/server/controllers/system/stickerCatalogSystemContext.js +27 -6
- package/server/controllers/system/systemController.js +228 -1
- package/server/controllers/userController.js +6 -0
- package/server/email/emailAutomationRuntime.js +36 -1
- package/server/email/emailAutomationService.js +42 -1
- package/server/email/emailTemplateService.js +140 -33
- package/server/http/httpRequestUtils.js +18 -14
- package/server/http/httpServer.js +8 -4
- package/server/middleware/securityHeaders.js +35 -3
- package/server/routes/admin/systemAdminRouter.js +6 -0
- package/server/routes/indexRouter.js +50 -6
- package/server/routes/observability/grafanaProxyRouter.js +254 -0
- package/server/routes/payments/paymentsRouter.js +47 -0
- package/server/routes/static/staticPageRouter.js +30 -1
- package/server/utils/publicContact.js +31 -0
- package/utils/whatsapp/contactEnv.js +39 -0
- package/vite.config.mjs +5 -1
- package/app/modules/playModule/local/installYtDlp.js +0 -25
- package/app/modules/playModule/local/ytDlpInstaller.js +0 -28
|
@@ -13,20 +13,177 @@ import path from 'node:path';
|
|
|
13
13
|
import { pipeline } from 'node:stream/promises';
|
|
14
14
|
import { Readable } from 'node:stream';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
|
|
16
|
+
import { getMultiSessionRuntimeConfig } from './sessionConfig.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Utilitários centrais de integração com Baileys.
|
|
20
|
+
*
|
|
21
|
+
* Responsabilidades deste módulo:
|
|
22
|
+
* - validar/normalizar JIDs e tipos de presença;
|
|
23
|
+
* - encapsular chamadas seguras no socket ativo;
|
|
24
|
+
* - extrair/detectar mídia em mensagens;
|
|
25
|
+
* - resolver mapeamento LID <-> JID com cache e persistência.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {import('@whiskeysockets/baileys').WASocket} BaileysSocket
|
|
30
|
+
* @typedef {import('@whiskeysockets/baileys').WAMessage} BaileysMessage
|
|
31
|
+
* @typedef {import('@whiskeysockets/baileys').WAProto.IMessage} BaileysProtoMessage
|
|
32
|
+
* @typedef {'lid'|'pn'} AddressingMode
|
|
33
|
+
* @typedef {{includeAllTypes?: boolean, includeQuoted?: boolean, includeUnknown?: boolean}} MediaExtractionOptions
|
|
34
|
+
* @typedef {{
|
|
35
|
+
* mediaType: string,
|
|
36
|
+
* mediaKey: Record<string, any>,
|
|
37
|
+
* messageKey: string,
|
|
38
|
+
* isQuoted: boolean,
|
|
39
|
+
* isBinary: boolean,
|
|
40
|
+
* isUnknownType?: boolean,
|
|
41
|
+
* hasUrl: boolean,
|
|
42
|
+
* hasDirectPath: boolean,
|
|
43
|
+
* hasMediaKey: boolean,
|
|
44
|
+
* hasFileEncSha256: boolean,
|
|
45
|
+
* mimetype: string|null,
|
|
46
|
+
* fileLength: number|string|null,
|
|
47
|
+
* fileName: string|null,
|
|
48
|
+
* caption: string|null
|
|
49
|
+
* }} MediaEntry
|
|
50
|
+
* @typedef {{
|
|
51
|
+
* mediaType: string,
|
|
52
|
+
* mediaKey: Record<string, any>,
|
|
53
|
+
* isQuoted: boolean,
|
|
54
|
+
* details: {
|
|
55
|
+
* messageKey: string,
|
|
56
|
+
* isBinary: boolean,
|
|
57
|
+
* isUnknownType?: boolean,
|
|
58
|
+
* hasUrl: boolean,
|
|
59
|
+
* hasDirectPath: boolean,
|
|
60
|
+
* hasMediaKey: boolean,
|
|
61
|
+
* hasFileEncSha256: boolean,
|
|
62
|
+
* mimetype: string|null,
|
|
63
|
+
* fileLength: number|string|null,
|
|
64
|
+
* fileName: string|null,
|
|
65
|
+
* caption: string|null,
|
|
66
|
+
* allMediaFound: MediaEntry[]|null
|
|
67
|
+
* }
|
|
68
|
+
* }} MediaExtractionResult
|
|
69
|
+
* @typedef {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} IdentityParams
|
|
70
|
+
* @typedef {{jid: string|null, expiresAt: number, lastStoredAt: number|null}} LidCacheEntry
|
|
71
|
+
* @typedef {{
|
|
72
|
+
* lid: string|null,
|
|
73
|
+
* jid: string|null,
|
|
74
|
+
* participantAlt: string|null,
|
|
75
|
+
* remoteJid: string|null,
|
|
76
|
+
* remoteJidAlt: string|null,
|
|
77
|
+
* groupMessage: boolean
|
|
78
|
+
* }} SenderInfo
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Versão local de fallback do Baileys.
|
|
83
|
+
* @type {number[]}
|
|
84
|
+
*/
|
|
17
85
|
const DEFAULT_BAILEYS_VERSION = [7, 0, 0];
|
|
86
|
+
const multiSessionRuntimeConfig = getMultiSessionRuntimeConfig();
|
|
87
|
+
const PRIMARY_BAILEYS_SESSION_ID = String(multiSessionRuntimeConfig?.primarySessionId || process.env.BAILEYS_AUTH_SESSION_ID || 'default').trim() || 'default';
|
|
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
|
+
*/
|
|
94
|
+
const normalizeSessionId = (sessionId) => {
|
|
95
|
+
const normalized = String(sessionId || '').trim();
|
|
96
|
+
return normalized || PRIMARY_BAILEYS_SESSION_ID;
|
|
97
|
+
};
|
|
18
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Mapa de sockets ativos por sessão.
|
|
101
|
+
* @type {Map<string, BaileysSocket>}
|
|
102
|
+
*/
|
|
103
|
+
const sessionSocketMap = new Map();
|
|
19
104
|
let activeSocket = null;
|
|
20
105
|
|
|
21
|
-
|
|
22
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Atualiza a referência global do socket ativo e o registro por sessão.
|
|
108
|
+
* Mantém compatibilidade com chamadas legadas que não informam `sessionId`.
|
|
109
|
+
* @param {BaileysSocket|null} socket Instância conectada ou `null` para limpar.
|
|
110
|
+
* @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID] Sessão da conexão.
|
|
111
|
+
* @returns {void}
|
|
112
|
+
*/
|
|
113
|
+
export const setActiveSocket = (socket, sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
|
|
114
|
+
const safeSessionId = normalizeSessionId(sessionId);
|
|
115
|
+
const previousSocket = sessionSocketMap.get(safeSessionId) || null;
|
|
116
|
+
|
|
117
|
+
if (socket) {
|
|
118
|
+
sessionSocketMap.set(safeSessionId, socket);
|
|
119
|
+
} else {
|
|
120
|
+
sessionSocketMap.delete(safeSessionId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (safeSessionId === PRIMARY_BAILEYS_SESSION_ID) {
|
|
124
|
+
activeSocket = socket || null;
|
|
125
|
+
} else if (!socket && previousSocket && activeSocket === previousSocket) {
|
|
126
|
+
activeSocket = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!activeSocket) {
|
|
130
|
+
activeSocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || sessionSocketMap.values().next()?.value || null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Atualiza o socket ativo de uma sessão específica.
|
|
136
|
+
* @param {string} sessionId Sessão alvo.
|
|
137
|
+
* @param {BaileysSocket|null} socket Instância conectada ou `null`.
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
export const setSocketBySession = (sessionId, socket) => {
|
|
141
|
+
setActiveSocket(socket, sessionId);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Retorna a instância de socket ativa no processo.
|
|
146
|
+
* @returns {BaileysSocket|null}
|
|
147
|
+
*/
|
|
148
|
+
export const getActiveSocket = () => {
|
|
149
|
+
if (isSocketOpen(activeSocket)) return activeSocket;
|
|
150
|
+
|
|
151
|
+
const primarySocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || null;
|
|
152
|
+
if (isSocketOpen(primarySocket)) {
|
|
153
|
+
activeSocket = primarySocket;
|
|
154
|
+
return activeSocket;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
for (const socket of sessionSocketMap.values()) {
|
|
158
|
+
if (isSocketOpen(socket)) {
|
|
159
|
+
activeSocket = socket;
|
|
160
|
+
return activeSocket;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
activeSocket = null;
|
|
165
|
+
return activeSocket;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Retorna o socket ativo associado a uma sessão.
|
|
170
|
+
* @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID]
|
|
171
|
+
* @returns {BaileysSocket|null}
|
|
172
|
+
*/
|
|
173
|
+
export const getSocketBySession = (sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
|
|
174
|
+
const safeSessionId = normalizeSessionId(sessionId);
|
|
175
|
+
return sessionSocketMap.get(safeSessionId) || null;
|
|
23
176
|
};
|
|
24
177
|
|
|
25
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Snapshot dos sockets ativos por sessão.
|
|
180
|
+
* @returns {Map<string, BaileysSocket>}
|
|
181
|
+
*/
|
|
182
|
+
export const getActiveSocketsBySession = () => new Map(sessionSocketMap);
|
|
26
183
|
|
|
27
184
|
/**
|
|
28
185
|
* Indica se uma instância de socket está aberta para operações.
|
|
29
|
-
* @param {
|
|
186
|
+
* @param {BaileysSocket|null|undefined} socket Instância de socket.
|
|
30
187
|
* @returns {boolean}
|
|
31
188
|
*/
|
|
32
189
|
export const isSocketOpen = (socket) => {
|
|
@@ -43,7 +200,7 @@ export const isActiveSocketOpen = () => isSocketOpen(activeSocket);
|
|
|
43
200
|
|
|
44
201
|
/**
|
|
45
202
|
* Executa um método em uma instância de socket validando disponibilidade.
|
|
46
|
-
* @param {
|
|
203
|
+
* @param {BaileysSocket|null|undefined} socket Instância de socket.
|
|
47
204
|
* @param {string} methodName Nome do método no socket.
|
|
48
205
|
* @param {...any} args Argumentos do método.
|
|
49
206
|
* @returns {Promise<any>}
|
|
@@ -69,9 +226,18 @@ export const runSocketMethod = async (socket, methodName, ...args) => {
|
|
|
69
226
|
*/
|
|
70
227
|
export const runActiveSocketMethod = async (methodName, ...args) => runSocketMethod(activeSocket, methodName, ...args);
|
|
71
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Executa um método no socket de uma sessão específica.
|
|
231
|
+
* @param {string} sessionId Sessão alvo.
|
|
232
|
+
* @param {string} methodName Nome do método no socket.
|
|
233
|
+
* @param {...any} args Argumentos do método.
|
|
234
|
+
* @returns {Promise<any>}
|
|
235
|
+
*/
|
|
236
|
+
export const runSessionSocketMethod = async (sessionId, methodName, ...args) => runSocketMethod(getSocketBySession(sessionId), methodName, ...args);
|
|
237
|
+
|
|
72
238
|
/**
|
|
73
239
|
* Recupera a blocklist da conta conectada.
|
|
74
|
-
* @returns {Promise<
|
|
240
|
+
* @returns {Promise<string[]>}
|
|
75
241
|
*/
|
|
76
242
|
export const fetchBlocklistFromActiveSocket = async () => runActiveSocketMethod('fetchBlocklist');
|
|
77
243
|
|
|
@@ -112,6 +278,10 @@ export const WHATSAPP_USER_JID_SERVERS = new Set(['s.whatsapp.net', 'c.us', 'hos
|
|
|
112
278
|
*/
|
|
113
279
|
export const LID_USER_JID_SERVERS = new Set(['lid', 'hosted.lid']);
|
|
114
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
|
+
*/
|
|
115
285
|
const decodeJidParts = (() => {
|
|
116
286
|
let lastJid = null;
|
|
117
287
|
let lastDecoded = null;
|
|
@@ -128,7 +298,8 @@ const decodeJidParts = (() => {
|
|
|
128
298
|
|
|
129
299
|
/**
|
|
130
300
|
* Tipos de mensagem conhecidos do Baileys
|
|
131
|
-
*
|
|
301
|
+
* (mapeamento de chaves do proto.Message para tipos normalizados).
|
|
302
|
+
* @type {Record<string, string>}
|
|
132
303
|
*/
|
|
133
304
|
export const MEDIA_TYPE_MAPPING = {
|
|
134
305
|
conversation: 'text',
|
|
@@ -182,14 +353,25 @@ export const MEDIA_TYPE_MAPPING = {
|
|
|
182
353
|
};
|
|
183
354
|
|
|
184
355
|
/**
|
|
185
|
-
* Tipos de
|
|
356
|
+
* Tipos de mídia que carregam conteúdo binário/arquivo.
|
|
357
|
+
* @type {Set<string>}
|
|
186
358
|
*/
|
|
187
359
|
export const BINARY_MEDIA_TYPES = new Set(['image', 'video', 'videoNote', 'audio', 'voice', 'document', 'sticker']);
|
|
188
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
|
+
*/
|
|
189
366
|
const normalizeMessage = (message) => normalizeMessageContent(message) || message;
|
|
190
367
|
|
|
191
368
|
const MESSAGE_CONTENT_WRAPPER_KEYS = ['ephemeralMessage', 'viewOnceMessage', 'viewOnceMessageV2', 'viewOnceMessageV2Extension', 'deviceSentMessage', 'documentWithCaptionMessage', 'botInvokeMessage', 'editedMessage', 'keepInChatMessage'];
|
|
192
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
|
+
*/
|
|
193
375
|
const resolveSingleWrapperMessage = (node) => {
|
|
194
376
|
if (!node || typeof node !== 'object') return null;
|
|
195
377
|
|
|
@@ -204,6 +386,12 @@ const resolveSingleWrapperMessage = (node) => {
|
|
|
204
386
|
return null;
|
|
205
387
|
};
|
|
206
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
|
+
*/
|
|
207
395
|
const unwrapMessageContent = (message, maxDepth = 8) => {
|
|
208
396
|
let current = normalizeMessage(message);
|
|
209
397
|
const visited = new Set();
|
|
@@ -237,6 +425,11 @@ const unwrapMessageContent = (message, maxDepth = 8) => {
|
|
|
237
425
|
return current || message;
|
|
238
426
|
};
|
|
239
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Verifica se uma mediaKey está presente e não vazia.
|
|
430
|
+
* @param {unknown} mediaKey
|
|
431
|
+
* @returns {boolean}
|
|
432
|
+
*/
|
|
240
433
|
const hasNonEmptyMediaKey = (mediaKey) => {
|
|
241
434
|
if (!mediaKey) return false;
|
|
242
435
|
|
|
@@ -262,6 +455,15 @@ const hasNonEmptyMediaKey = (mediaKey) => {
|
|
|
262
455
|
return Boolean(mediaKey);
|
|
263
456
|
};
|
|
264
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
|
+
*/
|
|
265
467
|
const buildMediaEntry = (mediaType, messageKey, value, isQuoted, overrides = {}) => ({
|
|
266
468
|
mediaType,
|
|
267
469
|
mediaKey: value,
|
|
@@ -279,6 +481,12 @@ const buildMediaEntry = (mediaType, messageKey, value, isQuoted, overrides = {})
|
|
|
279
481
|
...overrides,
|
|
280
482
|
});
|
|
281
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
|
+
*/
|
|
282
490
|
const collectMediaFromMessage = (message, { includeQuoted = true } = {}) => {
|
|
283
491
|
if (!message || !message.message) {
|
|
284
492
|
return [];
|
|
@@ -297,6 +505,12 @@ const collectMediaFromMessage = (message, { includeQuoted = true } = {}) => {
|
|
|
297
505
|
return allMedia;
|
|
298
506
|
};
|
|
299
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
|
+
*/
|
|
300
514
|
const filterMedia = (media, { includeAllTypes = false, includeUnknown = false } = {}) => {
|
|
301
515
|
let filtered = media;
|
|
302
516
|
|
|
@@ -311,6 +525,11 @@ const filterMedia = (media, { includeAllTypes = false, includeUnknown = false }
|
|
|
311
525
|
return filtered;
|
|
312
526
|
};
|
|
313
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Procura o campo `contextInfo.expiration` de forma recursiva no payload.
|
|
530
|
+
* @param {Record<string, any>} root
|
|
531
|
+
* @returns {number|null}
|
|
532
|
+
*/
|
|
314
533
|
const findExpiration = (root) => {
|
|
315
534
|
if (!root || typeof root !== 'object') return null;
|
|
316
535
|
|
|
@@ -336,6 +555,11 @@ const findExpiration = (root) => {
|
|
|
336
555
|
return null;
|
|
337
556
|
};
|
|
338
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Resolve extensão padrão por tipo de mídia.
|
|
560
|
+
* @param {string} type
|
|
561
|
+
* @returns {string}
|
|
562
|
+
*/
|
|
339
563
|
const getMediaExtension = (type) => {
|
|
340
564
|
if (type === 'image') return 'jpeg';
|
|
341
565
|
if (type === 'video') return 'mp4';
|
|
@@ -343,6 +567,11 @@ const getMediaExtension = (type) => {
|
|
|
343
567
|
return 'bin';
|
|
344
568
|
};
|
|
345
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Detecta erros de decrypt inválido (OpenSSL).
|
|
572
|
+
* @param {any} error
|
|
573
|
+
* @returns {boolean}
|
|
574
|
+
*/
|
|
346
575
|
const isBadDecryptError = (error) => {
|
|
347
576
|
if (!error || typeof error !== 'object') return false;
|
|
348
577
|
if (error.code === 'ERR_OSSL_BAD_DECRYPT') return true;
|
|
@@ -521,13 +750,22 @@ export function isWhatsAppJid(jid) {
|
|
|
521
750
|
return Boolean(server && WHATSAPP_USER_JID_SERVERS.has(server));
|
|
522
751
|
}
|
|
523
752
|
|
|
753
|
+
/**
|
|
754
|
+
* Modo de endereçamento por LID.
|
|
755
|
+
* @type {'lid'}
|
|
756
|
+
*/
|
|
524
757
|
export const ADDRESSING_MODE_LID = 'lid';
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Modo de endereçamento por PN/JID canônico.
|
|
761
|
+
* @type {'pn'}
|
|
762
|
+
*/
|
|
525
763
|
export const ADDRESSING_MODE_PN = 'pn';
|
|
526
764
|
|
|
527
765
|
/**
|
|
528
766
|
* Normaliza um modo de endereçamento (lid/pn).
|
|
529
767
|
* @param {unknown} value
|
|
530
|
-
* @returns {
|
|
768
|
+
* @returns {AddressingMode|undefined}
|
|
531
769
|
*/
|
|
532
770
|
export const normalizeAddressingMode = (value) => {
|
|
533
771
|
if (value === undefined || value === null) return undefined;
|
|
@@ -540,8 +778,8 @@ export const normalizeAddressingMode = (value) => {
|
|
|
540
778
|
/**
|
|
541
779
|
* Resolve modo de endereçamento a partir da chave da mensagem.
|
|
542
780
|
* @param {object} [key={}]
|
|
543
|
-
* @param {
|
|
544
|
-
* @returns {
|
|
781
|
+
* @param {SenderInfo|Record<string, any>} [senderInfo={}]
|
|
782
|
+
* @returns {AddressingMode|undefined}
|
|
545
783
|
*/
|
|
546
784
|
export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) => {
|
|
547
785
|
const explicit = normalizeAddressingMode(key?.addressingMode);
|
|
@@ -560,7 +798,7 @@ export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) =
|
|
|
560
798
|
|
|
561
799
|
/**
|
|
562
800
|
* Resolve JID canônico de usuário WhatsApp a partir de candidatos.
|
|
563
|
-
* @param {...string} candidates
|
|
801
|
+
* @param {...(string|null|undefined)} candidates
|
|
564
802
|
* @returns {string}
|
|
565
803
|
*/
|
|
566
804
|
export const resolveCanonicalWhatsAppJid = (...candidates) => {
|
|
@@ -716,7 +954,7 @@ export async function resolveBaileysVersion() {
|
|
|
716
954
|
|
|
717
955
|
/**
|
|
718
956
|
* Baixa a foto de perfil associada à mensagem recebida.
|
|
719
|
-
* @param {
|
|
957
|
+
* @param {BaileysSocket} sock - Instância conectada do socket.
|
|
720
958
|
* @param {import('@whiskeysockets/baileys').proto.IWebMessageInfo} msg - Mensagem usada para resolver o JID.
|
|
721
959
|
* @returns {Promise<Buffer|null>} Buffer da imagem ou `null` se indisponível.
|
|
722
960
|
*/
|
|
@@ -740,17 +978,17 @@ export async function getProfilePicBuffer(sock, msg) {
|
|
|
740
978
|
|
|
741
979
|
/**
|
|
742
980
|
* Extrai o valor de expiração de uma mensagem do WhatsApp, ou retorna 24 horas (em segundos) por padrão.
|
|
743
|
-
* @param {{message?:
|
|
981
|
+
* @param {{message?: Record<string, any>}|null|undefined} messageInfo - Estrutura contendo a propriedade `message`.
|
|
744
982
|
* @returns {number} Tempo de expiração em segundos.
|
|
745
983
|
*/
|
|
746
|
-
export function getExpiration(
|
|
984
|
+
export function getExpiration(messageInfo) {
|
|
747
985
|
const DEFAULT_EXPIRATION_SECONDS = 24 * 60 * 60;
|
|
748
986
|
|
|
749
|
-
if (!
|
|
987
|
+
if (!messageInfo || typeof messageInfo !== 'object' || !messageInfo.message) {
|
|
750
988
|
return DEFAULT_EXPIRATION_SECONDS;
|
|
751
989
|
}
|
|
752
990
|
|
|
753
|
-
const normalizedMessage = normalizeMessage(
|
|
991
|
+
const normalizedMessage = normalizeMessage(messageInfo.message);
|
|
754
992
|
const expiration = findExpiration(normalizedMessage);
|
|
755
993
|
|
|
756
994
|
return typeof expiration === 'number' ? expiration : DEFAULT_EXPIRATION_SECONDS;
|
|
@@ -758,7 +996,7 @@ export function getExpiration(sock) {
|
|
|
758
996
|
|
|
759
997
|
/**
|
|
760
998
|
* Extrai o conteúdo de texto de uma mensagem do WhatsApp.
|
|
761
|
-
* @param {{message?:
|
|
999
|
+
* @param {{message?: Record<string, any>}} messageInfo - Objeto que contém o payload da mensagem.
|
|
762
1000
|
* @returns {string} Conteúdo textual extraído ou descrição do tipo de mensagem.
|
|
763
1001
|
*/
|
|
764
1002
|
export const extractMessageContent = ({ message }) => {
|
|
@@ -810,7 +1048,7 @@ export const extractMessageContent = ({ message }) => {
|
|
|
810
1048
|
|
|
811
1049
|
/**
|
|
812
1050
|
* Faz o download de mídia a partir de uma mensagem do Baileys.
|
|
813
|
-
* @param {
|
|
1051
|
+
* @param {BaileysProtoMessage} message - Objeto da mídia a ser baixada.
|
|
814
1052
|
* @param {string} type - Tipo de mídia (ex.: `image`, `video`, `audio`, `document`).
|
|
815
1053
|
* @param {string} outputPath - Diretório onde o arquivo será salvo.
|
|
816
1054
|
* @returns {Promise<string|null>} Caminho do arquivo salvo ou `null` em caso de falha.
|
|
@@ -867,10 +1105,10 @@ export const downloadMediaMessage = async (message, type, outputPath) => {
|
|
|
867
1105
|
};
|
|
868
1106
|
|
|
869
1107
|
/**
|
|
870
|
-
* Detecta dinamicamente
|
|
871
|
-
* @param {
|
|
872
|
-
* @param {boolean} isQuoted
|
|
873
|
-
* @returns {
|
|
1108
|
+
* Detecta dinamicamente os tipos de mídia presentes em um payload de mensagem.
|
|
1109
|
+
* @param {Record<string, any>} messageContent Conteúdo da mensagem (normal ou quoted).
|
|
1110
|
+
* @param {boolean} [isQuoted=false] Sinaliza se o payload veio de uma citação.
|
|
1111
|
+
* @returns {MediaEntry[]} Lista de mídias detectadas (pode estar vazia).
|
|
874
1112
|
*/
|
|
875
1113
|
export function detectAllMediaTypes(messageContent, isQuoted = false) {
|
|
876
1114
|
if (!messageContent || typeof messageContent !== 'object') {
|
|
@@ -914,13 +1152,10 @@ export function detectAllMediaTypes(messageContent, isQuoted = false) {
|
|
|
914
1152
|
}
|
|
915
1153
|
|
|
916
1154
|
/**
|
|
917
|
-
* Extrai
|
|
918
|
-
* @param {
|
|
919
|
-
* @param {
|
|
920
|
-
* @
|
|
921
|
-
* @param {boolean} options.includeQuoted - Se deve incluir midia de mensagens citadas
|
|
922
|
-
* @param {boolean} options.includeUnknown - Se deve incluir tipos desconhecidos
|
|
923
|
-
* @returns {{mediaType: string, mediaKey: object, details: object}|null} - Detalhes da midia ou null se nao encontrada
|
|
1155
|
+
* Extrai a mídia primária de uma mensagem com filtros configuráveis.
|
|
1156
|
+
* @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
|
|
1157
|
+
* @param {MediaExtractionOptions} [options={}] Opções de filtragem.
|
|
1158
|
+
* @returns {MediaExtractionResult|null} Estrutura da mídia primária ou `null` quando não houver mídia.
|
|
924
1159
|
*/
|
|
925
1160
|
export function extractMediaDetails(message, options = {}) {
|
|
926
1161
|
const { includeAllTypes = false, includeQuoted = true, includeUnknown = false } = options;
|
|
@@ -955,10 +1190,10 @@ export function extractMediaDetails(message, options = {}) {
|
|
|
955
1190
|
}
|
|
956
1191
|
|
|
957
1192
|
/**
|
|
958
|
-
* Extrai
|
|
959
|
-
* @param {
|
|
960
|
-
* @param {
|
|
961
|
-
* @returns {
|
|
1193
|
+
* Extrai todas as mídias detectadas de uma mensagem.
|
|
1194
|
+
* @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
|
|
1195
|
+
* @param {MediaExtractionOptions} [options={}] Opções de filtragem.
|
|
1196
|
+
* @returns {MediaEntry[]} Array com todas as mídias encontradas.
|
|
962
1197
|
*/
|
|
963
1198
|
export function extractAllMediaDetails(message, options = {}) {
|
|
964
1199
|
const { includeAllTypes = true, includeQuoted = true, includeUnknown = true } = options;
|
|
@@ -968,10 +1203,10 @@ export function extractAllMediaDetails(message, options = {}) {
|
|
|
968
1203
|
}
|
|
969
1204
|
|
|
970
1205
|
/**
|
|
971
|
-
* Verifica se uma mensagem
|
|
972
|
-
* @param {
|
|
973
|
-
* @param {string} specificType
|
|
974
|
-
* @returns {boolean}
|
|
1206
|
+
* Verifica se uma mensagem contém mídia.
|
|
1207
|
+
* @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
|
|
1208
|
+
* @param {string|null} [specificType=null] Tipo específico para filtrar (ex.: `image`).
|
|
1209
|
+
* @returns {boolean} `true` quando ao menos uma mídia compatível é encontrada.
|
|
975
1210
|
*/
|
|
976
1211
|
export function hasMedia(message, specificType = null) {
|
|
977
1212
|
const allMedia = collectMediaFromMessage(message, { includeQuoted: true });
|
|
@@ -989,8 +1224,13 @@ export function hasMedia(message, specificType = null) {
|
|
|
989
1224
|
}
|
|
990
1225
|
|
|
991
1226
|
/**
|
|
992
|
-
*
|
|
993
|
-
* @returns {
|
|
1227
|
+
* Obtém metadados dos tipos de mídia suportados.
|
|
1228
|
+
* @returns {{
|
|
1229
|
+
* knownTypes: string[],
|
|
1230
|
+
* binaryTypes: string[],
|
|
1231
|
+
* typeMapping: Record<string, string>,
|
|
1232
|
+
* totalKnownTypes: number
|
|
1233
|
+
* }}
|
|
994
1234
|
*/
|
|
995
1235
|
export function getMediaTypeInfo() {
|
|
996
1236
|
return {
|
|
@@ -1002,9 +1242,9 @@ export function getMediaTypeInfo() {
|
|
|
1002
1242
|
}
|
|
1003
1243
|
|
|
1004
1244
|
/**
|
|
1005
|
-
*
|
|
1245
|
+
* ===========================
|
|
1006
1246
|
* LID Map Utilities
|
|
1007
|
-
*
|
|
1247
|
+
* ===========================
|
|
1008
1248
|
*/
|
|
1009
1249
|
const CACHE_TTL_MS = 20 * 60 * 1000;
|
|
1010
1250
|
const NEGATIVE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -1015,10 +1255,7 @@ const BACKFILL_SOURCE = 'backfill';
|
|
|
1015
1255
|
const __filename = fileURLToPath(import.meta.url);
|
|
1016
1256
|
const __dirname = path.dirname(__filename);
|
|
1017
1257
|
const BAILEYS_AUTH_DIR = path.resolve(__dirname, '../connection/auth');
|
|
1018
|
-
const BAILEYS_AUTH_SESSION_ID =
|
|
1019
|
-
const raw = String(process.env.BAILEYS_AUTH_SESSION_ID || '').trim();
|
|
1020
|
-
return raw || 'default';
|
|
1021
|
-
})();
|
|
1258
|
+
const BAILEYS_AUTH_SESSION_ID = PRIMARY_BAILEYS_SESSION_ID;
|
|
1022
1259
|
|
|
1023
1260
|
const lidCache = new Map();
|
|
1024
1261
|
const lidWriteBuffer = new Map();
|
|
@@ -1026,6 +1263,10 @@ const authReverseLidCache = new Map();
|
|
|
1026
1263
|
|
|
1027
1264
|
let backfillPromise = null;
|
|
1028
1265
|
|
|
1266
|
+
/**
|
|
1267
|
+
* Atualiza métrica de profundidade da fila `lid_map`.
|
|
1268
|
+
* @returns {void}
|
|
1269
|
+
*/
|
|
1029
1270
|
const updateLidQueueMetric = () => {
|
|
1030
1271
|
setQueueDepth('lid_map', lidWriteBuffer.size);
|
|
1031
1272
|
};
|
|
@@ -1036,20 +1277,40 @@ const updateLidQueueMetric = () => {
|
|
|
1036
1277
|
*/
|
|
1037
1278
|
const now = () => __timeNowMs();
|
|
1038
1279
|
|
|
1280
|
+
/**
|
|
1281
|
+
* Normaliza um identificador LID.
|
|
1282
|
+
* @param {string|null|undefined} lid
|
|
1283
|
+
* @returns {string|null}
|
|
1284
|
+
*/
|
|
1039
1285
|
const normalizeLid = (lid) => {
|
|
1040
1286
|
if (!lid || !isLidJid(lid)) return null;
|
|
1041
1287
|
const normalized = normalizeJid(lid);
|
|
1042
1288
|
return normalized || null;
|
|
1043
1289
|
};
|
|
1044
1290
|
|
|
1291
|
+
/**
|
|
1292
|
+
* Normaliza JID de usuário WhatsApp.
|
|
1293
|
+
* @param {string|null|undefined} jid
|
|
1294
|
+
* @returns {string|null}
|
|
1295
|
+
*/
|
|
1045
1296
|
const normalizeWhatsAppJid = (jid) => {
|
|
1046
1297
|
if (!jid || !isWhatsAppJid(jid)) return null;
|
|
1047
1298
|
const normalized = normalizeJid(jid);
|
|
1048
1299
|
return normalized || null;
|
|
1049
1300
|
};
|
|
1050
1301
|
|
|
1302
|
+
/**
|
|
1303
|
+
* Mantém apenas dígitos de um valor.
|
|
1304
|
+
* @param {unknown} value
|
|
1305
|
+
* @returns {string}
|
|
1306
|
+
*/
|
|
1051
1307
|
const toDigits = (value) => String(value || '').replace(/\D+/g, '');
|
|
1052
1308
|
|
|
1309
|
+
/**
|
|
1310
|
+
* Extrai telefone (dígitos) de payload reverso LID.
|
|
1311
|
+
* @param {unknown} content
|
|
1312
|
+
* @returns {string}
|
|
1313
|
+
*/
|
|
1053
1314
|
const parseReverseMappingPhoneDigits = (content) => {
|
|
1054
1315
|
const raw = String(content || '').trim();
|
|
1055
1316
|
if (!raw) return '';
|
|
@@ -1065,6 +1326,11 @@ const parseReverseMappingPhoneDigits = (content) => {
|
|
|
1065
1326
|
return digits.length >= 10 && digits.length <= 15 ? digits : '';
|
|
1066
1327
|
};
|
|
1067
1328
|
|
|
1329
|
+
/**
|
|
1330
|
+
* Resolve JID a partir de LID consultando auth state (MySQL/arquivos).
|
|
1331
|
+
* @param {string} lid
|
|
1332
|
+
* @returns {Promise<string|null>}
|
|
1333
|
+
*/
|
|
1068
1334
|
const resolveAuthStoreJidByLid = async (lid) => {
|
|
1069
1335
|
const normalizedLid = normalizeLid(lid);
|
|
1070
1336
|
if (!normalizedLid) return null;
|
|
@@ -1132,7 +1398,7 @@ const maskJid = (jid) => {
|
|
|
1132
1398
|
/**
|
|
1133
1399
|
* Busca entrada do cache (com expiração).
|
|
1134
1400
|
* @param {string|null|undefined} lid
|
|
1135
|
-
* @returns {
|
|
1401
|
+
* @returns {LidCacheEntry|null}
|
|
1136
1402
|
*/
|
|
1137
1403
|
const getCacheEntry = (lid) => {
|
|
1138
1404
|
if (!lid) return null;
|
|
@@ -1194,9 +1460,10 @@ export const getCachedJidForLid = (lid) => {
|
|
|
1194
1460
|
|
|
1195
1461
|
/**
|
|
1196
1462
|
* Divide lista em batches.
|
|
1197
|
-
* @
|
|
1463
|
+
* @template T
|
|
1464
|
+
* @param {T[]} items
|
|
1198
1465
|
* @param {number} [limit=BATCH_LIMIT]
|
|
1199
|
-
* @returns {
|
|
1466
|
+
* @returns {T[][]}
|
|
1200
1467
|
*/
|
|
1201
1468
|
const buildChunks = (items, limit = BATCH_LIMIT) => {
|
|
1202
1469
|
const chunks = [];
|
|
@@ -1326,7 +1593,7 @@ const buildServerLikeFilter = (column, servers) => {
|
|
|
1326
1593
|
/**
|
|
1327
1594
|
* Resolve candidatos principais de identidade de usuário.
|
|
1328
1595
|
* Centraliza regra usada por `resolveUserIdCached` e `resolveUserId`.
|
|
1329
|
-
* @param {
|
|
1596
|
+
* @param {IdentityParams} [params]
|
|
1330
1597
|
* @returns {{directJid: string|null, lidValue: string|null, fallback: string|null}}
|
|
1331
1598
|
*/
|
|
1332
1599
|
const resolveIdentityCandidates = ({ lid, jid, participantAlt } = {}) => {
|
|
@@ -1355,6 +1622,11 @@ const resolveIdentityCandidates = ({ lid, jid, participantAlt } = {}) => {
|
|
|
1355
1622
|
};
|
|
1356
1623
|
};
|
|
1357
1624
|
|
|
1625
|
+
/**
|
|
1626
|
+
* Monta SQL de upsert para lote do `lid_map`.
|
|
1627
|
+
* @param {number} rows
|
|
1628
|
+
* @returns {string}
|
|
1629
|
+
*/
|
|
1358
1630
|
const buildLidUpsertSql = (rows) => {
|
|
1359
1631
|
const placeholders = buildRowPlaceholders(rows, '(?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?)');
|
|
1360
1632
|
return `
|
|
@@ -1431,7 +1703,7 @@ export const queueLidUpdate = (lid, jid, source = 'message') => {
|
|
|
1431
1703
|
|
|
1432
1704
|
/**
|
|
1433
1705
|
* Resolve ID canônico usando apenas cache.
|
|
1434
|
-
* @param {
|
|
1706
|
+
* @param {IdentityParams} [params]
|
|
1435
1707
|
* @returns {string|null}
|
|
1436
1708
|
*/
|
|
1437
1709
|
export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
|
|
@@ -1445,9 +1717,9 @@ export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
|
|
|
1445
1717
|
};
|
|
1446
1718
|
|
|
1447
1719
|
/**
|
|
1448
|
-
* Extrai
|
|
1449
|
-
* @param {
|
|
1450
|
-
* @returns {
|
|
1720
|
+
* Extrai informações de identidade do remetente a partir da mensagem.
|
|
1721
|
+
* @param {BaileysMessage} msg
|
|
1722
|
+
* @returns {SenderInfo}
|
|
1451
1723
|
*/
|
|
1452
1724
|
export const extractSenderInfoFromMessage = (msg) => {
|
|
1453
1725
|
const remoteJid = normalizeJid(msg?.key?.remoteJid || '') || null;
|
|
@@ -1556,7 +1828,7 @@ const fetchJidByLid = async (lid) => {
|
|
|
1556
1828
|
|
|
1557
1829
|
/**
|
|
1558
1830
|
* Resolve ID canônico consultando banco se necessário.
|
|
1559
|
-
* @param {
|
|
1831
|
+
* @param {IdentityParams} [params]
|
|
1560
1832
|
* @returns {Promise<string|null>}
|
|
1561
1833
|
*/
|
|
1562
1834
|
export const resolveUserId = async ({ lid, jid, participantAlt } = {}) => {
|
|
@@ -1604,6 +1876,10 @@ export const reconcileLidToJid = async ({ lid, jid, source = 'map' } = {}) => {
|
|
|
1604
1876
|
return { updated };
|
|
1605
1877
|
};
|
|
1606
1878
|
|
|
1879
|
+
/**
|
|
1880
|
+
* Executa flush em lote do buffer de atualizações LID->JID.
|
|
1881
|
+
* @returns {Promise<void>}
|
|
1882
|
+
*/
|
|
1607
1883
|
const flushLidQueueCore = async () => {
|
|
1608
1884
|
if (lidWriteBuffer.size === 0) return;
|
|
1609
1885
|
const entries = Array.from(lidWriteBuffer.values());
|
|
@@ -1669,6 +1945,13 @@ export const flushLidQueue = async () => {
|
|
|
1669
1945
|
await lidFlushRunner.run();
|
|
1670
1946
|
};
|
|
1671
1947
|
|
|
1948
|
+
/**
|
|
1949
|
+
* Enfileira atualização de mapa LID/JID e retorna estado simplificado.
|
|
1950
|
+
* @param {string} lid
|
|
1951
|
+
* @param {string|null} jid
|
|
1952
|
+
* @param {string} [source='message']
|
|
1953
|
+
* @returns {Promise<{stored: boolean, reconciled: boolean}>}
|
|
1954
|
+
*/
|
|
1672
1955
|
export const maybeStoreLidMap = async (lid, jid, source = 'message') => {
|
|
1673
1956
|
const result = queueLidUpdate(lid, jid, source);
|
|
1674
1957
|
return { stored: result.queued, reconciled: result.reconciled };
|
|
@@ -1690,6 +1973,10 @@ export const extractUserIdInfo = (value) => {
|
|
|
1690
1973
|
};
|
|
1691
1974
|
}
|
|
1692
1975
|
|
|
1976
|
+
/**
|
|
1977
|
+
* @param {unknown} entry
|
|
1978
|
+
* @returns {string|null}
|
|
1979
|
+
*/
|
|
1693
1980
|
const readJid = (entry) => (typeof entry === 'string' ? normalizeJid(entry) || null : null);
|
|
1694
1981
|
|
|
1695
1982
|
const participantAlt = readJid(value.participantAlt);
|