@kaikybrofc/omnizap-system 2.2.9 → 2.3.0
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.
- package/README.md +20 -18
- package/app/config/adminIdentity.js +1 -3
- package/app/connection/socketController.js +10 -20
- package/app/controllers/messageController.js +7 -28
- package/app/modules/aiModule/catCommand.js +29 -192
- package/app/modules/broadcastModule/noticeCommand.js +28 -97
- package/app/modules/gameModule/diceCommand.js +6 -32
- package/app/modules/playModule/playCommand.js +57 -258
- package/app/modules/quoteModule/quoteCommand.js +2 -4
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1 -13
- package/app/modules/statsModule/noMessageCommand.js +16 -84
- package/app/modules/statsModule/rankingCommand.js +5 -25
- package/app/modules/statsModule/rankingCommon.js +1 -9
- package/app/modules/stickerModule/convertToWebp.js +4 -27
- package/app/modules/stickerModule/stickerCommand.js +13 -24
- package/app/modules/stickerModule/stickerTextCommand.js +13 -25
- package/app/modules/stickerPackModule/autoPackCollectorService.js +16 -7
- package/app/modules/stickerPackModule/domainEventOutboxRepository.js +20 -36
- package/app/modules/stickerPackModule/domainEvents.js +2 -11
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +13 -50
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +2 -15
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +14 -41
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +25 -95
- package/app/modules/stickerPackModule/stickerAssetRepository.js +12 -31
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +13 -18
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +284 -709
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +27 -106
- package/app/modules/stickerPackModule/stickerClassificationService.js +46 -77
- package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +13 -53
- package/app/modules/stickerPackModule/stickerDomainEventBus.js +10 -16
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +13 -34
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +1 -4
- package/app/modules/stickerPackModule/stickerObjectStorageService.js +26 -26
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +32 -187
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +6 -15
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +6 -32
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +12 -36
- package/app/modules/stickerPackModule/stickerPackMessageService.js +12 -40
- package/app/modules/stickerPackModule/stickerPackRepository.js +23 -66
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +9 -21
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +10 -40
- package/app/modules/stickerPackModule/stickerPackService.js +50 -115
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +2 -21
- package/app/modules/stickerPackModule/stickerPackUtils.js +13 -3
- package/app/modules/stickerPackModule/stickerStorageService.js +16 -65
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +4 -22
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +14 -29
- package/app/modules/systemMetricsModule/pingCommand.js +9 -39
- package/app/modules/tiktokModule/tiktokCommand.js +17 -109
- package/app/modules/userModule/userCommand.js +2 -88
- package/app/observability/metrics.js +5 -16
- package/app/services/captchaService.js +1 -6
- package/app/services/dbWriteQueue.js +3 -18
- package/app/services/featureFlagService.js +2 -8
- package/app/services/newsBroadcastService.js +0 -1
- package/app/services/queueUtils.js +2 -4
- package/app/services/whatsappLoginLinkService.js +7 -9
- package/app/store/premiumUserStore.js +1 -2
- package/app/utils/antiLink/antiLinkModule.js +3 -233
- package/app/utils/logger/loggerModule.js +9 -34
- package/app/utils/systemMetrics/systemMetricsModule.js +1 -4
- package/database/init.js +1 -8
- package/docker-compose.yml +27 -27
- package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +220 -0
- package/docs/seo/satellite-page-template.md +91 -0
- package/docs/seo/satellite-pages-phase1.json +349 -0
- package/eslint.config.js +2 -15
- package/index.js +8 -36
- package/ml/clip_classifier/README.md +4 -6
- package/observability/alert-rules.yml +12 -12
- package/observability/grafana/provisioning/dashboards/dashboards.yml +1 -1
- package/package.json +8 -3
- package/public/api-docs/index.html +224 -141
- package/public/bot-whatsapp-para-grupo/index.html +306 -0
- package/public/bot-whatsapp-sem-programar/index.html +306 -0
- package/public/comandos/index.html +428 -0
- package/public/como-automatizar-avisos-no-whatsapp/index.html +306 -0
- package/public/como-criar-comandos-whatsapp/index.html +306 -0
- package/public/como-evitar-spam-no-whatsapp/index.html +306 -0
- package/public/como-moderar-grupo-whatsapp/index.html +306 -0
- package/public/como-organizar-comunidade-whatsapp/index.html +306 -0
- package/public/css/github-project-panel.css +20 -15
- package/public/css/stickers-admin.css +55 -39
- package/public/css/styles.css +37 -29
- package/public/index.html +1060 -1417
- package/public/js/apps/apiDocsApp.js +36 -153
- package/public/js/apps/createPackApp.js +69 -332
- package/public/js/apps/homeApp.js +201 -434
- package/public/js/apps/loginApp.js +3 -12
- package/public/js/apps/stickersAdminApp.js +190 -181
- package/public/js/apps/stickersApp.js +507 -1366
- package/public/js/catalog.js +11 -74
- package/public/js/github-panel/components/ErrorState.js +1 -8
- package/public/js/github-panel/components/GithubProjectPanel.js +2 -9
- package/public/js/github-panel/components/SkeletonPanel.js +1 -11
- package/public/js/github-panel/components/StatCard.js +1 -7
- package/public/js/github-panel/vendor/react.js +1 -9
- package/public/js/runtime/react-runtime.js +1 -9
- package/public/licenca/index.html +104 -86
- package/public/login/index.html +315 -321
- package/public/melhor-bot-whatsapp-para-grupos/index.html +306 -0
- package/public/sitemap.xml +45 -0
- package/public/stickers/admin/index.html +14 -19
- package/public/stickers/create/index.html +39 -43
- package/public/stickers/index.html +97 -41
- package/public/termos-de-uso/index.html +142 -115
- package/public/user/index.html +347 -346
- package/scripts/cache-bust.mjs +5 -24
- package/scripts/generate-seo-satellite-pages.mjs +431 -0
- package/scripts/run-prettier-all.mjs +25 -0
- package/scripts/sticker-catalog-loadtest.mjs +13 -11
- package/scripts/sticker-worker-task.mjs +1 -4
- package/scripts/sync-readme-snapshot.mjs +3 -2
- package/server/controllers/stickerCatalogController.js +407 -704
- package/server/http/httpServer.js +2 -10
- package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +1 -8
- package/server/routes/stickerCatalog/catalogHandlers/catalogAuthHttp.js +1 -9
- package/server/routes/stickerCatalog/catalogHandlers/catalogPublicHttp.js +10 -11
- package/server/routes/stickerCatalog/catalogHandlers/catalogUploadHttp.js +1 -10
- package/server/routes/stickerCatalog/catalogRouter.js +11 -13
- package/kaikybrofc-omnizap-system-2.2.9.tgz +0 -0
|
@@ -2,9 +2,7 @@ import logger from '../../utils/logger/loggerModule.js';
|
|
|
2
2
|
import { getSystemMetrics } from '../../utils/systemMetrics/systemMetricsModule.js';
|
|
3
3
|
import { sendAndStore } from '../../services/messagePersistenceService.js';
|
|
4
4
|
|
|
5
|
-
const METRICS_ENDPOINT =
|
|
6
|
-
process.env.METRICS_ENDPOINT ||
|
|
7
|
-
`http://localhost:${process.env.METRICS_PORT || 9102}${process.env.METRICS_PATH || '/metrics'}`;
|
|
5
|
+
const METRICS_ENDPOINT = process.env.METRICS_ENDPOINT || `http://localhost:${process.env.METRICS_PORT || 9102}${process.env.METRICS_PATH || '/metrics'}`;
|
|
8
6
|
const METRICS_TIMEOUT_MS = Number(process.env.METRICS_PING_TIMEOUT_MS || 1500);
|
|
9
7
|
|
|
10
8
|
const formatLoadAverage = (values) => values.map((value) => value.toFixed(2)).join(' | ');
|
|
@@ -51,10 +49,7 @@ const formatStatusLevel = (status) => `${status.emoji} ${status.label}`;
|
|
|
51
49
|
|
|
52
50
|
const padNumber = (value) => String(value).padStart(2, '0');
|
|
53
51
|
|
|
54
|
-
const formatDateTime = (date = new Date()) =>
|
|
55
|
-
`${padNumber(date.getDate())}/${padNumber(date.getMonth() + 1)}/${date.getFullYear()} ${padNumber(
|
|
56
|
-
date.getHours(),
|
|
57
|
-
)}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}`;
|
|
52
|
+
const formatDateTime = (date = new Date()) => `${padNumber(date.getDate())}/${padNumber(date.getMonth() + 1)}/${date.getFullYear()} ${padNumber(date.getHours())}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}`;
|
|
58
53
|
|
|
59
54
|
const parseLabels = (raw) => {
|
|
60
55
|
if (!raw) return {};
|
|
@@ -115,14 +110,7 @@ const getLabelValue = (series, name, labelKey) => {
|
|
|
115
110
|
return entry ? entry.labels[labelKey] : null;
|
|
116
111
|
};
|
|
117
112
|
|
|
118
|
-
const buildPingMessage = ({
|
|
119
|
-
systemMetrics,
|
|
120
|
-
metricsSummary,
|
|
121
|
-
metricsOk,
|
|
122
|
-
metricsError,
|
|
123
|
-
latencyMs,
|
|
124
|
-
generatedAt,
|
|
125
|
-
}) => {
|
|
113
|
+
const buildPingMessage = ({ systemMetrics, metricsSummary, metricsOk, metricsError, latencyMs, generatedAt }) => {
|
|
126
114
|
const responseTime = Number.isFinite(latencyMs) ? `${Math.max(0, Math.round(latencyMs))}ms` : 'n/a';
|
|
127
115
|
|
|
128
116
|
const hostCpuStatus = getStatusLevel(systemMetrics.usoCpuPercentual, 65, 85);
|
|
@@ -177,9 +165,7 @@ ${systemPart}
|
|
|
177
165
|
const dbStatus = getStatusLevel(slowRate, 5, 15);
|
|
178
166
|
const slowRateText = slowRate === null ? 'n/a' : `${slowRate.toFixed(2)}%`;
|
|
179
167
|
|
|
180
|
-
const queueValues = [metricsSummary.queues.messages, metricsSummary.queues.chats, metricsSummary.queues.lid_map]
|
|
181
|
-
.map((value) => parseMetricNumber(value))
|
|
182
|
-
.filter((value) => value !== null);
|
|
168
|
+
const queueValues = [metricsSummary.queues.messages, metricsSummary.queues.chats, metricsSummary.queues.lid_map].map((value) => parseMetricNumber(value)).filter((value) => value !== null);
|
|
183
169
|
const queuePeak = queueValues.length ? Math.max(...queueValues) : null;
|
|
184
170
|
const queueStatus = getStatusLevel(queuePeak, 30, 120);
|
|
185
171
|
const queuePeakText = queuePeak === null ? 'n/a' : String(Math.round(queuePeak));
|
|
@@ -252,10 +238,7 @@ ${glossaryPart}
|
|
|
252
238
|
};
|
|
253
239
|
|
|
254
240
|
const fetchMetricsSnapshot = async () => {
|
|
255
|
-
const controller =
|
|
256
|
-
typeof globalThis.AbortController === 'function'
|
|
257
|
-
? new globalThis.AbortController()
|
|
258
|
-
: null;
|
|
241
|
+
const controller = typeof globalThis.AbortController === 'function' ? new globalThis.AbortController() : null;
|
|
259
242
|
const timeout = setTimeout(() => controller?.abort(), METRICS_TIMEOUT_MS);
|
|
260
243
|
try {
|
|
261
244
|
if (typeof globalThis.fetch !== 'function') {
|
|
@@ -329,8 +312,7 @@ const fetchMetricsSnapshot = async () => {
|
|
|
329
312
|
upsertMessages[type] = (upsertMessages[type] || 0) + entry.value;
|
|
330
313
|
});
|
|
331
314
|
|
|
332
|
-
const formatNumber = (value, digits = 2) =>
|
|
333
|
-
Number.isFinite(value) ? value.toFixed(digits) : 'n/a';
|
|
315
|
+
const formatNumber = (value, digits = 2) => (Number.isFinite(value) ? value.toFixed(digits) : 'n/a');
|
|
334
316
|
|
|
335
317
|
return {
|
|
336
318
|
processUptime,
|
|
@@ -356,9 +338,7 @@ const fetchMetricsSnapshot = async () => {
|
|
|
356
338
|
lastQuery: {
|
|
357
339
|
messages: Number.isFinite(lastQuery.messages) ? lastQuery.messages.toFixed(2) : 'n/a',
|
|
358
340
|
lid_map: Number.isFinite(lastQuery.lid_map) ? lastQuery.lid_map.toFixed(2) : 'n/a',
|
|
359
|
-
groups_metadata: Number.isFinite(lastQuery.groups_metadata)
|
|
360
|
-
? lastQuery.groups_metadata.toFixed(2)
|
|
361
|
-
: 'n/a',
|
|
341
|
+
groups_metadata: Number.isFinite(lastQuery.groups_metadata) ? lastQuery.groups_metadata.toFixed(2) : 'n/a',
|
|
362
342
|
},
|
|
363
343
|
queues: {
|
|
364
344
|
messages: Math.round(queues.messages || 0),
|
|
@@ -403,19 +383,9 @@ export async function handlePingCommand({ sock, remoteJid, messageInfo, expirati
|
|
|
403
383
|
latencyMs: Date.now() - startedAt,
|
|
404
384
|
generatedAt: new Date(),
|
|
405
385
|
});
|
|
406
|
-
await sendAndStore(
|
|
407
|
-
sock,
|
|
408
|
-
remoteJid,
|
|
409
|
-
{ text },
|
|
410
|
-
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
411
|
-
);
|
|
386
|
+
await sendAndStore(sock, remoteJid, { text }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
412
387
|
} catch (error) {
|
|
413
388
|
logger.error('Erro ao gerar status do sistema:', { error: error.message });
|
|
414
|
-
await sendAndStore(
|
|
415
|
-
sock,
|
|
416
|
-
remoteJid,
|
|
417
|
-
{ text: 'Erro ao obter informações do sistema.' },
|
|
418
|
-
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
419
|
-
);
|
|
389
|
+
await sendAndStore(sock, remoteJid, { text: 'Erro ao obter informações do sistema.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
420
390
|
}
|
|
421
391
|
}
|
|
@@ -22,25 +22,11 @@ const CAPTION_MAX_CHARS = 950;
|
|
|
22
22
|
|
|
23
23
|
const TEMP_DIR = path.join(os.tmpdir(), 'omnizap-tiktok');
|
|
24
24
|
const URL_REGEX = /https?:\/\/[^\s<>"']+/gi;
|
|
25
|
-
const IMAGE_PATH_HINTS = [
|
|
26
|
-
'image',
|
|
27
|
-
'images',
|
|
28
|
-
'img',
|
|
29
|
-
'photo',
|
|
30
|
-
'photos',
|
|
31
|
-
'pic',
|
|
32
|
-
'pics',
|
|
33
|
-
'slide',
|
|
34
|
-
'slideshow',
|
|
35
|
-
'gallery',
|
|
36
|
-
'carousel',
|
|
37
|
-
'album',
|
|
38
|
-
];
|
|
25
|
+
const IMAGE_PATH_HINTS = ['image', 'images', 'img', 'photo', 'photos', 'pic', 'pics', 'slide', 'slideshow', 'gallery', 'carousel', 'album'];
|
|
39
26
|
const ALBUM_KIND_HINTS = ['slide', 'album', 'image', 'images', 'photo', 'carousel'];
|
|
40
27
|
|
|
41
28
|
const HEADERS = {
|
|
42
|
-
'User-Agent':
|
|
43
|
-
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
29
|
+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
|
44
30
|
Accept: '*/*',
|
|
45
31
|
};
|
|
46
32
|
|
|
@@ -121,25 +107,9 @@ const buildCaption = ({ requestedUrl, video, tiktok, mediaType = 'video' }) => {
|
|
|
121
107
|
const likes = formatStat(video?.stats?.likes);
|
|
122
108
|
const comments = formatStat(video?.stats?.comments);
|
|
123
109
|
const shares = formatStat(video?.stats?.shares);
|
|
124
|
-
const title =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
: "┏━〔 🎬 TikTok Sem Marca d'Água 〕━⬣";
|
|
128
|
-
|
|
129
|
-
const lines = [
|
|
130
|
-
title,
|
|
131
|
-
`┃ 👤 Autor: *${author}*`,
|
|
132
|
-
username ? `┃ 🆔 Perfil: *@${username.replace(/^@+/, '')}*` : null,
|
|
133
|
-
`┃ ❤️ Curtidas: *${likes}*`,
|
|
134
|
-
`┃ 💬 Comentários: *${comments}*`,
|
|
135
|
-
`┃ 🔁 Compart.: *${shares}*`,
|
|
136
|
-
'┗━━━━━━━━━━━━━━━⬣',
|
|
137
|
-
'',
|
|
138
|
-
'📝 *Descrição*',
|
|
139
|
-
description,
|
|
140
|
-
'',
|
|
141
|
-
`🔗 ${requestedUrl}`,
|
|
142
|
-
].filter(Boolean);
|
|
110
|
+
const title = mediaType === 'images' ? "┏━〔 🖼️ TikTok Imagens Sem Marca d'Água 〕━⬣" : "┏━〔 🎬 TikTok Sem Marca d'Água 〕━⬣";
|
|
111
|
+
|
|
112
|
+
const lines = [title, `┃ 👤 Autor: *${author}*`, username ? `┃ 🆔 Perfil: *@${username.replace(/^@+/, '')}*` : null, `┃ ❤️ Curtidas: *${likes}*`, `┃ 💬 Comentários: *${comments}*`, `┃ 🔁 Compart.: *${shares}*`, '┗━━━━━━━━━━━━━━━⬣', '', '📝 *Descrição*', description, '', `🔗 ${requestedUrl}`].filter(Boolean);
|
|
143
113
|
|
|
144
114
|
return truncate(lines.join('\n'), CAPTION_MAX_CHARS);
|
|
145
115
|
};
|
|
@@ -277,14 +247,7 @@ const isAlbumPayload = (payload) => {
|
|
|
277
247
|
const postType = payload?.tiktok?.post_type;
|
|
278
248
|
const slideButtonAvailable = Boolean(payload?.download_buttons?.slide?.available);
|
|
279
249
|
|
|
280
|
-
return (
|
|
281
|
-
albumCount > 0 ||
|
|
282
|
-
slideImageCount > 0 ||
|
|
283
|
-
slideDownloadCount > 0 ||
|
|
284
|
-
isAlbumKind(preferredKind) ||
|
|
285
|
-
isAlbumKind(postType) ||
|
|
286
|
-
slideButtonAvailable
|
|
287
|
-
);
|
|
250
|
+
return albumCount > 0 || slideImageCount > 0 || slideDownloadCount > 0 || isAlbumKind(preferredKind) || isAlbumKind(postType) || slideButtonAvailable;
|
|
288
251
|
};
|
|
289
252
|
|
|
290
253
|
const hasImagePathHint = (pathSegments) => {
|
|
@@ -343,14 +306,7 @@ const collectImageCandidates = (payload) => {
|
|
|
343
306
|
return filtered;
|
|
344
307
|
};
|
|
345
308
|
|
|
346
|
-
const sendImageCollection = async ({
|
|
347
|
-
sock,
|
|
348
|
-
remoteJid,
|
|
349
|
-
messageInfo,
|
|
350
|
-
expirationMessage,
|
|
351
|
-
imageUrls,
|
|
352
|
-
caption,
|
|
353
|
-
}) => {
|
|
309
|
+
const sendImageCollection = async ({ sock, remoteJid, messageInfo, expirationMessage, imageUrls, caption }) => {
|
|
354
310
|
const maxImages = toPositiveInt(TIKTOK_MAX_IMAGES_PER_POST, 10);
|
|
355
311
|
const selected = imageUrls.slice(0, maxImages);
|
|
356
312
|
const skipped = Math.max(0, imageUrls.length - selected.length);
|
|
@@ -359,10 +315,7 @@ const sendImageCollection = async ({
|
|
|
359
315
|
for (let index = 0; index < selected.length; index += 1) {
|
|
360
316
|
const imageUrl = selected[index];
|
|
361
317
|
const indexLabel = `🖼️ Imagem ${index + 1}/${selected.length}`;
|
|
362
|
-
const firstCaption =
|
|
363
|
-
skipped > 0
|
|
364
|
-
? `${caption}\n\n${indexLabel}\n⚠️ Mostrando ${selected.length}/${imageUrls.length} imagens.`
|
|
365
|
-
: `${caption}\n\n${indexLabel}`;
|
|
318
|
+
const firstCaption = skipped > 0 ? `${caption}\n\n${indexLabel}\n⚠️ Mostrando ${selected.length}/${imageUrls.length} imagens.` : `${caption}\n\n${indexLabel}`;
|
|
366
319
|
|
|
367
320
|
try {
|
|
368
321
|
await sendAndStore(
|
|
@@ -408,8 +361,7 @@ const downloadToTempFile = async (url) => {
|
|
|
408
361
|
});
|
|
409
362
|
|
|
410
363
|
const contentType = `${response.headers?.['content-type'] || ''}`.toLowerCase();
|
|
411
|
-
const looksLikeMedia =
|
|
412
|
-
!contentType || contentType.includes('video') || contentType.includes('octet-stream') || contentType.includes('mp4');
|
|
364
|
+
const looksLikeMedia = !contentType || contentType.includes('video') || contentType.includes('octet-stream') || contentType.includes('mp4');
|
|
413
365
|
if (!looksLikeMedia) {
|
|
414
366
|
throw new Error(`Content-Type inválido para vídeo: ${contentType || 'desconhecido'}`);
|
|
415
367
|
}
|
|
@@ -471,14 +423,7 @@ const tryDownloadCandidates = async (candidates) => {
|
|
|
471
423
|
throw new Error(detailed || 'Nenhum link de download disponível.');
|
|
472
424
|
};
|
|
473
425
|
|
|
474
|
-
const trySendDirectCandidates = async ({
|
|
475
|
-
sock,
|
|
476
|
-
remoteJid,
|
|
477
|
-
messageInfo,
|
|
478
|
-
expirationMessage,
|
|
479
|
-
caption,
|
|
480
|
-
candidates,
|
|
481
|
-
}) => {
|
|
426
|
+
const trySendDirectCandidates = async ({ sock, remoteJid, messageInfo, expirationMessage, caption, candidates }) => {
|
|
482
427
|
const failures = [];
|
|
483
428
|
|
|
484
429
|
for (const candidate of candidates) {
|
|
@@ -518,26 +463,12 @@ const sendUsage = async ({ sock, remoteJid, messageInfo, expirationMessage, comm
|
|
|
518
463
|
sock,
|
|
519
464
|
remoteJid,
|
|
520
465
|
{
|
|
521
|
-
text: [
|
|
522
|
-
'🎬 *TikTok Downloader*',
|
|
523
|
-
'',
|
|
524
|
-
`Uso: *${commandPrefix}tiktok <link1> [link2 ...]*`,
|
|
525
|
-
'',
|
|
526
|
-
`Exemplo: *${commandPrefix}tiktok https://www.tiktok.com/@usuario/video/123*`,
|
|
527
|
-
'',
|
|
528
|
-
"✅ Suporta múltiplos links e posts de imagem (carrossel).",
|
|
529
|
-
].join('\n'),
|
|
466
|
+
text: ['🎬 *TikTok Downloader*', '', `Uso: *${commandPrefix}tiktok <link1> [link2 ...]*`, '', `Exemplo: *${commandPrefix}tiktok https://www.tiktok.com/@usuario/video/123*`, '', '✅ Suporta múltiplos links e posts de imagem (carrossel).'].join('\n'),
|
|
530
467
|
},
|
|
531
468
|
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
532
469
|
);
|
|
533
470
|
|
|
534
|
-
const processTikTokUrl = async ({
|
|
535
|
-
sock,
|
|
536
|
-
remoteJid,
|
|
537
|
-
messageInfo,
|
|
538
|
-
expirationMessage,
|
|
539
|
-
inputUrl,
|
|
540
|
-
}) => {
|
|
471
|
+
const processTikTokUrl = async ({ sock, remoteJid, messageInfo, expirationMessage, inputUrl }) => {
|
|
541
472
|
let tempFilePath = null;
|
|
542
473
|
try {
|
|
543
474
|
const payload = await requestExtract(inputUrl);
|
|
@@ -675,14 +606,7 @@ const processTikTokUrl = async ({
|
|
|
675
606
|
}
|
|
676
607
|
};
|
|
677
608
|
|
|
678
|
-
export async function handleTikTokCommand({
|
|
679
|
-
sock,
|
|
680
|
-
remoteJid,
|
|
681
|
-
messageInfo,
|
|
682
|
-
expirationMessage,
|
|
683
|
-
text,
|
|
684
|
-
commandPrefix = DEFAULT_COMMAND_PREFIX,
|
|
685
|
-
}) {
|
|
609
|
+
export async function handleTikTokCommand({ sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix = DEFAULT_COMMAND_PREFIX }) {
|
|
686
610
|
const inputUrls = collectInputUrls({ text, messageInfo });
|
|
687
611
|
if (!inputUrls.length) {
|
|
688
612
|
await sendUsage({ sock, remoteJid, messageInfo, expirationMessage, commandPrefix });
|
|
@@ -694,19 +618,13 @@ export async function handleTikTokCommand({
|
|
|
694
618
|
const ignoredCount = Math.max(0, inputUrls.length - urls.length);
|
|
695
619
|
|
|
696
620
|
try {
|
|
697
|
-
const startText =
|
|
698
|
-
urls.length === 1
|
|
699
|
-
? "⏳ Baixando TikTok sem marca d'água, aguarde..."
|
|
700
|
-
: `⏳ Processando ${urls.length} links do TikTok...`;
|
|
621
|
+
const startText = urls.length === 1 ? "⏳ Baixando TikTok sem marca d'água, aguarde..." : `⏳ Processando ${urls.length} links do TikTok...`;
|
|
701
622
|
|
|
702
623
|
await sendAndStore(
|
|
703
624
|
sock,
|
|
704
625
|
remoteJid,
|
|
705
626
|
{
|
|
706
|
-
text:
|
|
707
|
-
ignoredCount > 0
|
|
708
|
-
? `${startText}\n⚠️ Limite por comando: ${maxUrls}. Ignorados: ${ignoredCount}.`
|
|
709
|
-
: startText,
|
|
627
|
+
text: ignoredCount > 0 ? `${startText}\n⚠️ Limite por comando: ${maxUrls}. Ignorados: ${ignoredCount}.` : startText,
|
|
710
628
|
},
|
|
711
629
|
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
712
630
|
);
|
|
@@ -761,23 +679,13 @@ export async function handleTikTokCommand({
|
|
|
761
679
|
throw new Error(failureDetails || 'Nenhum link foi processado com sucesso.');
|
|
762
680
|
}
|
|
763
681
|
|
|
764
|
-
const summaryLines = [
|
|
765
|
-
'✅ Processamento do TikTok concluído.',
|
|
766
|
-
`• Itens enviados: ${deliveredCount}/${urls.length}`,
|
|
767
|
-
`• Vídeos: ${videosSent}`,
|
|
768
|
-
`• Posts de imagem: ${imagesSent}`,
|
|
769
|
-
];
|
|
682
|
+
const summaryLines = ['✅ Processamento do TikTok concluído.', `• Itens enviados: ${deliveredCount}/${urls.length}`, `• Vídeos: ${videosSent}`, `• Posts de imagem: ${imagesSent}`];
|
|
770
683
|
|
|
771
684
|
if (failures.length > 0) {
|
|
772
685
|
summaryLines.push(`• Falhas: ${failures.length}`);
|
|
773
686
|
}
|
|
774
687
|
|
|
775
|
-
await sendAndStore(
|
|
776
|
-
sock,
|
|
777
|
-
remoteJid,
|
|
778
|
-
{ text: summaryLines.join('\n') },
|
|
779
|
-
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
780
|
-
);
|
|
688
|
+
await sendAndStore(sock, remoteJid, { text: summaryLines.join('\n') }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
781
689
|
}
|
|
782
690
|
} catch (error) {
|
|
783
691
|
logger.error('tiktok: falha ao processar comando.', {
|
|
@@ -979,86 +979,7 @@ const withVerticalSpacing = (lines = []) => lines.flatMap((line, index) => (inde
|
|
|
979
979
|
* @param {object} data Dados agregados do usuário para renderização.
|
|
980
980
|
* @returns {string} Texto completo enviado no comando de perfil.
|
|
981
981
|
*/
|
|
982
|
-
const buildProfileMessage = ({
|
|
983
|
-
mentionLabel,
|
|
984
|
-
displayName,
|
|
985
|
-
phone,
|
|
986
|
-
canonicalTarget,
|
|
987
|
-
status,
|
|
988
|
-
firstMessage,
|
|
989
|
-
tempoDeCasa,
|
|
990
|
-
lastInteraction,
|
|
991
|
-
diasSemFalar,
|
|
992
|
-
totalMessages,
|
|
993
|
-
rankingLabel,
|
|
994
|
-
trendLabel,
|
|
995
|
-
avgPerDay,
|
|
996
|
-
activeDays,
|
|
997
|
-
streakDays,
|
|
998
|
-
activeHourLabel,
|
|
999
|
-
favoriteTypeLabel,
|
|
1000
|
-
dominantTypeByPeriodLabel,
|
|
1001
|
-
socialScore,
|
|
1002
|
-
socialSent,
|
|
1003
|
-
socialReceived,
|
|
1004
|
-
responseRateLabel,
|
|
1005
|
-
socialPartners,
|
|
1006
|
-
topPartnerLabel,
|
|
1007
|
-
topPartnersLabel,
|
|
1008
|
-
topGroupsLabel,
|
|
1009
|
-
globalShareLabel,
|
|
1010
|
-
groupShareLabel,
|
|
1011
|
-
tags,
|
|
1012
|
-
}) =>
|
|
1013
|
-
[
|
|
1014
|
-
'👤 *PERFIL DO USUÁRIO*',
|
|
1015
|
-
'━━━━━━━━━━━━━━━━━━━━',
|
|
1016
|
-
'',
|
|
1017
|
-
'🧾 *Identificação*',
|
|
1018
|
-
...withVerticalSpacing([
|
|
1019
|
-
`• Usuário: ${mentionLabel}`,
|
|
1020
|
-
`• Nome: ${displayName}`,
|
|
1021
|
-
`• Número: ${phone}`,
|
|
1022
|
-
`• ID: ${canonicalTarget || 'N/D'}`,
|
|
1023
|
-
`• Status: *${status}*`,
|
|
1024
|
-
]),
|
|
1025
|
-
'',
|
|
1026
|
-
'📈 *Mensagens e Ranking*',
|
|
1027
|
-
...withVerticalSpacing([
|
|
1028
|
-
`• Primeira mensagem: ${firstMessage}`,
|
|
1029
|
-
`• Tempo de casa no bot: ${tempoDeCasa}`,
|
|
1030
|
-
`• Última interação: ${lastInteraction}`,
|
|
1031
|
-
`• Dias sem falar: ${diasSemFalar}`,
|
|
1032
|
-
`• Mensagens gerais registradas: ${totalMessages}`,
|
|
1033
|
-
`• Participação global: ${globalShareLabel}`,
|
|
1034
|
-
`• Participação no grupo atual: ${groupShareLabel}`,
|
|
1035
|
-
`• Posição no ranking (mensagens): ${rankingLabel}`,
|
|
1036
|
-
`• Tendência de mensagens: ${trendLabel}`,
|
|
1037
|
-
`• Média/dia (global): ${avgPerDay}`,
|
|
1038
|
-
`• Dias ativos (global): ${activeDays}`,
|
|
1039
|
-
`• Streak (global): ${streakDays} dia(s)`,
|
|
1040
|
-
`• Horário mais ativo: ${activeHourLabel}`,
|
|
1041
|
-
`• Tipo favorito (global): ${favoriteTypeLabel}`,
|
|
1042
|
-
`• Tipo dominante por período: ${dominantTypeByPeriodLabel}`,
|
|
1043
|
-
]),
|
|
1044
|
-
'',
|
|
1045
|
-
'🌐 *Interações Sociais*',
|
|
1046
|
-
...withVerticalSpacing([
|
|
1047
|
-
`• Interações sociais (${SOCIAL_RECENT_DAYS}d): ${socialScore}`,
|
|
1048
|
-
`• Respostas enviadas (${SOCIAL_RECENT_DAYS}d): ${socialSent}`,
|
|
1049
|
-
`• Respostas recebidas (${SOCIAL_RECENT_DAYS}d): ${socialReceived}`,
|
|
1050
|
-
`• Taxa de resposta (${SOCIAL_RECENT_DAYS}d): ${responseRateLabel}`,
|
|
1051
|
-
`• Parceiros sociais (${SOCIAL_RECENT_DAYS}d): ${socialPartners}`,
|
|
1052
|
-
`• Parceiro principal (${SOCIAL_RECENT_DAYS}d): ${topPartnerLabel}`,
|
|
1053
|
-
`• Top 3 parceiros (${SOCIAL_RECENT_DAYS}d):\n${topPartnersLabel}`,
|
|
1054
|
-
]),
|
|
1055
|
-
'',
|
|
1056
|
-
'🏘️ *Presença em Grupos*',
|
|
1057
|
-
...withVerticalSpacing([`• Top grupos onde fala:\n${topGroupsLabel}`]),
|
|
1058
|
-
'',
|
|
1059
|
-
'🏷️ *Contexto*',
|
|
1060
|
-
...withVerticalSpacing([`• Tags: ${tags.length ? tags.join(', ') : 'sem tags'}`]),
|
|
1061
|
-
].join('\n');
|
|
982
|
+
const buildProfileMessage = ({ mentionLabel, displayName, phone, canonicalTarget, status, firstMessage, tempoDeCasa, lastInteraction, diasSemFalar, totalMessages, rankingLabel, trendLabel, avgPerDay, activeDays, streakDays, activeHourLabel, favoriteTypeLabel, dominantTypeByPeriodLabel, socialScore, socialSent, socialReceived, responseRateLabel, socialPartners, topPartnerLabel, topPartnersLabel, topGroupsLabel, globalShareLabel, groupShareLabel, tags }) => ['👤 *PERFIL DO USUÁRIO*', '━━━━━━━━━━━━━━━━━━━━', '', '🧾 *Identificação*', ...withVerticalSpacing([`• Usuário: ${mentionLabel}`, `• Nome: ${displayName}`, `• Número: ${phone}`, `• ID: ${canonicalTarget || 'N/D'}`, `• Status: *${status}*`]), '', '📈 *Mensagens e Ranking*', ...withVerticalSpacing([`• Primeira mensagem: ${firstMessage}`, `• Tempo de casa no bot: ${tempoDeCasa}`, `• Última interação: ${lastInteraction}`, `• Dias sem falar: ${diasSemFalar}`, `• Mensagens gerais registradas: ${totalMessages}`, `• Participação global: ${globalShareLabel}`, `• Participação no grupo atual: ${groupShareLabel}`, `• Posição no ranking (mensagens): ${rankingLabel}`, `• Tendência de mensagens: ${trendLabel}`, `• Média/dia (global): ${avgPerDay}`, `• Dias ativos (global): ${activeDays}`, `• Streak (global): ${streakDays} dia(s)`, `• Horário mais ativo: ${activeHourLabel}`, `• Tipo favorito (global): ${favoriteTypeLabel}`, `• Tipo dominante por período: ${dominantTypeByPeriodLabel}`]), '', '🌐 *Interações Sociais*', ...withVerticalSpacing([`• Interações sociais (${SOCIAL_RECENT_DAYS}d): ${socialScore}`, `• Respostas enviadas (${SOCIAL_RECENT_DAYS}d): ${socialSent}`, `• Respostas recebidas (${SOCIAL_RECENT_DAYS}d): ${socialReceived}`, `• Taxa de resposta (${SOCIAL_RECENT_DAYS}d): ${responseRateLabel}`, `• Parceiros sociais (${SOCIAL_RECENT_DAYS}d): ${socialPartners}`, `• Parceiro principal (${SOCIAL_RECENT_DAYS}d): ${topPartnerLabel}`, `• Top 3 parceiros (${SOCIAL_RECENT_DAYS}d):\n${topPartnersLabel}`]), '', '🏘️ *Presença em Grupos*', ...withVerticalSpacing([`• Top grupos onde fala:\n${topGroupsLabel}`]), '', '🏷️ *Contexto*', ...withVerticalSpacing([`• Tags: ${tags.length ? tags.join(', ') : 'sem tags'}`])].join('\n');
|
|
1062
983
|
|
|
1063
984
|
/**
|
|
1064
985
|
* Seleciona o primeiro ID de usuário válido dentro de uma lista.
|
|
@@ -1106,14 +1027,7 @@ export async function handleUserCommand({ sock, remoteJid, messageInfo, expirati
|
|
|
1106
1027
|
const senderCanonical = resolveUserIdCached({ jid: senderJid, lid: senderJid, participantAlt: null });
|
|
1107
1028
|
const rankingTargetId = mentionJid || canonicalTarget;
|
|
1108
1029
|
|
|
1109
|
-
const [stats, ranking, latestPushName, premiumUsers, blocked, groupAdmin] = await Promise.all([
|
|
1110
|
-
fetchUserStats({ canonicalId: rankingTargetId, senderIds: normalizedTargetIds }),
|
|
1111
|
-
fetchUserRanking(rankingTargetId),
|
|
1112
|
-
fetchLatestPushName(normalizedTargetIds),
|
|
1113
|
-
premiumUserStore.getPremiumUsers(),
|
|
1114
|
-
isTargetBlocked(sock, normalizedTargetIds),
|
|
1115
|
-
isGroupMessage ? isUserAdmin(remoteJid, mentionJid || canonicalTarget) : Promise.resolve(false),
|
|
1116
|
-
]);
|
|
1030
|
+
const [stats, ranking, latestPushName, premiumUsers, blocked, groupAdmin] = await Promise.all([fetchUserStats({ canonicalId: rankingTargetId, senderIds: normalizedTargetIds }), fetchUserRanking(rankingTargetId), fetchLatestPushName(normalizedTargetIds), premiumUserStore.getPremiumUsers(), isTargetBlocked(sock, normalizedTargetIds), isGroupMessage ? isUserAdmin(remoteJid, mentionJid || canonicalTarget) : Promise.resolve(false)]);
|
|
1117
1031
|
const [globalInsights, socialInsights, trendInsights, activeHourInsights, dominantTypeByPeriod, topGroups, participationInsights] = await Promise.all([
|
|
1118
1032
|
fetchUserGlobalRankingInsights({
|
|
1119
1033
|
canonicalId: rankingTargetId,
|
|
@@ -42,7 +42,9 @@ const normalizeLabel = (value, fallback = 'unknown') => {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const normalizeHttpMethod = (method) => {
|
|
45
|
-
const normalized = String(method || '')
|
|
45
|
+
const normalized = String(method || '')
|
|
46
|
+
.trim()
|
|
47
|
+
.toUpperCase();
|
|
46
48
|
if (!normalized) return 'UNKNOWN';
|
|
47
49
|
if (normalized.length > 12) return normalized.slice(0, 12);
|
|
48
50
|
return normalized;
|
|
@@ -700,14 +702,7 @@ export const recordSocialXpCapHit = ({ scope = 'earn' } = {}) => {
|
|
|
700
702
|
m.socialXpCapHitsTotal.inc({ scope: normalizeLabel(scope, 'earn') });
|
|
701
703
|
};
|
|
702
704
|
|
|
703
|
-
export const recordStickerAutoPackCycle = ({
|
|
704
|
-
durationMs,
|
|
705
|
-
assetsScanned = 0,
|
|
706
|
-
assetsAdded = 0,
|
|
707
|
-
duplicateRate = null,
|
|
708
|
-
rejectionRate = null,
|
|
709
|
-
fillRate = null,
|
|
710
|
-
} = {}) => {
|
|
705
|
+
export const recordStickerAutoPackCycle = ({ durationMs, assetsScanned = 0, assetsAdded = 0, duplicateRate = null, rejectionRate = null, fillRate = null } = {}) => {
|
|
711
706
|
const m = ensureMetrics();
|
|
712
707
|
if (!m) return;
|
|
713
708
|
|
|
@@ -742,13 +737,7 @@ export const recordStickerAutoPackCycle = ({
|
|
|
742
737
|
}
|
|
743
738
|
};
|
|
744
739
|
|
|
745
|
-
export const recordStickerClassificationCycle = ({
|
|
746
|
-
status = 'ok',
|
|
747
|
-
durationMs,
|
|
748
|
-
processed = 0,
|
|
749
|
-
classified = 0,
|
|
750
|
-
failed = 0,
|
|
751
|
-
} = {}) => {
|
|
740
|
+
export const recordStickerClassificationCycle = ({ status = 'ok', durationMs, processed = 0, classified = 0, failed = 0 } = {}) => {
|
|
752
741
|
const m = ensureMetrics();
|
|
753
742
|
if (!m) return;
|
|
754
743
|
|
|
@@ -11,12 +11,7 @@ const CAPTCHA_OK_EMOJI = process.env.CAPTCHA_OK_EMOJI || '✅';
|
|
|
11
11
|
|
|
12
12
|
const pendingCaptchas = new Map();
|
|
13
13
|
const captchaMessageState = new Map();
|
|
14
|
-
const NON_HUMAN_CAPTCHA_MESSAGE_TEXTS = new Set([
|
|
15
|
-
'Mensagem vazia',
|
|
16
|
-
'Tipo de mensagem não suportado ou sem conteúdo.',
|
|
17
|
-
'[Histórico de mensagens]',
|
|
18
|
-
'[Aviso de histórico de mensagens]',
|
|
19
|
-
]);
|
|
14
|
+
const NON_HUMAN_CAPTCHA_MESSAGE_TEXTS = new Set(['Mensagem vazia', 'Tipo de mensagem não suportado ou sem conteúdo.', '[Histórico de mensagens]', '[Aviso de histórico de mensagens]']);
|
|
20
15
|
|
|
21
16
|
const buildMessageStateKey = (groupId, messageId) => `${groupId}:${messageId}`;
|
|
22
17
|
const normalizeMessageText = (messageText) => (typeof messageText === 'string' ? messageText.trim() : '');
|
|
@@ -23,10 +23,7 @@ const parseNumber = (value, fallback) => {
|
|
|
23
23
|
*
|
|
24
24
|
* @type {number}
|
|
25
25
|
*/
|
|
26
|
-
const FLUSH_INTERVAL_MS = Math.min(
|
|
27
|
-
3000,
|
|
28
|
-
Math.max(1000, parseNumber(process.env.DB_WRITE_FLUSH_MS, 1500)),
|
|
29
|
-
);
|
|
26
|
+
const FLUSH_INTERVAL_MS = Math.min(3000, Math.max(1000, parseNumber(process.env.DB_WRITE_FLUSH_MS, 1500)));
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
29
|
* Tamanho máximo do batch de mensagens por INSERT.
|
|
@@ -56,10 +53,7 @@ const CHAT_COOLDOWN_MS = Math.max(1000, Math.floor(parseNumber(process.env.DB_CH
|
|
|
56
53
|
*
|
|
57
54
|
* @type {number}
|
|
58
55
|
*/
|
|
59
|
-
const MESSAGE_QUEUE_MAX = Math.max(
|
|
60
|
-
MESSAGE_BATCH_SIZE * 5,
|
|
61
|
-
Math.floor(parseNumber(process.env.DB_MESSAGE_QUEUE_MAX, 5000)),
|
|
62
|
-
);
|
|
56
|
+
const MESSAGE_QUEUE_MAX = Math.max(MESSAGE_BATCH_SIZE * 5, Math.floor(parseNumber(process.env.DB_MESSAGE_QUEUE_MAX, 5000)));
|
|
63
57
|
|
|
64
58
|
/**
|
|
65
59
|
* Regex de erro para payload JSON inválido no MySQL.
|
|
@@ -85,7 +79,6 @@ const messageQueue = [];
|
|
|
85
79
|
*/
|
|
86
80
|
const messagePendingIds = new Set();
|
|
87
81
|
|
|
88
|
-
|
|
89
82
|
/**
|
|
90
83
|
* Fila (por chat.id) com atualizações pendentes de chats.
|
|
91
84
|
* @type {Map<string, {id:string, name:(string|null), raw:(Object|null), rawHash:string, queuedAt:number, nextAllowedAt:number}>}
|
|
@@ -106,7 +99,6 @@ const chatQueue = new Map();
|
|
|
106
99
|
*/
|
|
107
100
|
const chatCache = new Map();
|
|
108
101
|
|
|
109
|
-
|
|
110
102
|
/**
|
|
111
103
|
* Indica se já há um flush agendado via setImmediate.
|
|
112
104
|
* @type {boolean}
|
|
@@ -237,14 +229,7 @@ const insertMessageBatch = async (batch) => {
|
|
|
237
229
|
const placeholders = buildPlaceholders(batch.length, 6);
|
|
238
230
|
const params = [];
|
|
239
231
|
for (const message of batch) {
|
|
240
|
-
params.push(
|
|
241
|
-
message.message_id,
|
|
242
|
-
message.chat_id,
|
|
243
|
-
message.sender_id,
|
|
244
|
-
message.content,
|
|
245
|
-
message.raw_message,
|
|
246
|
-
message.timestamp,
|
|
247
|
-
);
|
|
232
|
+
params.push(message.message_id, message.chat_id, message.sender_id, message.content, message.raw_message, message.timestamp);
|
|
248
233
|
}
|
|
249
234
|
|
|
250
235
|
const sql = `INSERT IGNORE INTO ${TABLES.MESSAGES}
|
|
@@ -3,10 +3,7 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import logger from '../utils/logger/loggerModule.js';
|
|
4
4
|
import { executeQuery, TABLES } from '../../database/index.js';
|
|
5
5
|
|
|
6
|
-
const FEATURE_FLAG_CACHE_TTL_MS = Math.max(
|
|
7
|
-
5_000,
|
|
8
|
-
Number(process.env.FEATURE_FLAG_CACHE_TTL_MS) || 30_000,
|
|
9
|
-
);
|
|
6
|
+
const FEATURE_FLAG_CACHE_TTL_MS = Math.max(5_000, Number(process.env.FEATURE_FLAG_CACHE_TTL_MS) || 30_000);
|
|
10
7
|
|
|
11
8
|
let cacheState = {
|
|
12
9
|
loadedAt: 0,
|
|
@@ -106,10 +103,7 @@ const resolveEnvFallback = (flagName, fallback) => {
|
|
|
106
103
|
return toBool(process.env[envKey], fallback);
|
|
107
104
|
};
|
|
108
105
|
|
|
109
|
-
export const isFeatureEnabled = async (
|
|
110
|
-
flagName,
|
|
111
|
-
{ fallback = false, subjectKey = '' } = {},
|
|
112
|
-
) => {
|
|
106
|
+
export const isFeatureEnabled = async (flagName, { fallback = false, subjectKey = '' } = {}) => {
|
|
113
107
|
const normalizedFlagName = normalizeFlagName(flagName);
|
|
114
108
|
if (!normalizedFlagName) return Boolean(fallback);
|
|
115
109
|
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* @param {number} cols
|
|
5
5
|
* @returns {string}
|
|
6
6
|
*/
|
|
7
|
-
export const buildPlaceholders = (rows, cols) =>
|
|
8
|
-
Array.from({ length: rows }, () => `(${Array(cols).fill('?').join(', ')})`).join(', ');
|
|
7
|
+
export const buildPlaceholders = (rows, cols) => Array.from({ length: rows }, () => `(${Array(cols).fill('?').join(', ')})`).join(', ');
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Cria placeholders repetindo um template por linha.
|
|
@@ -13,8 +12,7 @@ export const buildPlaceholders = (rows, cols) =>
|
|
|
13
12
|
* @param {string} rowTemplate
|
|
14
13
|
* @returns {string}
|
|
15
14
|
*/
|
|
16
|
-
export const buildRowPlaceholders = (rows, rowTemplate) =>
|
|
17
|
-
Array.from({ length: rows }, () => rowTemplate).join(', ');
|
|
15
|
+
export const buildRowPlaceholders = (rows, rowTemplate) => Array.from({ length: rows }, () => rowTemplate).join(', ');
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* Cria um executor de flush com controle de concorrencia e re-try imediato.
|
|
@@ -45,8 +45,12 @@ function buildHintSignature(phoneDigits, tsSeconds) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function safeHexCompare(left, right) {
|
|
48
|
-
const leftHex = String(left || '')
|
|
49
|
-
|
|
48
|
+
const leftHex = String(left || '')
|
|
49
|
+
.trim()
|
|
50
|
+
.toLowerCase();
|
|
51
|
+
const rightHex = String(right || '')
|
|
52
|
+
.trim()
|
|
53
|
+
.toLowerCase();
|
|
50
54
|
if (!leftHex || !rightHex || leftHex.length !== rightHex.length) return false;
|
|
51
55
|
|
|
52
56
|
try {
|
|
@@ -60,13 +64,7 @@ function safeHexCompare(left, right) {
|
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
function resolveLoginBaseUrl(explicitBaseUrl = '') {
|
|
63
|
-
const candidates = [
|
|
64
|
-
explicitBaseUrl,
|
|
65
|
-
process.env.WHATSAPP_LOGIN_BASE_URL,
|
|
66
|
-
process.env.SITE_ORIGIN,
|
|
67
|
-
process.env.PUBLIC_WEB_BASE_URL,
|
|
68
|
-
DEFAULT_LOGIN_BASE_URL,
|
|
69
|
-
];
|
|
67
|
+
const candidates = [explicitBaseUrl, process.env.WHATSAPP_LOGIN_BASE_URL, process.env.SITE_ORIGIN, process.env.PUBLIC_WEB_BASE_URL, DEFAULT_LOGIN_BASE_URL];
|
|
70
68
|
|
|
71
69
|
for (const candidate of candidates) {
|
|
72
70
|
const raw = String(candidate || '').trim();
|
|
@@ -2,8 +2,7 @@ import groupConfigStore from './groupConfigStore.js';
|
|
|
2
2
|
|
|
3
3
|
const PREMIUM_CONFIG_ID = 'system:premium_users';
|
|
4
4
|
|
|
5
|
-
const normalizeList = (list) =>
|
|
6
|
-
Array.from(new Set((Array.isArray(list) ? list : []).filter(Boolean)));
|
|
5
|
+
const normalizeList = (list) => Array.from(new Set((Array.isArray(list) ? list : []).filter(Boolean)));
|
|
7
6
|
|
|
8
7
|
const premiumUserStore = {
|
|
9
8
|
getPremiumUsers: async function () {
|