@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,36 +1,53 @@
1
- import groupConfigStore from './groupConfigStore.js';
1
+ import { TABLES, executeQuery, findById, remove, upsert } from '../../database/index.js';
2
+ import { normalizeJid } from '../config/index.js';
2
3
 
3
- const PROMPT_CONFIG_ID = 'system:ai_prompts';
4
+ const AI_PROMPTS_TABLE = TABLES.SYSTEM_AI_PROMPTS;
5
+ const SELECT_AI_PROMPTS_SQL = `SELECT id, prompt FROM \`${AI_PROMPTS_TABLE}\` ORDER BY id ASC`;
4
6
 
5
- const normalizeMap = (map) => (map && typeof map === 'object' ? map : {});
7
+ const normalizePromptJid = (jid) => {
8
+ const raw = String(jid || '').trim();
9
+ if (!raw) return '';
10
+ return normalizeJid(raw) || raw;
11
+ };
12
+
13
+ const normalizePromptValue = (prompt) => {
14
+ if (typeof prompt === 'string') return prompt;
15
+ if (prompt === null || prompt === undefined) return '';
16
+ return String(prompt);
17
+ };
6
18
 
7
19
  const aiPromptStore = {
8
20
  getAllPrompts: async function () {
9
- const config = await groupConfigStore.getGroupConfig(PROMPT_CONFIG_ID);
10
- return normalizeMap(config.prompts);
21
+ const rows = await executeQuery(SELECT_AI_PROMPTS_SQL);
22
+ const prompts = {};
23
+ for (const row of rows) {
24
+ const promptJid = normalizePromptJid(row?.id);
25
+ if (!promptJid) continue;
26
+ prompts[promptJid] = normalizePromptValue(row?.prompt);
27
+ }
28
+ return prompts;
11
29
  },
12
30
 
13
31
  getPrompt: async function (jid) {
14
- if (!jid) return null;
15
- const prompts = await this.getAllPrompts();
16
- return prompts[jid] || null;
32
+ const normalizedJid = normalizePromptJid(jid);
33
+ if (!normalizedJid) return null;
34
+ const row = await findById(AI_PROMPTS_TABLE, normalizedJid);
35
+ if (!row) return null;
36
+ return normalizePromptValue(row.prompt);
17
37
  },
18
38
 
19
39
  setPrompt: async function (jid, prompt) {
20
- if (!jid) return null;
21
- const prompts = await this.getAllPrompts();
22
- prompts[jid] = prompt;
23
- await groupConfigStore.updateGroupConfig(PROMPT_CONFIG_ID, { prompts });
24
- return prompt;
40
+ const normalizedJid = normalizePromptJid(jid);
41
+ if (!normalizedJid) return null;
42
+ const normalizedPrompt = normalizePromptValue(prompt);
43
+ await upsert(AI_PROMPTS_TABLE, { id: normalizedJid, prompt: normalizedPrompt });
44
+ return normalizedPrompt;
25
45
  },
26
46
 
27
47
  clearPrompt: async function (jid) {
28
- if (!jid) return null;
29
- const prompts = await this.getAllPrompts();
30
- if (prompts[jid]) {
31
- delete prompts[jid];
32
- await groupConfigStore.updateGroupConfig(PROMPT_CONFIG_ID, { prompts });
33
- }
48
+ const normalizedJid = normalizePromptJid(jid);
49
+ if (!normalizedJid) return null;
50
+ await remove(AI_PROMPTS_TABLE, normalizedJid);
34
51
  return true;
35
52
  },
36
53
  };
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  const DEFAULT_SESSION_TTL_MS = 15 * 60 * 1000;
2
3
  const DEFAULT_HISTORY_LIMIT = 8;
3
4
 
@@ -22,7 +23,7 @@ const buildSessionKey = ({ chatId, userId, scope = 'private' } = {}) => {
22
23
  return `${safeScope}:${safeChatId}:${safeUserId}`;
23
24
  };
24
25
 
25
- const pruneExpiredSessions = (nowMs = Date.now()) => {
26
+ const pruneExpiredSessions = (nowMs = __timeNowMs()) => {
26
27
  for (const [key, session] of sessions.entries()) {
27
28
  if (!session || !Number.isFinite(session.expiresAt) || session.expiresAt <= nowMs) {
28
29
  sessions.delete(key);
@@ -47,7 +48,7 @@ const getOrCreateSession = ({ chatId, userId, scope = 'private', ttlMs }) => {
47
48
  const key = buildSessionKey({ chatId, userId, scope });
48
49
  if (!key) return null;
49
50
 
50
- const nowMs = Date.now();
51
+ const nowMs = __timeNowMs();
51
52
  const safeTtlMs = toFinitePositiveInt(ttlMs, DEFAULT_SESSION_TTL_MS, 1_000);
52
53
  const existing = sessions.get(key);
53
54
 
@@ -90,7 +91,7 @@ export const appendConversationSessionMessage = ({ chatId, userId, scope = 'priv
90
91
  role: safeRole,
91
92
  text: safeText,
92
93
  metadata: metadata && typeof metadata === 'object' ? { ...metadata } : null,
93
- createdAt: new Date().toISOString(),
94
+ createdAt: __timeNowIso(),
94
95
  });
95
96
  const maxHistory = toFinitePositiveInt(historyLimit, DEFAULT_HISTORY_LIMIT, 1);
96
97
  if (session.history.length > maxHistory) {
@@ -98,7 +99,7 @@ export const appendConversationSessionMessage = ({ chatId, userId, scope = 'priv
98
99
  }
99
100
  }
100
101
 
101
- session.updatedAt = new Date().toISOString();
102
+ session.updatedAt = __timeNowIso();
102
103
  return cloneSession(session);
103
104
  };
104
105
 
@@ -110,10 +111,10 @@ export const setConversationSessionIntent = ({ chatId, userId, scope = 'private'
110
111
  intent && typeof intent === 'object'
111
112
  ? {
112
113
  ...intent,
113
- updatedAt: new Date().toISOString(),
114
+ updatedAt: __timeNowIso(),
114
115
  }
115
116
  : null;
116
- session.updatedAt = new Date().toISOString();
117
+ session.updatedAt = __timeNowIso();
117
118
  return cloneSession(session);
118
119
  };
119
120
 
@@ -1,6 +1,31 @@
1
1
  import logger from '#logger';
2
+ import { isGroupJid, normalizeJid } from '../config/index.js';
2
3
  import { findById, upsert } from '../../database/index.js';
3
4
 
5
+ const SYSTEM_CONFIG_PREFIX = 'system:';
6
+
7
+ const normalizeGroupConfigId = (groupId) => {
8
+ const raw = String(groupId || '').trim();
9
+ if (!raw) return '';
10
+ return normalizeJid(raw) || raw;
11
+ };
12
+
13
+ const isReservedSystemConfigId = (groupId) => groupId.startsWith(SYSTEM_CONFIG_PREFIX);
14
+
15
+ const assertWritableGroupConfigId = (groupId) => {
16
+ if (!groupId) {
17
+ throw new Error('O identificador do grupo é obrigatório para persistir configurações.');
18
+ }
19
+
20
+ if (isReservedSystemConfigId(groupId)) {
21
+ throw new Error(`O id ${groupId} é reservado para configurações de sistema e não pode ser salvo em group_configs.`);
22
+ }
23
+
24
+ if (!isGroupJid(groupId)) {
25
+ throw new Error(`O id ${groupId} não representa um grupo válido para group_configs.`);
26
+ }
27
+ };
28
+
4
29
  const groupConfigStore = {
5
30
  /**
6
31
  * Recupera a configuracao de um grupo especifico.
@@ -8,8 +33,17 @@ const groupConfigStore = {
8
33
  * @returns {object} A configuracao do grupo, ou um objeto vazio se nao encontrado.
9
34
  */
10
35
  getGroupConfig: async function (groupId) {
36
+ const normalizedGroupId = normalizeGroupConfigId(groupId);
37
+ if (!normalizedGroupId || !isGroupJid(normalizedGroupId)) {
38
+ return {};
39
+ }
40
+ if (isReservedSystemConfigId(normalizedGroupId)) {
41
+ logger.warn('Tentativa bloqueada de leitura de configuração reservada em group_configs.', { groupId: normalizedGroupId });
42
+ return {};
43
+ }
44
+
11
45
  try {
12
- const record = await findById('group_configs', groupId);
46
+ const record = await findById('group_configs', normalizedGroupId);
13
47
  if (!record || record.config === null || record.config === undefined) {
14
48
  return {};
15
49
  }
@@ -23,7 +57,7 @@ const groupConfigStore = {
23
57
  } catch (error) {
24
58
  logger.error('Error loading group configuration from DB:', {
25
59
  error: error.message,
26
- groupId,
60
+ groupId: normalizedGroupId,
27
61
  });
28
62
  return {};
29
63
  }
@@ -37,18 +71,20 @@ const groupConfigStore = {
37
71
  * @param {string} [newConfig.farewellMedia] - Caminho opcional para midia de despedida.
38
72
  */
39
73
  updateGroupConfig: async function (groupId, newConfig) {
40
- const currentConfig = await this.getGroupConfig(groupId);
74
+ const normalizedGroupId = normalizeGroupConfigId(groupId);
75
+ assertWritableGroupConfigId(normalizedGroupId);
76
+ const currentConfig = await this.getGroupConfig(normalizedGroupId);
41
77
  const updatedConfig = { ...currentConfig, ...newConfig };
42
78
  try {
43
79
  await upsert('group_configs', {
44
- id: groupId,
80
+ id: normalizedGroupId,
45
81
  config: JSON.stringify(updatedConfig),
46
82
  });
47
83
  return updatedConfig;
48
84
  } catch (error) {
49
85
  logger.error('Error updating group configuration in DB:', {
50
86
  error: error.message,
51
- groupId,
87
+ groupId: normalizedGroupId,
52
88
  });
53
89
  throw error;
54
90
  }
@@ -1,7 +1,10 @@
1
- import groupConfigStore from './groupConfigStore.js';
1
+ import { TABLES, executeQuery, withTransaction } from '../../database/index.js';
2
2
  import { isSameJidUser, normalizeJid } from '../config/index.js';
3
3
 
4
- const PREMIUM_CONFIG_ID = 'system:premium_users';
4
+ const PREMIUM_USERS_TABLE = TABLES.SYSTEM_PREMIUM_USERS;
5
+ const SELECT_PREMIUM_USERS_SQL = `SELECT id FROM \`${PREMIUM_USERS_TABLE}\` ORDER BY id ASC`;
6
+ const DELETE_ALL_PREMIUM_USERS_SQL = `DELETE FROM \`${PREMIUM_USERS_TABLE}\``;
7
+ const INSERT_PREMIUM_USER_SQL = `INSERT INTO \`${PREMIUM_USERS_TABLE}\` (id) VALUES (?)`;
5
8
 
6
9
  const normalizePremiumEntry = (value) => {
7
10
  const raw = String(value || '').trim();
@@ -23,22 +26,33 @@ const normalizeList = (list) => {
23
26
  return normalizedList;
24
27
  };
25
28
 
29
+ const loadPremiumUsersFromDb = async () => {
30
+ const rows = await executeQuery(SELECT_PREMIUM_USERS_SQL);
31
+ return normalizeList(rows.map((row) => row.id));
32
+ };
33
+
26
34
  const premiumUserStore = {
27
35
  getPremiumUsers: async function () {
28
- const config = await groupConfigStore.getGroupConfig(PREMIUM_CONFIG_ID);
29
- return normalizeList(config.premiumUsers);
36
+ return loadPremiumUsersFromDb();
30
37
  },
31
38
 
32
39
  setPremiumUsers: async function (premiumUsers) {
33
40
  const normalized = normalizeList(premiumUsers);
34
- await groupConfigStore.updateGroupConfig(PREMIUM_CONFIG_ID, { premiumUsers: normalized });
41
+
42
+ await withTransaction(async (connection) => {
43
+ await executeQuery(DELETE_ALL_PREMIUM_USERS_SQL, [], connection);
44
+ for (const premiumJid of normalized) {
45
+ await executeQuery(INSERT_PREMIUM_USER_SQL, [premiumJid], connection);
46
+ }
47
+ });
48
+
35
49
  return normalized;
36
50
  },
37
51
 
38
52
  addPremiumUsers: async function (usersToAdd) {
39
53
  const current = await this.getPremiumUsers();
40
54
  const updated = normalizeList([...current, ...usersToAdd]);
41
- await groupConfigStore.updateGroupConfig(PREMIUM_CONFIG_ID, { premiumUsers: updated });
55
+ await this.setPremiumUsers(updated);
42
56
  return updated;
43
57
  },
44
58
 
@@ -46,7 +60,7 @@ const premiumUserStore = {
46
60
  const current = await this.getPremiumUsers();
47
61
  const normalizedTargets = normalizeList(usersToRemove);
48
62
  const updated = current.filter((jid) => !normalizedTargets.some((target) => target === jid || isSameJidUser(target, jid)));
49
- await groupConfigStore.updateGroupConfig(PREMIUM_CONFIG_ID, { premiumUsers: updated });
63
+ await this.setPremiumUsers(updated);
50
64
  return updated;
51
65
  },
52
66
  };