@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
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
const FALLBACK_THUMB_URL = '/assets/images/brand-logo-128.webp';
|
|
2
|
+
const HOME_BOOTSTRAP_ENDPOINT = '/api/sticker-packs/home-bootstrap';
|
|
3
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
4
|
+
let homeBootstrapPayloadPromise = null;
|
|
5
|
+
|
|
6
|
+
const fetchHomeBootstrapPayload = async () => {
|
|
7
|
+
if (!homeBootstrapPayloadPromise) {
|
|
8
|
+
homeBootstrapPayloadPromise = fetch(HOME_BOOTSTRAP_ENDPOINT, { credentials: 'include' })
|
|
9
|
+
.then((response) => {
|
|
10
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
11
|
+
return response.json();
|
|
12
|
+
})
|
|
13
|
+
.then((payload) => payload?.data || {})
|
|
14
|
+
.catch((error) => {
|
|
15
|
+
homeBootstrapPayloadPromise = null;
|
|
16
|
+
throw error;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return homeBootstrapPayloadPromise;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const createIcon = (iconId, className = 'icon') => {
|
|
23
|
+
const wrapper = document.createElement('span');
|
|
24
|
+
wrapper.className = className;
|
|
25
|
+
wrapper.setAttribute('aria-hidden', 'true');
|
|
26
|
+
|
|
27
|
+
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
28
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
29
|
+
svg.setAttribute('focusable', 'false');
|
|
30
|
+
|
|
31
|
+
const use = document.createElementNS(SVG_NS, 'use');
|
|
32
|
+
use.setAttribute('href', `#${iconId}`);
|
|
33
|
+
|
|
34
|
+
svg.appendChild(use);
|
|
35
|
+
wrapper.appendChild(svg);
|
|
36
|
+
return wrapper;
|
|
37
|
+
};
|
|
2
38
|
|
|
3
39
|
const shortNum = (value) =>
|
|
4
40
|
new Intl.NumberFormat('pt-BR', {
|
|
@@ -6,45 +42,48 @@ const shortNum = (value) =>
|
|
|
6
42
|
maximumFractionDigits: Number(value) >= 1000 ? 1 : 0,
|
|
7
43
|
}).format(Math.max(0, Number(value) || 0));
|
|
8
44
|
|
|
9
|
-
const animateCountUp = (element, value,
|
|
45
|
+
const animateCountUp = (element, value, durationMs = 780) => {
|
|
10
46
|
if (!element) return;
|
|
47
|
+
|
|
11
48
|
const target = Math.max(0, Number(value) || 0);
|
|
12
49
|
if (!Number.isFinite(target)) {
|
|
13
|
-
element.textContent =
|
|
50
|
+
element.textContent = shortNum(0);
|
|
14
51
|
return;
|
|
15
52
|
}
|
|
16
53
|
|
|
17
54
|
if (typeof requestAnimationFrame !== 'function' || typeof performance === 'undefined') {
|
|
55
|
+
element.textContent = shortNum(target);
|
|
18
56
|
element.dataset.value = String(target);
|
|
19
|
-
element.textContent = formatter(target);
|
|
20
57
|
return;
|
|
21
58
|
}
|
|
22
59
|
|
|
23
60
|
const previous = Number(element.dataset.value || 0);
|
|
24
61
|
const start = Number.isFinite(previous) ? previous : 0;
|
|
25
62
|
const delta = target - start;
|
|
26
|
-
const
|
|
27
|
-
const easeOut = (t) => 1 - Math.pow(1 - t, 3);
|
|
63
|
+
const startAt = performance.now();
|
|
28
64
|
|
|
29
65
|
const tick = (now) => {
|
|
30
|
-
const progress = Math.min(1, (now -
|
|
31
|
-
const eased =
|
|
32
|
-
const
|
|
33
|
-
element.textContent =
|
|
34
|
-
if (progress < 1)
|
|
66
|
+
const progress = Math.min(1, (now - startAt) / durationMs);
|
|
67
|
+
const eased = 1 - Math.pow(1 - progress, 3);
|
|
68
|
+
const current = start + delta * eased;
|
|
69
|
+
element.textContent = shortNum(current);
|
|
70
|
+
if (progress < 1) {
|
|
71
|
+
requestAnimationFrame(tick);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
element.dataset.value = String(target);
|
|
35
75
|
};
|
|
36
76
|
|
|
37
|
-
element.dataset.value = String(target);
|
|
38
77
|
requestAnimationFrame(tick);
|
|
39
78
|
};
|
|
40
79
|
|
|
41
|
-
const runAfterLoadIdle = (callback, { delayMs = 0, timeoutMs =
|
|
80
|
+
const runAfterLoadIdle = (callback, { delayMs = 0, timeoutMs = 1800 } = {}) => {
|
|
42
81
|
let cancelled = false;
|
|
43
|
-
let loadListener = null;
|
|
44
82
|
let timeoutId = null;
|
|
45
83
|
let idleId = null;
|
|
84
|
+
let loadHandler = null;
|
|
46
85
|
|
|
47
|
-
const
|
|
86
|
+
const run = () => {
|
|
48
87
|
if (cancelled) return;
|
|
49
88
|
callback();
|
|
50
89
|
};
|
|
@@ -52,313 +91,118 @@ const runAfterLoadIdle = (callback, { delayMs = 0, timeoutMs = 2200 } = {}) => {
|
|
|
52
91
|
const schedule = () => {
|
|
53
92
|
if (cancelled) return;
|
|
54
93
|
|
|
55
|
-
const
|
|
94
|
+
const invoke = () => {
|
|
56
95
|
if (cancelled) return;
|
|
57
96
|
if (delayMs > 0) {
|
|
58
|
-
timeoutId = window.setTimeout(
|
|
97
|
+
timeoutId = window.setTimeout(run, delayMs);
|
|
59
98
|
return;
|
|
60
99
|
}
|
|
61
|
-
|
|
100
|
+
run();
|
|
62
101
|
};
|
|
63
102
|
|
|
64
103
|
if (typeof window.requestIdleCallback === 'function') {
|
|
65
|
-
idleId = window.requestIdleCallback(
|
|
104
|
+
idleId = window.requestIdleCallback(invoke, { timeout: timeoutMs });
|
|
66
105
|
return;
|
|
67
106
|
}
|
|
68
107
|
|
|
69
|
-
timeoutId = window.setTimeout(
|
|
108
|
+
timeoutId = window.setTimeout(invoke, Math.min(240, delayMs || 120));
|
|
70
109
|
};
|
|
71
110
|
|
|
72
111
|
if (document.readyState === 'complete') {
|
|
73
112
|
schedule();
|
|
74
113
|
} else {
|
|
75
|
-
|
|
76
|
-
window.addEventListener('load',
|
|
114
|
+
loadHandler = () => schedule();
|
|
115
|
+
window.addEventListener('load', loadHandler, { once: true });
|
|
77
116
|
}
|
|
78
117
|
|
|
79
118
|
return () => {
|
|
80
119
|
cancelled = true;
|
|
81
|
-
if (
|
|
82
|
-
window.removeEventListener('load', loadListener);
|
|
83
|
-
}
|
|
84
|
-
if (timeoutId !== null) {
|
|
85
|
-
window.clearTimeout(timeoutId);
|
|
86
|
-
}
|
|
120
|
+
if (timeoutId !== null) window.clearTimeout(timeoutId);
|
|
87
121
|
if (idleId !== null && typeof window.cancelIdleCallback === 'function') {
|
|
88
122
|
window.cancelIdleCallback(idleId);
|
|
89
123
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const initMarketplacePreview = () => {
|
|
94
|
-
const proofPacks = document.getElementById('proof-packs');
|
|
95
|
-
const proofStickers = document.getElementById('proof-stickers');
|
|
96
|
-
const proofDownloads = document.getElementById('proof-downloads');
|
|
97
|
-
const proofUsers = document.getElementById('proof-users');
|
|
98
|
-
const proofGroups = document.getElementById('proof-groups');
|
|
99
|
-
const proofSystem = document.getElementById('proof-system');
|
|
100
|
-
const previewStatus = document.getElementById('hero-preview-status');
|
|
101
|
-
const previewGrid = document.getElementById('hero-pack-preview');
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
!proofPacks
|
|
105
|
-
|| !proofStickers
|
|
106
|
-
|| !proofDownloads
|
|
107
|
-
|| !proofUsers
|
|
108
|
-
|| !proofGroups
|
|
109
|
-
|| !proofSystem
|
|
110
|
-
|| !previewStatus
|
|
111
|
-
|| !previewGrid
|
|
112
|
-
) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const isAutoPack = (pack) =>
|
|
117
|
-
Number(pack?.is_auto_pack || pack?.auto_pack || 0) === 1 || /\[auto\]/i.test(String(pack?.name || ''));
|
|
118
|
-
|
|
119
|
-
const renderPreviewSkeleton = (count = 6) => {
|
|
120
|
-
previewGrid.innerHTML = Array.from({ length: count })
|
|
121
|
-
.map(
|
|
122
|
-
() =>
|
|
123
|
-
'<article class="market-pack is-loading">' +
|
|
124
|
-
'<div class="market-pack-skeleton-thumb"></div>' +
|
|
125
|
-
'<div class="market-pack-skeleton-body">' +
|
|
126
|
-
'<span class="market-pack-skeleton-line"></span>' +
|
|
127
|
-
'<span class="market-pack-skeleton-line short"></span>' +
|
|
128
|
-
'</div>' +
|
|
129
|
-
'</article>',
|
|
130
|
-
)
|
|
131
|
-
.join('');
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const renderPreview = (packs) => {
|
|
135
|
-
previewGrid.innerHTML = '';
|
|
136
|
-
if (!Array.isArray(packs) || !packs.length) {
|
|
137
|
-
previewStatus.textContent = 'Sem packs em destaque no momento.';
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
previewStatus.textContent = `${packs.length} packs sugeridos agora`;
|
|
142
|
-
packs.slice(0, 6).forEach((pack, index) => {
|
|
143
|
-
const card = document.createElement('a');
|
|
144
|
-
card.className = 'market-pack reveal';
|
|
145
|
-
card.href = pack.web_url || `/stickers/${encodeURIComponent(pack.pack_key || '')}`;
|
|
146
|
-
card.innerHTML =
|
|
147
|
-
`<img class="market-pack-thumb" loading="lazy" decoding="async" fetchpriority="low" width="320" height="320" src="${pack.cover_url || FALLBACK_THUMB_URL}" alt="${String(
|
|
148
|
-
pack.name || 'Pack',
|
|
149
|
-
).replace(/"/g, '"')}">` +
|
|
150
|
-
(isAutoPack(pack) ? '<span class="market-pack-tag">auto</span>' : '') +
|
|
151
|
-
'<div class="market-pack-body">' +
|
|
152
|
-
`<p class="market-pack-name">${pack.name || 'Pack sem nome'}</p>` +
|
|
153
|
-
`<p class="market-pack-meta">${shortNum(pack.sticker_count || 0)} stickers · ${shortNum(
|
|
154
|
-
pack?.engagement?.open_count || 0,
|
|
155
|
-
)} aberturas</p>` +
|
|
156
|
-
'</div>';
|
|
157
|
-
card.style.transitionDelay = `${index * 40}ms`;
|
|
158
|
-
previewGrid.appendChild(card);
|
|
159
|
-
requestAnimationFrame(() => card.classList.add('in-view'));
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const loadMarketplaceData = async () => {
|
|
164
|
-
try {
|
|
165
|
-
const [statsResponse, intentsResponse] = await Promise.all([
|
|
166
|
-
fetch('/api/sticker-packs/stats'),
|
|
167
|
-
fetch('/api/sticker-packs/intents?limit=12'),
|
|
168
|
-
]);
|
|
169
|
-
|
|
170
|
-
const statsPayload = statsResponse.ok ? await statsResponse.json() : null;
|
|
171
|
-
const intentsPayload = intentsResponse.ok ? await intentsResponse.json() : null;
|
|
172
|
-
const stats = statsPayload?.data || {};
|
|
173
|
-
const intents = intentsPayload?.data || {};
|
|
174
|
-
const trending = Array.isArray(intents?.em_alta) ? intents.em_alta : [];
|
|
175
|
-
|
|
176
|
-
animateCountUp(proofPacks, stats.packs_total || 0);
|
|
177
|
-
animateCountUp(proofStickers, stats.stickers_total || 0);
|
|
178
|
-
animateCountUp(proofDownloads, stats.downloads_total || 0);
|
|
179
|
-
renderPreview(trending);
|
|
180
|
-
} catch {
|
|
181
|
-
proofPacks.textContent = 'n/d';
|
|
182
|
-
proofStickers.textContent = 'n/d';
|
|
183
|
-
proofDownloads.textContent = 'n/d';
|
|
184
|
-
proofUsers.textContent = 'n/d';
|
|
185
|
-
proofGroups.textContent = 'n/d';
|
|
186
|
-
proofSystem.textContent = 'n/d';
|
|
187
|
-
previewGrid.innerHTML = '';
|
|
188
|
-
previewStatus.textContent = 'Não foi possível carregar o preview agora.';
|
|
124
|
+
if (loadHandler) {
|
|
125
|
+
window.removeEventListener('load', loadHandler);
|
|
189
126
|
}
|
|
190
127
|
};
|
|
191
|
-
|
|
192
|
-
renderPreviewSkeleton(6);
|
|
193
|
-
return runAfterLoadIdle(() => {
|
|
194
|
-
void loadMarketplaceData();
|
|
195
|
-
}, { timeoutMs: 1200 });
|
|
196
128
|
};
|
|
197
129
|
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
if (!
|
|
202
|
-
|
|
203
|
-
const formatDate = (value) => {
|
|
204
|
-
const time = Date.parse(String(value || ''));
|
|
205
|
-
if (!Number.isFinite(time)) return 'n/d';
|
|
206
|
-
return new Intl.DateTimeFormat('pt-BR', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(time));
|
|
207
|
-
};
|
|
130
|
+
const initNavToggle = () => {
|
|
131
|
+
const toggle = document.getElementById('nav-toggle');
|
|
132
|
+
const nav = document.getElementById('main-nav');
|
|
133
|
+
if (!toggle || !nav) return null;
|
|
208
134
|
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
135
|
+
const closeMenu = () => {
|
|
136
|
+
nav.classList.remove('open');
|
|
137
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
212
138
|
};
|
|
213
139
|
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
summaryEl.textContent =
|
|
218
|
-
`Top recentes: ${Number(data?.top_share_percent || 0).toFixed(
|
|
219
|
-
2,
|
|
220
|
-
)}% das ${Number(data?.total_messages || 0)} mensagens · Tipo mais usado: ${topType} · Janela: ${Number(
|
|
221
|
-
data?.sample_limit || 0,
|
|
222
|
-
)} msgs · Atualizado: ${formatDate(
|
|
223
|
-
data?.updated_at,
|
|
224
|
-
)}`;
|
|
225
|
-
|
|
226
|
-
if (!rows.length) {
|
|
227
|
-
renderFallback('Ainda não há mensagens suficientes para o ranking global.');
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
listEl.innerHTML = '';
|
|
232
|
-
rows.forEach((row) => {
|
|
233
|
-
const item = document.createElement('li');
|
|
234
|
-
item.className = 'rank-item';
|
|
235
|
-
const userWrap = document.createElement('span');
|
|
236
|
-
userWrap.className = 'rank-user';
|
|
237
|
-
const avatar = document.createElement('img');
|
|
238
|
-
avatar.className = 'rank-avatar';
|
|
239
|
-
avatar.alt = row.display_name || 'Usuário';
|
|
240
|
-
avatar.loading = 'lazy';
|
|
241
|
-
avatar.decoding = 'async';
|
|
242
|
-
avatar.setAttribute('fetchpriority', 'low');
|
|
243
|
-
avatar.width = 34;
|
|
244
|
-
avatar.height = 34;
|
|
245
|
-
avatar.src = row.avatar_url || FALLBACK_THUMB_URL;
|
|
246
|
-
avatar.onerror = () => {
|
|
247
|
-
avatar.src = FALLBACK_THUMB_URL;
|
|
248
|
-
};
|
|
249
|
-
const name = document.createElement('span');
|
|
250
|
-
name.className = 'rank-name';
|
|
251
|
-
name.textContent = `${row.position}. ${row.display_name || 'Desconhecido'}`;
|
|
252
|
-
userWrap.append(avatar, name);
|
|
253
|
-
const value = document.createElement('span');
|
|
254
|
-
value.className = 'rank-value';
|
|
255
|
-
value.textContent = `${Number(row.total_messages || 0)} msg · ${Number(row.percent_of_total || 0).toFixed(2)}%`;
|
|
256
|
-
item.append(userWrap, value);
|
|
257
|
-
listEl.appendChild(item);
|
|
258
|
-
});
|
|
140
|
+
const onClick = () => {
|
|
141
|
+
const isOpen = nav.classList.toggle('open');
|
|
142
|
+
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
|
259
143
|
};
|
|
260
144
|
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (!response.ok) throw new Error('Falha ao carregar ranking');
|
|
265
|
-
return response.json();
|
|
266
|
-
})
|
|
267
|
-
.then((payload) => {
|
|
268
|
-
renderRanking(payload?.data || {});
|
|
269
|
-
})
|
|
270
|
-
.catch(() => {
|
|
271
|
-
renderFallback('Ranking indisponível no momento.');
|
|
272
|
-
});
|
|
145
|
+
const onNavClick = (event) => {
|
|
146
|
+
const target = event.target;
|
|
147
|
+
if (!(target instanceof Element)) return;
|
|
273
148
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
void loadRanking();
|
|
277
|
-
intervalId = window.setInterval(() => {
|
|
278
|
-
void loadRanking();
|
|
279
|
-
}, 10 * 60 * 1000);
|
|
280
|
-
}, { delayMs: 450, timeoutMs: 2200 });
|
|
149
|
+
const link = target.closest('a[href]');
|
|
150
|
+
if (!link) return;
|
|
281
151
|
|
|
282
|
-
|
|
283
|
-
cancelScheduledLoad();
|
|
284
|
-
if (intervalId !== null) {
|
|
285
|
-
window.clearInterval(intervalId);
|
|
286
|
-
}
|
|
152
|
+
closeMenu();
|
|
287
153
|
};
|
|
288
|
-
};
|
|
289
154
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const nav = document.getElementById('main-nav');
|
|
293
|
-
if (!toggle || !nav) return null;
|
|
155
|
+
toggle.addEventListener('click', onClick);
|
|
156
|
+
nav.addEventListener('click', onNavClick);
|
|
294
157
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
158
|
+
return () => {
|
|
159
|
+
toggle.removeEventListener('click', onClick);
|
|
160
|
+
nav.removeEventListener('click', onNavClick);
|
|
298
161
|
};
|
|
299
|
-
|
|
300
|
-
toggle.addEventListener('click', handleClick);
|
|
301
|
-
return () => toggle.removeEventListener('click', handleClick);
|
|
302
162
|
};
|
|
303
163
|
|
|
304
164
|
const initAuthSession = () => {
|
|
305
165
|
const authLink = document.getElementById('nav-auth-link');
|
|
306
|
-
const schedulerLink = document.getElementById('nav-scheduler-link');
|
|
307
166
|
const heroLoginCta = document.getElementById('hero-login-cta');
|
|
167
|
+
const finalLoginCta = document.getElementById('final-login-cta');
|
|
308
168
|
if (!authLink) return null;
|
|
309
169
|
|
|
310
170
|
const clearChildren = (node) => {
|
|
311
|
-
while (node.firstChild)
|
|
312
|
-
node.removeChild(node.firstChild);
|
|
313
|
-
}
|
|
171
|
+
while (node.firstChild) node.removeChild(node.firstChild);
|
|
314
172
|
};
|
|
315
173
|
|
|
316
|
-
const
|
|
317
|
-
document.body.classList.remove('home-authenticated');
|
|
174
|
+
const setLoginState = () => {
|
|
318
175
|
authLink.classList.remove('nav-user-chip');
|
|
319
176
|
authLink.href = '/login/';
|
|
320
177
|
authLink.removeAttribute('title');
|
|
321
178
|
authLink.removeAttribute('aria-label');
|
|
322
179
|
clearChildren(authLink);
|
|
323
180
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
181
|
+
const icon = createIcon('icon-login');
|
|
182
|
+
|
|
183
|
+
authLink.append(icon, document.createTextNode('Entrar'));
|
|
328
184
|
|
|
329
185
|
if (heroLoginCta) {
|
|
330
186
|
heroLoginCta.hidden = false;
|
|
331
187
|
heroLoginCta.removeAttribute('aria-hidden');
|
|
332
188
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
authLink.append(icon, document.createTextNode('Login'));
|
|
189
|
+
if (finalLoginCta) {
|
|
190
|
+
finalLoginCta.hidden = false;
|
|
191
|
+
finalLoginCta.removeAttribute('aria-hidden');
|
|
192
|
+
}
|
|
338
193
|
};
|
|
339
194
|
|
|
340
|
-
const
|
|
195
|
+
const setLoggedState = (sessionData) => {
|
|
341
196
|
const profile = sessionData?.user || {};
|
|
342
197
|
const resolvedName = String(profile?.name || profile?.email || 'Conta Google').trim() || 'Conta Google';
|
|
343
198
|
const resolvedPhoto = String(profile?.picture || '').trim() || FALLBACK_THUMB_URL;
|
|
344
199
|
|
|
345
|
-
document.body.classList.add('home-authenticated');
|
|
346
200
|
authLink.classList.add('nav-user-chip');
|
|
347
201
|
authLink.href = '/user/';
|
|
348
202
|
authLink.title = `${resolvedName} (sessão ativa)`;
|
|
349
203
|
authLink.setAttribute('aria-label', `Sessão ativa de ${resolvedName}`);
|
|
350
204
|
clearChildren(authLink);
|
|
351
205
|
|
|
352
|
-
if (schedulerLink) {
|
|
353
|
-
schedulerLink.hidden = true;
|
|
354
|
-
schedulerLink.setAttribute('aria-hidden', 'true');
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (heroLoginCta) {
|
|
358
|
-
heroLoginCta.hidden = true;
|
|
359
|
-
heroLoginCta.setAttribute('aria-hidden', 'true');
|
|
360
|
-
}
|
|
361
|
-
|
|
362
206
|
const avatarBubble = document.createElement('span');
|
|
363
207
|
avatarBubble.className = 'nav-user-avatar-bubble';
|
|
364
208
|
|
|
@@ -368,7 +212,6 @@ const initAuthSession = () => {
|
|
|
368
212
|
photo.alt = `Foto de ${resolvedName}`;
|
|
369
213
|
photo.loading = 'lazy';
|
|
370
214
|
photo.decoding = 'async';
|
|
371
|
-
photo.setAttribute('fetchpriority', 'low');
|
|
372
215
|
photo.width = 34;
|
|
373
216
|
photo.height = 34;
|
|
374
217
|
photo.onerror = () => {
|
|
@@ -379,9 +222,7 @@ const initAuthSession = () => {
|
|
|
379
222
|
const nameBubble = document.createElement('span');
|
|
380
223
|
nameBubble.className = 'nav-user-name-bubble';
|
|
381
224
|
|
|
382
|
-
const icon =
|
|
383
|
-
icon.className = 'fa-solid fa-user nav-user-icon';
|
|
384
|
-
icon.setAttribute('aria-hidden', 'true');
|
|
225
|
+
const icon = createIcon('icon-user', 'icon nav-user-icon');
|
|
385
226
|
|
|
386
227
|
const name = document.createElement('span');
|
|
387
228
|
name.className = 'nav-user-name';
|
|
@@ -389,198 +230,127 @@ const initAuthSession = () => {
|
|
|
389
230
|
|
|
390
231
|
nameBubble.append(icon, name);
|
|
391
232
|
authLink.append(avatarBubble, nameBubble);
|
|
233
|
+
|
|
234
|
+
if (heroLoginCta) {
|
|
235
|
+
heroLoginCta.hidden = true;
|
|
236
|
+
heroLoginCta.setAttribute('aria-hidden', 'true');
|
|
237
|
+
}
|
|
238
|
+
if (finalLoginCta) {
|
|
239
|
+
finalLoginCta.hidden = true;
|
|
240
|
+
finalLoginCta.setAttribute('aria-hidden', 'true');
|
|
241
|
+
}
|
|
392
242
|
};
|
|
393
243
|
|
|
394
|
-
return runAfterLoadIdle(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}, { delayMs: 200, timeoutMs: 1400 });
|
|
244
|
+
return runAfterLoadIdle(
|
|
245
|
+
() => {
|
|
246
|
+
fetchHomeBootstrapPayload()
|
|
247
|
+
.then((bootstrapData) => {
|
|
248
|
+
const sessionData = bootstrapData?.session || {};
|
|
249
|
+
if (!sessionData?.authenticated || !sessionData?.user?.sub) {
|
|
250
|
+
setLoginState();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
setLoggedState(sessionData);
|
|
254
|
+
})
|
|
255
|
+
.catch(() => {
|
|
256
|
+
setLoginState();
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
{ delayMs: 520, timeoutMs: 1200 },
|
|
260
|
+
);
|
|
412
261
|
};
|
|
413
262
|
|
|
414
|
-
const
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
263
|
+
const initAddBotCtas = () => {
|
|
264
|
+
const ctas = Array.from(document.querySelectorAll('[data-add-bot-cta]'));
|
|
265
|
+
const floatButton = document.getElementById('wpp-float');
|
|
266
|
+
if (!ctas.length && !floatButton) return null;
|
|
267
|
+
|
|
268
|
+
const applyLink = (url) => {
|
|
269
|
+
const safeUrl = String(url || '').trim();
|
|
270
|
+
if (!safeUrl) return false;
|
|
271
|
+
|
|
272
|
+
ctas.forEach((element) => {
|
|
273
|
+
element.href = safeUrl;
|
|
274
|
+
element.target = '_blank';
|
|
275
|
+
element.rel = 'noreferrer noopener';
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (floatButton) {
|
|
279
|
+
floatButton.href = safeUrl;
|
|
280
|
+
floatButton.hidden = false;
|
|
281
|
+
}
|
|
426
282
|
return true;
|
|
427
283
|
};
|
|
428
284
|
|
|
429
|
-
return runAfterLoadIdle(
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
285
|
+
return runAfterLoadIdle(
|
|
286
|
+
() => {
|
|
287
|
+
fetchHomeBootstrapPayload()
|
|
288
|
+
.then((bootstrapData) => {
|
|
289
|
+
const url = String(bootstrapData?.support?.url || '').trim();
|
|
290
|
+
const applied = applyLink(url);
|
|
291
|
+
if (!applied && floatButton) {
|
|
292
|
+
floatButton.hidden = true;
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
.catch(() => {
|
|
296
|
+
if (floatButton) floatButton.hidden = true;
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
{ delayMs: 480, timeoutMs: 1400 },
|
|
300
|
+
);
|
|
444
301
|
};
|
|
445
302
|
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
303
|
+
const initSocialProof = () => {
|
|
304
|
+
const packsEl = document.getElementById('proof-packs');
|
|
305
|
+
const stickersEl = document.getElementById('proof-stickers');
|
|
306
|
+
const groupsEl = document.getElementById('proof-groups');
|
|
307
|
+
const statusEl = document.getElementById('proof-status');
|
|
308
|
+
|
|
309
|
+
if (!packsEl || !stickersEl || !groupsEl) return null;
|
|
310
|
+
|
|
311
|
+
const setFallback = () => {
|
|
312
|
+
packsEl.textContent = 'n/d';
|
|
313
|
+
stickersEl.textContent = 'n/d';
|
|
314
|
+
groupsEl.textContent = 'n/d';
|
|
315
|
+
if (statusEl) statusEl.textContent = 'bot pronto';
|
|
316
|
+
};
|
|
455
317
|
|
|
456
318
|
const normalizeStatus = (value) => {
|
|
457
319
|
const normalized = String(value || '')
|
|
458
320
|
.trim()
|
|
459
321
|
.toLowerCase();
|
|
460
|
-
if (!normalized) return '
|
|
461
|
-
if (['online', '
|
|
462
|
-
if (['
|
|
463
|
-
if (['
|
|
464
|
-
return '
|
|
322
|
+
if (!normalized) return 'pronto';
|
|
323
|
+
if (['online', 'ok', 'healthy'].includes(normalized)) return 'online';
|
|
324
|
+
if (['connecting', 'opening', 'reconnecting'].includes(normalized)) return 'conectando';
|
|
325
|
+
if (['offline', 'down'].includes(normalized)) return 'instável';
|
|
326
|
+
return 'pronto';
|
|
465
327
|
};
|
|
466
328
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const fmt = (value) => (Number.isFinite(value) ? value.toFixed(2) : 'n/d');
|
|
489
|
-
|
|
490
|
-
return runAfterLoadIdle(() => {
|
|
491
|
-
fetch('/api/sticker-packs/system-summary')
|
|
492
|
-
.then((response) => {
|
|
493
|
-
if (!response.ok) throw new Error('Falha ao carregar métricas');
|
|
494
|
-
return response.json();
|
|
495
|
-
})
|
|
496
|
-
.then((payload) => {
|
|
497
|
-
const data = payload && payload.data ? payload.data : {};
|
|
498
|
-
const host = data.host || {};
|
|
499
|
-
const process = data.process || {};
|
|
500
|
-
const observability = data.observability || {};
|
|
501
|
-
const platform = data.platform || {};
|
|
502
|
-
const bot = data.bot || {};
|
|
503
|
-
const systemStatus = normalizeStatus(data.system_status || bot.connection_status);
|
|
504
|
-
|
|
505
|
-
cpuEl.textContent = 'CPU host: ' + fmt(Number(host.cpu_percent)) + '%';
|
|
506
|
-
memEl.textContent =
|
|
507
|
-
'RAM host: ' +
|
|
508
|
-
String(host.memory_used || 'n/d') +
|
|
509
|
-
' / ' +
|
|
510
|
-
String(host.memory_total || 'n/d') +
|
|
511
|
-
' (' +
|
|
512
|
-
fmt(Number(host.memory_percent)) +
|
|
513
|
-
'%)';
|
|
514
|
-
uptimeEl.textContent = 'Uptime processo: ' + String(process.uptime || 'n/d');
|
|
515
|
-
|
|
516
|
-
const lag = Number(observability.lag_p99_ms);
|
|
517
|
-
const dbTotal = observability.db_total;
|
|
518
|
-
const dbSlow = observability.db_slow;
|
|
519
|
-
obsEl.textContent =
|
|
520
|
-
'Lag p99: ' +
|
|
521
|
-
(Number.isFinite(lag) ? lag.toFixed(2) + 'ms' : 'n/d') +
|
|
522
|
-
' | DB slow: ' +
|
|
523
|
-
(Number.isFinite(Number(dbSlow)) && Number.isFinite(Number(dbTotal)) ? String(dbSlow) + '/' + String(dbTotal) : 'n/d');
|
|
524
|
-
|
|
525
|
-
if (proofUsers) animateCountUp(proofUsers, platform.total_users || 0);
|
|
526
|
-
if (proofGroups) animateCountUp(proofGroups, platform.total_groups || 0);
|
|
527
|
-
if (proofSystem) {
|
|
528
|
-
proofSystem.textContent = formatSystemStatusLabel(systemStatus);
|
|
529
|
-
const card = proofSystem.closest('.proof-card');
|
|
530
|
-
if (card) card.dataset.status = systemStatus;
|
|
531
|
-
}
|
|
532
|
-
})
|
|
533
|
-
.catch(() => {
|
|
534
|
-
setFallback();
|
|
535
|
-
});
|
|
536
|
-
}, { delayMs: 650, timeoutMs: 2600 });
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
const initRevealAnimations = () => {
|
|
540
|
-
const rawTargets = Array.from(
|
|
541
|
-
document.querySelectorAll(
|
|
542
|
-
'.market-preview, .section-title, .grid .card, .api, .rank-panel, .final-cta, .hero-stats .chip',
|
|
543
|
-
),
|
|
544
|
-
);
|
|
545
|
-
if (!rawTargets.length) return null;
|
|
546
|
-
|
|
547
|
-
const viewportHeight = window.innerHeight || 0;
|
|
548
|
-
const revealTargets = rawTargets.filter((element) => {
|
|
549
|
-
const rect = element.getBoundingClientRect();
|
|
550
|
-
return rect.top > viewportHeight * 0.7;
|
|
551
|
-
});
|
|
552
|
-
if (!revealTargets.length) return null;
|
|
553
|
-
|
|
554
|
-
revealTargets.forEach((element) => element.classList.add('reveal'));
|
|
555
|
-
if (typeof IntersectionObserver !== 'function') {
|
|
556
|
-
revealTargets.forEach((element) => element.classList.add('in-view'));
|
|
557
|
-
return null;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const observer = new IntersectionObserver(
|
|
561
|
-
(entries) => {
|
|
562
|
-
entries.forEach((entry) => {
|
|
563
|
-
if (entry.isIntersecting) {
|
|
564
|
-
entry.target.classList.add('in-view');
|
|
565
|
-
observer.unobserve(entry.target);
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
root: null,
|
|
571
|
-
threshold: 0.14,
|
|
572
|
-
rootMargin: '0px 0px -8% 0px',
|
|
329
|
+
return runAfterLoadIdle(
|
|
330
|
+
() => {
|
|
331
|
+
fetchHomeBootstrapPayload()
|
|
332
|
+
.then((bootstrapData) => {
|
|
333
|
+
const stats = bootstrapData?.stats || {};
|
|
334
|
+
const summary = bootstrapData?.system_summary || {};
|
|
335
|
+
|
|
336
|
+
animateCountUp(packsEl, Number(stats.packs_total || 0));
|
|
337
|
+
animateCountUp(stickersEl, Number(stats.stickers_total || 0));
|
|
338
|
+
animateCountUp(groupsEl, Number(summary?.platform?.total_groups || 0));
|
|
339
|
+
|
|
340
|
+
if (statusEl) {
|
|
341
|
+
statusEl.textContent = `bot ${normalizeStatus(summary?.system_status || summary?.bot?.connection_status)}`;
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
.catch(() => {
|
|
345
|
+
setFallback();
|
|
346
|
+
});
|
|
573
347
|
},
|
|
348
|
+
{ delayMs: 620, timeoutMs: 1500 },
|
|
574
349
|
);
|
|
575
|
-
|
|
576
|
-
revealTargets.forEach((element) => observer.observe(element));
|
|
577
|
-
return () => observer.disconnect();
|
|
578
350
|
};
|
|
579
351
|
|
|
580
|
-
const registerCleanup = (cleanups,
|
|
581
|
-
if (typeof
|
|
582
|
-
cleanups.push(cleanupFn);
|
|
583
|
-
}
|
|
352
|
+
const registerCleanup = (cleanups, cleanup) => {
|
|
353
|
+
if (typeof cleanup === 'function') cleanups.push(cleanup);
|
|
584
354
|
};
|
|
585
355
|
|
|
586
356
|
const initHomeApp = () => {
|
|
@@ -588,20 +358,17 @@ const initHomeApp = () => {
|
|
|
588
358
|
window.__omnizapHomeAppReady = true;
|
|
589
359
|
|
|
590
360
|
const cleanups = [];
|
|
591
|
-
registerCleanup(cleanups, initMarketplacePreview());
|
|
592
|
-
registerCleanup(cleanups, initRankingPanel());
|
|
593
361
|
registerCleanup(cleanups, initNavToggle());
|
|
594
362
|
registerCleanup(cleanups, initAuthSession());
|
|
595
|
-
registerCleanup(cleanups,
|
|
596
|
-
registerCleanup(cleanups,
|
|
597
|
-
registerCleanup(cleanups, initRevealAnimations());
|
|
363
|
+
registerCleanup(cleanups, initAddBotCtas());
|
|
364
|
+
registerCleanup(cleanups, initSocialProof());
|
|
598
365
|
|
|
599
366
|
window.addEventListener(
|
|
600
367
|
'pagehide',
|
|
601
368
|
() => {
|
|
602
|
-
cleanups.forEach((
|
|
369
|
+
cleanups.forEach((cleanup) => {
|
|
603
370
|
try {
|
|
604
|
-
|
|
371
|
+
cleanup();
|
|
605
372
|
} catch {
|
|
606
373
|
// no-op
|
|
607
374
|
}
|