@omnizap-system/omnizap 2.6.0 → 2.6.2

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.
Files changed (261) hide show
  1. package/.env.example +58 -13
  2. package/.github/workflows/ci.yml +5 -5
  3. package/.github/workflows/codeql.yml +1 -1
  4. package/.github/workflows/db-migration-check.yml +2 -2
  5. package/.github/workflows/dependency-review.yml +1 -1
  6. package/.github/workflows/deploy.yml +2 -2
  7. package/.github/workflows/release.yml +2 -2
  8. package/.github/workflows/security-attest-provenance.yml +2 -2
  9. package/.github/workflows/security-gitleaks.yml +13 -4
  10. package/.github/workflows/security-runner-hardening.yml +2 -2
  11. package/.github/workflows/security-scorecard.yml +1 -1
  12. package/.github/workflows/security-zap-baseline.yml +1 -1
  13. package/.github/workflows/security-zap-full-scan.yml +2 -1
  14. package/.github/workflows/security-zizmor.yml +1 -1
  15. package/.github/workflows/wiki-sync.yml +1 -1
  16. package/.gitleaksignore +9 -0
  17. package/CODE_OF_CONDUCT.md +2 -2
  18. package/GEMINI.md +64 -0
  19. package/README.md +52 -82
  20. package/SECURITY.md +1 -1
  21. package/app/config/index.js +2 -0
  22. package/app/configParts/adminIdentity.js +5 -5
  23. package/app/configParts/baileysConfig.js +230 -58
  24. package/app/configParts/groupUtils.js +5 -0
  25. package/app/configParts/messagePersistenceService.js +145 -4
  26. package/app/configParts/sessionConfig.js +157 -0
  27. package/app/connection/baileysCompatibility.test.js +1 -1
  28. package/app/connection/groupOwnerWriteStateResolver.js +109 -0
  29. package/app/connection/socketController.js +660 -158
  30. package/app/connection/socketController.multiSession.test.js +108 -0
  31. package/app/controllers/messageController.js +1 -1
  32. package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
  33. package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
  34. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
  35. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
  36. package/app/controllers/messageProcessingPipeline.js +93 -13
  37. package/app/controllers/messageProcessingPipeline.test.js +200 -0
  38. package/app/modules/adminModule/AGENT.md +1 -1
  39. package/app/modules/adminModule/commandConfig.json +3318 -1347
  40. package/app/modules/adminModule/groupCommandHandlers.js +858 -15
  41. package/app/modules/adminModule/groupCommandHandlers.test.js +378 -11
  42. package/app/modules/adminModule/groupWarningRepository.js +152 -0
  43. package/app/modules/aiModule/AGENT.md +47 -30
  44. package/app/modules/aiModule/aiConfigRuntime.js +1 -0
  45. package/app/modules/aiModule/catCommand.js +135 -27
  46. package/app/modules/aiModule/commandConfig.json +114 -28
  47. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
  48. package/app/modules/gameModule/AGENT.md +1 -1
  49. package/app/modules/gameModule/commandConfig.json +29 -0
  50. package/app/modules/menuModule/AGENT.md +1 -1
  51. package/app/modules/menuModule/commandConfig.json +45 -10
  52. package/app/modules/menuModule/menuCatalogService.js +190 -0
  53. package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
  54. package/app/modules/menuModule/menuDynamicService.js +511 -0
  55. package/app/modules/menuModule/menuDynamicService.test.js +141 -0
  56. package/app/modules/menuModule/menus.js +36 -5
  57. package/app/modules/playModule/AGENT.md +10 -5
  58. package/app/modules/playModule/commandConfig.json +140 -12
  59. package/app/modules/playModule/playCommand.js +1 -1417
  60. package/app/modules/playModule/playCommandConstants.js +80 -0
  61. package/app/modules/playModule/playCommandCore.js +361 -0
  62. package/app/modules/playModule/playCommandHandlers.js +41 -0
  63. package/app/modules/playModule/playCommandMediaClient.js +1872 -0
  64. package/app/modules/playModule/playConfigRuntime.js +245 -4
  65. package/app/modules/playModule/playModuleCriticalFlows.test.js +152 -0
  66. package/app/modules/quoteModule/AGENT.md +1 -1
  67. package/app/modules/quoteModule/commandConfig.json +29 -0
  68. package/app/modules/quoteModule/quoteCommand.js +3 -2
  69. package/app/modules/rpgPokemonModule/AGENT.md +1 -1
  70. package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
  71. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +5 -4
  72. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +2 -1
  73. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +2 -1
  74. package/app/modules/rpgPokemonModule/rpgPokemonService.js +38 -37
  75. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +4 -3
  76. package/app/modules/statsModule/AGENT.md +1 -1
  77. package/app/modules/statsModule/commandConfig.json +58 -0
  78. package/app/modules/statsModule/rankingCommon.js +5 -4
  79. package/app/modules/stickerModule/AGENT.md +1 -1
  80. package/app/modules/stickerModule/addStickerMetadata.js +4 -3
  81. package/app/modules/stickerModule/commandConfig.json +145 -0
  82. package/app/modules/stickerModule/stickerCommand.js +1 -1
  83. package/app/modules/stickerPackModule/AGENT.md +1 -1
  84. package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
  85. package/app/modules/stickerPackModule/commandConfig.json +29 -0
  86. package/app/modules/stickerPackModule/semanticThemeClusterService.js +7 -6
  87. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +10 -9
  88. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +9 -8
  89. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +3 -2
  90. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +2 -1
  91. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +80 -58
  92. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +2 -1
  93. package/app/modules/stickerPackModule/stickerPackRepository.js +2 -1
  94. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +5 -4
  95. package/app/modules/stickerPackModule/stickerPackService.js +13 -6
  96. package/app/modules/stickerPackModule/stickerStorageService.js +3 -2
  97. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +2 -1
  98. package/app/modules/systemMetricsModule/AGENT.md +1 -1
  99. package/app/modules/systemMetricsModule/commandConfig.json +29 -0
  100. package/app/modules/systemMetricsModule/pingCommand.js +6 -5
  101. package/app/modules/tiktokModule/AGENT.md +1 -1
  102. package/app/modules/tiktokModule/commandConfig.json +29 -0
  103. package/app/modules/tiktokModule/tiktokCommand.js +2 -1
  104. package/app/modules/userModule/AGENT.md +1 -1
  105. package/app/modules/userModule/commandConfig.json +29 -0
  106. package/app/modules/userModule/userCommand.js +72 -23
  107. package/app/modules/waifuPicsModule/AGENT.md +57 -27
  108. package/app/modules/waifuPicsModule/commandConfig.json +87 -0
  109. package/app/modules/waifuPicsModule/waifuPicsCommand.js +3 -2
  110. package/app/observability/metrics.js +136 -0
  111. package/app/services/ai/commandConfigEnrichmentService.js +229 -47
  112. package/app/services/ai/conversationRouterService.js +4 -3
  113. package/app/services/ai/geminiService.js +132 -7
  114. package/app/services/ai/geminiService.test.js +59 -2
  115. package/app/services/ai/globalModuleAiHelpService.js +3 -2
  116. package/app/services/ai/messageCommandExecutionService.js +2 -1
  117. package/app/services/ai/moduleAiHelpCoreService.js +45 -14
  118. package/app/services/ai/moduleToolExecutorService.js +3 -2
  119. package/app/services/ai/moduleToolRegistryService.js +2 -1
  120. package/app/services/ai/toolCandidateSelectorService.js +6 -5
  121. package/app/services/auth/googleWebLinkService.js +3 -2
  122. package/app/services/auth/whatsappLoginLinkService.js +3 -2
  123. package/app/services/external/pokeApiService.js +4 -3
  124. package/app/services/group/groupMetadataService.js +24 -1
  125. package/app/services/infra/dbWriteQueue.js +57 -26
  126. package/app/services/infra/featureFlagService.js +2 -1
  127. package/app/services/messaging/captchaService.js +3 -2
  128. package/app/services/messaging/newsBroadcastService.js +846 -29
  129. package/app/services/multiSession/assignmentBalancerService.js +457 -0
  130. package/app/services/multiSession/groupOwnershipRepository.js +381 -0
  131. package/app/services/multiSession/groupOwnershipService.js +890 -0
  132. package/app/services/multiSession/groupOwnershipService.test.js +309 -0
  133. package/app/services/multiSession/sessionRegistryService.js +293 -0
  134. package/app/services/sticker/stickerFocusService.js +11 -10
  135. package/app/store/aiPromptStore.js +36 -19
  136. package/app/store/conversationSessionStore.js +7 -6
  137. package/app/store/groupConfigStore.js +41 -5
  138. package/app/store/premiumUserStore.js +21 -7
  139. package/app/utils/antiLink/antiLinkModule.js +352 -16
  140. package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
  141. package/app/workers/aiLearningWorker.js +6 -5
  142. package/app/workers/commandConfigEnrichmentWorker.js +4 -3
  143. package/database/index.js +14 -8
  144. package/database/migrations/20260307_d0_hardening_down.sql +1 -1
  145. package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
  146. package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
  147. package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
  148. package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
  149. package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
  150. package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
  151. package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
  152. package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
  153. package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
  154. package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
  155. package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
  156. package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
  157. package/database/schema.sql +102 -1
  158. package/docker-compose.yml +4 -1
  159. package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
  160. package/docs/compliance/dpa-b2b-standard-2026-03-07.md +1 -1
  161. package/docs/compliance/privacy-policy-2026-03-07.md +4 -4
  162. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
  163. package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +1 -1
  164. package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
  165. package/docs/security/omnizap-static-security-headers.conf +25 -0
  166. package/docs/wiki/Home.md +1 -1
  167. package/ecosystem.prod.config.cjs +32 -12
  168. package/index.js +57 -23
  169. package/observability/alert-rules.yml +20 -0
  170. package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
  171. package/observability/mysql-setup.sql +4 -4
  172. package/observability/system-admin-observability.md +26 -0
  173. package/package.json +20 -6
  174. package/public/apple-touch-icon.png +0 -0
  175. package/public/comandos/commands-catalog.json +2853 -3326
  176. package/public/favicon-16x16.png +0 -0
  177. package/public/favicon-32x32.png +0 -0
  178. package/public/favicon.ico +0 -0
  179. package/public/js/apps/apiDocsApp.js +3 -2
  180. package/public/js/apps/commandsReactApp.js +280 -99
  181. package/public/js/apps/createPackApp.js +11 -10
  182. package/public/js/apps/homeReactApp.js +181 -130
  183. package/public/js/apps/loginReactApp.js +1 -1
  184. package/public/js/apps/stickersApp.js +263 -110
  185. package/public/js/apps/termsReactApp.js +73 -24
  186. package/public/js/apps/userApp.js +4 -3
  187. package/public/js/apps/userPasswordResetReactApp.js +406 -0
  188. package/public/js/apps/userReactApp.js +355 -280
  189. package/public/js/apps/userSystemAdmReactApp.js +1506 -0
  190. package/public/pages/api-docs.html +1 -1
  191. package/public/pages/aup.html +2 -2
  192. package/public/pages/dpa.html +3 -3
  193. package/public/pages/licenca.html +4 -4
  194. package/public/pages/login.html +1 -1
  195. package/public/pages/notice-and-takedown.html +2 -2
  196. package/public/pages/politica-de-privacidade.html +6 -6
  197. package/public/pages/seo-bot-whatsapp-para-grupo.html +3 -3
  198. package/public/pages/seo-bot-whatsapp-sem-programar.html +3 -3
  199. package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +3 -3
  200. package/public/pages/seo-como-criar-comandos-whatsapp.html +3 -3
  201. package/public/pages/seo-como-evitar-spam-no-whatsapp.html +3 -3
  202. package/public/pages/seo-como-moderar-grupo-whatsapp.html +3 -3
  203. package/public/pages/seo-como-organizar-comunidade-whatsapp.html +3 -3
  204. package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +3 -3
  205. package/public/pages/stickers-admin.html +1 -1
  206. package/public/pages/stickers-create.html +1 -1
  207. package/public/pages/stickers.html +6 -6
  208. package/public/pages/suboperadores.html +2 -2
  209. package/public/pages/termos-de-uso-texto-integral.html +6 -6
  210. package/public/pages/termos-de-uso.html +3 -3
  211. package/public/pages/user-password-reset.html +4 -5
  212. package/public/pages/user-systemadm.html +9 -463
  213. package/public/pages/user.html +2 -2
  214. package/scripts/clear-whatsapp-session.sh +123 -0
  215. package/scripts/core-ai-mode.mjs +163 -0
  216. package/scripts/deploy.sh +11 -1
  217. package/scripts/email-broadcast-terms-update.mjs +2 -1
  218. package/scripts/enrich-command-config-ux-openai.mjs +492 -0
  219. package/scripts/generate-commands-catalog.mjs +166 -2
  220. package/scripts/generate-module-agents.mjs +2 -1
  221. package/scripts/generate-seo-satellite-pages.mjs +5 -4
  222. package/scripts/github-deploy-notify.mjs +2 -1
  223. package/scripts/github-release-notify.mjs +25 -10
  224. package/scripts/new-whatsapp-session.sh +317 -0
  225. package/scripts/release.sh +2 -19
  226. package/scripts/security-smoketest.mjs +6 -5
  227. package/scripts/security-web-surface-check.mjs +218 -0
  228. package/scripts/sticker-catalog-loadtest.mjs +5 -4
  229. package/server/auth/googleWebAuth/googleWebAuthService.js +8 -7
  230. package/server/auth/jwt/webJwtService.js +1 -1
  231. package/server/auth/stickerCatalogAuthContext.js +2 -1
  232. package/server/auth/termsAcceptance/termsAcceptanceHandler.js +2 -1
  233. package/server/auth/userPassword/userPasswordAuthService.js +2 -1
  234. package/server/auth/userPassword/userPasswordRecoveryService.js +4 -3
  235. package/server/auth/webAccount/webAccountHandlers.js +9 -10
  236. package/server/controllers/admin/adminPanelHandlers.js +267 -16
  237. package/server/controllers/admin/systemAdminController.js +267 -0
  238. package/server/controllers/seo/stickerCatalogSeoContext.js +10 -9
  239. package/server/controllers/sticker/nonCatalogHandlers.js +2 -1
  240. package/server/controllers/sticker/stickerCatalogController.js +23 -36
  241. package/server/controllers/system/contactController.js +9 -17
  242. package/server/controllers/system/githubController.js +3 -2
  243. package/server/controllers/system/stickerCatalogSystemContext.js +41 -19
  244. package/server/controllers/system/systemController.js +254 -1
  245. package/server/controllers/system/systemMetricsController.js +2 -1
  246. package/server/controllers/userController.js +6 -0
  247. package/server/email/emailTemplateService.js +5 -3
  248. package/server/http/httpServer.js +11 -6
  249. package/server/middleware/rateLimit.js +2 -1
  250. package/server/middleware/securityHeaders.js +20 -1
  251. package/server/routes/admin/systemAdminRouter.js +6 -0
  252. package/server/routes/indexRouter.js +30 -6
  253. package/server/routes/observability/grafanaProxyRouter.js +254 -0
  254. package/server/routes/static/staticPageRouter.js +27 -1
  255. package/server/utils/publicContact.js +31 -0
  256. package/utils/time/timeModule.js +135 -0
  257. package/utils/time/timeModule.test.js +65 -0
  258. package/utils/whatsapp/contactEnv.js +39 -0
  259. package/vite.config.mjs +7 -1
  260. package/public/assets/images/brand-icon-192.png +0 -0
  261. package/scripts/sync-readme-snapshot.mjs +0 -133
@@ -1,11 +1,8 @@
1
1
  import { encodeJid, getJidUser, isSameJidUser, normalizeJid } from './baileysConfig.js';
2
2
  import { extractUserIdInfo, resolveUserId, resolveUserIdCached } from './baileysConfig.js';
3
+ import { normalizePhoneDigits, resolveAdminIdentityRawFromEnv, resolveAdminPhoneFromEnv } from '../../utils/whatsapp/contactEnv.js';
3
4
 
4
- const ADMIN_ENV_KEY = 'USER_ADMIN';
5
-
6
- const normalizePhoneDigits = (value) => String(value || '').replace(/\D+/g, '');
7
-
8
- export const getAdminRawValue = () => String(process.env[ADMIN_ENV_KEY] || '').trim();
5
+ export const getAdminRawValue = () => resolveAdminIdentityRawFromEnv();
9
6
 
10
7
  export const getAdminJid = () => {
11
8
  const raw = getAdminRawValue();
@@ -28,6 +25,9 @@ export const getAdminJid = () => {
28
25
  };
29
26
 
30
27
  export const getAdminPhone = () => {
28
+ const explicitAdminPhone = resolveAdminPhoneFromEnv({ fallback: '' });
29
+ if (explicitAdminPhone) return explicitAdminPhone;
30
+
31
31
  const adminJid = getAdminJid();
32
32
  if (!adminJid) return null;
33
33
 
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  /* eslint-disable no-unused-vars */
2
3
  /* eslint-disable no-useless-escape */
3
4
  import { fetchLatestBaileysVersion, downloadContentFromMessage, jidNormalizedUser, jidEncode, jidDecode, areJidsSameUser, normalizeMessageContent, isJidMetaAI, isPnUser, isLidUser, isJidBroadcast, isJidGroup, isJidStatusBroadcast, isJidNewsletter, isHostedPnUser, isHostedLidUser, isJidBot, SERVER_JID, PSA_WID, STORIES_JID, META_AI_JID, delay } from '@whiskeysockets/baileys';
@@ -12,20 +13,164 @@ import path from 'node:path';
12
13
  import { pipeline } from 'node:stream/promises';
13
14
  import { Readable } from 'node:stream';
14
15
  import { fileURLToPath } from 'node:url';
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
+ */
15
80
 
16
81
  const DEFAULT_BAILEYS_VERSION = [7, 0, 0];
82
+ const multiSessionRuntimeConfig = getMultiSessionRuntimeConfig();
83
+ const PRIMARY_BAILEYS_SESSION_ID = String(multiSessionRuntimeConfig?.primarySessionId || process.env.BAILEYS_AUTH_SESSION_ID || 'default').trim() || 'default';
84
+
85
+ const normalizeSessionId = (sessionId) => {
86
+ const normalized = String(sessionId || '').trim();
87
+ return normalized || PRIMARY_BAILEYS_SESSION_ID;
88
+ };
17
89
 
90
+ const sessionSocketMap = new Map();
18
91
  let activeSocket = null;
19
92
 
20
- export const setActiveSocket = (socket) => {
21
- activeSocket = socket;
93
+ /**
94
+ * Atualiza a referência global do socket ativo e o registro por sessão.
95
+ * Mantém compatibilidade com chamadas legadas que não informam `sessionId`.
96
+ * @param {BaileysSocket|null} socket Instância conectada ou `null` para limpar.
97
+ * @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID] Sessão da conexão.
98
+ * @returns {void}
99
+ */
100
+ export const setActiveSocket = (socket, sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
101
+ const safeSessionId = normalizeSessionId(sessionId);
102
+ const previousSocket = sessionSocketMap.get(safeSessionId) || null;
103
+
104
+ if (socket) {
105
+ sessionSocketMap.set(safeSessionId, socket);
106
+ } else {
107
+ sessionSocketMap.delete(safeSessionId);
108
+ }
109
+
110
+ if (safeSessionId === PRIMARY_BAILEYS_SESSION_ID) {
111
+ activeSocket = socket || null;
112
+ } else if (!socket && previousSocket && activeSocket === previousSocket) {
113
+ activeSocket = null;
114
+ }
115
+
116
+ if (!activeSocket) {
117
+ activeSocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || sessionSocketMap.values().next()?.value || null;
118
+ }
119
+ };
120
+
121
+ /**
122
+ * Atualiza o socket ativo de uma sessão específica.
123
+ * @param {string} sessionId Sessão alvo.
124
+ * @param {BaileysSocket|null} socket Instância conectada ou `null`.
125
+ * @returns {void}
126
+ */
127
+ export const setSocketBySession = (sessionId, socket) => {
128
+ setActiveSocket(socket, sessionId);
129
+ };
130
+
131
+ /**
132
+ * Retorna a instância de socket ativa no processo.
133
+ * @returns {BaileysSocket|null}
134
+ */
135
+ export const getActiveSocket = () => {
136
+ if (isSocketOpen(activeSocket)) return activeSocket;
137
+
138
+ const primarySocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || null;
139
+ if (isSocketOpen(primarySocket)) {
140
+ activeSocket = primarySocket;
141
+ return activeSocket;
142
+ }
143
+
144
+ for (const socket of sessionSocketMap.values()) {
145
+ if (isSocketOpen(socket)) {
146
+ activeSocket = socket;
147
+ return activeSocket;
148
+ }
149
+ }
150
+
151
+ activeSocket = null;
152
+ return activeSocket;
22
153
  };
23
154
 
24
- export const getActiveSocket = () => activeSocket;
155
+ /**
156
+ * Retorna o socket ativo associado a uma sessão.
157
+ * @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID]
158
+ * @returns {BaileysSocket|null}
159
+ */
160
+ export const getSocketBySession = (sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
161
+ const safeSessionId = normalizeSessionId(sessionId);
162
+ return sessionSocketMap.get(safeSessionId) || null;
163
+ };
164
+
165
+ /**
166
+ * Snapshot dos sockets ativos por sessão.
167
+ * @returns {Map<string, BaileysSocket>}
168
+ */
169
+ export const getActiveSocketsBySession = () => new Map(sessionSocketMap);
25
170
 
26
171
  /**
27
172
  * Indica se uma instância de socket está aberta para operações.
28
- * @param {object|null|undefined} socket Instância de socket.
173
+ * @param {BaileysSocket|null|undefined} socket Instância de socket.
29
174
  * @returns {boolean}
30
175
  */
31
176
  export const isSocketOpen = (socket) => {
@@ -42,7 +187,7 @@ export const isActiveSocketOpen = () => isSocketOpen(activeSocket);
42
187
 
43
188
  /**
44
189
  * Executa um método em uma instância de socket validando disponibilidade.
45
- * @param {object|null|undefined} socket Instância de socket.
190
+ * @param {BaileysSocket|null|undefined} socket Instância de socket.
46
191
  * @param {string} methodName Nome do método no socket.
47
192
  * @param {...any} args Argumentos do método.
48
193
  * @returns {Promise<any>}
@@ -68,9 +213,18 @@ export const runSocketMethod = async (socket, methodName, ...args) => {
68
213
  */
69
214
  export const runActiveSocketMethod = async (methodName, ...args) => runSocketMethod(activeSocket, methodName, ...args);
70
215
 
216
+ /**
217
+ * Executa um método no socket de uma sessão específica.
218
+ * @param {string} sessionId Sessão alvo.
219
+ * @param {string} methodName Nome do método no socket.
220
+ * @param {...any} args Argumentos do método.
221
+ * @returns {Promise<any>}
222
+ */
223
+ export const runSessionSocketMethod = async (sessionId, methodName, ...args) => runSocketMethod(getSocketBySession(sessionId), methodName, ...args);
224
+
71
225
  /**
72
226
  * Recupera a blocklist da conta conectada.
73
- * @returns {Promise<(string|undefined)[]>}
227
+ * @returns {Promise<string[]>}
74
228
  */
75
229
  export const fetchBlocklistFromActiveSocket = async () => runActiveSocketMethod('fetchBlocklist');
76
230
 
@@ -127,7 +281,8 @@ const decodeJidParts = (() => {
127
281
 
128
282
  /**
129
283
  * Tipos de mensagem conhecidos do Baileys
130
- * Mapeamento de chaves do proto.Message para tipos normalizados
284
+ * (mapeamento de chaves do proto.Message para tipos normalizados).
285
+ * @type {Record<string, string>}
131
286
  */
132
287
  export const MEDIA_TYPE_MAPPING = {
133
288
  conversation: 'text',
@@ -181,7 +336,8 @@ export const MEDIA_TYPE_MAPPING = {
181
336
  };
182
337
 
183
338
  /**
184
- * Tipos de midia que contem conteudo binario/arquivo
339
+ * Tipos de mídia que carregam conteúdo binário/arquivo.
340
+ * @type {Set<string>}
185
341
  */
186
342
  export const BINARY_MEDIA_TYPES = new Set(['image', 'video', 'videoNote', 'audio', 'voice', 'document', 'sticker']);
187
343
 
@@ -520,13 +676,22 @@ export function isWhatsAppJid(jid) {
520
676
  return Boolean(server && WHATSAPP_USER_JID_SERVERS.has(server));
521
677
  }
522
678
 
679
+ /**
680
+ * Modo de endereçamento por LID.
681
+ * @type {'lid'}
682
+ */
523
683
  export const ADDRESSING_MODE_LID = 'lid';
684
+
685
+ /**
686
+ * Modo de endereçamento por PN/JID canônico.
687
+ * @type {'pn'}
688
+ */
524
689
  export const ADDRESSING_MODE_PN = 'pn';
525
690
 
526
691
  /**
527
692
  * Normaliza um modo de endereçamento (lid/pn).
528
693
  * @param {unknown} value
529
- * @returns {'lid'|'pn'|undefined}
694
+ * @returns {AddressingMode|undefined}
530
695
  */
531
696
  export const normalizeAddressingMode = (value) => {
532
697
  if (value === undefined || value === null) return undefined;
@@ -539,8 +704,8 @@ export const normalizeAddressingMode = (value) => {
539
704
  /**
540
705
  * Resolve modo de endereçamento a partir da chave da mensagem.
541
706
  * @param {object} [key={}]
542
- * @param {object} [senderInfo={}]
543
- * @returns {'lid'|'pn'|undefined}
707
+ * @param {SenderInfo|Record<string, any>} [senderInfo={}]
708
+ * @returns {AddressingMode|undefined}
544
709
  */
545
710
  export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) => {
546
711
  const explicit = normalizeAddressingMode(key?.addressingMode);
@@ -559,7 +724,7 @@ export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) =
559
724
 
560
725
  /**
561
726
  * Resolve JID canônico de usuário WhatsApp a partir de candidatos.
562
- * @param {...string} candidates
727
+ * @param {...(string|null|undefined)} candidates
563
728
  * @returns {string}
564
729
  */
565
730
  export const resolveCanonicalWhatsAppJid = (...candidates) => {
@@ -715,7 +880,7 @@ export async function resolveBaileysVersion() {
715
880
 
716
881
  /**
717
882
  * Baixa a foto de perfil associada à mensagem recebida.
718
- * @param {import('@whiskeysockets/baileys').WASocket} sock - Instância conectada do socket.
883
+ * @param {BaileysSocket} sock - Instância conectada do socket.
719
884
  * @param {import('@whiskeysockets/baileys').proto.IWebMessageInfo} msg - Mensagem usada para resolver o JID.
720
885
  * @returns {Promise<Buffer|null>} Buffer da imagem ou `null` se indisponível.
721
886
  */
@@ -739,17 +904,17 @@ export async function getProfilePicBuffer(sock, msg) {
739
904
 
740
905
  /**
741
906
  * Extrai o valor de expiração de uma mensagem do WhatsApp, ou retorna 24 horas (em segundos) por padrão.
742
- * @param {{message?: object}|null|undefined} sock - Estrutura contendo a propriedade `message`.
907
+ * @param {{message?: Record<string, any>}|null|undefined} messageInfo - Estrutura contendo a propriedade `message`.
743
908
  * @returns {number} Tempo de expiração em segundos.
744
909
  */
745
- export function getExpiration(sock) {
910
+ export function getExpiration(messageInfo) {
746
911
  const DEFAULT_EXPIRATION_SECONDS = 24 * 60 * 60;
747
912
 
748
- if (!sock || typeof sock !== 'object' || !sock.message) {
913
+ if (!messageInfo || typeof messageInfo !== 'object' || !messageInfo.message) {
749
914
  return DEFAULT_EXPIRATION_SECONDS;
750
915
  }
751
916
 
752
- const normalizedMessage = normalizeMessage(sock.message);
917
+ const normalizedMessage = normalizeMessage(messageInfo.message);
753
918
  const expiration = findExpiration(normalizedMessage);
754
919
 
755
920
  return typeof expiration === 'number' ? expiration : DEFAULT_EXPIRATION_SECONDS;
@@ -757,7 +922,7 @@ export function getExpiration(sock) {
757
922
 
758
923
  /**
759
924
  * Extrai o conteúdo de texto de uma mensagem do WhatsApp.
760
- * @param {{message?: object}} messageInfo - Objeto que contém o payload da mensagem.
925
+ * @param {{message?: Record<string, any>}} messageInfo - Objeto que contém o payload da mensagem.
761
926
  * @returns {string} Conteúdo textual extraído ou descrição do tipo de mensagem.
762
927
  */
763
928
  export const extractMessageContent = ({ message }) => {
@@ -809,7 +974,7 @@ export const extractMessageContent = ({ message }) => {
809
974
 
810
975
  /**
811
976
  * Faz o download de mídia a partir de uma mensagem do Baileys.
812
- * @param {import('@whiskeysockets/baileys').WAProto.IMessage} message - Objeto da mídia a ser baixada.
977
+ * @param {BaileysProtoMessage} message - Objeto da mídia a ser baixada.
813
978
  * @param {string} type - Tipo de mídia (ex.: `image`, `video`, `audio`, `document`).
814
979
  * @param {string} outputPath - Diretório onde o arquivo será salvo.
815
980
  * @returns {Promise<string|null>} Caminho do arquivo salvo ou `null` em caso de falha.
@@ -837,9 +1002,9 @@ export const downloadMediaMessage = async (message, type, outputPath) => {
837
1002
  try {
838
1003
  const stream = await downloadContentFromMessage(message, type);
839
1004
 
840
- const fileId = message.key?.id || Date.now();
1005
+ const fileId = message.key?.id || __timeNowMs();
841
1006
  const extension = getMediaExtension(type);
842
- const fileName = `${Date.now()}-${fileId}.${extension}`;
1007
+ const fileName = `${__timeNowMs()}-${fileId}.${extension}`;
843
1008
  const filePath = path.join(outputPath, fileName);
844
1009
 
845
1010
  await pipeline(Readable.from(stream), createWriteStream(filePath));
@@ -866,10 +1031,10 @@ export const downloadMediaMessage = async (message, type, outputPath) => {
866
1031
  };
867
1032
 
868
1033
  /**
869
- * Detecta dinamicamente todos os tipos de midia em um objeto de mensagem
870
- * @param {object} messageContent - Conteudo da mensagem
871
- * @param {boolean} isQuoted - Se e de uma mensagem citada
872
- * @returns {Array} Array de objetos com detalhes da midia encontrada
1034
+ * Detecta dinamicamente os tipos de mídia presentes em um payload de mensagem.
1035
+ * @param {Record<string, any>} messageContent Conteúdo da mensagem (normal ou quoted).
1036
+ * @param {boolean} [isQuoted=false] Sinaliza se o payload veio de uma citação.
1037
+ * @returns {MediaEntry[]} Lista de mídias detectadas (pode estar vazia).
873
1038
  */
874
1039
  export function detectAllMediaTypes(messageContent, isQuoted = false) {
875
1040
  if (!messageContent || typeof messageContent !== 'object') {
@@ -913,13 +1078,10 @@ export function detectAllMediaTypes(messageContent, isQuoted = false) {
913
1078
  }
914
1079
 
915
1080
  /**
916
- * Extrai detalhes da midia da mensagem de forma dinamica
917
- * @param {object} message - O objeto da mensagem
918
- * @param {object} options - Opcoes de configuracao
919
- * @param {boolean} options.includeAllTypes - Se deve incluir todos os tipos, nao apenas binarios
920
- * @param {boolean} options.includeQuoted - Se deve incluir midia de mensagens citadas
921
- * @param {boolean} options.includeUnknown - Se deve incluir tipos desconhecidos
922
- * @returns {{mediaType: string, mediaKey: object, details: object}|null} - Detalhes da midia ou null se nao encontrada
1081
+ * Extrai a mídia primária de uma mensagem com filtros configuráveis.
1082
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1083
+ * @param {MediaExtractionOptions} [options={}] Opções de filtragem.
1084
+ * @returns {MediaExtractionResult|null} Estrutura da mídia primária ou `null` quando não houver mídia.
923
1085
  */
924
1086
  export function extractMediaDetails(message, options = {}) {
925
1087
  const { includeAllTypes = false, includeQuoted = true, includeUnknown = false } = options;
@@ -954,10 +1116,10 @@ export function extractMediaDetails(message, options = {}) {
954
1116
  }
955
1117
 
956
1118
  /**
957
- * Extrai todos os tipos de midia de uma mensagem
958
- * @param {object} message - O objeto da mensagem
959
- * @param {object} options - Opcoes de configuracao
960
- * @returns {Array} Array com todos os tipos de midia encontrados
1119
+ * Extrai todas as mídias detectadas de uma mensagem.
1120
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1121
+ * @param {MediaExtractionOptions} [options={}] Opções de filtragem.
1122
+ * @returns {MediaEntry[]} Array com todas as mídias encontradas.
961
1123
  */
962
1124
  export function extractAllMediaDetails(message, options = {}) {
963
1125
  const { includeAllTypes = true, includeQuoted = true, includeUnknown = true } = options;
@@ -967,10 +1129,10 @@ export function extractAllMediaDetails(message, options = {}) {
967
1129
  }
968
1130
 
969
1131
  /**
970
- * Verifica se uma mensagem contem midia
971
- * @param {object} message - O objeto da mensagem
972
- * @param {string} specificType - Tipo especifico para verificar (opcional)
973
- * @returns {boolean} True se contem midia
1132
+ * Verifica se uma mensagem contém mídia.
1133
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1134
+ * @param {string|null} [specificType=null] Tipo específico para filtrar (ex.: `image`).
1135
+ * @returns {boolean} `true` quando ao menos uma mídia compatível é encontrada.
974
1136
  */
975
1137
  export function hasMedia(message, specificType = null) {
976
1138
  const allMedia = collectMediaFromMessage(message, { includeQuoted: true });
@@ -988,8 +1150,13 @@ export function hasMedia(message, specificType = null) {
988
1150
  }
989
1151
 
990
1152
  /**
991
- * Obtem informacoes sobre os tipos de midia suportados
992
- * @returns {object} Informacoes sobre tipos de midia
1153
+ * Obtém metadados dos tipos de mídia suportados.
1154
+ * @returns {{
1155
+ * knownTypes: string[],
1156
+ * binaryTypes: string[],
1157
+ * typeMapping: Record<string, string>,
1158
+ * totalKnownTypes: number
1159
+ * }}
993
1160
  */
994
1161
  export function getMediaTypeInfo() {
995
1162
  return {
@@ -1001,9 +1168,9 @@ export function getMediaTypeInfo() {
1001
1168
  }
1002
1169
 
1003
1170
  /**
1004
- * ===============================
1171
+ * ===========================
1005
1172
  * LID Map Utilities
1006
- * ===============================
1173
+ * ===========================
1007
1174
  */
1008
1175
  const CACHE_TTL_MS = 20 * 60 * 1000;
1009
1176
  const NEGATIVE_TTL_MS = 5 * 60 * 1000;
@@ -1014,10 +1181,7 @@ const BACKFILL_SOURCE = 'backfill';
1014
1181
  const __filename = fileURLToPath(import.meta.url);
1015
1182
  const __dirname = path.dirname(__filename);
1016
1183
  const BAILEYS_AUTH_DIR = path.resolve(__dirname, '../connection/auth');
1017
- const BAILEYS_AUTH_SESSION_ID = (() => {
1018
- const raw = String(process.env.BAILEYS_AUTH_SESSION_ID || '').trim();
1019
- return raw || 'default';
1020
- })();
1184
+ const BAILEYS_AUTH_SESSION_ID = PRIMARY_BAILEYS_SESSION_ID;
1021
1185
 
1022
1186
  const lidCache = new Map();
1023
1187
  const lidWriteBuffer = new Map();
@@ -1033,7 +1197,7 @@ const updateLidQueueMetric = () => {
1033
1197
  * Retorna timestamp atual em ms.
1034
1198
  * @returns {number}
1035
1199
  */
1036
- const now = () => Date.now();
1200
+ const now = () => __timeNowMs();
1037
1201
 
1038
1202
  const normalizeLid = (lid) => {
1039
1203
  if (!lid || !isLidJid(lid)) return null;
@@ -1131,7 +1295,7 @@ const maskJid = (jid) => {
1131
1295
  /**
1132
1296
  * Busca entrada do cache (com expiração).
1133
1297
  * @param {string|null|undefined} lid
1134
- * @returns {{jid: string|null, expiresAt: number, lastStoredAt: number|null}|null}
1298
+ * @returns {LidCacheEntry|null}
1135
1299
  */
1136
1300
  const getCacheEntry = (lid) => {
1137
1301
  if (!lid) return null;
@@ -1193,9 +1357,10 @@ export const getCachedJidForLid = (lid) => {
1193
1357
 
1194
1358
  /**
1195
1359
  * Divide lista em batches.
1196
- * @param {Array<any>} items
1360
+ * @template T
1361
+ * @param {T[]} items
1197
1362
  * @param {number} [limit=BATCH_LIMIT]
1198
- * @returns {Array<Array<any>>}
1363
+ * @returns {T[][]}
1199
1364
  */
1200
1365
  const buildChunks = (items, limit = BATCH_LIMIT) => {
1201
1366
  const chunks = [];
@@ -1325,7 +1490,7 @@ const buildServerLikeFilter = (column, servers) => {
1325
1490
  /**
1326
1491
  * Resolve candidatos principais de identidade de usuário.
1327
1492
  * Centraliza regra usada por `resolveUserIdCached` e `resolveUserId`.
1328
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1493
+ * @param {IdentityParams} [params]
1329
1494
  * @returns {{directJid: string|null, lidValue: string|null, fallback: string|null}}
1330
1495
  */
1331
1496
  const resolveIdentityCandidates = ({ lid, jid, participantAlt } = {}) => {
@@ -1430,7 +1595,7 @@ export const queueLidUpdate = (lid, jid, source = 'message') => {
1430
1595
 
1431
1596
  /**
1432
1597
  * Resolve ID canônico usando apenas cache.
1433
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1598
+ * @param {IdentityParams} [params]
1434
1599
  * @returns {string|null}
1435
1600
  */
1436
1601
  export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
@@ -1444,9 +1609,9 @@ export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
1444
1609
  };
1445
1610
 
1446
1611
  /**
1447
- * Extrai informacoes do remetente a partir de uma mensagem do Baileys.
1448
- * @param {import('@whiskeysockets/baileys').WAMessage} msg
1449
- * @returns {{lid: string|null, jid: string|null, participantAlt: string|null, remoteJid: string|null, remoteJidAlt: string|null, groupMessage: boolean}}
1612
+ * Extrai informações de identidade do remetente a partir da mensagem.
1613
+ * @param {BaileysMessage} msg
1614
+ * @returns {SenderInfo}
1450
1615
  */
1451
1616
  export const extractSenderInfoFromMessage = (msg) => {
1452
1617
  const remoteJid = normalizeJid(msg?.key?.remoteJid || '') || null;
@@ -1555,7 +1720,7 @@ const fetchJidByLid = async (lid) => {
1555
1720
 
1556
1721
  /**
1557
1722
  * Resolve ID canônico consultando banco se necessário.
1558
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1723
+ * @param {IdentityParams} [params]
1559
1724
  * @returns {Promise<string|null>}
1560
1725
  */
1561
1726
  export const resolveUserId = async ({ lid, jid, participantAlt } = {}) => {
@@ -1668,6 +1833,13 @@ export const flushLidQueue = async () => {
1668
1833
  await lidFlushRunner.run();
1669
1834
  };
1670
1835
 
1836
+ /**
1837
+ * Enfileira atualização de mapa LID/JID e retorna estado simplificado.
1838
+ * @param {string} lid
1839
+ * @param {string|null} jid
1840
+ * @param {string} [source='message']
1841
+ * @returns {Promise<{stored: boolean, reconciled: boolean}>}
1842
+ */
1671
1843
  export const maybeStoreLidMap = async (lid, jid, source = 'message') => {
1672
1844
  const result = queueLidUpdate(lid, jid, source);
1673
1845
  return { stored: result.queued, reconciled: result.reconciled };
@@ -288,6 +288,7 @@ export async function getGroupInfoAsync(groupId) {
288
288
  const participants = parseParticipantsFromDb(data.participants || data.participants_json || null);
289
289
  const creation = _normalizeNumberOrNull(data.creation);
290
290
  const ephemeralDuration = _normalizeNumberOrNull(data.ephemeral_duration ?? data.ephemeralDuration);
291
+ const linkedParent = _normalizeGroupId(data.linked_parent_jid || data.linkedParent) || null;
291
292
 
292
293
  const group = {
293
294
  id: _normalizeGroupId(data.id) || normalizedGroupId,
@@ -299,6 +300,10 @@ export async function getGroupInfoAsync(groupId) {
299
300
  restrict: Boolean(data.restrict),
300
301
  announce: Boolean(data.announce),
301
302
  isCommunity: Boolean(data.is_community ?? data.isCommunity),
303
+ isCommunityAnnounce: Boolean(data.is_community_announce ?? data.isCommunityAnnounce),
304
+ linkedParent,
305
+ memberAddMode: data.member_add_mode === null || data.member_add_mode === undefined ? null : Boolean(data.member_add_mode),
306
+ joinApprovalMode: data.join_approval_mode === null || data.join_approval_mode === undefined ? null : Boolean(data.join_approval_mode),
302
307
  addressingMode: data.addressing_mode || data.addressingMode || null,
303
308
  ephemeralDuration,
304
309
  };