@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
|
@@ -5,12 +5,8 @@ const parseEnvList = (value) =>
|
|
|
5
5
|
.filter(Boolean);
|
|
6
6
|
|
|
7
7
|
const VERIFIED_PUBLISHERS = new Set(parseEnvList(process.env.STICKER_CREATOR_VERIFIED_PUBLISHERS).map((entry) => entry.toLowerCase()));
|
|
8
|
-
const NSFW_EXPLICIT_THRESHOLD = Number.isFinite(Number(process.env.STICKER_NSFW_EXPLICIT_THRESHOLD))
|
|
9
|
-
|
|
10
|
-
: 0.78;
|
|
11
|
-
const NSFW_SUGGESTIVE_THRESHOLD = Number.isFinite(Number(process.env.STICKER_NSFW_SUGGESTIVE_THRESHOLD))
|
|
12
|
-
? Number(process.env.STICKER_NSFW_SUGGESTIVE_THRESHOLD)
|
|
13
|
-
: 0.4;
|
|
8
|
+
const NSFW_EXPLICIT_THRESHOLD = Number.isFinite(Number(process.env.STICKER_NSFW_EXPLICIT_THRESHOLD)) ? Number(process.env.STICKER_NSFW_EXPLICIT_THRESHOLD) : 0.78;
|
|
9
|
+
const NSFW_SUGGESTIVE_THRESHOLD = Number.isFinite(Number(process.env.STICKER_NSFW_SUGGESTIVE_THRESHOLD)) ? Number(process.env.STICKER_NSFW_SUGGESTIVE_THRESHOLD) : 0.4;
|
|
14
10
|
const AGE_DECAY_DAYS = Math.max(1, Number(process.env.STICKER_PACK_AGE_DECAY_DAYS) || 45);
|
|
15
11
|
|
|
16
12
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
@@ -165,16 +161,7 @@ const computeDuplicatePenalty = ({ itemClassifications = [], duplicateRate = 0 }
|
|
|
165
161
|
return Number(clamp(semanticDuplicateRate * 0.7 + safeNumber(duplicateRate) * 0.3, 0, 1).toFixed(6));
|
|
166
162
|
};
|
|
167
163
|
|
|
168
|
-
export const computePackSignals = ({
|
|
169
|
-
pack,
|
|
170
|
-
engagement,
|
|
171
|
-
packClassification,
|
|
172
|
-
itemClassifications = [],
|
|
173
|
-
interactionStats = null,
|
|
174
|
-
duplicateRate = 0,
|
|
175
|
-
scoringWeights = null,
|
|
176
|
-
ageDecayDays = AGE_DECAY_DAYS,
|
|
177
|
-
}) => {
|
|
164
|
+
export const computePackSignals = ({ pack, engagement, packClassification, itemClassifications = [], interactionStats = null, duplicateRate = 0, scoringWeights = null, ageDecayDays = AGE_DECAY_DAYS }) => {
|
|
178
165
|
const resolvedWeights = {
|
|
179
166
|
classification: clamp(safeNumber(scoringWeights?.classification, 0.4), 0.1, 0.7),
|
|
180
167
|
engagement: clamp(safeNumber(scoringWeights?.engagement, 0.3), 0.1, 0.7),
|
|
@@ -194,12 +181,7 @@ export const computePackSignals = ({
|
|
|
194
181
|
const trendScore = computeTrendScore(interactionStats);
|
|
195
182
|
const nsfwLevel = resolveNsfwLevel(packClassification);
|
|
196
183
|
const sensitiveContent = nsfwLevel !== 'safe';
|
|
197
|
-
const packScoreRaw =
|
|
198
|
-
classificationConfidence * resolvedWeights.classification +
|
|
199
|
-
engagementScore * resolvedWeights.engagement +
|
|
200
|
-
qualityScore * resolvedWeights.quality +
|
|
201
|
-
diversityScore * resolvedWeights.diversity -
|
|
202
|
-
duplicatePenalty * 0.25;
|
|
184
|
+
const packScoreRaw = classificationConfidence * resolvedWeights.classification + engagementScore * resolvedWeights.engagement + qualityScore * resolvedWeights.quality + diversityScore * resolvedWeights.diversity - duplicatePenalty * 0.25;
|
|
203
185
|
const packScore = Number(clamp(packScoreRaw, 0, 1.5).toFixed(6));
|
|
204
186
|
const referenceDate = pack?.updated_at || pack?.created_at || null;
|
|
205
187
|
const ageMs = referenceDate ? Date.now() - Date.parse(referenceDate) : 0;
|
|
@@ -227,8 +209,7 @@ export const computePackSignals = ({
|
|
|
227
209
|
};
|
|
228
210
|
};
|
|
229
211
|
|
|
230
|
-
const sortByScoreDesc = (list, field) =>
|
|
231
|
-
[...list].sort((left, right) => safeNumber(right?.signals?.[field]) - safeNumber(left?.signals?.[field]));
|
|
212
|
+
const sortByScoreDesc = (list, field) => [...list].sort((left, right) => safeNumber(right?.signals?.[field]) - safeNumber(left?.signals?.[field]));
|
|
232
213
|
|
|
233
214
|
const sortByUpdatedDesc = (list) =>
|
|
234
215
|
[...list].sort((left, right) => {
|
|
@@ -246,14 +227,14 @@ export const buildIntentCollections = (entries, { limit = 18 } = {}) => {
|
|
|
246
227
|
return {
|
|
247
228
|
em_alta: pick(sortByScoreDesc(safeOnly, 'ranking_score')),
|
|
248
229
|
novos: pick(sortByUpdatedDesc(safeOnly)),
|
|
249
|
-
crescendo_agora: pick(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
(a, b) =>
|
|
254
|
-
safeNumber(b?.engagement?.like_count) - safeNumber(b?.engagement?.dislike_count) - (safeNumber(a?.engagement?.like_count) - safeNumber(a?.engagement?.dislike_count)),
|
|
230
|
+
crescendo_agora: pick(
|
|
231
|
+
sortByScoreDesc(
|
|
232
|
+
all.filter((entry) => entry?.signals?.trending_now),
|
|
233
|
+
'trend_score',
|
|
255
234
|
),
|
|
256
235
|
),
|
|
236
|
+
mais_curtidos: pick([...safeOnly].sort((a, b) => safeNumber(b?.engagement?.like_count) - safeNumber(a?.engagement?.like_count))),
|
|
237
|
+
melhor_avaliados: pick([...safeOnly].sort((a, b) => safeNumber(b?.engagement?.like_count) - safeNumber(b?.engagement?.dislike_count) - (safeNumber(a?.engagement?.like_count) - safeNumber(a?.engagement?.dislike_count)))),
|
|
257
238
|
};
|
|
258
239
|
};
|
|
259
240
|
|
|
@@ -313,12 +294,7 @@ export const buildViewerTagAffinity = ({ viewerEntries = [], packClassificationB
|
|
|
313
294
|
return affinity;
|
|
314
295
|
};
|
|
315
296
|
|
|
316
|
-
export const buildPersonalizedRecommendations = ({
|
|
317
|
-
entries = [],
|
|
318
|
-
viewerAffinity = new Map(),
|
|
319
|
-
excludePackIds = new Set(),
|
|
320
|
-
limit = 18,
|
|
321
|
-
}) => {
|
|
297
|
+
export const buildPersonalizedRecommendations = ({ entries = [], viewerAffinity = new Map(), excludePackIds = new Set(), limit = 18 }) => {
|
|
322
298
|
const safeLimit = Math.max(4, Math.min(50, Number(limit) || 18));
|
|
323
299
|
const ranked = [];
|
|
324
300
|
for (const entry of entries) {
|
|
@@ -17,7 +17,13 @@ const PACK_VISUAL_DIVIDER = '━━━━━━━━━━━━━━━━━
|
|
|
17
17
|
* @param {unknown} value Valor de origem.
|
|
18
18
|
* @returns {string[]} Emojis válidos.
|
|
19
19
|
*/
|
|
20
|
-
const normalizeEmojis = (value) =>
|
|
20
|
+
const normalizeEmojis = (value) =>
|
|
21
|
+
Array.isArray(value)
|
|
22
|
+
? value
|
|
23
|
+
.map((item) => String(item))
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.slice(0, 8)
|
|
26
|
+
: [];
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
29
|
* Renderiza linha textual de um item para o preview fallback.
|
|
@@ -45,33 +51,9 @@ const buildPreviewText = ({ pack, items, sentCount }) => {
|
|
|
45
51
|
previewLines.push(`... e mais ${items.length - MAX_PREVIEW_LIST_LINES} figurinha(s).`);
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
const compatibilityNote =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
: `✅ Envio completo no fallback: ${sentCount}/${items.length} figurinha(s).`;
|
|
52
|
-
|
|
53
|
-
return [
|
|
54
|
-
'📦 *GERENCIADOR DE PACKS DE FIGURINHAS*',
|
|
55
|
-
'',
|
|
56
|
-
'📤 *ENVIO EM MODO DE COMPATIBILIDADE*',
|
|
57
|
-
'Seu cliente não aceitou o pack nativo, então enviei preview + figurinhas individuais.',
|
|
58
|
-
'',
|
|
59
|
-
PACK_VISUAL_DIVIDER,
|
|
60
|
-
'📌 *RESUMO DO PACK*',
|
|
61
|
-
'',
|
|
62
|
-
`📛 Nome: *${pack.name}*`,
|
|
63
|
-
`👤 Publisher: *${pack.publisher}*`,
|
|
64
|
-
`🆔 ID: \`${pack.pack_key}\``,
|
|
65
|
-
`🧩 Figurinhas disponíveis: *${items.length}*`,
|
|
66
|
-
'',
|
|
67
|
-
PACK_VISUAL_DIVIDER,
|
|
68
|
-
'🖼 *PRÉVIA DAS FIGURINHAS*',
|
|
69
|
-
'',
|
|
70
|
-
previewLines.join('\n') || 'Nenhuma figurinha disponível para listar.',
|
|
71
|
-
'',
|
|
72
|
-
PACK_VISUAL_DIVIDER,
|
|
73
|
-
compatibilityNote,
|
|
74
|
-
].join('\n');
|
|
54
|
+
const compatibilityNote = sentCount < items.length ? `⚠️ Por compatibilidade, enviei ${sentCount}/${items.length} figurinha(s) neste fallback.` : `✅ Envio completo no fallback: ${sentCount}/${items.length} figurinha(s).`;
|
|
55
|
+
|
|
56
|
+
return ['📦 *GERENCIADOR DE PACKS DE FIGURINHAS*', '', '📤 *ENVIO EM MODO DE COMPATIBILIDADE*', 'Seu cliente não aceitou o pack nativo, então enviei preview + figurinhas individuais.', '', PACK_VISUAL_DIVIDER, '📌 *RESUMO DO PACK*', '', `📛 Nome: *${pack.name}*`, `👤 Publisher: *${pack.publisher}*`, `🆔 ID: \`${pack.pack_key}\``, `🧩 Figurinhas disponíveis: *${items.length}*`, '', PACK_VISUAL_DIVIDER, '🖼 *PRÉVIA DAS FIGURINHAS*', '', previewLines.join('\n') || 'Nenhuma figurinha disponível para listar.', '', PACK_VISUAL_DIVIDER, compatibilityNote].join('\n');
|
|
75
57
|
};
|
|
76
58
|
|
|
77
59
|
/**
|
|
@@ -156,10 +138,7 @@ export async function buildStickerPackMessage(packDetails) {
|
|
|
156
138
|
}
|
|
157
139
|
|
|
158
140
|
if (!preparedItems.length) {
|
|
159
|
-
throw new StickerPackError(
|
|
160
|
-
STICKER_PACK_ERROR_CODES.STORAGE_ERROR,
|
|
161
|
-
'Nenhuma figurinha do pack está disponível para envio.',
|
|
162
|
-
);
|
|
141
|
+
throw new StickerPackError(STICKER_PACK_ERROR_CODES.STORAGE_ERROR, 'Nenhuma figurinha do pack está disponível para envio.');
|
|
163
142
|
}
|
|
164
143
|
|
|
165
144
|
const coverItem = preparedItems.find((item) => item.sticker_id === pack.cover_sticker_id) || preparedItems[0];
|
|
@@ -206,14 +185,7 @@ export async function buildStickerPackMessage(packDetails) {
|
|
|
206
185
|
* }} params Contexto de envio.
|
|
207
186
|
* @returns {Promise<{ mode: 'native'|'fallback', sentCount: number, total?: number, nativeError?: string|null }>}
|
|
208
187
|
*/
|
|
209
|
-
export async function sendStickerPackWithFallback({
|
|
210
|
-
sock,
|
|
211
|
-
jid,
|
|
212
|
-
messageInfo,
|
|
213
|
-
expirationMessage,
|
|
214
|
-
packBuild,
|
|
215
|
-
fallbackLimit = FALLBACK_SEND_LIMIT,
|
|
216
|
-
}) {
|
|
188
|
+
export async function sendStickerPackWithFallback({ sock, jid, messageInfo, expirationMessage, packBuild, fallbackLimit = FALLBACK_SEND_LIMIT }) {
|
|
217
189
|
const options = {
|
|
218
190
|
quoted: messageInfo,
|
|
219
191
|
ephemeralExpiration: expirationMessage,
|
|
@@ -86,11 +86,7 @@ export async function findStickerPackByPackKey(packKey, { includeDeleted = false
|
|
|
86
86
|
* @param {{ includeDeleted?: boolean, connection?: import('mysql2/promise').PoolConnection|null }} [options]
|
|
87
87
|
* @returns {Promise<object|null>} Pack encontrado.
|
|
88
88
|
*/
|
|
89
|
-
export async function findStickerPackByOwnerAndIdentifier(
|
|
90
|
-
ownerJid,
|
|
91
|
-
identifier,
|
|
92
|
-
{ includeDeleted = false, connection = null } = {},
|
|
93
|
-
) {
|
|
89
|
+
export async function findStickerPackByOwnerAndIdentifier(ownerJid, identifier, { includeDeleted = false, connection = null } = {}) {
|
|
94
90
|
if (!identifier) return null;
|
|
95
91
|
|
|
96
92
|
const idOrPack = await executeQuery(
|
|
@@ -133,10 +129,7 @@ export async function findStickerPackByOwnerAndIdentifier(
|
|
|
133
129
|
* @param {{ includeDeleted?: boolean, limit?: number, offset?: number, connection?: import('mysql2/promise').PoolConnection|null }} [options]
|
|
134
130
|
* @returns {Promise<object[]>} Lista de packs.
|
|
135
131
|
*/
|
|
136
|
-
export async function listStickerPacksByOwner(
|
|
137
|
-
ownerJid,
|
|
138
|
-
{ includeDeleted = false, limit = 50, offset = 0, connection = null } = {},
|
|
139
|
-
) {
|
|
132
|
+
export async function listStickerPacksByOwner(ownerJid, { includeDeleted = false, limit = 50, offset = 0, connection = null } = {}) {
|
|
140
133
|
const safeLimit = Math.max(1, Number(limit) || 50);
|
|
141
134
|
const safeOffset = Math.max(0, Number(offset) || 0);
|
|
142
135
|
|
|
@@ -167,15 +160,7 @@ export async function listStickerPacksByOwner(
|
|
|
167
160
|
* }} [options]
|
|
168
161
|
* @returns {Promise<object[]>}
|
|
169
162
|
*/
|
|
170
|
-
export async function listStickerAutoPacksForCuration({
|
|
171
|
-
ownerJids = [],
|
|
172
|
-
includeArchived = true,
|
|
173
|
-
themeKey = '',
|
|
174
|
-
includeLegacyMarkers = true,
|
|
175
|
-
limit = 2000,
|
|
176
|
-
offset = 0,
|
|
177
|
-
connection = null,
|
|
178
|
-
} = {}) {
|
|
163
|
+
export async function listStickerAutoPacksForCuration({ ownerJids = [], includeArchived = true, themeKey = '', includeLegacyMarkers = true, limit = 2000, offset = 0, connection = null } = {}) {
|
|
179
164
|
const owners = Array.from(new Set((Array.isArray(ownerJids) ? ownerJids : []).filter(Boolean)));
|
|
180
165
|
if (!owners.length) return [];
|
|
181
166
|
|
|
@@ -183,10 +168,7 @@ export async function listStickerAutoPacksForCuration({
|
|
|
183
168
|
const safeOffset = Math.max(0, Number(offset) || 0);
|
|
184
169
|
const ownerPlaceholders = owners.map(() => '?').join(', ');
|
|
185
170
|
|
|
186
|
-
const whereClauses = [
|
|
187
|
-
'p.deleted_at IS NULL',
|
|
188
|
-
`p.owner_jid IN (${ownerPlaceholders})`,
|
|
189
|
-
];
|
|
171
|
+
const whereClauses = ['p.deleted_at IS NULL', `p.owner_jid IN (${ownerPlaceholders})`];
|
|
190
172
|
const params = [...owners];
|
|
191
173
|
|
|
192
174
|
if (includeLegacyMarkers) {
|
|
@@ -195,9 +177,11 @@ export async function listStickerAutoPacksForCuration({
|
|
|
195
177
|
whereClauses.push('p.is_auto_pack = 1');
|
|
196
178
|
}
|
|
197
179
|
|
|
198
|
-
const normalizedThemeKey = String(themeKey || '')
|
|
180
|
+
const normalizedThemeKey = String(themeKey || '')
|
|
181
|
+
.trim()
|
|
182
|
+
.toLowerCase();
|
|
199
183
|
if (normalizedThemeKey) {
|
|
200
|
-
whereClauses.push(
|
|
184
|
+
whereClauses.push("LOWER(COALESCE(p.pack_theme_key, '')) = ?");
|
|
201
185
|
params.push(normalizedThemeKey);
|
|
202
186
|
}
|
|
203
187
|
|
|
@@ -234,39 +218,26 @@ export async function listStickerAutoPacksForCuration({
|
|
|
234
218
|
* }} [options] Filtros de listagem.
|
|
235
219
|
* @returns {Promise<{ packs: object[], hasMore: boolean }>} Resultado paginado.
|
|
236
220
|
*/
|
|
237
|
-
export async function listStickerPacksForCatalog({
|
|
238
|
-
visibility = 'public',
|
|
239
|
-
search = '',
|
|
240
|
-
limit = 24,
|
|
241
|
-
offset = 0,
|
|
242
|
-
connection = null,
|
|
243
|
-
} = {}) {
|
|
221
|
+
export async function listStickerPacksForCatalog({ visibility = 'public', search = '', limit = 24, offset = 0, connection = null } = {}) {
|
|
244
222
|
const safeLimit = Math.max(1, Math.min(60, Number(limit) || 24));
|
|
245
223
|
const safeOffset = Math.max(0, Number(offset) || 0);
|
|
246
224
|
const safeLimitWithSentinel = safeLimit + 1;
|
|
247
225
|
|
|
248
|
-
const normalizedVisibility = String(visibility || 'public')
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
: normalizedVisibility === 'unlisted'
|
|
253
|
-
? ['unlisted']
|
|
254
|
-
: ['public'];
|
|
226
|
+
const normalizedVisibility = String(visibility || 'public')
|
|
227
|
+
.trim()
|
|
228
|
+
.toLowerCase();
|
|
229
|
+
const visibilityValues = normalizedVisibility === 'all' ? ['public', 'unlisted'] : normalizedVisibility === 'unlisted' ? ['unlisted'] : ['public'];
|
|
255
230
|
|
|
256
|
-
const normalizedSearch = String(search || '')
|
|
231
|
+
const normalizedSearch = String(search || '')
|
|
232
|
+
.trim()
|
|
233
|
+
.toLowerCase()
|
|
234
|
+
.slice(0, 120);
|
|
257
235
|
const params = [...visibilityValues];
|
|
258
|
-
const whereClauses = [
|
|
259
|
-
'p.deleted_at IS NULL',
|
|
260
|
-
"p.status = 'published'",
|
|
261
|
-
"COALESCE(p.pack_status, 'ready') = 'ready'",
|
|
262
|
-
`p.visibility IN (${visibilityValues.map(() => '?').join(', ')})`,
|
|
263
|
-
];
|
|
236
|
+
const whereClauses = ['p.deleted_at IS NULL', "p.status = 'published'", "COALESCE(p.pack_status, 'ready') = 'ready'", `p.visibility IN (${visibilityValues.map(() => '?').join(', ')})`];
|
|
264
237
|
|
|
265
238
|
if (normalizedSearch) {
|
|
266
239
|
const like = `%${normalizedSearch}%`;
|
|
267
|
-
whereClauses.push(
|
|
268
|
-
'(LOWER(p.name) LIKE ? OR LOWER(p.publisher) LIKE ? OR LOWER(COALESCE(p.description, \'\')) LIKE ? OR LOWER(p.pack_key) LIKE ?)',
|
|
269
|
-
);
|
|
240
|
+
whereClauses.push("(LOWER(p.name) LIKE ? OR LOWER(p.publisher) LIKE ? OR LOWER(COALESCE(p.description, '')) LIKE ? OR LOWER(p.pack_key) LIKE ?)");
|
|
270
241
|
params.push(like, like, like, like);
|
|
271
242
|
}
|
|
272
243
|
|
|
@@ -319,23 +290,7 @@ export async function createStickerPack(pack, connection = null) {
|
|
|
319
290
|
version
|
|
320
291
|
)
|
|
321
292
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
322
|
-
[
|
|
323
|
-
pack.id,
|
|
324
|
-
pack.owner_jid,
|
|
325
|
-
pack.name,
|
|
326
|
-
pack.publisher,
|
|
327
|
-
pack.description ?? null,
|
|
328
|
-
pack.pack_key,
|
|
329
|
-
pack.cover_sticker_id ?? null,
|
|
330
|
-
pack.visibility,
|
|
331
|
-
pack.status ?? 'published',
|
|
332
|
-
pack.pack_status ?? 'ready',
|
|
333
|
-
pack.pack_theme_key ?? null,
|
|
334
|
-
pack.pack_volume ?? null,
|
|
335
|
-
pack.is_auto_pack ? 1 : 0,
|
|
336
|
-
pack.last_rebalanced_at ?? null,
|
|
337
|
-
pack.version ?? 1,
|
|
338
|
-
],
|
|
293
|
+
[pack.id, pack.owner_jid, pack.name, pack.publisher, pack.description ?? null, pack.pack_key, pack.cover_sticker_id ?? null, pack.visibility, pack.status ?? 'published', pack.pack_status ?? 'ready', pack.pack_theme_key ?? null, pack.pack_volume ?? null, pack.is_auto_pack ? 1 : 0, pack.last_rebalanced_at ?? null, pack.version ?? 1],
|
|
339
294
|
connection,
|
|
340
295
|
);
|
|
341
296
|
|
|
@@ -420,7 +375,9 @@ export async function updateStickerPackFields(packId, fields, connection = null)
|
|
|
420
375
|
fields: Object.keys(fields || {}).slice(0, 30),
|
|
421
376
|
},
|
|
422
377
|
priority: 70,
|
|
423
|
-
idempotencyKey: `pack_updated:${packId}:${Object.keys(fields || {})
|
|
378
|
+
idempotencyKey: `pack_updated:${packId}:${Object.keys(fields || {})
|
|
379
|
+
.sort()
|
|
380
|
+
.join(',')}`,
|
|
424
381
|
},
|
|
425
382
|
{ connection },
|
|
426
383
|
);
|
|
@@ -27,7 +27,9 @@ const clampScore = (value) => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const normalizeNsfwLevel = (value) => {
|
|
30
|
-
const normalized = String(value || '')
|
|
30
|
+
const normalized = String(value || '')
|
|
31
|
+
.trim()
|
|
32
|
+
.toLowerCase();
|
|
31
33
|
if (['safe', 'suggestive', 'explicit'].includes(normalized)) return normalized;
|
|
32
34
|
return 'safe';
|
|
33
35
|
};
|
|
@@ -82,37 +84,23 @@ const normalizeSnapshotInput = (entry) => {
|
|
|
82
84
|
nsfw_level: normalizeNsfwLevel(signals.nsfw_level),
|
|
83
85
|
sticker_count: Math.max(0, Number(entry.sticker_count || 0)),
|
|
84
86
|
tags: Array.isArray(entry.tags) ? entry.tags.slice(0, 30) : [],
|
|
85
|
-
source_version:
|
|
87
|
+
source_version:
|
|
88
|
+
String(entry.source_version || 'v1')
|
|
89
|
+
.trim()
|
|
90
|
+
.slice(0, 32) || 'v1',
|
|
86
91
|
scores_json: signals,
|
|
87
92
|
};
|
|
88
93
|
};
|
|
89
94
|
|
|
90
95
|
export async function upsertStickerPackScoreSnapshots(entries = [], connection = null) {
|
|
91
|
-
const normalized = (Array.isArray(entries) ? entries : [])
|
|
92
|
-
.map((entry) => normalizeSnapshotInput(entry))
|
|
93
|
-
.filter(Boolean);
|
|
96
|
+
const normalized = (Array.isArray(entries) ? entries : []).map((entry) => normalizeSnapshotInput(entry)).filter(Boolean);
|
|
94
97
|
if (!normalized.length) return 0;
|
|
95
98
|
|
|
96
99
|
let written = 0;
|
|
97
100
|
for (let offset = 0; offset < normalized.length; offset += 100) {
|
|
98
101
|
const chunk = normalized.slice(offset, offset + 100);
|
|
99
102
|
const placeholders = chunk.map(() => '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UTC_TIMESTAMP())').join(', ');
|
|
100
|
-
const params = chunk.flatMap((entry) => [
|
|
101
|
-
entry.pack_id,
|
|
102
|
-
entry.ranking_score,
|
|
103
|
-
entry.pack_score,
|
|
104
|
-
entry.trend_score,
|
|
105
|
-
entry.quality_score,
|
|
106
|
-
entry.engagement_score,
|
|
107
|
-
entry.diversity_score,
|
|
108
|
-
entry.cohesion_score,
|
|
109
|
-
entry.sensitive_content,
|
|
110
|
-
entry.nsfw_level,
|
|
111
|
-
entry.sticker_count,
|
|
112
|
-
JSON.stringify(entry.tags || []),
|
|
113
|
-
JSON.stringify(entry.scores_json || {}),
|
|
114
|
-
entry.source_version,
|
|
115
|
-
]);
|
|
103
|
+
const params = chunk.flatMap((entry) => [entry.pack_id, entry.ranking_score, entry.pack_score, entry.trend_score, entry.quality_score, entry.engagement_score, entry.diversity_score, entry.cohesion_score, entry.sensitive_content, entry.nsfw_level, entry.sticker_count, JSON.stringify(entry.tags || []), JSON.stringify(entry.scores_json || {}), entry.source_version]);
|
|
116
104
|
const result = await executeQuery(
|
|
117
105
|
`INSERT INTO ${TABLES.STICKER_PACK_SCORE_SNAPSHOT}
|
|
118
106
|
(
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { executeQuery, TABLES } from '../../../database/index.js';
|
|
2
2
|
import logger from '../../utils/logger/loggerModule.js';
|
|
3
|
-
import {
|
|
4
|
-
getEmptyStickerPackEngagement,
|
|
5
|
-
getStickerPackEngagementByPackId,
|
|
6
|
-
} from './stickerPackEngagementRepository.js';
|
|
3
|
+
import { getEmptyStickerPackEngagement, getStickerPackEngagementByPackId } from './stickerPackEngagementRepository.js';
|
|
7
4
|
import { listStickerPackItems } from './stickerPackItemRepository.js';
|
|
8
5
|
import { listStickerClassificationsByAssetIds } from './stickerAssetClassificationRepository.js';
|
|
9
6
|
import { getPackClassificationSummaryByAssetIds } from './stickerClassificationService.js';
|
|
10
7
|
import { listStickerPackInteractionStatsByPackIds } from './stickerPackInteractionEventRepository.js';
|
|
11
8
|
import { getMarketplaceDriftSnapshot } from './stickerMarketplaceDriftService.js';
|
|
12
9
|
import { computePackSignals } from './stickerPackMarketplaceService.js';
|
|
13
|
-
import {
|
|
14
|
-
removeSnapshotsForDeletedPacks,
|
|
15
|
-
upsertStickerPackScoreSnapshots,
|
|
16
|
-
} from './stickerPackScoreSnapshotRepository.js';
|
|
10
|
+
import { removeSnapshotsForDeletedPacks, upsertStickerPackScoreSnapshots } from './stickerPackScoreSnapshotRepository.js';
|
|
17
11
|
import { setQueueDepth } from '../../observability/metrics.js';
|
|
18
12
|
|
|
19
13
|
const parseEnvBool = (value, fallback) => {
|
|
@@ -25,31 +19,13 @@ const parseEnvBool = (value, fallback) => {
|
|
|
25
19
|
};
|
|
26
20
|
|
|
27
21
|
const SNAPSHOT_ENABLED = parseEnvBool(process.env.STICKER_SCORE_SNAPSHOT_ENABLED, true);
|
|
28
|
-
const SNAPSHOT_STARTUP_DELAY_MS = Math.max(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
const SNAPSHOT_REFRESH_INTERVAL_MS = Math.max(
|
|
33
|
-
30_000,
|
|
34
|
-
Number(process.env.STICKER_SCORE_SNAPSHOT_REFRESH_INTERVAL_MS) || 5 * 60_000,
|
|
35
|
-
);
|
|
36
|
-
const SNAPSHOT_BATCH_SIZE = Math.max(
|
|
37
|
-
10,
|
|
38
|
-
Math.min(500, Number(process.env.STICKER_SCORE_SNAPSHOT_BATCH_SIZE) || 120),
|
|
39
|
-
);
|
|
40
|
-
const SNAPSHOT_TARGETED_BATCH_SIZE = Math.max(
|
|
41
|
-
5,
|
|
42
|
-
Math.min(200, Number(process.env.STICKER_SCORE_SNAPSHOT_TARGETED_BATCH_SIZE) || 60),
|
|
43
|
-
);
|
|
22
|
+
const SNAPSHOT_STARTUP_DELAY_MS = Math.max(1_000, Number(process.env.STICKER_SCORE_SNAPSHOT_STARTUP_DELAY_MS) || 20_000);
|
|
23
|
+
const SNAPSHOT_REFRESH_INTERVAL_MS = Math.max(30_000, Number(process.env.STICKER_SCORE_SNAPSHOT_REFRESH_INTERVAL_MS) || 5 * 60_000);
|
|
24
|
+
const SNAPSHOT_BATCH_SIZE = Math.max(10, Math.min(500, Number(process.env.STICKER_SCORE_SNAPSHOT_BATCH_SIZE) || 120));
|
|
25
|
+
const SNAPSHOT_TARGETED_BATCH_SIZE = Math.max(5, Math.min(200, Number(process.env.STICKER_SCORE_SNAPSHOT_TARGETED_BATCH_SIZE) || 60));
|
|
44
26
|
const SNAPSHOT_SOURCE_VERSION = String(process.env.STICKER_SCORE_SNAPSHOT_SOURCE_VERSION || 'v1').trim() || 'v1';
|
|
45
|
-
const SNAPSHOT_MAX_PENDING_PACKS = Math.max(
|
|
46
|
-
|
|
47
|
-
Math.min(20_000, Number(process.env.STICKER_SCORE_SNAPSHOT_MAX_PENDING_PACKS) || 2_000),
|
|
48
|
-
);
|
|
49
|
-
const SNAPSHOT_FULL_REBUILD_EVERY_CYCLES = Math.max(
|
|
50
|
-
1,
|
|
51
|
-
Math.min(500, Number(process.env.STICKER_SCORE_SNAPSHOT_FULL_REBUILD_EVERY_CYCLES) || 12),
|
|
52
|
-
);
|
|
27
|
+
const SNAPSHOT_MAX_PENDING_PACKS = Math.max(20, Math.min(20_000, Number(process.env.STICKER_SCORE_SNAPSHOT_MAX_PENDING_PACKS) || 2_000));
|
|
28
|
+
const SNAPSHOT_FULL_REBUILD_EVERY_CYCLES = Math.max(1, Math.min(500, Number(process.env.STICKER_SCORE_SNAPSHOT_FULL_REBUILD_EVERY_CYCLES) || 12));
|
|
53
29
|
|
|
54
30
|
let startupHandle = null;
|
|
55
31
|
let cycleHandle = null;
|
|
@@ -94,12 +70,7 @@ const listPacksByIdsForSnapshot = async (packIds = []) => {
|
|
|
94
70
|
const buildSnapshotForPack = async ({ pack, driftWeights }) => {
|
|
95
71
|
const items = await listStickerPackItems(pack.id);
|
|
96
72
|
const stickerIds = items.map((item) => item.sticker_id).filter(Boolean);
|
|
97
|
-
const [packClassification, itemClassifications, engagement, interactionStatsByPackId] = await Promise.all([
|
|
98
|
-
getPackClassificationSummaryByAssetIds(stickerIds),
|
|
99
|
-
stickerIds.length ? listStickerClassificationsByAssetIds(stickerIds) : Promise.resolve([]),
|
|
100
|
-
getStickerPackEngagementByPackId(pack.id),
|
|
101
|
-
listStickerPackInteractionStatsByPackIds([pack.id]),
|
|
102
|
-
]);
|
|
73
|
+
const [packClassification, itemClassifications, engagement, interactionStatsByPackId] = await Promise.all([getPackClassificationSummaryByAssetIds(stickerIds), stickerIds.length ? listStickerClassificationsByAssetIds(stickerIds) : Promise.resolve([]), getStickerPackEngagementByPackId(pack.id), listStickerPackInteractionStatsByPackIds([pack.id])]);
|
|
103
74
|
|
|
104
75
|
const byAssetId = new Map((Array.isArray(itemClassifications) ? itemClassifications : []).map((entry) => [entry.asset_id, entry]));
|
|
105
76
|
const orderedClassifications = stickerIds.map((id) => byAssetId.get(id)).filter(Boolean);
|
|
@@ -199,8 +170,7 @@ export const runStickerPackScoreSnapshotCycle = async () => {
|
|
|
199
170
|
}
|
|
200
171
|
|
|
201
172
|
cycleCounter += 1;
|
|
202
|
-
fullRebuildExecuted =
|
|
203
|
-
cycleCounter % SNAPSHOT_FULL_REBUILD_EVERY_CYCLES === 0 || targetedPackIds.length === 0;
|
|
173
|
+
fullRebuildExecuted = cycleCounter % SNAPSHOT_FULL_REBUILD_EVERY_CYCLES === 0 || targetedPackIds.length === 0;
|
|
204
174
|
|
|
205
175
|
if (fullRebuildExecuted) {
|
|
206
176
|
let offset = 0;
|