@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
@@ -187,28 +187,9 @@ export function createStickerPackService(options = {}) {
187
187
  const hasOwnerPackLimit = Number.isFinite(maxPacksPerOwner);
188
188
  const runInTransaction = options.runInTransaction || withTransaction;
189
189
 
190
- const requiredPackMethods = [
191
- 'createStickerPack',
192
- 'listStickerPacksByOwner',
193
- 'findStickerPackByOwnerAndIdentifier',
194
- 'findStickerPackByPackKey',
195
- 'updateStickerPackFields',
196
- 'softDeleteStickerPack',
197
- 'ensureUniquePackKey',
198
- 'bumpStickerPackVersion',
199
- ];
200
-
201
- const requiredItemMethods = [
202
- 'listStickerPackItems',
203
- 'countStickerPackItems',
204
- 'getMaxStickerPackPosition',
205
- 'createStickerPackItem',
206
- 'getStickerPackItemByStickerId',
207
- 'getStickerPackItemByPosition',
208
- 'removeStickerPackItemByStickerId',
209
- 'shiftStickerPackPositionsAfter',
210
- 'bulkUpdateStickerPackPositions',
211
- ];
190
+ const requiredPackMethods = ['createStickerPack', 'listStickerPacksByOwner', 'findStickerPackByOwnerAndIdentifier', 'findStickerPackByPackKey', 'updateStickerPackFields', 'softDeleteStickerPack', 'ensureUniquePackKey', 'bumpStickerPackVersion'];
191
+
192
+ const requiredItemMethods = ['listStickerPackItems', 'countStickerPackItems', 'getMaxStickerPackPosition', 'createStickerPackItem', 'getStickerPackItemByStickerId', 'getStickerPackItemByPosition', 'removeStickerPackItemByStickerId', 'shiftStickerPackPositionsAfter', 'bulkUpdateStickerPackPositions'];
212
193
 
213
194
  for (const methodName of requiredPackMethods) {
214
195
  if (typeof deps.packRepository[methodName] !== 'function') {
@@ -224,11 +205,7 @@ export function createStickerPackService(options = {}) {
224
205
 
225
206
  const runAction = async (action, context, handler, { expectedErrorCodes = [] } = {}) => {
226
207
  const start = process.hrtime.bigint();
227
- const expectedCodesSet = new Set(
228
- Array.isArray(expectedErrorCodes)
229
- ? expectedErrorCodes.map((code) => String(code || '').trim()).filter(Boolean)
230
- : [],
231
- );
208
+ const expectedCodesSet = new Set(Array.isArray(expectedErrorCodes) ? expectedErrorCodes.map((code) => String(code || '').trim()).filter(Boolean) : []);
232
209
 
233
210
  try {
234
211
  const result = await handler();
@@ -343,9 +320,7 @@ export function createStickerPackService(options = {}) {
343
320
  const loadPackDetails = async (pack, { connection = null } = {}) => {
344
321
  const items = await deps.itemRepository.listStickerPackItems(pack.id, connection);
345
322
  const coverItem = items.find((item) => item.sticker_id === pack.cover_sticker_id);
346
- const packClassification = await getPackClassificationSummaryByAssetIds(items.map((item) => item.sticker_id)).catch(
347
- () => null,
348
- );
323
+ const packClassification = await getPackClassificationSummaryByAssetIds(items.map((item) => item.sticker_id)).catch(() => null);
349
324
 
350
325
  return {
351
326
  ...pack,
@@ -373,23 +348,9 @@ export function createStickerPackService(options = {}) {
373
348
  };
374
349
  };
375
350
 
376
- const createPack = async ({
377
- ownerJid,
378
- name,
379
- publisher,
380
- description,
381
- visibility = 'public',
382
- status = 'published',
383
- packStatus = undefined,
384
- packThemeKey = undefined,
385
- packVolume = undefined,
386
- isAutoPack = undefined,
387
- lastRebalancedAt = undefined,
388
- }) => {
351
+ const createPack = async ({ ownerJid, name, publisher, description, visibility = 'public', status = 'published', packStatus = undefined, packThemeKey = undefined, packVolume = undefined, isAutoPack = undefined, lastRebalancedAt = undefined }) => {
389
352
  const owner = resolveOwner(ownerJid);
390
- const normalizedStatus = PACK_STATUS_VALUES.has(String(status || '').toLowerCase())
391
- ? String(status).toLowerCase()
392
- : 'published';
353
+ const normalizedStatus = PACK_STATUS_VALUES.has(String(status || '').toLowerCase()) ? String(status).toLowerCase() : 'published';
393
354
 
394
355
  return runAction('create_pack', { owner_jid: owner }, async () => {
395
356
  return runInTransaction(async (connection) => {
@@ -400,11 +361,7 @@ export function createStickerPackService(options = {}) {
400
361
  connection,
401
362
  });
402
363
 
403
- ensureValue(
404
- existing.length < maxPacksPerOwner,
405
- STICKER_PACK_ERROR_CODES.PACK_LIMIT_REACHED,
406
- `Limite de packs atingido (${maxPacksPerOwner}).`,
407
- );
364
+ ensureValue(existing.length < maxPacksPerOwner, STICKER_PACK_ERROR_CODES.PACK_LIMIT_REACHED, `Limite de packs atingido (${maxPacksPerOwner}).`);
408
365
  }
409
366
 
410
367
  const packKey = await ensurePackKey(owner, metadata.name, connection);
@@ -507,11 +464,7 @@ export function createStickerPackService(options = {}) {
507
464
  const owner = resolveOwner(ownerJid);
508
465
  const normalizedVisibility = toVisibility(visibility, null);
509
466
 
510
- ensureValue(
511
- normalizedVisibility,
512
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
513
- 'Visibilidade inválida. Use: private, public ou unlisted.',
514
- );
467
+ ensureValue(normalizedVisibility, STICKER_PACK_ERROR_CODES.INVALID_INPUT, 'Visibilidade inválida. Use: private, public ou unlisted.');
515
468
 
516
469
  return runAction('set_visibility', { owner_jid: owner }, async () => {
517
470
  const pack = await resolveOwnedPack(owner, identifier);
@@ -542,14 +495,7 @@ export function createStickerPackService(options = {}) {
542
495
  });
543
496
  };
544
497
 
545
- const addStickerToPack = async ({
546
- ownerJid,
547
- identifier,
548
- asset,
549
- emojis = [],
550
- accessibilityLabel = null,
551
- expectedErrorCodes = [],
552
- }) => {
498
+ const addStickerToPack = async ({ ownerJid, identifier, asset, emojis = [], accessibilityLabel = null, expectedErrorCodes = [] }) => {
553
499
  const owner = resolveOwner(ownerJid);
554
500
 
555
501
  ensureValue(asset?.id, STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND, 'Nenhuma figurinha válida foi encontrada.');
@@ -558,57 +504,54 @@ export function createStickerPackService(options = {}) {
558
504
  allowEmpty: true,
559
505
  });
560
506
 
561
- return runAction('add_sticker', { owner_jid: owner }, async () => {
562
- const pack = await resolveOwnedPack(owner, identifier);
507
+ return runAction(
508
+ 'add_sticker',
509
+ { owner_jid: owner },
510
+ async () => {
511
+ const pack = await resolveOwnedPack(owner, identifier);
563
512
 
564
- return runInTransaction(async (connection) => {
565
- const existingItem = await deps.itemRepository.getStickerPackItemByStickerId(pack.id, asset.id, connection);
566
- ensureValue(
567
- !existingItem,
568
- STICKER_PACK_ERROR_CODES.DUPLICATE_STICKER,
569
- 'Essa figurinha já está no pack.',
570
- );
513
+ return runInTransaction(async (connection) => {
514
+ const existingItem = await deps.itemRepository.getStickerPackItemByStickerId(pack.id, asset.id, connection);
515
+ ensureValue(!existingItem, STICKER_PACK_ERROR_CODES.DUPLICATE_STICKER, 'Essa figurinha já está no pack.');
571
516
 
572
- const total = await deps.itemRepository.countStickerPackItems(pack.id, connection);
573
- ensureValue(
574
- total < maxStickersPerPack,
575
- STICKER_PACK_ERROR_CODES.PACK_LIMIT_REACHED,
576
- `O pack atingiu o limite de ${maxStickersPerPack} figurinhas.`,
577
- );
517
+ const total = await deps.itemRepository.countStickerPackItems(pack.id, connection);
518
+ ensureValue(total < maxStickersPerPack, STICKER_PACK_ERROR_CODES.PACK_LIMIT_REACHED, `O pack atingiu o limite de ${maxStickersPerPack} figurinhas.`);
578
519
 
579
- const maxPosition = await deps.itemRepository.getMaxStickerPackPosition(pack.id, connection);
520
+ const maxPosition = await deps.itemRepository.getMaxStickerPackPosition(pack.id, connection);
580
521
 
581
- await deps.itemRepository.createStickerPackItem(
582
- {
583
- id: randomUUID(),
584
- pack_id: pack.id,
585
- sticker_id: asset.id,
586
- position: maxPosition + 1,
587
- emojis: parseEmojiList(emojis),
588
- accessibility_label: normalizedLabel || null,
589
- },
590
- connection,
591
- );
592
-
593
- if (!pack.cover_sticker_id) {
594
- await deps.packRepository.updateStickerPackFields(
595
- pack.id,
522
+ await deps.itemRepository.createStickerPackItem(
596
523
  {
597
- cover_sticker_id: asset.id,
524
+ id: randomUUID(),
525
+ pack_id: pack.id,
526
+ sticker_id: asset.id,
527
+ position: maxPosition + 1,
528
+ emojis: parseEmojiList(emojis),
529
+ accessibility_label: normalizedLabel || null,
598
530
  },
599
531
  connection,
600
532
  );
601
- } else {
602
- await deps.packRepository.bumpStickerPackVersion(pack.id, connection);
603
- }
604
533
 
605
- const reloaded = await deps.packRepository.findStickerPackByOwnerAndIdentifier(owner, pack.id, {
606
- connection,
607
- });
534
+ if (!pack.cover_sticker_id) {
535
+ await deps.packRepository.updateStickerPackFields(
536
+ pack.id,
537
+ {
538
+ cover_sticker_id: asset.id,
539
+ },
540
+ connection,
541
+ );
542
+ } else {
543
+ await deps.packRepository.bumpStickerPackVersion(pack.id, connection);
544
+ }
545
+
546
+ const reloaded = await deps.packRepository.findStickerPackByOwnerAndIdentifier(owner, pack.id, {
547
+ connection,
548
+ });
608
549
 
609
- return loadPackDetails(reloaded, { connection });
610
- });
611
- }, { expectedErrorCodes });
550
+ return loadPackDetails(reloaded, { connection });
551
+ });
552
+ },
553
+ { expectedErrorCodes },
554
+ );
612
555
  };
613
556
 
614
557
  const removeStickerFromPack = async ({ ownerJid, identifier, selector }) => {
@@ -665,11 +608,7 @@ export function createStickerPackService(options = {}) {
665
608
 
666
609
  const reorderPackItems = async ({ ownerJid, identifier, orderStickerIds }) => {
667
610
  const owner = resolveOwner(ownerJid);
668
- ensureValue(
669
- Array.isArray(orderStickerIds) && orderStickerIds.length > 0,
670
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
671
- 'Envie a nova ordem de figurinhas.',
672
- );
611
+ ensureValue(Array.isArray(orderStickerIds) && orderStickerIds.length > 0, STICKER_PACK_ERROR_CODES.INVALID_INPUT, 'Envie a nova ordem de figurinhas.');
673
612
 
674
613
  return runAction('reorder_pack', { owner_jid: owner }, async () => {
675
614
  const pack = await resolveOwnedPack(owner, identifier);
@@ -690,11 +629,7 @@ export function createStickerPackService(options = {}) {
690
629
  seen.add(id);
691
630
  }
692
631
 
693
- ensureValue(
694
- requestedIds.length > 0,
695
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
696
- 'A ordem enviada não corresponde às figurinhas do pack.',
697
- );
632
+ ensureValue(requestedIds.length > 0, STICKER_PACK_ERROR_CODES.INVALID_INPUT, 'A ordem enviada não corresponde às figurinhas do pack.');
698
633
 
699
634
  const finalOrder = [...requestedIds, ...currentIds.filter((id) => !seen.has(id))];
700
635
 
@@ -1,25 +1,6 @@
1
1
  import { createStickerPackService } from './stickerPackService.js';
2
- import {
3
- bumpStickerPackVersion,
4
- createStickerPack,
5
- ensureUniquePackKey,
6
- findStickerPackByPackKey,
7
- findStickerPackByOwnerAndIdentifier,
8
- listStickerPacksByOwner,
9
- softDeleteStickerPack,
10
- updateStickerPackFields,
11
- } from './stickerPackRepository.js';
12
- import {
13
- bulkUpdateStickerPackPositions,
14
- countStickerPackItems,
15
- createStickerPackItem,
16
- getMaxStickerPackPosition,
17
- getStickerPackItemByPosition,
18
- getStickerPackItemByStickerId,
19
- listStickerPackItems,
20
- removeStickerPackItemByStickerId,
21
- shiftStickerPackPositionsAfter,
22
- } from './stickerPackItemRepository.js';
2
+ import { bumpStickerPackVersion, createStickerPack, ensureUniquePackKey, findStickerPackByPackKey, findStickerPackByOwnerAndIdentifier, listStickerPacksByOwner, softDeleteStickerPack, updateStickerPackFields } from './stickerPackRepository.js';
3
+ import { bulkUpdateStickerPackPositions, countStickerPackItems, createStickerPackItem, getMaxStickerPackPosition, getStickerPackItemByPosition, getStickerPackItemByStickerId, listStickerPackItems, removeStickerPackItemByStickerId, shiftStickerPackPositionsAfter } from './stickerPackItemRepository.js';
23
4
 
24
5
  /**
25
6
  * Serviço principal de sticker pack com dependências concretas de runtime.
@@ -54,7 +54,11 @@ export const slugify = (value, { fallback = 'pack', maxLength = 32 } = {}) => {
54
54
  * @param {number} [size=8] Quantidade de caracteres retornados.
55
55
  * @returns {string} Hash hexadecimal truncado.
56
56
  */
57
- export const shortHash = (value, size = 8) => createHash('sha256').update(String(value ?? '')).digest('hex').slice(0, size);
57
+ export const shortHash = (value, size = 8) =>
58
+ createHash('sha256')
59
+ .update(String(value ?? ''))
60
+ .digest('hex')
61
+ .slice(0, size);
58
62
 
59
63
  /**
60
64
  * Normaliza o JID do dono mantendo fallback para o valor original.
@@ -72,7 +76,9 @@ export const normalizeOwnerJid = (jid) => normalizeJid(jid || '') || jid || '';
72
76
  * @returns {'private'|'public'|'unlisted'|null} Valor de visibilidade válido.
73
77
  */
74
78
  export const toVisibility = (value, fallback = 'private') => {
75
- const normalized = String(value || '').trim().toLowerCase();
79
+ const normalized = String(value || '')
80
+ .trim()
81
+ .toLowerCase();
76
82
  if (normalized === 'public' || normalized === 'unlisted' || normalized === 'private') {
77
83
  return normalized;
78
84
  }
@@ -87,7 +93,11 @@ export const toVisibility = (value, fallback = 'private') => {
87
93
  */
88
94
  export const parseEmojiList = (value) => {
89
95
  if (!value) return [];
90
- if (Array.isArray(value)) return value.map((item) => String(item)).filter(Boolean).slice(0, 8);
96
+ if (Array.isArray(value))
97
+ return value
98
+ .map((item) => String(item))
99
+ .filter(Boolean)
100
+ .slice(0, 8);
91
101
 
92
102
  return String(value)
93
103
  .split(',')
@@ -5,22 +5,11 @@ import { createHash, randomUUID } from 'node:crypto';
5
5
  import logger from '../../utils/logger/loggerModule.js';
6
6
  import { downloadMediaMessage, extractMediaDetails } from '../../config/baileysConfig.js';
7
7
  import { isFeatureEnabled } from '../../services/featureFlagService.js';
8
- import {
9
- createStickerAsset,
10
- findLatestStickerAssetByOwner,
11
- findStickerAssetById,
12
- findStickerAssetBySha256,
13
- updateStickerAssetStoragePath,
14
- } from './stickerAssetRepository.js';
8
+ import { createStickerAsset, findLatestStickerAssetByOwner, findStickerAssetById, findStickerAssetBySha256, updateStickerAssetStoragePath } from './stickerAssetRepository.js';
15
9
  import { ensureStickerAssetClassified } from './stickerClassificationService.js';
16
10
  import { STICKER_PACK_ERROR_CODES, StickerPackError } from './stickerPackErrors.js';
17
11
  import { normalizeOwnerJid } from './stickerPackUtils.js';
18
- import {
19
- getStickerObjectStorageUrl,
20
- isStickerObjectStorageEnabled,
21
- readStickerFromObjectStorage,
22
- uploadStickerToObjectStorage,
23
- } from './stickerObjectStorageService.js';
12
+ import { getStickerObjectStorageUrl, isStickerObjectStorageEnabled, readStickerFromObjectStorage, uploadStickerToObjectStorage } from './stickerObjectStorageService.js';
24
13
 
25
14
  /**
26
15
  * Camada de storage local para assets de figurinha do sistema de packs.
@@ -30,10 +19,7 @@ const TEMP_ROOT = path.join(process.cwd(), 'temp', 'sticker-pack-assets');
30
19
  const DEFAULT_MAX_STICKER_BYTES = 2 * 1024 * 1024;
31
20
  const MAX_STICKER_BYTES = Math.max(64 * 1024, Number(process.env.STICKER_PACK_MAX_STICKER_BYTES) || DEFAULT_MAX_STICKER_BYTES);
32
21
  const LAST_STICKER_TTL_MS = Math.max(60_000, Number(process.env.STICKER_PACK_LAST_CACHE_TTL_MS) || 6 * 60 * 60 * 1000);
33
- const OBJECT_STORAGE_UPLOAD_TIMEOUT_MS = Math.max(
34
- 1_000,
35
- Number(process.env.STICKER_OBJECT_STORAGE_UPLOAD_TIMEOUT_MS) || 10_000,
36
- );
22
+ const OBJECT_STORAGE_UPLOAD_TIMEOUT_MS = Math.max(1_000, Number(process.env.STICKER_OBJECT_STORAGE_UPLOAD_TIMEOUT_MS) || 10_000);
37
23
 
38
24
  const lastStickerCache = new Map();
39
25
 
@@ -61,7 +47,9 @@ const isObjectStorageDeliveryEnabled = async (subjectKey = '') =>
61
47
  */
62
48
  const safeOwnerToken = (ownerJid) => {
63
49
  const normalized = normalizeOwnerJid(ownerJid);
64
- const token = String(normalized || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 100);
50
+ const token = String(normalized || 'unknown')
51
+ .replace(/[^a-zA-Z0-9._-]/g, '_')
52
+ .slice(0, 100);
65
53
  return token || 'unknown';
66
54
  };
67
55
 
@@ -263,17 +251,11 @@ const validateStickerBuffer = (buffer) => {
263
251
  }
264
252
 
265
253
  if (buffer.length > MAX_STICKER_BYTES) {
266
- throw new StickerPackError(
267
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
268
- `Figurinha excede o limite de ${(MAX_STICKER_BYTES / (1024 * 1024)).toFixed(1)} MB.`,
269
- );
254
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.INVALID_INPUT, `Figurinha excede o limite de ${(MAX_STICKER_BYTES / (1024 * 1024)).toFixed(1)} MB.`);
270
255
  }
271
256
 
272
257
  if (!isLikelyWebp(buffer)) {
273
- throw new StickerPackError(
274
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
275
- 'A mídia precisa estar no formato WEBP para entrar no pack.',
276
- );
258
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.INVALID_INPUT, 'A mídia precisa estar no formato WEBP para entrar no pack.');
277
259
  }
278
260
  };
279
261
 
@@ -390,10 +372,7 @@ async function persistStickerAssetFromDetails({ mediaDetails, ownerJid }) {
390
372
  const mediaSize = Number(mediaDetails?.details?.fileLength || mediaDetails?.mediaKey?.fileLength || 0);
391
373
 
392
374
  if (mediaSize > MAX_STICKER_BYTES) {
393
- throw new StickerPackError(
394
- STICKER_PACK_ERROR_CODES.INVALID_INPUT,
395
- `Figurinha excede o limite de ${(MAX_STICKER_BYTES / (1024 * 1024)).toFixed(1)} MB.`,
396
- );
375
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.INVALID_INPUT, `Figurinha excede o limite de ${(MAX_STICKER_BYTES / (1024 * 1024)).toFixed(1)} MB.`);
397
376
  }
398
377
 
399
378
  const tempOwnerDir = path.join(TEMP_ROOT, safeOwnerToken(normalizedOwner));
@@ -404,10 +383,7 @@ async function persistStickerAssetFromDetails({ mediaDetails, ownerJid }) {
404
383
  try {
405
384
  downloadedPath = await downloadMediaMessage(mediaDetails.mediaKey, 'sticker', tempOwnerDir);
406
385
  if (!downloadedPath) {
407
- throw new StickerPackError(
408
- STICKER_PACK_ERROR_CODES.STORAGE_ERROR,
409
- 'Não foi possível baixar a figurinha para armazenamento.',
410
- );
386
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, 'Não foi possível baixar a figurinha para armazenamento.');
411
387
  }
412
388
 
413
389
  const buffer = await fs.readFile(downloadedPath);
@@ -427,11 +403,7 @@ async function persistStickerAssetFromDetails({ mediaDetails, ownerJid }) {
427
403
  owner_jid: normalizedOwner,
428
404
  error: error.message,
429
405
  });
430
- throw new StickerPackError(
431
- STICKER_PACK_ERROR_CODES.STORAGE_ERROR,
432
- 'Falha ao salvar figurinha no servidor.',
433
- error,
434
- );
406
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, 'Falha ao salvar figurinha no servidor.', error);
435
407
  } finally {
436
408
  if (downloadedPath) {
437
409
  await fs.unlink(downloadedPath).catch(() => {});
@@ -461,11 +433,7 @@ export async function saveStickerAssetFromBuffer({ ownerJid, buffer, mimetype =
461
433
  error: error.message,
462
434
  });
463
435
 
464
- throw new StickerPackError(
465
- STICKER_PACK_ERROR_CODES.STORAGE_ERROR,
466
- 'Falha ao salvar figurinha gerada no servidor.',
467
- error,
468
- );
436
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, 'Falha ao salvar figurinha gerada no servidor.', error);
469
437
  }
470
438
  }
471
439
 
@@ -525,12 +493,7 @@ export async function getLastStickerAssetForOwner(ownerJid) {
525
493
  * }} params Contexto de resolução.
526
494
  * @returns {Promise<object|null>} Asset resolvido.
527
495
  */
528
- export async function resolveStickerAssetForCommand({
529
- messageInfo,
530
- ownerJid,
531
- includeQuoted = true,
532
- fallbackToLast = true,
533
- }) {
496
+ export async function resolveStickerAssetForCommand({ messageInfo, ownerJid, includeQuoted = true, fallbackToLast = true }) {
534
497
  const mediaDetails = resolveStickerMediaDetails(messageInfo, { includeQuoted });
535
498
 
536
499
  if (mediaDetails) {
@@ -560,11 +523,7 @@ export async function readStickerAssetBuffer(asset) {
560
523
  if (Buffer.isBuffer(externalBuffer) && externalBuffer.length) {
561
524
  return externalBuffer;
562
525
  }
563
- throw new StickerPackError(
564
- STICKER_PACK_ERROR_CODES.STORAGE_ERROR,
565
- `Não foi possível ler a figurinha em disco (${asset.storage_path}).`,
566
- error,
567
- );
526
+ throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, `Não foi possível ler a figurinha em disco (${asset.storage_path}).`, error);
568
527
  }
569
528
  }
570
529
 
@@ -576,17 +535,9 @@ export async function readStickerAssetBuffer(asset) {
576
535
  throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, 'Caminho do sticker não encontrado no storage.');
577
536
  }
578
537
 
579
- export async function getStickerAssetExternalUrl(
580
- asset,
581
- {
582
- secure = true,
583
- expiresInSeconds = 300,
584
- } = {},
585
- ) {
538
+ export async function getStickerAssetExternalUrl(asset, { secure = true, expiresInSeconds = 300 } = {}) {
586
539
  if (!asset) return null;
587
- const canUseExternalDelivery = await isObjectStorageDeliveryEnabled(
588
- normalizeOwnerJid(asset?.owner_jid || '') || String(asset?.id || asset?.sha256 || ''),
589
- );
540
+ const canUseExternalDelivery = await isObjectStorageDeliveryEnabled(normalizeOwnerJid(asset?.owner_jid || '') || String(asset?.id || asset?.sha256 || ''));
590
541
  if (!canUseExternalDelivery) return null;
591
542
  return getStickerObjectStorageUrl(asset, {
592
543
  secure,
@@ -3,14 +3,7 @@ import { setQueueDepth } from '../../observability/metrics.js';
3
3
  import { isFeatureEnabled } from '../../services/featureFlagService.js';
4
4
  import { runStickerClassificationCycle } from './stickerClassificationBackgroundRuntime.js';
5
5
  import { runStickerAutoPackByTagsCycle } from './stickerAutoPackByTagsRuntime.js';
6
- import {
7
- claimWorkerTask,
8
- completeWorkerTask,
9
- countWorkerTasksByStatus,
10
- enqueueWorkerTask,
11
- failWorkerTask,
12
- hasPendingWorkerTask,
13
- } from './stickerWorkerTaskQueueRepository.js';
6
+ import { claimWorkerTask, completeWorkerTask, countWorkerTasksByStatus, enqueueWorkerTask, failWorkerTask, hasPendingWorkerTask } from './stickerWorkerTaskQueueRepository.js';
14
7
 
15
8
  const parseEnvBool = (value, fallback) => {
16
9
  if (value === undefined || value === null || value === '') return fallback;
@@ -39,10 +32,7 @@ const TASK_PRIORITY = {
39
32
  curation_cycle: 55,
40
33
  rebuild_cycle: 50,
41
34
  };
42
- const PIPELINE_PROCESS_COHORT_KEY =
43
- String(process.env.STICKER_WORKER_PIPELINE_COHORT_KEY || process.env.HOSTNAME || process.pid)
44
- .trim()
45
- || 'pipeline';
35
+ const PIPELINE_PROCESS_COHORT_KEY = String(process.env.STICKER_WORKER_PIPELINE_COHORT_KEY || process.env.HOSTNAME || process.pid).trim() || 'pipeline';
46
36
 
47
37
  let startupHandle = null;
48
38
  let schedulerHandle = null;
@@ -59,11 +49,7 @@ const taskHandlers = {
59
49
 
60
50
  const refreshQueueDepthMetrics = async () => {
61
51
  if (!taskQueueAvailable) return;
62
- const [pending, processing, failed] = await Promise.all([
63
- countWorkerTasksByStatus('pending'),
64
- countWorkerTasksByStatus('processing'),
65
- countWorkerTasksByStatus('failed'),
66
- ]);
52
+ const [pending, processing, failed] = await Promise.all([countWorkerTasksByStatus('pending'), countWorkerTasksByStatus('processing'), countWorkerTasksByStatus('failed')]);
67
53
  setQueueDepth('sticker_worker_tasks_pending', pending);
68
54
  setQueueDepth('sticker_worker_tasks_processing', processing);
69
55
  setQueueDepth('sticker_worker_tasks_failed', failed);
@@ -107,11 +93,7 @@ const schedulerTick = async () => {
107
93
  if (!PIPELINE_ENABLED) return;
108
94
 
109
95
  try {
110
- await Promise.all([
111
- scheduleTaskIfNeeded('classification_cycle'),
112
- scheduleTaskIfNeeded('curation_cycle'),
113
- scheduleTaskIfNeeded('rebuild_cycle'),
114
- ]);
96
+ await Promise.all([scheduleTaskIfNeeded('classification_cycle'), scheduleTaskIfNeeded('curation_cycle'), scheduleTaskIfNeeded('rebuild_cycle')]);
115
97
  await refreshQueueDepthMetrics();
116
98
  } catch (error) {
117
99
  if (error?.code === 'ER_NO_SUCH_TABLE') {
@@ -3,13 +3,17 @@ import { randomUUID } from 'node:crypto';
3
3
  import { executeQuery, TABLES } from '../../../database/index.js';
4
4
 
5
5
  const normalizeTaskType = (value) => {
6
- const normalized = String(value || '').trim().toLowerCase();
6
+ const normalized = String(value || '')
7
+ .trim()
8
+ .toLowerCase();
7
9
  if (['classification_cycle', 'curation_cycle', 'rebuild_cycle'].includes(normalized)) return normalized;
8
10
  return null;
9
11
  };
10
12
 
11
13
  const normalizeStatus = (value) => {
12
- const normalized = String(value || '').trim().toLowerCase();
14
+ const normalized = String(value || '')
15
+ .trim()
16
+ .toLowerCase();
13
17
  if (['pending', 'processing', 'completed', 'failed'].includes(normalized)) return normalized;
14
18
  return null;
15
19
  };
@@ -20,12 +24,7 @@ const clampInt = (value, fallback, min, max) => {
20
24
  return Math.max(min, Math.min(max, Math.floor(numeric)));
21
25
  };
22
26
 
23
- const CLAIM_LOCK_TIMEOUT_SECONDS = clampInt(
24
- process.env.STICKER_WORKER_TASK_LOCK_TIMEOUT_SECONDS,
25
- 15 * 60,
26
- 30,
27
- 24 * 60 * 60,
28
- );
27
+ const CLAIM_LOCK_TIMEOUT_SECONDS = clampInt(process.env.STICKER_WORKER_TASK_LOCK_TIMEOUT_SECONDS, 15 * 60, 30, 24 * 60 * 60);
29
28
 
30
29
  const normalizeIdempotencyKey = (value) =>
31
30
  String(value || '')
@@ -76,10 +75,7 @@ const normalizeRow = (row) => {
76
75
  };
77
76
  };
78
77
 
79
- export async function enqueueWorkerTask(
80
- { taskType, payload = {}, priority = 50, scheduledAt = null, maxAttempts = 5, idempotencyKey = '' },
81
- connection = null,
82
- ) {
78
+ export async function enqueueWorkerTask({ taskType, payload = {}, priority = 50, scheduledAt = null, maxAttempts = 5, idempotencyKey = '' }, connection = null) {
83
79
  const normalizedTaskType = normalizeTaskType(taskType);
84
80
  if (!normalizedTaskType) return false;
85
81
 
@@ -99,14 +95,7 @@ export async function enqueueWorkerTask(
99
95
  scheduled_at = LEAST(scheduled_at, VALUES(scheduled_at)),
100
96
  status = IF(status = 'failed' AND attempts < max_attempts, 'pending', status),
101
97
  updated_at = UTC_TIMESTAMP()`,
102
- [
103
- normalizedTaskType,
104
- normalizedIdempotencyKey,
105
- JSON.stringify(payload || {}),
106
- safePriority,
107
- scheduledValue,
108
- safeMaxAttempts,
109
- ],
98
+ [normalizedTaskType, normalizedIdempotencyKey, JSON.stringify(payload || {}), safePriority, scheduledValue, safeMaxAttempts],
110
99
  connection,
111
100
  );
112
101
  return true;
@@ -196,17 +185,13 @@ export async function completeWorkerTask(taskId, connection = null) {
196
185
  return true;
197
186
  }
198
187
 
199
- export async function failWorkerTask(
200
- taskId,
201
- {
202
- error = null,
203
- retryDelaySeconds = 0,
204
- } = {},
205
- connection = null,
206
- ) {
188
+ export async function failWorkerTask(taskId, { error = null, retryDelaySeconds = 0 } = {}, connection = null) {
207
189
  if (!taskId) return false;
208
190
  const safeDelay = clampInt(retryDelaySeconds, 0, 0, 86400 * 7);
209
- const message = String(error || '').trim().slice(0, 255) || null;
191
+ const message =
192
+ String(error || '')
193
+ .trim()
194
+ .slice(0, 255) || null;
210
195
 
211
196
  await executeQuery(
212
197
  `UPDATE ${TABLES.STICKER_WORKER_TASK_QUEUE}