@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
@@ -116,237 +116,8 @@ const EDGE_PUNCTUATION_CHARS = new Set([',', '!', '?', ';', ':', ')', '(', '[',
116
116
  const TOKEN_SEPARATOR_CHARS = new Set([',', ';', '|']);
117
117
  const HOST_TERMINATORS = new Set(['/', '?', '#', ':', '\\', ',', ';']);
118
118
  const URL_HINTS = ['https://', 'http://', 'www.'];
119
- const STRICT_TLD_SUFFIXES = new Set([
120
- 'com',
121
- 'net',
122
- 'org',
123
- 'edu',
124
- 'gov',
125
- 'mil',
126
- 'io',
127
- 'me',
128
- 'tv',
129
- 'co',
130
- 'cc',
131
- 'gg',
132
- 'gl',
133
- 'ly',
134
- 'so',
135
- 'br',
136
- 'us',
137
- 'uk',
138
- 'eu',
139
- 'de',
140
- 'fr',
141
- 'es',
142
- 'pt',
143
- 'it',
144
- 'nl',
145
- 'be',
146
- 'ch',
147
- 'at',
148
- 'se',
149
- 'no',
150
- 'fi',
151
- 'dk',
152
- 'ie',
153
- 'pl',
154
- 'cz',
155
- 'sk',
156
- 'hu',
157
- 'ro',
158
- 'bg',
159
- 'gr',
160
- 'ru',
161
- 'ua',
162
- 'tr',
163
- 'il',
164
- 'ae',
165
- 'sa',
166
- 'qa',
167
- 'eg',
168
- 'ma',
169
- 'tn',
170
- 'dz',
171
- 'za',
172
- 'ng',
173
- 'ke',
174
- 'gh',
175
- 'in',
176
- 'pk',
177
- 'bd',
178
- 'lk',
179
- 'cn',
180
- 'jp',
181
- 'kr',
182
- 'tw',
183
- 'hk',
184
- 'sg',
185
- 'my',
186
- 'th',
187
- 'vn',
188
- 'ph',
189
- 'id',
190
- 'au',
191
- 'nz',
192
- 'ca',
193
- 'mx',
194
- 'ar',
195
- 'cl',
196
- 'pe',
197
- 'uy',
198
- 'py',
199
- 'bo',
200
- 'ec',
201
- 've',
202
- 'do',
203
- 'cu',
204
- 'pa',
205
- 'cr',
206
- 'gt',
207
- 'hn',
208
- 'ni',
209
- 'sv',
210
- 'pr',
211
- 'com.br',
212
- 'net.br',
213
- 'org.br',
214
- 'gov.br',
215
- 'edu.br',
216
- 'jus.br',
217
- 'mil.br',
218
- 'co.uk',
219
- 'org.uk',
220
- 'gov.uk',
221
- 'ac.uk',
222
- 'co.jp',
223
- 'ne.jp',
224
- 'or.jp',
225
- 'go.jp',
226
- 'ac.jp',
227
- 'com.au',
228
- 'net.au',
229
- 'org.au',
230
- 'edu.au',
231
- 'gov.au',
232
- 'com.mx',
233
- 'com.ar',
234
- 'com.co',
235
- 'com.pe',
236
- 'com.tr',
237
- 'com.sg',
238
- 'com.my',
239
- 'com.ph',
240
- 'co.in',
241
- 'firm.in',
242
- 'net.in',
243
- 'org.in',
244
- 'gen.in',
245
- 'ind.in',
246
- 'co.id',
247
- 'or.id',
248
- 'go.id',
249
- 'web.id',
250
- 'co.za',
251
- 'org.za',
252
- 'net.za',
253
- 'com.ng',
254
- 'com.gh',
255
- 'com.eg',
256
- 'com.sa',
257
- 'com.qa',
258
- 'com.ae',
259
- 'page.link',
260
- 'g.page',
261
- ]);
262
- const EXTRA_TLD_SUFFIXES = new Set([
263
- 'ai',
264
- 'app',
265
- 'dev',
266
- 'xyz',
267
- 'site',
268
- 'online',
269
- 'store',
270
- 'shop',
271
- 'blog',
272
- 'tech',
273
- 'cloud',
274
- 'digital',
275
- 'live',
276
- 'media',
277
- 'news',
278
- 'one',
279
- 'top',
280
- 'club',
281
- 'vip',
282
- 'fun',
283
- 'games',
284
- 'game',
285
- 'space',
286
- 'world',
287
- 'today',
288
- 'agency',
289
- 'email',
290
- 'center',
291
- 'company',
292
- 'group',
293
- 'solutions',
294
- 'systems',
295
- 'services',
296
- 'network',
297
- 'social',
298
- 'design',
299
- 'studio',
300
- 'photo',
301
- 'video',
302
- 'audio',
303
- 'music',
304
- 'art',
305
- 'wiki',
306
- 'finance',
307
- 'capital',
308
- 'money',
309
- 'loans',
310
- 'insurance',
311
- 'legal',
312
- 'law',
313
- 'health',
314
- 'care',
315
- 'clinic',
316
- 'dental',
317
- 'academy',
318
- 'school',
319
- 'college',
320
- 'university',
321
- 'education',
322
- 'training',
323
- 'support',
324
- 'chat',
325
- 'forum',
326
- 'community',
327
- 'events',
328
- 'travel',
329
- 'tours',
330
- 'hotel',
331
- 'homes',
332
- 'house',
333
- 'auto',
334
- 'cars',
335
- 'bike',
336
- 'food',
337
- 'restaurant',
338
- 'cafe',
339
- 'bar',
340
- 'pizza',
341
- 'delivery',
342
- 'fashion',
343
- 'beauty',
344
- 'style',
345
- 'fit',
346
- 'fitness',
347
- 'sports',
348
- 'download',
349
- ]);
119
+ const STRICT_TLD_SUFFIXES = new Set(['com', 'net', 'org', 'edu', 'gov', 'mil', 'io', 'me', 'tv', 'co', 'cc', 'gg', 'gl', 'ly', 'so', 'br', 'us', 'uk', 'eu', 'de', 'fr', 'es', 'pt', 'it', 'nl', 'be', 'ch', 'at', 'se', 'no', 'fi', 'dk', 'ie', 'pl', 'cz', 'sk', 'hu', 'ro', 'bg', 'gr', 'ru', 'ua', 'tr', 'il', 'ae', 'sa', 'qa', 'eg', 'ma', 'tn', 'dz', 'za', 'ng', 'ke', 'gh', 'in', 'pk', 'bd', 'lk', 'cn', 'jp', 'kr', 'tw', 'hk', 'sg', 'my', 'th', 'vn', 'ph', 'id', 'au', 'nz', 'ca', 'mx', 'ar', 'cl', 'pe', 'uy', 'py', 'bo', 'ec', 've', 'do', 'cu', 'pa', 'cr', 'gt', 'hn', 'ni', 'sv', 'pr', 'com.br', 'net.br', 'org.br', 'gov.br', 'edu.br', 'jus.br', 'mil.br', 'co.uk', 'org.uk', 'gov.uk', 'ac.uk', 'co.jp', 'ne.jp', 'or.jp', 'go.jp', 'ac.jp', 'com.au', 'net.au', 'org.au', 'edu.au', 'gov.au', 'com.mx', 'com.ar', 'com.co', 'com.pe', 'com.tr', 'com.sg', 'com.my', 'com.ph', 'co.in', 'firm.in', 'net.in', 'org.in', 'gen.in', 'ind.in', 'co.id', 'or.id', 'go.id', 'web.id', 'co.za', 'org.za', 'net.za', 'com.ng', 'com.gh', 'com.eg', 'com.sa', 'com.qa', 'com.ae', 'page.link', 'g.page']);
120
+ const EXTRA_TLD_SUFFIXES = new Set(['ai', 'app', 'dev', 'xyz', 'site', 'online', 'store', 'shop', 'blog', 'tech', 'cloud', 'digital', 'live', 'media', 'news', 'one', 'top', 'club', 'vip', 'fun', 'games', 'game', 'space', 'world', 'today', 'agency', 'email', 'center', 'company', 'group', 'solutions', 'systems', 'services', 'network', 'social', 'design', 'studio', 'photo', 'video', 'audio', 'music', 'art', 'wiki', 'finance', 'capital', 'money', 'loans', 'insurance', 'legal', 'law', 'health', 'care', 'clinic', 'dental', 'academy', 'school', 'college', 'university', 'education', 'training', 'support', 'chat', 'forum', 'community', 'events', 'travel', 'tours', 'hotel', 'homes', 'house', 'auto', 'cars', 'bike', 'food', 'restaurant', 'cafe', 'bar', 'pizza', 'delivery', 'fashion', 'beauty', 'style', 'fit', 'fitness', 'sports', 'download']);
350
121
  const ANY_TLD_SUFFIXES = new Set([...STRICT_TLD_SUFFIXES, ...EXTRA_TLD_SUFFIXES]);
351
122
 
352
123
  /**
@@ -697,8 +468,7 @@ const normalizeAllowedDomains = (allowedDomains = []) => {
697
468
  * @param {string[]} normalizedAllowedDomains
698
469
  * @returns {boolean}
699
470
  */
700
- const isDomainAllowed = (domain, normalizedAllowedDomains) =>
701
- normalizedAllowedDomains.some((allowedDomain) => domain === allowedDomain || domain.endsWith(`.${allowedDomain}`));
471
+ const isDomainAllowed = (domain, normalizedAllowedDomains) => normalizedAllowedDomains.some((allowedDomain) => domain === allowedDomain || domain.endsWith(`.${allowedDomain}`));
702
472
 
703
473
  /**
704
474
  * Monta a lista final de domínios permitidos (redes conhecidas + personalizados).
@@ -76,16 +76,7 @@ const consoleFormat = winston.format.combine(
76
76
  winston.format.colorize({ all: true }),
77
77
  winston.format.splat(),
78
78
  winston.format.metadata({
79
- fillExcept: [
80
- 'message',
81
- 'level',
82
- 'timestamp',
83
- 'label',
84
- 'service',
85
- 'instanceId',
86
- 'environment',
87
- 'stack',
88
- ],
79
+ fillExcept: ['message', 'level', 'timestamp', 'label', 'service', 'instanceId', 'environment', 'stack'],
89
80
  }),
90
81
  winston.format.printf((info) => {
91
82
  const { timestamp, level, message, metadata, stack } = info;
@@ -96,10 +87,7 @@ const consoleFormat = winston.format.combine(
96
87
  delete metaToPrint.label;
97
88
  }
98
89
 
99
- const metaPart =
100
- Object.keys(metaToPrint).length > 0
101
- ? ` ${util.inspect(metaToPrint, { colors: true, depth: 2 })}`
102
- : '';
90
+ const metaPart = Object.keys(metaToPrint).length > 0 ? ` ${util.inspect(metaToPrint, { colors: true, depth: 2 })}` : '';
103
91
 
104
92
  const servicePart = info.service ? ` [${info.service}]` : '';
105
93
  const instancePart = info.instanceId ? ` [${info.instanceId}]` : '';
@@ -180,13 +168,9 @@ function ensureLogDirectoryExists() {
180
168
  try {
181
169
  fs.mkdirSync(logDir, { recursive: true, mode: dirMode });
182
170
 
183
- console.log(
184
- `[ LoggerSetup ] Diretório de log garantido: '${logDir}' (modo ${dirMode.toString(8)})`,
185
- );
171
+ console.log(`[ LoggerSetup ] Diretório de log garantido: '${logDir}' (modo ${dirMode.toString(8)})`);
186
172
  } catch (error) {
187
- throw new Error(
188
- `Falha na configuração do Logger: Não foi possível acessar/criar o diretório de log '${logDir}'. Erro original: ${error.message}`,
189
- );
173
+ throw new Error(`Falha na configuração do Logger: Não foi possível acessar/criar o diretório de log '${logDir}'. Erro original: ${error.message}`);
190
174
  }
191
175
  }
192
176
 
@@ -199,8 +183,7 @@ const createLoggerInstance = (overrideOptions = {}) => {
199
183
  if (overrideOptions.transports) {
200
184
  configuredTransports = overrideOptions.transports;
201
185
  } else {
202
- const transportDefinitions =
203
- overrideOptions.transportDefinitions || getDefaultTransportDefinitions(effectiveLevel);
186
+ const transportDefinitions = overrideOptions.transportDefinitions || getDefaultTransportDefinitions(effectiveLevel);
204
187
  configuredTransports = transportDefinitions
205
188
  .map((def) => {
206
189
  try {
@@ -210,16 +193,11 @@ const createLoggerInstance = (overrideOptions = {}) => {
210
193
  case 'dailyRotateFile':
211
194
  return new DailyRotateFile(def.options);
212
195
  default:
213
- console.warn(
214
- `[ LoggerSetup ] Tipo de transporte desconhecido: ${def.type}. Pulando.`,
215
- );
196
+ console.warn(`[ LoggerSetup ] Tipo de transporte desconhecido: ${def.type}. Pulando.`);
216
197
  return null;
217
198
  }
218
199
  } catch (error) {
219
- console.error(
220
- `[ LoggerSetup ] Falha ao criar transporte tipo ${def.type}: ${error.message}`,
221
- error,
222
- );
200
+ console.error(`[ LoggerSetup ] Falha ao criar transporte tipo ${def.type}: ${error.message}`, error);
223
201
  return null;
224
202
  }
225
203
  })
@@ -234,8 +212,7 @@ const createLoggerInstance = (overrideOptions = {}) => {
234
212
 
235
213
  const defaultMeta = { ...baseDefaultMeta, ...(overrideOptions.defaultMeta || {}) };
236
214
 
237
- const loggerFormat =
238
- overrideOptions.format || winston.format.combine(winston.format.errors({ stack: true }));
215
+ const loggerFormat = overrideOptions.format || winston.format.combine(winston.format.errors({ stack: true }));
239
216
 
240
217
  const loggerInstance = winston.createLogger({
241
218
  level: effectiveLevel,
@@ -250,9 +227,7 @@ const createLoggerInstance = (overrideOptions = {}) => {
250
227
  console.error('Erro ocorrido dentro do Winston Logger:', error);
251
228
  });
252
229
 
253
- console.log(
254
- `[ LoggerSetup ] Instância do Logger criada. Nível: ${effectiveLevel}, Env: ${NODE_ENV}, Instância: ${INSTANCE_ID}, Serviço: ${ECOSYSTEM_NAME}`,
255
- );
230
+ console.log(`[ LoggerSetup ] Instância do Logger criada. Nível: ${effectiveLevel}, Env: ${NODE_ENV}, Instância: ${INSTANCE_ID}, Serviço: ${ECOSYSTEM_NAME}`);
256
231
 
257
232
  return loggerInstance;
258
233
  };
@@ -45,10 +45,7 @@ export const getSystemMetrics = () => {
45
45
  const uptime = formatDuration(processUptimeSeconds);
46
46
  const systemUptime = formatDuration(systemUptimeSeconds);
47
47
  const loadAverage = os.loadavg();
48
- const cpuSpeedAverage =
49
- totalCpus > 0
50
- ? Math.round(cpus.reduce((sum, cpu) => sum + (cpu.speed || 0), 0) / totalCpus)
51
- : 0;
48
+ const cpuSpeedAverage = totalCpus > 0 ? Math.round(cpus.reduce((sum, cpu) => sum + (cpu.speed || 0), 0) / totalCpus) : 0;
52
49
  const memoryUsage = process.memoryUsage();
53
50
 
54
51
  return {
package/database/index.js CHANGED
@@ -104,6 +104,7 @@ export const TABLES = {
104
104
  SEMANTIC_THEME_SUGGESTION_CACHE: 'semantic_theme_suggestion_cache',
105
105
  STICKER_PACK_ENGAGEMENT: 'sticker_pack_engagement',
106
106
  STICKER_PACK_INTERACTION_EVENT: 'sticker_pack_interaction_event',
107
+ WEB_VISIT_EVENT: 'web_visit_event',
107
108
  STICKER_PACK_SCORE_SNAPSHOT: 'sticker_pack_score_snapshot',
108
109
  STICKER_ASSET_REPROCESS_QUEUE: 'sticker_asset_reprocess_queue',
109
110
  STICKER_WORKER_TASK_QUEUE: 'sticker_worker_task_queue',
package/database/init.js CHANGED
@@ -474,14 +474,7 @@ export default async function initializeDatabase() {
474
474
  await connection.changeUser({ database: dbToCreate });
475
475
 
476
476
  // Cria todas as tabelas em paralelo
477
- await Promise.all([
478
- connection.query(createMessagesTableSQL),
479
- connection.query(createChatsTableSQL),
480
- connection.query(createGroupsMetadataTableSQL),
481
- connection.query(createGroupConfigsTableSQL),
482
- connection.query(createLidMapTableSQL),
483
- connection.query(createStickerAssetTableSQL),
484
- ]);
477
+ await Promise.all([connection.query(createMessagesTableSQL), connection.query(createChatsTableSQL), connection.query(createGroupsMetadataTableSQL), connection.query(createGroupConfigsTableSQL), connection.query(createLidMapTableSQL), connection.query(createStickerAssetTableSQL)]);
485
478
 
486
479
  // Ordem importa por conta das FKs: pack depende de asset e item depende de pack+asset.
487
480
  await connection.query(createStickerPackTableSQL);
@@ -0,0 +1,15 @@
1
+ CREATE TABLE IF NOT EXISTS web_visit_event (
2
+ id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
3
+ visitor_key VARCHAR(80) NOT NULL,
4
+ session_key VARCHAR(80) NOT NULL,
5
+ page_path VARCHAR(255) NOT NULL,
6
+ referrer VARCHAR(1024) NULL,
7
+ user_agent VARCHAR(512) NULL,
8
+ source VARCHAR(32) NOT NULL DEFAULT 'web',
9
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10
+ INDEX idx_web_visit_created_at (created_at),
11
+ INDEX idx_web_visit_page_created (page_path, created_at),
12
+ INDEX idx_web_visit_visitor_created (visitor_key, created_at),
13
+ INDEX idx_web_visit_session_created (session_key, created_at),
14
+ INDEX idx_web_visit_source_created (source, created_at)
15
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -3,17 +3,17 @@ services:
3
3
  image: prom/prometheus:${PROMETHEUS_IMAGE_TAG:-v2.52.0}
4
4
  restart: unless-stopped
5
5
  command:
6
- - "--config.file=/etc/prometheus/prometheus.yml"
7
- - "--storage.tsdb.retention.time=${PROMETHEUS_RETENTION_TIME:-15d}"
8
- - "--storage.tsdb.retention.size=${PROMETHEUS_RETENTION_SIZE:-20GB}"
6
+ - '--config.file=/etc/prometheus/prometheus.yml'
7
+ - '--storage.tsdb.retention.time=${PROMETHEUS_RETENTION_TIME:-15d}'
8
+ - '--storage.tsdb.retention.size=${PROMETHEUS_RETENTION_SIZE:-20GB}'
9
9
  volumes:
10
10
  - ${PROMETHEUS_CONFIG_PATH:-./observability/prometheus.yml}:/etc/prometheus/prometheus.yml:ro
11
11
  - ${PROMETHEUS_ALERT_RULES_PATH:-./observability/alert-rules.yml}:/etc/prometheus/alert-rules.yml:ro
12
12
  - prometheus_data:/prometheus
13
13
  ports:
14
- - "${PROMETHEUS_PORT:-9090}:9090"
14
+ - '${PROMETHEUS_PORT:-9090}:9090'
15
15
  extra_hosts:
16
- - "host.docker.internal:host-gateway"
16
+ - 'host.docker.internal:host-gateway'
17
17
 
18
18
  grafana:
19
19
  image: grafana/grafana:${GRAFANA_IMAGE_TAG:-10.4.3}
@@ -30,7 +30,7 @@ services:
30
30
  - ${GRAFANA_PROVISIONING_PATH:-./observability/grafana/provisioning}:/etc/grafana/provisioning:ro
31
31
  - ${GRAFANA_DASHBOARDS_PATH:-./observability/grafana/dashboards}:/var/lib/grafana/dashboards:ro
32
32
  ports:
33
- - "${GRAFANA_PORT:-3003}:3000"
33
+ - '${GRAFANA_PORT:-3003}:3000'
34
34
  depends_on:
35
35
  - prometheus
36
36
  - loki
@@ -43,7 +43,7 @@ services:
43
43
  - ${LOKI_CONFIG_PATH:-./observability/loki-config.yml}:/etc/loki/loki-config.yml:ro
44
44
  - loki_data:/loki
45
45
  ports:
46
- - "${LOKI_PORT:-3100}:3100"
46
+ - '${LOKI_PORT:-3100}:3100'
47
47
 
48
48
  promtail:
49
49
  image: grafana/promtail:${PROMTAIL_IMAGE_TAG:-2.9.4}
@@ -54,11 +54,11 @@ services:
54
54
  - ${APP_LOGS_PATH:-./logs}:/var/log/omnizap:ro
55
55
  - ${MYSQL_LOGS_PATH:-/var/log/mysql}:/var/log/mysql:ro
56
56
  ports:
57
- - "${PROMTAIL_PORT:-9080}:9080"
57
+ - '${PROMTAIL_PORT:-9080}:9080'
58
58
  depends_on:
59
59
  - loki
60
60
  extra_hosts:
61
- - "host.docker.internal:host-gateway"
61
+ - 'host.docker.internal:host-gateway'
62
62
 
63
63
  mysql-exporter:
64
64
  image: prom/mysqld-exporter:${MYSQL_EXPORTER_IMAGE_TAG:-v0.15.1}
@@ -66,38 +66,38 @@ services:
66
66
  environment:
67
67
  - DATA_SOURCE_NAME=${MYSQL_EXPORTER_DSN:-exporter:exporter@(host.docker.internal:3306)/}
68
68
  command:
69
- - "--config.my-cnf=/etc/mysql-exporter/my.cnf"
70
- - "--collect.global_status"
71
- - "--collect.global_variables"
72
- - "--no-collect.slave_status"
73
- - "--collect.info_schema.innodb_metrics"
74
- - "--collect.info_schema.processlist"
75
- - "--collect.info_schema.tables"
76
- - "--collect.perf_schema.eventsstatements"
77
- - "--collect.perf_schema.tableiowaits"
78
- - "--collect.perf_schema.tablelocks"
69
+ - '--config.my-cnf=/etc/mysql-exporter/my.cnf'
70
+ - '--collect.global_status'
71
+ - '--collect.global_variables'
72
+ - '--no-collect.slave_status'
73
+ - '--collect.info_schema.innodb_metrics'
74
+ - '--collect.info_schema.processlist'
75
+ - '--collect.info_schema.tables'
76
+ - '--collect.perf_schema.eventsstatements'
77
+ - '--collect.perf_schema.tableiowaits'
78
+ - '--collect.perf_schema.tablelocks'
79
79
  volumes:
80
80
  - ${MYSQL_EXPORTER_CNF_PATH:-./observability/mysql-exporter.cnf}:/etc/mysql-exporter/my.cnf:ro
81
81
  ports:
82
- - "${MYSQL_EXPORTER_PORT:-9104}:9104"
82
+ - '${MYSQL_EXPORTER_PORT:-9104}:9104'
83
83
  extra_hosts:
84
- - "host.docker.internal:host-gateway"
84
+ - 'host.docker.internal:host-gateway'
85
85
 
86
86
  node-exporter:
87
87
  image: prom/node-exporter:${NODE_EXPORTER_IMAGE_TAG:-v1.7.0}
88
88
  restart: unless-stopped
89
- pid: "host"
89
+ pid: 'host'
90
90
  command:
91
- - "--path.rootfs=/host"
91
+ - '--path.rootfs=/host'
92
92
  volumes:
93
93
  - /:/host:ro,rslave
94
94
  ports:
95
- - "${NODE_EXPORTER_PORT:-9100}:9100"
95
+ - '${NODE_EXPORTER_PORT:-9100}:9100'
96
96
 
97
97
  volumes:
98
98
  grafana_data:
99
- name: "${STACK_NAME:-omnizap}_grafana_data"
99
+ name: '${STACK_NAME:-omnizap}_grafana_data'
100
100
  prometheus_data:
101
- name: "${STACK_NAME:-omnizap}_prometheus_data"
101
+ name: '${STACK_NAME:-omnizap}_prometheus_data'
102
102
  loki_data:
103
- name: "${STACK_NAME:-omnizap}_loki_data"
103
+ name: '${STACK_NAME:-omnizap}_loki_data'
@@ -23,12 +23,14 @@ Objetivo: posicionar OmniZap como "bot pronto para WhatsApp" para usuario final,
23
23
  ## 1) FAQ + JSON-LD pronta
24
24
 
25
25
  Status implementado no projeto:
26
+
26
27
  - Home (`/`): FAQ visual + `FAQPage` JSON-LD alinhado ao posicionamento "adicionar e usar".
27
28
  - Catalogo (`/stickers/`): `FAQPage` JSON-LD no HTML publico.
28
29
  - Pack (`/stickers/{packKey}`): `FAQPage` JSON-LD dinamico no HTML SEO server-side.
29
30
  - API Docs (`/api-docs/`): ja possui `SoftwareApplication` + `FAQPage`.
30
31
 
31
32
  Checklist tecnico para manter:
33
+
32
34
  - FAQ da pagina deve bater 1:1 com o JSON-LD.
33
35
  - Linguagem da FAQ deve seguir intencao da pagina (usuario final na home, integracao no cluster tecnico).
34
36
  - Atualizar data de cache-bust quando houver mudanca textual relevante.
@@ -38,6 +40,7 @@ Checklist tecnico para manter:
38
40
  Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada e os demais itens seguem como backlog de expansao.
39
41
 
40
42
  ### Cluster principal (comercial)
43
+
41
44
  - `/bot-whatsapp-para-grupo/`
42
45
  - `/bot-whatsapp-para-comunidade/`
43
46
  - `/bot-whatsapp-para-loja-online/`
@@ -45,6 +48,7 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
45
48
  - `/bot-whatsapp-para-equipes-internas/`
46
49
 
47
50
  ### Cluster dores (intencao alta)
51
+
48
52
  - `/como-moderar-grupo-whatsapp/`
49
53
  - `/como-evitar-spam-no-whatsapp/`
50
54
  - `/como-organizar-comunidade-whatsapp/`
@@ -52,17 +56,20 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
52
56
  - `/como-criar-comandos-whatsapp/`
53
57
 
54
58
  ### Cluster comparativo (captura de decisao)
59
+
55
60
  - `/melhor-bot-whatsapp-para-grupos/`
56
61
  - `/omnizap-vs-blip/`
57
62
  - `/omnizap-vs-zenvia/`
58
63
  - `/omnizap-vs-huggy/`
59
64
 
60
65
  ### Cluster de feature (stickers como modulo)
66
+
61
67
  - `/stickers-para-bot-whatsapp/`
62
68
  - `/como-usar-stickers-no-bot/`
63
69
  - `/pack-de-stickers-para-grupos/`
64
70
 
65
71
  ### Regra de interlink obrigatoria
72
+
66
73
  - Toda pagina satelite linka para: `/`, `/api-docs/`, `/stickers/`.
67
74
  - Toda pagina de pack linka para: `/api-docs/` + `/` + `/stickers/`.
68
75
  - Home aponta para satelites comerciais e para dores.
@@ -70,6 +77,7 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
70
77
  ## 3) Mapa de palavras-chave Brasil
71
78
 
72
79
  ### Head terms (topo de volume)
80
+
73
81
  - bot para whatsapp
74
82
  - chatbot para whatsapp
75
83
  - automacao whatsapp
@@ -77,6 +85,7 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
77
85
  - bot para grupo whatsapp
78
86
 
79
87
  ### Mid-tail (intencao de solucao)
88
+
80
89
  - bot para moderar grupo whatsapp
81
90
  - bot para comunidade whatsapp
82
91
  - bot para responder mensagens no whatsapp
@@ -84,6 +93,7 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
84
93
  - bot de atendimento whatsapp sem programar
85
94
 
86
95
  ### Long-tail (oportunidade de rank rapido)
96
+
87
97
  - como organizar grupo de whatsapp automaticamente
88
98
  - como evitar spam em grupo de whatsapp com bot
89
99
  - bot pronto para grupo de estudos no whatsapp
@@ -91,12 +101,14 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
91
101
  - como adicionar bot no grupo do whatsapp
92
102
 
93
103
  ### Keywords de modulo (stickers)
104
+
94
105
  - sticker para bot whatsapp
95
106
  - pack de stickers para whatsapp
96
107
  - catalogo de stickers para bot
97
108
  - stickers integrados via api
98
109
 
99
110
  ### Mapa por intencao
111
+
100
112
  - Descoberta: "como", "o que e", "vale a pena".
101
113
  - Consideracao: "melhor", "comparativo", "preco", "funciona".
102
114
  - Conversao: "adicionar bot", "testar bot", "bot pronto", "sem configuracao".
@@ -104,22 +116,26 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
104
116
  ## 4) Estrategia para ranquear antes dos concorrentes
105
117
 
106
118
  ### Fase 1 (dias 0-30) - ganhar velocidade
119
+
107
120
  - Publicar 8-12 conteudos long-tail de dor real (moderar, spam, avisos, comunidade).
108
121
  - Criar comparativos orientados a decisao (sem ataque de marca, foco em "para quem serve").
109
122
  - Fortalecer CTR com titles de resultado: "em 1 minuto", "sem configuracao", "sem programar".
110
123
  - Revisar links internos para formar ciclo fechado entre Home -> API -> Stickers -> Packs.
111
124
 
112
125
  ### Fase 2 (dias 31-60) - escalar cobertura
126
+
113
127
  - Expandir para paginas satelite por nicho (comunidade, loja, estudo, creators).
114
128
  - Criar template de prova social por caso de uso (antes/depois em metricas simples).
115
129
  - Atualizar sitemap com prioridade para novas satelites e hubs de cluster.
116
130
 
117
131
  ### Fase 3 (dias 61-90) - defender posicao
132
+
118
133
  - Criar FAQ adicional por pagina (3-5 perguntas de objecao).
119
134
  - Otimizar paginas que baterem top 20 para top 10 (title, intro, links internos, schema).
120
135
  - Construir backlinks de contexto (comunidades, blogs de creators, automacao e WhatsApp).
121
136
 
122
137
  ### KPI alvo (90 dias)
138
+
123
139
  - +30% impressoes organicas (GSC) nas queries com "bot", "grupo", "whatsapp".
124
140
  - +20% CTR medio nas paginas comerciais.
125
141
  - 20+ keywords long-tail em top 10.
@@ -128,44 +144,54 @@ Observacao: abaixo esta o mapa estrategico de satelites. A fase 1 foi publicada
128
144
  ## 5) Analise de concorrencia nacional (snapshot)
129
145
 
130
146
  ### Camada 1: suites enterprise (fortes em marca e CAC)
147
+
131
148
  1. Blip
149
+
132
150
  - Sinal: posicionamento forte em WhatsApp + IA + vendas.
133
151
  - Evidencia publica: "plataforma oficial", numeros altos de mensagens/chatbots.
134
152
  - Risco para OmniZap: domina termos genericos institucionais e enterprise.
135
153
  - Brecha para OmniZap: pouca proposta "bot pronto para grupo em 1 minuto".
136
154
 
137
155
  2. Zenvia
156
+
138
157
  - Sinal: narrativa de Customer Cloud multicanal com WhatsApp no centro.
139
158
  - Evidencia publica: foco em automacoes, campanhas, API e atendimento.
140
159
  - Risco para OmniZap: forte presenca B2B em termos de atendimento e vendas.
141
160
  - Brecha para OmniZap: proposta da Zenvia tende a ser mais "suite" e menos "plug-and-play para comunidade".
142
161
 
143
162
  3. Huggy
163
+
144
164
  - Sinal: oferta de chatbot/atendimento, com claim de 4000 negocios.
145
165
  - Evidencia publica: foco em centralizacao de canais + automacao 24/7.
146
166
  - Risco para OmniZap: captura media-tail de atendimento WhatsApp.
147
167
  - Brecha para OmniZap: mensagem menos focada em "grupo/comunidade".
148
168
 
149
169
  4. Leadster
170
+
150
171
  - Sinal: entrada forte em marketing conversacional + WhatsApp.
151
172
  - Evidencia publica: foco em captacao e qualificacao de leads.
152
173
  - Risco para OmniZap: ocupa termos de "chatbot para vender".
153
174
  - Brecha para OmniZap: menos foco em operacao de grupos e moderacao.
154
175
 
155
176
  ### Camada 2: nicho "bot para grupos" (fragmentado)
177
+
156
178
  1. BotAdmin
179
+
157
180
  - Foco: moderacao de grupos, comandos e automacao.
158
181
  - Risco: pode capturar suas palavras exatas de intencao alta (grupo/moderacao).
159
182
 
160
183
  2. AutoGrupo
184
+
161
185
  - Foco: monetizacao de grupos pagos.
162
186
  - Risco: captura nicho de creators infoproduto.
163
187
 
164
188
  3. Sites pequenos de "bot de figurinhas"
189
+
165
190
  - Foco: termos de cauda longa e transacional de baixo ticket.
166
191
  - Risco: volume pulverizado, baixa qualidade media, mas alta capilaridade de indexacao.
167
192
 
168
193
  ### Leitura competitiva objetiva
194
+
169
195
  - Grandes players defendem termos amplos (chatbot, plataforma, API, atendimento).
170
196
  - Oportunidade real de OmniZap esta no "desconforto operacional" de quem administra grupos.
171
197
  - Melhor estrategia: dominar long-tail de dor + CTA de uso imediato.