@kaikybrofc/omnizap-system 2.2.10 → 2.3.1

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 (123) hide show
  1. package/README.md +13 -13
  2. package/app/config/adminIdentity.js +1 -3
  3. package/app/connection/socketController.js +10 -20
  4. package/app/controllers/messageController.js +7 -28
  5. package/app/modules/aiModule/catCommand.js +29 -192
  6. package/app/modules/broadcastModule/noticeCommand.js +28 -97
  7. package/app/modules/gameModule/diceCommand.js +6 -32
  8. package/app/modules/playModule/playCommand.js +57 -258
  9. package/app/modules/quoteModule/quoteCommand.js +2 -4
  10. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1 -13
  11. package/app/modules/statsModule/noMessageCommand.js +16 -84
  12. package/app/modules/statsModule/rankingCommand.js +5 -25
  13. package/app/modules/statsModule/rankingCommon.js +1 -9
  14. package/app/modules/stickerModule/convertToWebp.js +4 -27
  15. package/app/modules/stickerModule/stickerCommand.js +13 -24
  16. package/app/modules/stickerModule/stickerTextCommand.js +13 -25
  17. package/app/modules/stickerPackModule/autoPackCollectorService.js +16 -7
  18. package/app/modules/stickerPackModule/domainEventOutboxRepository.js +20 -36
  19. package/app/modules/stickerPackModule/domainEvents.js +2 -11
  20. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +13 -50
  21. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +2 -15
  22. package/app/modules/stickerPackModule/semanticThemeClusterService.js +14 -41
  23. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +25 -95
  24. package/app/modules/stickerPackModule/stickerAssetRepository.js +12 -31
  25. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +13 -18
  26. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +284 -709
  27. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +27 -106
  28. package/app/modules/stickerPackModule/stickerClassificationService.js +46 -77
  29. package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +13 -53
  30. package/app/modules/stickerPackModule/stickerDomainEventBus.js +10 -16
  31. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +40 -39
  32. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +1 -4
  33. package/app/modules/stickerPackModule/stickerObjectStorageService.js +26 -26
  34. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +32 -187
  35. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +6 -15
  36. package/app/modules/stickerPackModule/stickerPackItemRepository.js +6 -32
  37. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +12 -36
  38. package/app/modules/stickerPackModule/stickerPackMessageService.js +12 -40
  39. package/app/modules/stickerPackModule/stickerPackRepository.js +23 -66
  40. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +9 -21
  41. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +10 -40
  42. package/app/modules/stickerPackModule/stickerPackService.js +50 -115
  43. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +2 -21
  44. package/app/modules/stickerPackModule/stickerPackUtils.js +13 -3
  45. package/app/modules/stickerPackModule/stickerStorageService.js +16 -65
  46. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +4 -22
  47. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +14 -29
  48. package/app/modules/systemMetricsModule/pingCommand.js +9 -39
  49. package/app/modules/tiktokModule/tiktokCommand.js +17 -109
  50. package/app/modules/userModule/userCommand.js +2 -88
  51. package/app/observability/metrics.js +5 -16
  52. package/app/services/captchaService.js +1 -6
  53. package/app/services/dbWriteQueue.js +3 -18
  54. package/app/services/featureFlagService.js +2 -8
  55. package/app/services/newsBroadcastService.js +0 -1
  56. package/app/services/queueUtils.js +2 -4
  57. package/app/services/whatsappLoginLinkService.js +7 -9
  58. package/app/store/premiumUserStore.js +1 -2
  59. package/app/utils/antiLink/antiLinkModule.js +3 -233
  60. package/app/utils/logger/loggerModule.js +9 -34
  61. package/app/utils/systemMetrics/systemMetricsModule.js +1 -4
  62. package/database/index.js +1 -0
  63. package/database/init.js +1 -8
  64. package/database/migrations/20260228_0027_web_visit_event.sql +15 -0
  65. package/docker-compose.yml +27 -27
  66. package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +26 -0
  67. package/docs/seo/satellite-page-template.md +2 -0
  68. package/docs/seo/satellite-pages-phase1.json +40 -177
  69. package/eslint.config.js +2 -15
  70. package/index.js +8 -36
  71. package/ml/clip_classifier/README.md +4 -6
  72. package/observability/alert-rules.yml +12 -12
  73. package/observability/grafana/provisioning/dashboards/dashboards.yml +1 -1
  74. package/package.json +6 -3
  75. package/public/api-docs/index.html +220 -193
  76. package/public/bot-whatsapp-para-grupo/index.html +291 -261
  77. package/public/bot-whatsapp-sem-programar/index.html +291 -261
  78. package/public/comandos/index.html +421 -406
  79. package/public/como-automatizar-avisos-no-whatsapp/index.html +291 -261
  80. package/public/como-criar-comandos-whatsapp/index.html +291 -261
  81. package/public/como-evitar-spam-no-whatsapp/index.html +291 -261
  82. package/public/como-moderar-grupo-whatsapp/index.html +291 -261
  83. package/public/como-organizar-comunidade-whatsapp/index.html +291 -261
  84. package/public/css/github-project-panel.css +13 -8
  85. package/public/css/stickers-admin.css +25 -9
  86. package/public/css/styles.css +23 -16
  87. package/public/index.html +1106 -993
  88. package/public/js/apps/apiDocsApp.js +17 -167
  89. package/public/js/apps/createPackApp.js +69 -332
  90. package/public/js/apps/homeApp.js +274 -101
  91. package/public/js/apps/loginApp.js +3 -12
  92. package/public/js/apps/stickersAdminApp.js +190 -181
  93. package/public/js/apps/stickersApp.js +482 -1411
  94. package/public/js/apps/userApp.js +217 -1
  95. package/public/js/catalog.js +11 -74
  96. package/public/js/github-panel/components/ErrorState.js +1 -8
  97. package/public/js/github-panel/components/GithubProjectPanel.js +2 -9
  98. package/public/js/github-panel/components/SkeletonPanel.js +1 -11
  99. package/public/js/github-panel/components/StatCard.js +1 -7
  100. package/public/js/github-panel/vendor/react.js +1 -9
  101. package/public/js/runtime/react-runtime.js +1 -9
  102. package/public/licenca/index.html +200 -86
  103. package/public/login/index.html +315 -325
  104. package/public/melhor-bot-whatsapp-para-grupos/index.html +291 -261
  105. package/public/stickers/admin/index.html +14 -19
  106. package/public/stickers/create/index.html +39 -44
  107. package/public/stickers/index.html +96 -107
  108. package/public/termos-de-uso/index.html +369 -122
  109. package/public/user/index.html +527 -350
  110. package/scripts/cache-bust.mjs +5 -24
  111. package/scripts/generate-seo-satellite-pages.mjs +10 -13
  112. package/scripts/run-prettier-all.mjs +25 -0
  113. package/scripts/sticker-catalog-loadtest.mjs +13 -11
  114. package/scripts/sticker-worker-task.mjs +1 -4
  115. package/scripts/sync-readme-snapshot.mjs +3 -2
  116. package/server/auth/googleWebAuth/googleWebAuthService.js +614 -0
  117. package/server/controllers/stickerCatalogController.js +297 -632
  118. package/server/http/httpServer.js +2 -10
  119. package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +1 -8
  120. package/server/routes/stickerCatalog/catalogHandlers/catalogAuthHttp.js +1 -9
  121. package/server/routes/stickerCatalog/catalogHandlers/catalogPublicHttp.js +10 -11
  122. package/server/routes/stickerCatalog/catalogHandlers/catalogUploadHttp.js +1 -10
  123. package/server/routes/stickerCatalog/catalogRouter.js +11 -13
package/README.md CHANGED
@@ -53,27 +53,27 @@ Atualização em cache: **30 minutos** por padrão (`README_SUMMARY_CACHE_SECOND
53
53
  <!-- README_SNAPSHOT:START -->
54
54
  ### Snapshot do Sistema
55
55
 
56
- > Atualizado em `2026-02-28T10:31:26.715Z` | cache `1800s`
56
+ > Atualizado em `2026-02-28T21:56:21.021Z` | cache `1800s`
57
57
 
58
58
  | Métrica | Valor |
59
59
  | --- | ---: |
60
- | Usuários (lid_map) | 5.505 |
60
+ | Usuários (lid_map) | 5.515 |
61
61
  | Grupos | 116 |
62
- | Packs | 295 |
63
- | Stickers | 6.811 |
64
- | Mensagens registradas | 441.144 |
62
+ | Packs | 301 |
63
+ | Stickers | 7.170 |
64
+ | Mensagens registradas | 444.453 |
65
65
 
66
66
  #### Tipos de mensagem mais usados (amostra: 25.000)
67
67
  | Tipo | Total |
68
68
  | --- | ---: |
69
- | `texto` | 16.177 |
70
- | `figurinha` | 4.807 |
71
- | `reacao` | 1.502 |
72
- | `imagem` | 1.300 |
73
- | `outros` | 769 |
74
- | `video` | 227 |
75
- | `audio` | 213 |
76
- | `documento` | 5 |
69
+ | `texto` | 16.074 |
70
+ | `figurinha` | 4.780 |
71
+ | `reacao` | 1.546 |
72
+ | `imagem` | 1.339 |
73
+ | `outros` | 850 |
74
+ | `video` | 215 |
75
+ | `audio` | 190 |
76
+ | `documento` | 6 |
77
77
 
78
78
  <details><summary>Comandos disponíveis (62)</summary>
79
79
 
@@ -69,9 +69,7 @@ export const isAdminSenderAsync = async (senderIdentity) => {
69
69
  const normalizedCachedSender = normalizeJid(cachedSender || '');
70
70
  const resolvedSender = await resolveUserId(senderInfo).catch(() => null);
71
71
  const normalizedResolvedSender = normalizeJid(resolvedSender || '');
72
- const senderCandidates = new Set(
73
- [normalizedSender, normalizedCachedSender, normalizedResolvedSender].filter(Boolean),
74
- );
72
+ const senderCandidates = new Set([normalizedSender, normalizedCachedSender, normalizedResolvedSender].filter(Boolean));
75
73
  if (!senderCandidates.size) return false;
76
74
 
77
75
  const adminJid = (await resolveAdminJid()) || getAdminJid();
@@ -42,16 +42,14 @@ const parseEnvInt = (value, fallback, min, max) => {
42
42
  return Math.max(min, Math.min(max, Math.floor(parsed)));
43
43
  };
44
44
 
45
- const IS_PRODUCTION = String(process.env.NODE_ENV || '').trim().toLowerCase() === 'production';
45
+ const IS_PRODUCTION =
46
+ String(process.env.NODE_ENV || '')
47
+ .trim()
48
+ .toLowerCase() === 'production';
46
49
  const MSG_RETRY_CACHE_TTL_SECONDS = parseEnvInt(process.env.BAILEYS_MSG_RETRY_CACHE_TTL_SECONDS, 600, 60, 24 * 60 * 60);
47
50
  const MSG_RETRY_CACHE_CHECKPERIOD_SECONDS = parseEnvInt(process.env.BAILEYS_MSG_RETRY_CACHE_CHECKPERIOD_SECONDS, 120, 30, 3600);
48
51
  const BAILEYS_EVENT_LOG_ENABLED = parseEnvBool(process.env.BAILEYS_EVENT_LOG_ENABLED, !IS_PRODUCTION);
49
- const BAILEYS_RECONNECT_ATTEMPT_RESET_MS = parseEnvInt(
50
- process.env.BAILEYS_RECONNECT_ATTEMPT_RESET_MS,
51
- 10 * 60 * 1000,
52
- 60 * 1000,
53
- 24 * 60 * 60 * 1000,
54
- );
52
+ const BAILEYS_RECONNECT_ATTEMPT_RESET_MS = parseEnvInt(process.env.BAILEYS_RECONNECT_ATTEMPT_RESET_MS, 10 * 60 * 1000, 60 * 1000, 24 * 60 * 60 * 1000);
55
53
  const GROUP_SYNC_ON_CONNECT = parseEnvBool(process.env.GROUP_SYNC_ON_CONNECT, true);
56
54
  const GROUP_SYNC_TIMEOUT_MS = parseEnvInt(process.env.GROUP_SYNC_TIMEOUT_MS, 30 * 1000, 5 * 1000, 120 * 1000);
57
55
  const GROUP_SYNC_MAX_GROUPS = parseEnvInt(process.env.GROUP_SYNC_MAX_GROUPS, 0, 0, 10_000);
@@ -193,9 +191,7 @@ const shouldLogBaileysEvent = (eventName) => {
193
191
  };
194
192
 
195
193
  const registerBaileysEventLoggers = (sock) => {
196
- const eventsToLog = BAILEYS_EVENT_NAMES.filter(
197
- (eventName) => !BAILEYS_EVENTS_WITH_INTERNAL_LOG.has(eventName) && shouldLogBaileysEvent(eventName),
198
- );
194
+ const eventsToLog = BAILEYS_EVENT_NAMES.filter((eventName) => !BAILEYS_EVENTS_WITH_INTERNAL_LOG.has(eventName) && shouldLogBaileysEvent(eventName));
199
195
 
200
196
  for (const eventName of eventsToLog) {
201
197
  sock.ev.on(eventName, (payload) => {
@@ -388,16 +384,9 @@ const syncGroupsOnConnectionOpen = async (sock) => {
388
384
  return;
389
385
  }
390
386
 
391
- const allGroups = await withTimeout(
392
- sock.groupFetchAllParticipating(),
393
- GROUP_SYNC_TIMEOUT_MS,
394
- `groups_sync_timeout_${GROUP_SYNC_TIMEOUT_MS}ms`,
395
- );
387
+ const allGroups = await withTimeout(sock.groupFetchAllParticipating(), GROUP_SYNC_TIMEOUT_MS, `groups_sync_timeout_${GROUP_SYNC_TIMEOUT_MS}ms`);
396
388
  const allGroupEntries = Object.values(allGroups || {});
397
- const selectedGroups =
398
- GROUP_SYNC_MAX_GROUPS > 0
399
- ? allGroupEntries.slice(0, GROUP_SYNC_MAX_GROUPS)
400
- : allGroupEntries;
389
+ const selectedGroups = GROUP_SYNC_MAX_GROUPS > 0 ? allGroupEntries.slice(0, GROUP_SYNC_MAX_GROUPS) : allGroupEntries;
401
390
 
402
391
  let syncedCount = 0;
403
392
  let failedCount = 0;
@@ -408,7 +397,8 @@ const syncGroupsOnConnectionOpen = async (sock) => {
408
397
  batch.map((group) =>
409
398
  upsertGroupMetadata(group.id, buildGroupMetadataFromGroup(group), {
410
399
  mergeExisting: false,
411
- })),
400
+ }),
401
+ ),
412
402
  );
413
403
  for (const result of results) {
414
404
  if (result.status === 'fulfilled') syncedCount += 1;
@@ -30,9 +30,10 @@ import { buildWhatsAppGoogleLoginUrl } from '../services/whatsappLoginLinkServic
30
30
 
31
31
  const DEFAULT_COMMAND_PREFIX = process.env.COMMAND_PREFIX || '/';
32
32
  const COMMAND_REACT_EMOJI = process.env.COMMAND_REACT_EMOJI || '🤖';
33
- const START_LOGIN_TRIGGER = String(process.env.WHATSAPP_LOGIN_TRIGGER || 'iniciar')
34
- .trim()
35
- .toLowerCase() || 'iniciar';
33
+ const START_LOGIN_TRIGGER =
34
+ String(process.env.WHATSAPP_LOGIN_TRIGGER || 'iniciar')
35
+ .trim()
36
+ .toLowerCase() || 'iniciar';
36
37
  const WHATSAPP_USER_SERVERS = new Set(['s.whatsapp.net', 'c.us', 'hosted']);
37
38
  const WHATSAPP_LID_SERVERS = new Set(['lid', 'hosted.lid']);
38
39
 
@@ -61,17 +62,7 @@ const resolveCanonicalWhatsAppJid = (...candidates) => {
61
62
  return '';
62
63
  };
63
64
 
64
- const maybeHandleStartLoginMessage = async ({
65
- sock,
66
- messageInfo,
67
- extractedText,
68
- senderName,
69
- senderJid,
70
- remoteJid,
71
- expirationMessage,
72
- isMessageFromBot,
73
- isGroupMessage,
74
- }) => {
65
+ const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText, senderName, senderJid, remoteJid, expirationMessage, isMessageFromBot, isGroupMessage }) => {
75
66
  if (isMessageFromBot || !isStartLoginTrigger(extractedText)) return false;
76
67
 
77
68
  if (isGroupMessage) {
@@ -88,15 +79,7 @@ const maybeHandleStartLoginMessage = async ({
88
79
 
89
80
  const key = messageInfo?.key || {};
90
81
  const senderInfo = extractSenderInfoFromMessage(messageInfo);
91
- let canonicalUserId = resolveCanonicalWhatsAppJid(
92
- senderInfo?.jid,
93
- senderInfo?.lid,
94
- senderInfo?.participantAlt,
95
- key.participantAlt,
96
- key.participant,
97
- key.remoteJid,
98
- senderJid,
99
- );
82
+ let canonicalUserId = resolveCanonicalWhatsAppJid(senderInfo?.jid, senderInfo?.lid, senderInfo?.participantAlt, key.participantAlt, key.participant, key.remoteJid, senderJid);
100
83
  try {
101
84
  const resolvedUserId = await resolveUserId(senderInfo);
102
85
  canonicalUserId = resolveCanonicalWhatsAppJid(resolvedUserId, canonicalUserId, senderInfo?.jid, senderInfo?.lid);
@@ -133,11 +116,7 @@ const maybeHandleStartLoginMessage = async ({
133
116
  sock,
134
117
  remoteJid,
135
118
  {
136
- text:
137
- `${greeting}\n\n` +
138
- 'Para continuar no OmniZap, faca login com Google neste link:\n' +
139
- `${loginUrl}\n\n` +
140
- 'Seu numero do WhatsApp sera vinculado automaticamente a conta logada.',
119
+ text: `${greeting}\n\n` + 'Para continuar no OmniZap, faca login com Google neste link:\n' + `${loginUrl}\n\n` + 'Seu numero do WhatsApp sera vinculado automaticamente a conta logada.',
141
120
  },
142
121
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
143
122
  );
@@ -110,17 +110,12 @@ const TTS_OUTPUT_FORMAT = OPENAI_TTS_PTT ? 'opus' : SAFE_TTS_FORMAT;
110
110
  const TTS_MIME_TYPE = AUDIO_MIME_BY_FORMAT[TTS_OUTPUT_FORMAT] || 'audio/mpeg';
111
111
  const TTS_MAX_CHARS = Number.isFinite(OPENAI_TTS_MAX_CHARS) && OPENAI_TTS_MAX_CHARS > 0 ? OPENAI_TTS_MAX_CHARS : 4096;
112
112
  const OPENAI_TIMEOUT = Number.isFinite(OPENAI_TIMEOUT_MS) && OPENAI_TIMEOUT_MS > 0 ? OPENAI_TIMEOUT_MS : 30000;
113
- const OPENAI_IMAGE_TIMEOUT =
114
- Number.isFinite(OPENAI_IMAGE_TIMEOUT_MS) && OPENAI_IMAGE_TIMEOUT_MS > 0 ? OPENAI_IMAGE_TIMEOUT_MS : 120000;
113
+ const OPENAI_IMAGE_TIMEOUT = Number.isFinite(OPENAI_IMAGE_TIMEOUT_MS) && OPENAI_IMAGE_TIMEOUT_MS > 0 ? OPENAI_IMAGE_TIMEOUT_MS : 120000;
115
114
  const OPENAI_CLIENT_TIMEOUT = Math.max(OPENAI_TIMEOUT, OPENAI_IMAGE_TIMEOUT);
116
115
  const OPENAI_RETRIES = Number.isFinite(OPENAI_MAX_RETRIES) && OPENAI_MAX_RETRIES >= 0 ? OPENAI_MAX_RETRIES : 2;
117
- const OPENAI_RETRY_BASE =
118
- Number.isFinite(OPENAI_RETRY_BASE_MS) && OPENAI_RETRY_BASE_MS > 0 ? OPENAI_RETRY_BASE_MS : 500;
116
+ const OPENAI_RETRY_BASE = Number.isFinite(OPENAI_RETRY_BASE_MS) && OPENAI_RETRY_BASE_MS > 0 ? OPENAI_RETRY_BASE_MS : 500;
119
117
  const OPENAI_RETRY_MAX = Number.isFinite(OPENAI_RETRY_MAX_MS) && OPENAI_RETRY_MAX_MS > 0 ? OPENAI_RETRY_MAX_MS : 4000;
120
- const MAX_IMAGE_BYTES =
121
- Number.isFinite(OPENAI_MAX_IMAGE_MB) && OPENAI_MAX_IMAGE_MB > 0
122
- ? OPENAI_MAX_IMAGE_MB * 1024 * 1024
123
- : 50 * 1024 * 1024;
118
+ const MAX_IMAGE_BYTES = Number.isFinite(OPENAI_MAX_IMAGE_MB) && OPENAI_MAX_IMAGE_MB > 0 ? OPENAI_MAX_IMAGE_MB * 1024 * 1024 : 50 * 1024 * 1024;
124
119
 
125
120
  const getClient = () => {
126
121
  if (cachedClient) return cachedClient;
@@ -204,21 +199,7 @@ const sendUsage = async (sock, remoteJid, messageInfo, expirationMessage, comman
204
199
  sock,
205
200
  remoteJid,
206
201
  {
207
- text: [
208
- '🤖 *Comando CAT*',
209
- '',
210
- 'Use assim:',
211
- `*${commandPrefix}cat* [--audio] sua pergunta`,
212
- `*${commandPrefix}cat* (responda ou envie uma imagem com legenda)`,
213
- '',
214
- 'Opções:',
215
- '--audio | --texto',
216
- '--detail low | high | auto',
217
- '',
218
- 'Exemplo:',
219
- `*${commandPrefix}cat* Explique como funciona a fotossíntese.`,
220
- `*${commandPrefix}cat* --audio Resuma a imagem.`,
221
- ].join('\n'),
202
+ text: ['🤖 *Comando CAT*', '', 'Use assim:', `*${commandPrefix}cat* [--audio] sua pergunta`, `*${commandPrefix}cat* (responda ou envie uma imagem com legenda)`, '', 'Opções:', '--audio | --texto', '--detail low | high | auto', '', 'Exemplo:', `*${commandPrefix}cat* Explique como funciona a fotossíntese.`, `*${commandPrefix}cat* --audio Resuma a imagem.`].join('\n'),
222
203
  },
223
204
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
224
205
  );
@@ -253,70 +234,29 @@ const sendPremiumOnly = async (sock, remoteJid, messageInfo, expirationMessage)
253
234
  sock,
254
235
  remoteJid,
255
236
  {
256
- text: [
257
- '⭐ *Comando Premium*',
258
- '',
259
- 'Este comando é exclusivo para usuários premium.',
260
- 'Fale com o administrador para liberar o acesso.',
261
- ].join('\n'),
237
+ text: ['⭐ *Comando Premium*', '', 'Este comando é exclusivo para usuários premium.', 'Fale com o administrador para liberar o acesso.'].join('\n'),
262
238
  },
263
239
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
264
240
  );
265
241
  };
266
242
 
267
- const sendPromptUsage = async (
268
- sock,
269
- remoteJid,
270
- messageInfo,
271
- expirationMessage,
272
- commandPrefix = DEFAULT_COMMAND_PREFIX,
273
- ) => {
243
+ const sendPromptUsage = async (sock, remoteJid, messageInfo, expirationMessage, commandPrefix = DEFAULT_COMMAND_PREFIX) => {
274
244
  await sendAndStore(
275
245
  sock,
276
246
  remoteJid,
277
247
  {
278
- text: [
279
- '🧠 *Prompt da IA*',
280
- '',
281
- 'Use assim:',
282
- `*${commandPrefix}catprompt* seu novo prompt`,
283
- '',
284
- 'Para voltar ao padrão:',
285
- `*${commandPrefix}catprompt reset*`,
286
- ].join('\n'),
248
+ text: ['🧠 *Prompt da IA*', '', 'Use assim:', `*${commandPrefix}catprompt* seu novo prompt`, '', 'Para voltar ao padrão:', `*${commandPrefix}catprompt reset*`].join('\n'),
287
249
  },
288
250
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
289
251
  );
290
252
  };
291
253
 
292
- const sendImageUsage = async (
293
- sock,
294
- remoteJid,
295
- messageInfo,
296
- expirationMessage,
297
- commandPrefix = DEFAULT_COMMAND_PREFIX,
298
- ) => {
254
+ const sendImageUsage = async (sock, remoteJid, messageInfo, expirationMessage, commandPrefix = DEFAULT_COMMAND_PREFIX) => {
299
255
  await sendAndStore(
300
256
  sock,
301
257
  remoteJid,
302
258
  {
303
- text: [
304
- '🖼️ *Imagem IA*',
305
- '',
306
- 'Use assim:',
307
- `*${commandPrefix}catimg* seu prompt`,
308
- `*${commandPrefix}catimg* (responda uma imagem com legenda para editar)`,
309
- '',
310
- 'Opções:',
311
- '--size 1024x1024 | 1024x1536 | 1536x1024 | auto',
312
- '--quality low | medium | high | auto',
313
- '--format png | jpeg | webp',
314
- '--background transparent | opaque | auto',
315
- '--compression 0-100',
316
- '',
317
- 'Exemplo:',
318
- `*${commandPrefix}catimg* --size 1536x1024 Um gato astronauta em aquarela.`,
319
- ].join('\n'),
259
+ text: ['🖼️ *Imagem IA*', '', 'Use assim:', `*${commandPrefix}catimg* seu prompt`, `*${commandPrefix}catimg* (responda uma imagem com legenda para editar)`, '', 'Opções:', '--size 1024x1024 | 1024x1536 | 1536x1024 | auto', '--quality low | medium | high | auto', '--format png | jpeg | webp', '--background transparent | opaque | auto', '--compression 0-100', '', 'Exemplo:', `*${commandPrefix}catimg* --size 1536x1024 Um gato astronauta em aquarela.`].join('\n'),
320
260
  },
321
261
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
322
262
  );
@@ -581,15 +521,7 @@ const buildImageDataUrl = async (imageMedia, senderJid) => {
581
521
  }
582
522
  };
583
523
 
584
- export async function handleCatCommand({
585
- sock,
586
- remoteJid,
587
- messageInfo,
588
- expirationMessage,
589
- senderJid,
590
- text,
591
- commandPrefix = DEFAULT_COMMAND_PREFIX,
592
- }) {
524
+ export async function handleCatCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix = DEFAULT_COMMAND_PREFIX }) {
593
525
  const { prompt: rawPrompt, wantsAudio, imageDetail } = parseCatOptions(text || '');
594
526
 
595
527
  if (!process.env.OPENAI_API_KEY) {
@@ -598,11 +530,7 @@ export async function handleCatCommand({
598
530
  sock,
599
531
  remoteJid,
600
532
  {
601
- text: [
602
- '⚠️ *OpenAI não configurada*',
603
- '',
604
- 'Defina a variável *OPENAI_API_KEY* no `.env` para usar o comando *cat*.',
605
- ].join('\n'),
533
+ text: ['⚠️ *OpenAI não configurada*', '', 'Defina a variável *OPENAI_API_KEY* no `.env` para usar o comando *cat*.'].join('\n'),
606
534
  },
607
535
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
608
536
  );
@@ -632,12 +560,7 @@ export async function handleCatCommand({
632
560
  }
633
561
 
634
562
  if (imageResult.error === 'download_failed') {
635
- await sendAndStore(
636
- sock,
637
- remoteJid,
638
- { text: '⚠️ Não consegui baixar a imagem. Tente reenviar.' },
639
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
640
- );
563
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Não consegui baixar a imagem. Tente reenviar.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
641
564
  return;
642
565
  }
643
566
 
@@ -695,23 +618,13 @@ export async function handleCatCommand({
695
618
  });
696
619
 
697
620
  if (!outputText) {
698
- await sendAndStore(
699
- sock,
700
- remoteJid,
701
- { text: '⚠️ Não consegui gerar uma resposta agora. Tente novamente.' },
702
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
703
- );
621
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Não consegui gerar uma resposta agora. Tente novamente.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
704
622
  return;
705
623
  }
706
624
 
707
625
  if (wantsAudio) {
708
626
  if (outputText.length > TTS_MAX_CHARS) {
709
- await sendAndStore(
710
- sock,
711
- remoteJid,
712
- { text: '⚠️ A resposta ficou longa demais para áudio. Enviando em texto.' },
713
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
714
- );
627
+ await sendAndStore(sock, remoteJid, { text: '⚠️ A resposta ficou longa demais para áudio. Enviando em texto.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
715
628
  } else {
716
629
  try {
717
630
  const audioResponse = await callOpenAI(
@@ -739,22 +652,12 @@ export async function handleCatCommand({
739
652
  return;
740
653
  } catch (audioError) {
741
654
  logger.error('handleCatCommand: erro ao gerar audio.', audioError);
742
- await sendAndStore(
743
- sock,
744
- remoteJid,
745
- { text: '⚠️ Não consegui gerar o áudio agora. Enviando texto.' },
746
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
747
- );
655
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Não consegui gerar o áudio agora. Enviando texto.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
748
656
  }
749
657
  }
750
658
  }
751
659
 
752
- await sendAndStore(
753
- sock,
754
- remoteJid,
755
- { text: `🐈‍⬛ ${outputText}` },
756
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
757
- );
660
+ await sendAndStore(sock, remoteJid, { text: `🐈‍⬛ ${outputText}` }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
758
661
  } catch (error) {
759
662
  logger.error('handleCatCommand: erro ao chamar OpenAI.', error);
760
663
  await sendAndStore(
@@ -768,15 +671,7 @@ export async function handleCatCommand({
768
671
  }
769
672
  }
770
673
 
771
- export async function handleCatImageCommand({
772
- sock,
773
- remoteJid,
774
- messageInfo,
775
- expirationMessage,
776
- senderJid,
777
- text,
778
- commandPrefix = DEFAULT_COMMAND_PREFIX,
779
- }) {
674
+ export async function handleCatImageCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix = DEFAULT_COMMAND_PREFIX }) {
780
675
  const { prompt, toolOptions, errors } = parseImageGenOptions(text || '');
781
676
 
782
677
  if (!process.env.OPENAI_API_KEY) {
@@ -785,11 +680,7 @@ export async function handleCatImageCommand({
785
680
  sock,
786
681
  remoteJid,
787
682
  {
788
- text: [
789
- '⚠️ *OpenAI não configurada*',
790
- '',
791
- 'Defina a variável *OPENAI_API_KEY* no `.env` para usar o comando *catimg*.',
792
- ].join('\n'),
683
+ text: ['⚠️ *OpenAI não configurada*', '', 'Defina a variável *OPENAI_API_KEY* no `.env` para usar o comando *catimg*.'].join('\n'),
793
684
  },
794
685
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
795
686
  );
@@ -819,12 +710,7 @@ export async function handleCatImageCommand({
819
710
  }
820
711
 
821
712
  if (imageResult.error === 'download_failed') {
822
- await sendAndStore(
823
- sock,
824
- remoteJid,
825
- { text: '⚠️ Não consegui baixar a imagem. Tente reenviar.' },
826
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
827
- );
713
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Não consegui baixar a imagem. Tente reenviar.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
828
714
  return;
829
715
  }
830
716
 
@@ -838,12 +724,7 @@ export async function handleCatImageCommand({
838
724
  sock,
839
725
  remoteJid,
840
726
  {
841
- text: [
842
- '⚠️ Opções inválidas no comando.',
843
- `Detalhes: ${errors.join(', ')}`,
844
- '',
845
- `Use *${commandPrefix}catimg* sem opções para ver o formato correto.`,
846
- ].join('\n'),
727
+ text: ['⚠️ Opções inválidas no comando.', `Detalhes: ${errors.join(', ')}`, '', `Use *${commandPrefix}catimg* sem opções para ver o formato correto.`].join('\n'),
847
728
  },
848
729
  { quoted: messageInfo, ephemeralExpiration: expirationMessage },
849
730
  );
@@ -888,11 +769,7 @@ export async function handleCatImageCommand({
888
769
 
889
770
  try {
890
771
  const client = getClient();
891
- const response = await callOpenAI(
892
- () => client.responses.create(payload),
893
- 'responses.create.image',
894
- OPENAI_IMAGE_TIMEOUT,
895
- );
772
+ const response = await callOpenAI(() => client.responses.create(payload), 'responses.create.image', OPENAI_IMAGE_TIMEOUT);
896
773
  const outputText = response.output_text?.trim();
897
774
 
898
775
  sessionCache.set(sessionKey, {
@@ -900,28 +777,16 @@ export async function handleCatImageCommand({
900
777
  updatedAt: Date.now(),
901
778
  });
902
779
 
903
- const imageOutputs = Array.isArray(response.output)
904
- ? response.output.filter((output) => output.type === 'image_generation_call' && output.result)
905
- : [];
780
+ const imageOutputs = Array.isArray(response.output) ? response.output.filter((output) => output.type === 'image_generation_call' && output.result) : [];
906
781
  const imageBase64 = imageOutputs[0]?.result;
907
782
 
908
783
  if (!imageBase64) {
909
784
  if (outputText) {
910
- await sendAndStore(
911
- sock,
912
- remoteJid,
913
- { text: `🖼️ ${outputText}` },
914
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
915
- );
785
+ await sendAndStore(sock, remoteJid, { text: `🖼️ ${outputText}` }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
916
786
  return;
917
787
  }
918
788
 
919
- await sendAndStore(
920
- sock,
921
- remoteJid,
922
- { text: '⚠️ Não consegui gerar a imagem agora. Tente novamente.' },
923
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
924
- );
789
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Não consegui gerar a imagem agora. Tente novamente.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
925
790
  return;
926
791
  }
927
792
 
@@ -935,12 +800,7 @@ export async function handleCatImageCommand({
935
800
  const imageBuffer = Buffer.from(imageBase64, 'base64');
936
801
  const caption = outputText ? `🖼️ ${outputText}` : '🖼️ Imagem gerada.';
937
802
 
938
- await sendAndStore(
939
- sock,
940
- remoteJid,
941
- { image: imageBuffer, caption, mimetype },
942
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
943
- );
803
+ await sendAndStore(sock, remoteJid, { image: imageBuffer, caption, mimetype }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
944
804
  } catch (error) {
945
805
  logger.error('handleCatImageCommand: erro ao chamar OpenAI.', error);
946
806
  await sendAndStore(
@@ -954,15 +814,7 @@ export async function handleCatImageCommand({
954
814
  }
955
815
  }
956
816
 
957
- export async function handleCatPromptCommand({
958
- sock,
959
- remoteJid,
960
- messageInfo,
961
- expirationMessage,
962
- senderJid,
963
- text,
964
- commandPrefix = DEFAULT_COMMAND_PREFIX,
965
- }) {
817
+ export async function handleCatPromptCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix = DEFAULT_COMMAND_PREFIX }) {
966
818
  const promptText = text?.trim();
967
819
  if (!promptText) {
968
820
  await sendPromptUsage(sock, remoteJid, messageInfo, expirationMessage, commandPrefix);
@@ -977,30 +829,15 @@ export async function handleCatPromptCommand({
977
829
  const lower = promptText.toLowerCase();
978
830
  if (lower === 'reset' || lower === 'default' || lower === 'padrao' || lower === 'padrão') {
979
831
  await aiPromptStore.clearPrompt(senderJid);
980
- await sendAndStore(
981
- sock,
982
- remoteJid,
983
- { text: '✅ Prompt da IA restaurado para o padrão.' },
984
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
985
- );
832
+ await sendAndStore(sock, remoteJid, { text: '✅ Prompt da IA restaurado para o padrão.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
986
833
  return;
987
834
  }
988
835
 
989
836
  if (promptText.length > 2000) {
990
- await sendAndStore(
991
- sock,
992
- remoteJid,
993
- { text: '⚠️ Prompt muito longo. Limite: 2000 caracteres.' },
994
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
995
- );
837
+ await sendAndStore(sock, remoteJid, { text: '⚠️ Prompt muito longo. Limite: 2000 caracteres.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
996
838
  return;
997
839
  }
998
840
 
999
841
  await aiPromptStore.setPrompt(senderJid, promptText);
1000
- await sendAndStore(
1001
- sock,
1002
- remoteJid,
1003
- { text: '✅ Prompt da IA atualizado para você.' },
1004
- { quoted: messageInfo, ephemeralExpiration: expirationMessage },
1005
- );
842
+ await sendAndStore(sock, remoteJid, { text: '✅ Prompt da IA atualizado para você.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
1006
843
  }