@kaikybrofc/omnizap-system 2.2.10 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- 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 +40 -39
- 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/index.js +1 -0
- package/database/init.js +1 -8
- package/database/migrations/20260228_0027_web_visit_event.sql +15 -0
- package/docker-compose.yml +27 -27
- package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +26 -0
- package/docs/seo/satellite-page-template.md +2 -0
- package/docs/seo/satellite-pages-phase1.json +40 -177
- 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 +6 -3
- package/public/api-docs/index.html +220 -193
- package/public/bot-whatsapp-para-grupo/index.html +291 -261
- package/public/bot-whatsapp-sem-programar/index.html +291 -261
- package/public/comandos/index.html +421 -406
- package/public/como-automatizar-avisos-no-whatsapp/index.html +291 -261
- package/public/como-criar-comandos-whatsapp/index.html +291 -261
- package/public/como-evitar-spam-no-whatsapp/index.html +291 -261
- package/public/como-moderar-grupo-whatsapp/index.html +291 -261
- package/public/como-organizar-comunidade-whatsapp/index.html +291 -261
- package/public/css/github-project-panel.css +13 -8
- package/public/css/stickers-admin.css +25 -9
- package/public/css/styles.css +23 -16
- package/public/index.html +1106 -993
- package/public/js/apps/apiDocsApp.js +17 -167
- package/public/js/apps/createPackApp.js +69 -332
- package/public/js/apps/homeApp.js +274 -101
- package/public/js/apps/loginApp.js +3 -12
- package/public/js/apps/stickersAdminApp.js +190 -181
- package/public/js/apps/stickersApp.js +482 -1411
- package/public/js/apps/userApp.js +217 -1
- 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 +200 -86
- package/public/login/index.html +315 -325
- package/public/melhor-bot-whatsapp-para-grupos/index.html +291 -261
- package/public/stickers/admin/index.html +14 -19
- package/public/stickers/create/index.html +39 -44
- package/public/stickers/index.html +96 -107
- package/public/termos-de-uso/index.html +369 -122
- package/public/user/index.html +527 -350
- package/scripts/cache-bust.mjs +5 -24
- package/scripts/generate-seo-satellite-pages.mjs +10 -13
- 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/auth/googleWebAuth/googleWebAuthService.js +614 -0
- package/server/controllers/stickerCatalogController.js +297 -632
- 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
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import logger from '../../utils/logger/loggerModule.js';
|
|
2
2
|
import { setQueueDepth } from '../../observability/metrics.js';
|
|
3
3
|
import { isFeatureEnabled } from '../../services/featureFlagService.js';
|
|
4
|
-
import {
|
|
5
|
-
claimDomainEvent,
|
|
6
|
-
completeDomainEvent,
|
|
7
|
-
countDomainEventsByStatus,
|
|
8
|
-
failDomainEvent,
|
|
9
|
-
} from './domainEventOutboxRepository.js';
|
|
4
|
+
import { claimDomainEvent, completeDomainEvent, countDomainEventsByStatus, failDomainEvent } from './domainEventOutboxRepository.js';
|
|
10
5
|
import { STICKER_DOMAIN_EVENTS } from './domainEvents.js';
|
|
11
6
|
import { enqueueWorkerTask } from './stickerWorkerTaskQueueRepository.js';
|
|
12
7
|
import { enqueuePackScoreSnapshotRefresh } from './stickerPackScoreSnapshotRuntime.js';
|
|
@@ -21,32 +16,20 @@ const parseEnvBool = (value, fallback) => {
|
|
|
21
16
|
};
|
|
22
17
|
|
|
23
18
|
const CONSUMER_ENABLED = parseEnvBool(process.env.STICKER_DOMAIN_EVENT_CONSUMER_ENABLED, true);
|
|
24
|
-
const STARTUP_DELAY_MS = Math.max(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
const RETRY_DELAY_SECONDS = Math.max(
|
|
33
|
-
5,
|
|
34
|
-
Math.min(3600, Number(process.env.STICKER_DOMAIN_EVENT_CONSUMER_RETRY_DELAY_SECONDS) || 45),
|
|
35
|
-
);
|
|
36
|
-
const CONSUMER_COHORT_KEY =
|
|
37
|
-
String(process.env.STICKER_DOMAIN_EVENT_CONSUMER_COHORT_KEY || process.env.HOSTNAME || process.pid).trim()
|
|
38
|
-
|| 'consumer';
|
|
19
|
+
const STARTUP_DELAY_MS = Math.max(1_000, Number(process.env.STICKER_DOMAIN_EVENT_CONSUMER_STARTUP_DELAY_MS) || 8_000);
|
|
20
|
+
const POLLER_INTERVAL_MS = Math.max(1_000, Number(process.env.STICKER_DOMAIN_EVENT_CONSUMER_POLLER_INTERVAL_MS) || 2_000);
|
|
21
|
+
const RETRY_DELAY_SECONDS = Math.max(5, Math.min(3600, Number(process.env.STICKER_DOMAIN_EVENT_CONSUMER_RETRY_DELAY_SECONDS) || 45));
|
|
22
|
+
const CLASSIFICATION_COALESCE_WINDOW_SECONDS = Math.max(30, Math.min(3600, Number(process.env.STICKER_DOMAIN_EVENT_CLASSIFICATION_COALESCE_WINDOW_SECONDS) || 60));
|
|
23
|
+
const CURATION_COALESCE_WINDOW_SECONDS = Math.max(30, Math.min(3600, Number(process.env.STICKER_DOMAIN_EVENT_CURATION_COALESCE_WINDOW_SECONDS) || 60));
|
|
24
|
+
const REBUILD_COALESCE_WINDOW_SECONDS = Math.max(30, Math.min(3600, Number(process.env.STICKER_DOMAIN_EVENT_REBUILD_COALESCE_WINDOW_SECONDS) || 120));
|
|
25
|
+
const CONSUMER_COHORT_KEY = String(process.env.STICKER_DOMAIN_EVENT_CONSUMER_COHORT_KEY || process.env.HOSTNAME || process.pid).trim() || 'consumer';
|
|
39
26
|
|
|
40
27
|
let startupHandle = null;
|
|
41
28
|
let pollerHandle = null;
|
|
42
29
|
let running = false;
|
|
43
30
|
|
|
44
31
|
const refreshOutboxDepthMetrics = async () => {
|
|
45
|
-
const [pending, processing, failed] = await Promise.all([
|
|
46
|
-
countDomainEventsByStatus('pending'),
|
|
47
|
-
countDomainEventsByStatus('processing'),
|
|
48
|
-
countDomainEventsByStatus('failed'),
|
|
49
|
-
]);
|
|
32
|
+
const [pending, processing, failed] = await Promise.all([countDomainEventsByStatus('pending'), countDomainEventsByStatus('processing'), countDomainEventsByStatus('failed')]);
|
|
50
33
|
setQueueDepth('domain_event_outbox_pending', pending);
|
|
51
34
|
setQueueDepth('domain_event_outbox_processing', processing);
|
|
52
35
|
setQueueDepth('domain_event_outbox_failed', failed);
|
|
@@ -67,22 +50,38 @@ const enqueueTaskSafely = async ({ taskType, payload, priority, idempotencyKey }
|
|
|
67
50
|
});
|
|
68
51
|
};
|
|
69
52
|
|
|
53
|
+
const toUnixSeconds = (value) => {
|
|
54
|
+
if (!value) return Math.floor(Date.now() / 1000);
|
|
55
|
+
const numeric = Date.parse(value);
|
|
56
|
+
if (!Number.isFinite(numeric)) return Math.floor(Date.now() / 1000);
|
|
57
|
+
return Math.floor(numeric / 1000);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const toWindowBucket = (value, windowSeconds) => {
|
|
61
|
+
const safeWindow = Math.max(1, Math.floor(windowSeconds || 1));
|
|
62
|
+
return Math.floor(toUnixSeconds(value) / safeWindow);
|
|
63
|
+
};
|
|
64
|
+
|
|
70
65
|
const handleDomainEvent = async (event) => {
|
|
71
|
-
const eventType = String(event?.event_type || '')
|
|
66
|
+
const eventType = String(event?.event_type || '')
|
|
67
|
+
.trim()
|
|
68
|
+
.toUpperCase();
|
|
72
69
|
const aggregateId = String(event?.aggregate_id || '').trim();
|
|
73
70
|
const payload = event?.payload && typeof event.payload === 'object' ? event.payload : {};
|
|
74
71
|
|
|
75
72
|
if (eventType === STICKER_DOMAIN_EVENTS.STICKER_ASSET_CREATED) {
|
|
73
|
+
const coalesceBucket = toWindowBucket(event?.created_at, CLASSIFICATION_COALESCE_WINDOW_SECONDS);
|
|
76
74
|
await enqueueTaskSafely({
|
|
77
75
|
taskType: 'classification_cycle',
|
|
78
|
-
payload: { reason: 'domain_event', event_type: eventType, aggregate_id: aggregateId },
|
|
76
|
+
payload: { reason: 'domain_event', event_type: eventType, aggregate_id: aggregateId, coalesced: true },
|
|
79
77
|
priority: 80,
|
|
80
|
-
idempotencyKey: `evt:${
|
|
78
|
+
idempotencyKey: `evt:${eventType}:${coalesceBucket}:classification_cycle`,
|
|
81
79
|
});
|
|
82
80
|
return;
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
if (eventType === STICKER_DOMAIN_EVENTS.STICKER_CLASSIFIED) {
|
|
84
|
+
const coalesceBucket = toWindowBucket(event?.created_at, CURATION_COALESCE_WINDOW_SECONDS);
|
|
86
85
|
const assetId = String(payload?.asset_id || aggregateId || '').trim();
|
|
87
86
|
const relatedPackIds = assetId ? await listPackIdsByStickerId(assetId).catch(() => []) : [];
|
|
88
87
|
if (relatedPackIds.length) {
|
|
@@ -95,23 +94,28 @@ const handleDomainEvent = async (event) => {
|
|
|
95
94
|
event_type: eventType,
|
|
96
95
|
aggregate_id: aggregateId,
|
|
97
96
|
related_pack_ids: relatedPackIds,
|
|
97
|
+
coalesced: true,
|
|
98
98
|
},
|
|
99
99
|
priority: 65,
|
|
100
|
-
idempotencyKey: `evt:${
|
|
100
|
+
idempotencyKey: `evt:${eventType}:${coalesceBucket}:curation_cycle`,
|
|
101
101
|
});
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
if (eventType === STICKER_DOMAIN_EVENTS.PACK_UPDATED) {
|
|
106
|
+
const coalesceBucket = toWindowBucket(event?.created_at, REBUILD_COALESCE_WINDOW_SECONDS);
|
|
106
107
|
const packId = String(payload?.pack_id || aggregateId || '').trim();
|
|
107
108
|
if (packId) {
|
|
108
109
|
enqueuePackScoreSnapshotRefresh([packId]);
|
|
109
110
|
}
|
|
111
|
+
const rebuildIdempotency = packId
|
|
112
|
+
? `evt:${eventType}:${packId}:${coalesceBucket}:rebuild_cycle`
|
|
113
|
+
: `evt:${eventType}:${coalesceBucket}:rebuild_cycle`;
|
|
110
114
|
await enqueueTaskSafely({
|
|
111
115
|
taskType: 'rebuild_cycle',
|
|
112
|
-
payload: { reason: 'domain_event', event_type: eventType, aggregate_id: aggregateId, pack_id: packId || null },
|
|
116
|
+
payload: { reason: 'domain_event', event_type: eventType, aggregate_id: aggregateId, pack_id: packId || null, coalesced: true },
|
|
113
117
|
priority: 60,
|
|
114
|
-
idempotencyKey:
|
|
118
|
+
idempotencyKey: rebuildIdempotency,
|
|
115
119
|
});
|
|
116
120
|
return;
|
|
117
121
|
}
|
|
@@ -141,13 +145,10 @@ const pollOnce = async () => {
|
|
|
141
145
|
await handleDomainEvent(event);
|
|
142
146
|
await completeDomainEvent(event.id);
|
|
143
147
|
} catch (error) {
|
|
144
|
-
await failDomainEvent(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
retryDelaySeconds: RETRY_DELAY_SECONDS,
|
|
149
|
-
},
|
|
150
|
-
);
|
|
148
|
+
await failDomainEvent(event.id, {
|
|
149
|
+
error: error?.message || 'domain_event_consumer_failed',
|
|
150
|
+
retryDelaySeconds: RETRY_DELAY_SECONDS,
|
|
151
|
+
});
|
|
151
152
|
logger.warn('Evento de domínio falhou no consumidor interno.', {
|
|
152
153
|
action: 'sticker_domain_event_consumer_event_failed',
|
|
153
154
|
event_id: event.id,
|
|
@@ -80,10 +80,7 @@ export const getMarketplaceDriftSnapshot = async ({ force = false } = {}) => {
|
|
|
80
80
|
return cache;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const [distribution7d, distribution30d] = await Promise.all([
|
|
84
|
-
listClassificationCategoryDistribution({ days: 7 }),
|
|
85
|
-
listClassificationCategoryDistribution({ days: 30 }),
|
|
86
|
-
]);
|
|
83
|
+
const [distribution7d, distribution30d] = await Promise.all([listClassificationCategoryDistribution({ days: 7 }), listClassificationCategoryDistribution({ days: 30 })]);
|
|
87
84
|
|
|
88
85
|
const probability7d = toProbabilityMap(distribution7d);
|
|
89
86
|
const probability30d = toProbabilityMap(distribution30d);
|
|
@@ -14,15 +14,22 @@ const parseEnvBool = (value, fallback) => {
|
|
|
14
14
|
const OBJECT_STORAGE_ENABLED = parseEnvBool(process.env.STICKER_OBJECT_STORAGE_ENABLED, false);
|
|
15
15
|
const OBJECT_STORAGE_UPLOAD_ON_WRITE = parseEnvBool(process.env.STICKER_OBJECT_STORAGE_UPLOAD_ON_WRITE, true);
|
|
16
16
|
const OBJECT_STORAGE_SIGNED_URL_ENABLED = parseEnvBool(process.env.STICKER_OBJECT_STORAGE_SIGNED_URL_ENABLED, true);
|
|
17
|
-
const OBJECT_STORAGE_PROVIDER = String(process.env.STICKER_OBJECT_STORAGE_PROVIDER || 's3')
|
|
17
|
+
const OBJECT_STORAGE_PROVIDER = String(process.env.STICKER_OBJECT_STORAGE_PROVIDER || 's3')
|
|
18
|
+
.trim()
|
|
19
|
+
.toLowerCase();
|
|
18
20
|
const OBJECT_STORAGE_BUCKET = String(process.env.STICKER_OBJECT_STORAGE_BUCKET || '').trim();
|
|
19
21
|
const OBJECT_STORAGE_REGION = String(process.env.STICKER_OBJECT_STORAGE_REGION || 'us-east-1').trim() || 'us-east-1';
|
|
20
22
|
const OBJECT_STORAGE_ENDPOINT = String(process.env.STICKER_OBJECT_STORAGE_ENDPOINT || '').trim();
|
|
21
23
|
const OBJECT_STORAGE_ACCESS_KEY_ID = String(process.env.STICKER_OBJECT_STORAGE_ACCESS_KEY_ID || '').trim();
|
|
22
24
|
const OBJECT_STORAGE_SECRET_ACCESS_KEY = String(process.env.STICKER_OBJECT_STORAGE_SECRET_ACCESS_KEY || '').trim();
|
|
23
25
|
const OBJECT_STORAGE_FORCE_PATH_STYLE = parseEnvBool(process.env.STICKER_OBJECT_STORAGE_FORCE_PATH_STYLE, true);
|
|
24
|
-
const OBJECT_STORAGE_CDN_BASE_URL = String(process.env.STICKER_OBJECT_STORAGE_CDN_BASE_URL || '')
|
|
25
|
-
|
|
26
|
+
const OBJECT_STORAGE_CDN_BASE_URL = String(process.env.STICKER_OBJECT_STORAGE_CDN_BASE_URL || '')
|
|
27
|
+
.trim()
|
|
28
|
+
.replace(/\/+$/, '');
|
|
29
|
+
const OBJECT_STORAGE_KEY_PREFIX =
|
|
30
|
+
String(process.env.STICKER_OBJECT_STORAGE_KEY_PREFIX || 'stickers')
|
|
31
|
+
.trim()
|
|
32
|
+
.replace(/^\/+|\/+$/g, '') || 'stickers';
|
|
26
33
|
|
|
27
34
|
let sdkLoadState = {
|
|
28
35
|
loaded: false,
|
|
@@ -36,7 +43,9 @@ let s3Client = null;
|
|
|
36
43
|
|
|
37
44
|
const safeOwnerToken = (ownerJid) => {
|
|
38
45
|
const normalized = normalizeOwnerJid(ownerJid);
|
|
39
|
-
const token = String(normalized || 'unknown')
|
|
46
|
+
const token = String(normalized || 'unknown')
|
|
47
|
+
.replace(/[^a-zA-Z0-9._-]/g, '_')
|
|
48
|
+
.slice(0, 100);
|
|
40
49
|
return token || 'unknown';
|
|
41
50
|
};
|
|
42
51
|
|
|
@@ -62,7 +71,9 @@ const resolveStickerObjectKey = (asset) => {
|
|
|
62
71
|
const fromStoragePath = parseS3StoragePath(asset?.storage_path);
|
|
63
72
|
if (fromStoragePath?.key) return fromStoragePath.key;
|
|
64
73
|
const ownerToken = safeOwnerToken(asset?.owner_jid || 'unknown');
|
|
65
|
-
const sha256 = String(asset?.sha256 || '')
|
|
74
|
+
const sha256 = String(asset?.sha256 || '')
|
|
75
|
+
.trim()
|
|
76
|
+
.toLowerCase();
|
|
66
77
|
if (!sha256) return '';
|
|
67
78
|
return `${OBJECT_STORAGE_KEY_PREFIX}/${ownerToken}/${sha256}.webp`;
|
|
68
79
|
};
|
|
@@ -70,10 +81,7 @@ const resolveStickerObjectKey = (asset) => {
|
|
|
70
81
|
const loadAwsSdk = async () => {
|
|
71
82
|
if (sdkLoadState.loaded) return sdkLoadState;
|
|
72
83
|
try {
|
|
73
|
-
const [{ S3Client, PutObjectCommand, GetObjectCommand }, { getSignedUrl }] = await Promise.all([
|
|
74
|
-
import('@aws-sdk/client-s3'),
|
|
75
|
-
import('@aws-sdk/s3-request-presigner'),
|
|
76
|
-
]);
|
|
84
|
+
const [{ S3Client, PutObjectCommand, GetObjectCommand }, { getSignedUrl }] = await Promise.all([import('@aws-sdk/client-s3'), import('@aws-sdk/s3-request-presigner')]);
|
|
77
85
|
sdkLoadState = {
|
|
78
86
|
loaded: true,
|
|
79
87
|
warned: false,
|
|
@@ -149,15 +157,9 @@ const streamToBuffer = async (body) => {
|
|
|
149
157
|
return null;
|
|
150
158
|
};
|
|
151
159
|
|
|
152
|
-
export const isStickerObjectStorageEnabled = () =>
|
|
153
|
-
Boolean(OBJECT_STORAGE_ENABLED && OBJECT_STORAGE_PROVIDER === 's3' && OBJECT_STORAGE_BUCKET);
|
|
160
|
+
export const isStickerObjectStorageEnabled = () => Boolean(OBJECT_STORAGE_ENABLED && OBJECT_STORAGE_PROVIDER === 's3' && OBJECT_STORAGE_BUCKET);
|
|
154
161
|
|
|
155
|
-
export const uploadStickerToObjectStorage = async ({
|
|
156
|
-
ownerJid,
|
|
157
|
-
sha256,
|
|
158
|
-
buffer,
|
|
159
|
-
mimetype = 'image/webp',
|
|
160
|
-
} = {}) => {
|
|
162
|
+
export const uploadStickerToObjectStorage = async ({ ownerJid, sha256, buffer, mimetype = 'image/webp' } = {}) => {
|
|
161
163
|
if (!OBJECT_STORAGE_UPLOAD_ON_WRITE || !Buffer.isBuffer(buffer) || !buffer.length) {
|
|
162
164
|
return { uploaded: false, key: null };
|
|
163
165
|
}
|
|
@@ -165,7 +167,9 @@ export const uploadStickerToObjectStorage = async ({
|
|
|
165
167
|
return { uploaded: false, key: null };
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
const key = `${OBJECT_STORAGE_KEY_PREFIX}/${safeOwnerToken(ownerJid)}/${String(sha256 || '')
|
|
170
|
+
const key = `${OBJECT_STORAGE_KEY_PREFIX}/${safeOwnerToken(ownerJid)}/${String(sha256 || '')
|
|
171
|
+
.trim()
|
|
172
|
+
.toLowerCase()}.webp`;
|
|
169
173
|
if (!key || key.endsWith('/.webp')) return { uploaded: false, key: null };
|
|
170
174
|
|
|
171
175
|
try {
|
|
@@ -194,13 +198,7 @@ export const uploadStickerToObjectStorage = async ({
|
|
|
194
198
|
}
|
|
195
199
|
};
|
|
196
200
|
|
|
197
|
-
export const getStickerObjectStorageUrl = async (
|
|
198
|
-
asset,
|
|
199
|
-
{
|
|
200
|
-
secure = true,
|
|
201
|
-
expiresInSeconds = 300,
|
|
202
|
-
} = {},
|
|
203
|
-
) => {
|
|
201
|
+
export const getStickerObjectStorageUrl = async (asset, { secure = true, expiresInSeconds = 300 } = {}) => {
|
|
204
202
|
if (!isStickerObjectStorageEnabled()) return null;
|
|
205
203
|
|
|
206
204
|
const key = resolveStickerObjectKey(asset);
|
|
@@ -279,7 +277,9 @@ export const toStickerStoragePath = ({ localPath, ownerJid, sha256 }) => {
|
|
|
279
277
|
const normalizedLocalPath = path.resolve(String(localPath || ''));
|
|
280
278
|
if (!isStickerObjectStorageEnabled()) return normalizedLocalPath;
|
|
281
279
|
if (!OBJECT_STORAGE_UPLOAD_ON_WRITE) return normalizedLocalPath;
|
|
282
|
-
const key = `${OBJECT_STORAGE_KEY_PREFIX}/${safeOwnerToken(ownerJid)}/${String(sha256 || '')
|
|
280
|
+
const key = `${OBJECT_STORAGE_KEY_PREFIX}/${safeOwnerToken(ownerJid)}/${String(sha256 || '')
|
|
281
|
+
.trim()
|
|
282
|
+
.toLowerCase()}.webp`;
|
|
283
283
|
if (!key || key.endsWith('/.webp')) return normalizedLocalPath;
|
|
284
284
|
return `s3://${OBJECT_STORAGE_BUCKET}/${key}`;
|
|
285
285
|
};
|
|
@@ -3,10 +3,7 @@ import { sendAndStore } from '../../services/messagePersistenceService.js';
|
|
|
3
3
|
import { isUserJid } from '../../config/baileysConfig.js';
|
|
4
4
|
import stickerPackService from './stickerPackServiceRuntime.js';
|
|
5
5
|
import { STICKER_PACK_ERROR_CODES, StickerPackError } from './stickerPackErrors.js';
|
|
6
|
-
import {
|
|
7
|
-
captureIncomingStickerAsset,
|
|
8
|
-
resolveStickerAssetForCommand,
|
|
9
|
-
} from './stickerStorageService.js';
|
|
6
|
+
import { captureIncomingStickerAsset, resolveStickerAssetForCommand } from './stickerStorageService.js';
|
|
10
7
|
import { buildStickerPackMessage, sendStickerPackWithFallback } from './stickerPackMessageService.js';
|
|
11
8
|
import { sanitizeText } from './stickerPackUtils.js';
|
|
12
9
|
|
|
@@ -221,10 +218,7 @@ const buildPackVisualMessage = ({ intro = [], sections = [], footer = [] }) => {
|
|
|
221
218
|
const buildActionMessage = ({ title, explanation = [], details = [], nextSteps = [], footer = [] }) =>
|
|
222
219
|
buildPackVisualMessage({
|
|
223
220
|
intro: [title, ...normalizeMessageLines(explanation)],
|
|
224
|
-
sections: [
|
|
225
|
-
normalizeMessageLines(details).length ? { title: '📌 *DETALHES*', lines: details } : null,
|
|
226
|
-
normalizeMessageLines(nextSteps).length ? { title: '➡️ *PRÓXIMAS AÇÕES*', lines: nextSteps } : null,
|
|
227
|
-
],
|
|
221
|
+
sections: [normalizeMessageLines(details).length ? { title: '📌 *DETALHES*', lines: details } : null, normalizeMessageLines(nextSteps).length ? { title: '➡️ *PRÓXIMAS AÇÕES*', lines: nextSteps } : null],
|
|
228
222
|
footer,
|
|
229
223
|
});
|
|
230
224
|
|
|
@@ -238,18 +232,11 @@ const buildActionMessage = ({ title, explanation = [], details = [], nextSteps =
|
|
|
238
232
|
const formatPackList = (packs, prefix) => {
|
|
239
233
|
if (!packs.length) {
|
|
240
234
|
return buildPackVisualMessage({
|
|
241
|
-
intro: [
|
|
242
|
-
'📭 *Nenhum pack extra encontrado.*',
|
|
243
|
-
'As figurinhas que você cria continuam sendo salvas automaticamente no seu *Pack Principal*.',
|
|
244
|
-
],
|
|
235
|
+
intro: ['📭 *Nenhum pack extra encontrado.*', 'As figurinhas que você cria continuam sendo salvas automaticamente no seu *Pack Principal*.'],
|
|
245
236
|
sections: [
|
|
246
237
|
{
|
|
247
238
|
title: '🆕 *COMECE EM 3 PASSOS*',
|
|
248
|
-
lines: [
|
|
249
|
-
`1) Crie um pack: \`${prefix}pack create meupack\``,
|
|
250
|
-
`2) Responda uma figurinha e adicione: \`${prefix}pack add <pack>\``,
|
|
251
|
-
`3) Veja o resumo: \`${prefix}pack info <pack>\``,
|
|
252
|
-
],
|
|
239
|
+
lines: [`1) Crie um pack: \`${prefix}pack create meupack\``, `2) Responda uma figurinha e adicione: \`${prefix}pack add <pack>\``, `3) Veja o resumo: \`${prefix}pack info <pack>\``],
|
|
253
240
|
},
|
|
254
241
|
],
|
|
255
242
|
footer: ['💡 Dica: crie packs por tema (memes, animes, reactions) para achar tudo mais rápido.'],
|
|
@@ -258,28 +245,16 @@ const formatPackList = (packs, prefix) => {
|
|
|
258
245
|
|
|
259
246
|
const lines = packs.map((pack, index) => {
|
|
260
247
|
const count = Number(pack.sticker_count || 0);
|
|
261
|
-
return [
|
|
262
|
-
`${index + 1}. *${pack.name}*`,
|
|
263
|
-
` 🆔 ID: \`${pack.pack_key}\``,
|
|
264
|
-
` 🧩 Itens: ${count}/${MAX_PACK_ITEMS}`,
|
|
265
|
-
` 👁️ Visibilidade: ${formatVisibilityLabel(pack.visibility)}`,
|
|
266
|
-
].join('\n');
|
|
248
|
+
return [`${index + 1}. *${pack.name}*`, ` 🆔 ID: \`${pack.pack_key}\``, ` 🧩 Itens: ${count}/${MAX_PACK_ITEMS}`, ` 👁️ Visibilidade: ${formatVisibilityLabel(pack.visibility)}`].join('\n');
|
|
267
249
|
});
|
|
268
250
|
|
|
269
251
|
return buildPackVisualMessage({
|
|
270
|
-
intro: [
|
|
271
|
-
`📋 *Packs encontrados: ${packs.length}*`,
|
|
272
|
-
'Você pode usar o *nome* ou o *ID* do pack para ver detalhes, editar ou enviar.',
|
|
273
|
-
],
|
|
252
|
+
intro: [`📋 *Packs encontrados: ${packs.length}*`, 'Você pode usar o *nome* ou o *ID* do pack para ver detalhes, editar ou enviar.'],
|
|
274
253
|
sections: [
|
|
275
254
|
{ title: '📦 *SEUS PACKS*', lines },
|
|
276
255
|
{
|
|
277
256
|
title: '🛠 *ATALHOS*',
|
|
278
|
-
lines: [
|
|
279
|
-
`ℹ️ Detalhes: \`${prefix}pack info <pack>\``,
|
|
280
|
-
`📤 Enviar: \`${prefix}pack send <pack>\``,
|
|
281
|
-
`🆕 Criar novo: \`${prefix}pack create meupack\``,
|
|
282
|
-
],
|
|
257
|
+
lines: [`ℹ️ Detalhes: \`${prefix}pack info <pack>\``, `📤 Enviar: \`${prefix}pack send <pack>\``, `🆕 Criar novo: \`${prefix}pack create meupack\``],
|
|
283
258
|
},
|
|
284
259
|
],
|
|
285
260
|
footer: ['✅ Tudo pronto — escolha um pack e continue gerenciando.'],
|
|
@@ -307,22 +282,11 @@ const formatPackInfo = (pack, prefix) => {
|
|
|
307
282
|
}
|
|
308
283
|
|
|
309
284
|
return buildPackVisualMessage({
|
|
310
|
-
intro: [
|
|
311
|
-
`ℹ️ *Informações do pack: "${pack.name}"*`,
|
|
312
|
-
'Aqui você vê identificação, visibilidade e uma prévia dos itens cadastrados.',
|
|
313
|
-
],
|
|
285
|
+
intro: [`ℹ️ *Informações do pack: "${pack.name}"*`, 'Aqui você vê identificação, visibilidade e uma prévia dos itens cadastrados.'],
|
|
314
286
|
sections: [
|
|
315
287
|
{
|
|
316
288
|
title: '📌 *DADOS DO PACK*',
|
|
317
|
-
lines: [
|
|
318
|
-
`📛 Nome: *${pack.name}*`,
|
|
319
|
-
`🆔 ID: \`${pack.pack_key}\``,
|
|
320
|
-
`👤 Publisher: *${pack.publisher}*`,
|
|
321
|
-
`👁️ Visibilidade: ${formatVisibilityLabel(pack.visibility)}`,
|
|
322
|
-
`🧩 Itens: *${pack.items.length}/${MAX_PACK_ITEMS}*`,
|
|
323
|
-
`🖼️ Capa: *${coverLabel}*`,
|
|
324
|
-
`📝 Descrição: ${pack.description ? `"${pack.description}"` : 'não definida'}`,
|
|
325
|
-
],
|
|
289
|
+
lines: [`📛 Nome: *${pack.name}*`, `🆔 ID: \`${pack.pack_key}\``, `👤 Publisher: *${pack.publisher}*`, `👁️ Visibilidade: ${formatVisibilityLabel(pack.visibility)}`, `🧩 Itens: *${pack.items.length}/${MAX_PACK_ITEMS}*`, `🖼️ Capa: *${coverLabel}*`, `📝 Descrição: ${pack.description ? `"${pack.description}"` : 'não definida'}`],
|
|
326
290
|
},
|
|
327
291
|
{
|
|
328
292
|
title: '🖼️ *PRÉVIA (ATÉ 12 ITENS)*',
|
|
@@ -330,12 +294,7 @@ const formatPackInfo = (pack, prefix) => {
|
|
|
330
294
|
},
|
|
331
295
|
{
|
|
332
296
|
title: '⚙️ *AÇÕES DISPONÍVEIS*',
|
|
333
|
-
lines: [
|
|
334
|
-
`➕ Adicionar: \`${prefix}pack add ${pack.pack_key}\``,
|
|
335
|
-
`🖼 Definir capa: \`${prefix}pack setcover ${pack.pack_key}\``,
|
|
336
|
-
`🔀 Reordenar: \`${prefix}pack reorder ${pack.pack_key} 1 2 3 ...\``,
|
|
337
|
-
`📤 Enviar: \`${prefix}pack send ${pack.pack_key}\``,
|
|
338
|
-
],
|
|
297
|
+
lines: [`➕ Adicionar: \`${prefix}pack add ${pack.pack_key}\``, `🖼 Definir capa: \`${prefix}pack setcover ${pack.pack_key}\``, `🔀 Reordenar: \`${prefix}pack reorder ${pack.pack_key} 1 2 3 ...\``, `📤 Enviar: \`${prefix}pack send ${pack.pack_key}\``],
|
|
339
298
|
},
|
|
340
299
|
],
|
|
341
300
|
footer: ['💡 Se precisar, use o guia completo com `pack` para ver exemplos e comandos extras.'],
|
|
@@ -348,45 +307,7 @@ const formatPackInfo = (pack, prefix) => {
|
|
|
348
307
|
* @param {string} prefix Prefixo de comando.
|
|
349
308
|
* @returns {string} Guia textual.
|
|
350
309
|
*/
|
|
351
|
-
const buildPackHelp = (prefix) =>
|
|
352
|
-
[
|
|
353
|
-
'📦 *PACKS DE FIGURINHAS — GUIA RÁPIDO*',
|
|
354
|
-
'',
|
|
355
|
-
'Toda figurinha que você criar é salva automaticamente no seu *Pack Principal*.',
|
|
356
|
-
'Além disso, você pode criar packs extras para organizar por tema e enviar mais rápido.',
|
|
357
|
-
'',
|
|
358
|
-
PACK_VISUAL_DIVIDER,
|
|
359
|
-
'🧭 *COMANDOS PRINCIPAIS*',
|
|
360
|
-
'',
|
|
361
|
-
'🆕 Criar um pack',
|
|
362
|
-
`\`${prefix}pack create "Meus memes 😂" | publisher="Seu Nome" | desc="Descrição"\``,
|
|
363
|
-
'_Nome livre: espaços e emojis são permitidos._',
|
|
364
|
-
'',
|
|
365
|
-
'📋 Listar packs',
|
|
366
|
-
`\`${prefix}pack list\``,
|
|
367
|
-
'',
|
|
368
|
-
'ℹ️ Ver detalhes do pack',
|
|
369
|
-
`\`${prefix}pack info <pack>\``,
|
|
370
|
-
'',
|
|
371
|
-
'➕ Adicionar figurinha',
|
|
372
|
-
`\`${prefix}pack add <pack>\``,
|
|
373
|
-
'_Dica: responda uma figurinha (ou use a última enviada)._',
|
|
374
|
-
'',
|
|
375
|
-
'🖼 Definir capa',
|
|
376
|
-
`\`${prefix}pack setcover <pack>\``,
|
|
377
|
-
'',
|
|
378
|
-
'📤 Enviar pack no chat',
|
|
379
|
-
`\`${prefix}pack send "<nome do pack>"\``,
|
|
380
|
-
`_Ou use o ID: \`${prefix}pack send <pack_id>\`_`,
|
|
381
|
-
'',
|
|
382
|
-
PACK_VISUAL_DIVIDER,
|
|
383
|
-
'🧰 *COMANDOS EXTRAS*',
|
|
384
|
-
'',
|
|
385
|
-
'`rename` • `setpub` • `setdesc` • `remove` • `reorder` • `clone` • `publish` • `delete`',
|
|
386
|
-
'',
|
|
387
|
-
PACK_VISUAL_DIVIDER,
|
|
388
|
-
'✅ *Pronto!* Se quiser, diga o que você quer fazer (criar, organizar, enviar) que eu te guio.',
|
|
389
|
-
].join('\n');
|
|
310
|
+
const buildPackHelp = (prefix) => ['📦 *PACKS DE FIGURINHAS — GUIA RÁPIDO*', '', 'Toda figurinha que você criar é salva automaticamente no seu *Pack Principal*.', 'Além disso, você pode criar packs extras para organizar por tema e enviar mais rápido.', '', PACK_VISUAL_DIVIDER, '🧭 *COMANDOS PRINCIPAIS*', '', '🆕 Criar um pack', `\`${prefix}pack create "Meus memes 😂" | publisher="Seu Nome" | desc="Descrição"\``, '_Nome livre: espaços e emojis são permitidos._', '', '📋 Listar packs', `\`${prefix}pack list\``, '', 'ℹ️ Ver detalhes do pack', `\`${prefix}pack info <pack>\``, '', '➕ Adicionar figurinha', `\`${prefix}pack add <pack>\``, '_Dica: responda uma figurinha (ou use a última enviada)._', '', '🖼 Definir capa', `\`${prefix}pack setcover <pack>\``, '', '📤 Enviar pack no chat', `\`${prefix}pack send "<nome do pack>"\``, `_Ou use o ID: \`${prefix}pack send <pack_id>\`_`, '', PACK_VISUAL_DIVIDER, '🧰 *COMANDOS EXTRAS*', '', '`rename` • `setpub` • `setdesc` • `remove` • `reorder` • `clone` • `publish` • `delete`', '', PACK_VISUAL_DIVIDER, '✅ *Pronto!* Se quiser, diga o que você quer fazer (criar, organizar, enviar) que eu te guio.'].join('\n');
|
|
390
311
|
|
|
391
312
|
/**
|
|
392
313
|
* Template visual de erro orientado a resolução.
|
|
@@ -430,41 +351,28 @@ const formatErrorMessage = (error, commandPrefix) => {
|
|
|
430
351
|
return buildErrorMessage({
|
|
431
352
|
title: '🔎 *Pack não encontrado.*',
|
|
432
353
|
explanation: ['Não localizei um pack com esse nome ou ID.'],
|
|
433
|
-
steps: [
|
|
434
|
-
`Veja a lista com \`${commandPrefix}pack list\`.`,
|
|
435
|
-
'Copie o ID exatamente como aparece.',
|
|
436
|
-
`Depois tente novamente (ex.: \`${commandPrefix}pack info <pack>\`).`,
|
|
437
|
-
],
|
|
354
|
+
steps: [`Veja a lista com \`${commandPrefix}pack list\`.`, 'Copie o ID exatamente como aparece.', `Depois tente novamente (ex.: \`${commandPrefix}pack info <pack>\`).`],
|
|
438
355
|
commandPrefix,
|
|
439
356
|
});
|
|
440
357
|
case STICKER_PACK_ERROR_CODES.DUPLICATE_STICKER:
|
|
441
358
|
return buildErrorMessage({
|
|
442
359
|
title: '⚠️ *Essa figurinha já está no pack.*',
|
|
443
360
|
explanation: ['Para manter o pack organizado, não adiciono itens duplicados.'],
|
|
444
|
-
steps: [
|
|
445
|
-
`Veja os itens com \`${commandPrefix}pack info <pack>\`.`,
|
|
446
|
-
'Se quiser reorganizar, use `reorder`.',
|
|
447
|
-
],
|
|
361
|
+
steps: [`Veja os itens com \`${commandPrefix}pack info <pack>\`.`, 'Se quiser reorganizar, use `reorder`.'],
|
|
448
362
|
commandPrefix,
|
|
449
363
|
});
|
|
450
364
|
case STICKER_PACK_ERROR_CODES.PACK_LIMIT_REACHED:
|
|
451
365
|
return buildErrorMessage({
|
|
452
366
|
title: '⚠️ *Limite de figurinhas atingido.*',
|
|
453
367
|
explanation: [error.message || 'Este pack já está no limite e não aceita novos itens no momento.'],
|
|
454
|
-
steps: [
|
|
455
|
-
`Crie outro pack: \`${commandPrefix}pack create novopack\`.`,
|
|
456
|
-
'Depois continue adicionando as próximas figurinhas no novo pack.',
|
|
457
|
-
],
|
|
368
|
+
steps: [`Crie outro pack: \`${commandPrefix}pack create novopack\`.`, 'Depois continue adicionando as próximas figurinhas no novo pack.'],
|
|
458
369
|
commandPrefix,
|
|
459
370
|
});
|
|
460
371
|
case STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND:
|
|
461
372
|
return buildErrorMessage({
|
|
462
373
|
title: '🧩 *Não encontrei uma figurinha válida para usar.*',
|
|
463
374
|
explanation: ['Para esse comando, você precisa responder uma figurinha ou ter uma figurinha recente no contexto.'],
|
|
464
|
-
steps: [
|
|
465
|
-
'Responda diretamente a figurinha que você quer usar.',
|
|
466
|
-
'Ou envie uma figurinha e execute o comando novamente.',
|
|
467
|
-
],
|
|
375
|
+
steps: ['Responda diretamente a figurinha que você quer usar.', 'Ou envie uma figurinha e execute o comando novamente.'],
|
|
468
376
|
commandPrefix,
|
|
469
377
|
});
|
|
470
378
|
case STICKER_PACK_ERROR_CODES.INVALID_INPUT:
|
|
@@ -621,16 +529,7 @@ const resolveStickerFromCommandContext = async ({ messageInfo, ownerJid, include
|
|
|
621
529
|
* }} params Contexto da requisição.
|
|
622
530
|
* @returns {Promise<void>}
|
|
623
531
|
*/
|
|
624
|
-
export async function handlePackCommand({
|
|
625
|
-
sock,
|
|
626
|
-
remoteJid,
|
|
627
|
-
messageInfo,
|
|
628
|
-
expirationMessage,
|
|
629
|
-
senderJid,
|
|
630
|
-
senderName,
|
|
631
|
-
text,
|
|
632
|
-
commandPrefix,
|
|
633
|
-
}) {
|
|
532
|
+
export async function handlePackCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, senderName, text, commandPrefix }) {
|
|
634
533
|
const ownerJid = senderJid;
|
|
635
534
|
const rate = checkRateLimit(ownerJid);
|
|
636
535
|
|
|
@@ -682,16 +581,8 @@ export async function handlePackCommand({
|
|
|
682
581
|
text: buildActionMessage({
|
|
683
582
|
title: '✅ *Pack criado!*',
|
|
684
583
|
explanation: ['Seu pack já está disponível e pronto para receber figurinhas.'],
|
|
685
|
-
details: [
|
|
686
|
-
|
|
687
|
-
`🆔 ID: \`${created.pack_key}\``,
|
|
688
|
-
`👤 Publisher: *${created.publisher}*`,
|
|
689
|
-
`👁️ Visibilidade: ${formatVisibilityLabel(created.visibility)}`,
|
|
690
|
-
],
|
|
691
|
-
nextSteps: [
|
|
692
|
-
`Responda uma figurinha e use: \`${commandPrefix}pack add ${created.pack_key}\`.`,
|
|
693
|
-
`Para conferir: \`${commandPrefix}pack info ${created.pack_key}\`.`,
|
|
694
|
-
],
|
|
584
|
+
details: [`📛 Nome: *${created.name}*`, `🆔 ID: \`${created.pack_key}\``, `👤 Publisher: *${created.publisher}*`, `👁️ Visibilidade: ${formatVisibilityLabel(created.visibility)}`],
|
|
585
|
+
nextSteps: [`Responda uma figurinha e use: \`${commandPrefix}pack add ${created.pack_key}\`.`, `Para conferir: \`${commandPrefix}pack info ${created.pack_key}\`.`],
|
|
695
586
|
footer: ['💡 Dica: use packs por tema para organizar e enviar mais rápido.'],
|
|
696
587
|
}),
|
|
697
588
|
});
|
|
@@ -759,9 +650,7 @@ export async function handlePackCommand({
|
|
|
759
650
|
title: '👤 *Publisher atualizado!*',
|
|
760
651
|
explanation: ['O publisher deste pack foi ajustado e já aparece nas informações.'],
|
|
761
652
|
details: [`📦 Pack: *${updated.name}*`, `👤 Publisher: *${updated.publisher}*`, `🆔 ID: \`${updated.pack_key}\``],
|
|
762
|
-
nextSteps: [
|
|
763
|
-
`Se quiser, ajuste a descrição: \`${commandPrefix}pack setdesc ${updated.pack_key} "Nova descrição"\`.`,
|
|
764
|
-
],
|
|
653
|
+
nextSteps: [`Se quiser, ajuste a descrição: \`${commandPrefix}pack setdesc ${updated.pack_key} "Nova descrição"\`.`],
|
|
765
654
|
}),
|
|
766
655
|
});
|
|
767
656
|
return;
|
|
@@ -780,10 +669,7 @@ export async function handlePackCommand({
|
|
|
780
669
|
text: buildActionMessage({
|
|
781
670
|
title: '📝 *Descrição atualizada!*',
|
|
782
671
|
explanation: ['A descrição ajuda a identificar o tema do pack.'],
|
|
783
|
-
details: [
|
|
784
|
-
`📦 Pack: *${updated.name}*`,
|
|
785
|
-
description ? `📝 Descrição: "${updated.description}"` : '🧹 Descrição removida.',
|
|
786
|
-
],
|
|
672
|
+
details: [`📦 Pack: *${updated.name}*`, description ? `📝 Descrição: "${updated.description}"` : '🧹 Descrição removida.'],
|
|
787
673
|
nextSteps: [`Ver como ficou: \`${commandPrefix}pack info ${updated.pack_key}\`.`],
|
|
788
674
|
}),
|
|
789
675
|
});
|
|
@@ -795,10 +681,7 @@ export async function handlePackCommand({
|
|
|
795
681
|
const asset = await resolveStickerFromCommandContext({ messageInfo, ownerJid });
|
|
796
682
|
|
|
797
683
|
if (!asset) {
|
|
798
|
-
throw new StickerPackError(
|
|
799
|
-
STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND,
|
|
800
|
-
'Não encontrei uma figurinha para definir como capa.',
|
|
801
|
-
);
|
|
684
|
+
throw new StickerPackError(STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND, 'Não encontrei uma figurinha para definir como capa.');
|
|
802
685
|
}
|
|
803
686
|
|
|
804
687
|
const updated = await stickerPackService.setPackCover({
|
|
@@ -829,10 +712,7 @@ export async function handlePackCommand({
|
|
|
829
712
|
|
|
830
713
|
const asset = await resolveStickerFromCommandContext({ messageInfo, ownerJid });
|
|
831
714
|
if (!asset) {
|
|
832
|
-
throw new StickerPackError(
|
|
833
|
-
STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND,
|
|
834
|
-
'Não encontrei uma figurinha para adicionar.',
|
|
835
|
-
);
|
|
715
|
+
throw new StickerPackError(STICKER_PACK_ERROR_CODES.STICKER_NOT_FOUND, 'Não encontrei uma figurinha para adicionar.');
|
|
836
716
|
}
|
|
837
717
|
|
|
838
718
|
const updated = await stickerPackService.addStickerToPack({
|
|
@@ -851,15 +731,8 @@ export async function handlePackCommand({
|
|
|
851
731
|
text: buildActionMessage({
|
|
852
732
|
title: '➕ *Figurinha adicionada!*',
|
|
853
733
|
explanation: ['Item adicionado com sucesso ao pack selecionado.'],
|
|
854
|
-
details: [
|
|
855
|
-
|
|
856
|
-
`🧩 Itens: *${updated.items.length}/${MAX_PACK_ITEMS}*`,
|
|
857
|
-
`🆔 ID: \`${updated.pack_key}\``,
|
|
858
|
-
],
|
|
859
|
-
nextSteps: [
|
|
860
|
-
`Definir como capa: responda a figurinha e use \`${commandPrefix}pack setcover ${updated.pack_key}\`.`,
|
|
861
|
-
`Ver lista completa: \`${commandPrefix}pack info ${updated.pack_key}\`.`,
|
|
862
|
-
],
|
|
734
|
+
details: [`📦 Pack: *${updated.name}*`, `🧩 Itens: *${updated.items.length}/${MAX_PACK_ITEMS}*`, `🆔 ID: \`${updated.pack_key}\``],
|
|
735
|
+
nextSteps: [`Definir como capa: responda a figurinha e use \`${commandPrefix}pack setcover ${updated.pack_key}\`.`, `Ver lista completa: \`${commandPrefix}pack info ${updated.pack_key}\`.`],
|
|
863
736
|
}),
|
|
864
737
|
});
|
|
865
738
|
return;
|
|
@@ -883,11 +756,7 @@ export async function handlePackCommand({
|
|
|
883
756
|
text: buildActionMessage({
|
|
884
757
|
title: '🗑️ *Figurinha removida!*',
|
|
885
758
|
explanation: ['Remoção concluída e o pack foi reordenado automaticamente.'],
|
|
886
|
-
details: [
|
|
887
|
-
`📦 Pack: *${result.pack.name}*`,
|
|
888
|
-
`🔢 Item removido: figurinha #${result.removed.position}`,
|
|
889
|
-
`🧩 Itens: *${result.pack.items.length}/${MAX_PACK_ITEMS}*`,
|
|
890
|
-
],
|
|
759
|
+
details: [`📦 Pack: *${result.pack.name}*`, `🔢 Item removido: figurinha #${result.removed.position}`, `🧩 Itens: *${result.pack.items.length}/${MAX_PACK_ITEMS}*`],
|
|
891
760
|
nextSteps: [`Conferir: \`${commandPrefix}pack info ${result.pack.pack_key}\`.`],
|
|
892
761
|
}),
|
|
893
762
|
});
|
|
@@ -942,10 +811,7 @@ export async function handlePackCommand({
|
|
|
942
811
|
title: '🧬 *Clone criado!*',
|
|
943
812
|
explanation: ['O pack foi duplicado com as mesmas figurinhas e configurações.'],
|
|
944
813
|
details: [`📦 Novo pack: *${cloned.name}*`, `🆔 ID: \`${cloned.pack_key}\``],
|
|
945
|
-
nextSteps: [
|
|
946
|
-
`Renomear: \`${commandPrefix}pack rename ${cloned.pack_key} novonome\`.`,
|
|
947
|
-
`Enviar: \`${commandPrefix}pack send ${cloned.pack_key}\`.`,
|
|
948
|
-
],
|
|
814
|
+
nextSteps: [`Renomear: \`${commandPrefix}pack rename ${cloned.pack_key} novonome\`.`, `Enviar: \`${commandPrefix}pack send ${cloned.pack_key}\`.`],
|
|
949
815
|
}),
|
|
950
816
|
});
|
|
951
817
|
return;
|
|
@@ -988,11 +854,7 @@ export async function handlePackCommand({
|
|
|
988
854
|
text: buildActionMessage({
|
|
989
855
|
title: '🌐 *Visibilidade atualizada!*',
|
|
990
856
|
explanation: ['A configuração de privacidade foi aplicada ao pack.'],
|
|
991
|
-
details: [
|
|
992
|
-
`📦 Pack: *${updated.name}*`,
|
|
993
|
-
`👁️ Visibilidade: ${formatVisibilityLabel(updated.visibility)}`,
|
|
994
|
-
`🆔 ID: \`${updated.pack_key}\``,
|
|
995
|
-
],
|
|
857
|
+
details: [`📦 Pack: *${updated.name}*`, `👁️ Visibilidade: ${formatVisibilityLabel(updated.visibility)}`, `🆔 ID: \`${updated.pack_key}\``],
|
|
996
858
|
nextSteps: [`Compartilhar/enviar: \`${commandPrefix}pack send ${updated.pack_key}\`.`],
|
|
997
859
|
}),
|
|
998
860
|
});
|
|
@@ -1020,15 +882,8 @@ export async function handlePackCommand({
|
|
|
1020
882
|
text: buildActionMessage({
|
|
1021
883
|
title: '📤 *Pack enviado!*',
|
|
1022
884
|
explanation: ['Enviei no formato nativo (melhor experiência e compatibilidade).'],
|
|
1023
|
-
details: [
|
|
1024
|
-
|
|
1025
|
-
`🆔 ID: \`${packDetails.pack_key}\``,
|
|
1026
|
-
`🧩 Enviadas: *${sendResult.sentCount} figurinha(s)*`,
|
|
1027
|
-
],
|
|
1028
|
-
nextSteps: [
|
|
1029
|
-
`Ver detalhes: \`${commandPrefix}pack info ${packDetails.pack_key}\`.`,
|
|
1030
|
-
`Editar: \`${commandPrefix}pack add ${packDetails.pack_key}\` ou \`${commandPrefix}pack remove ${packDetails.pack_key} <item>\`.`,
|
|
1031
|
-
],
|
|
885
|
+
details: [`📦 Pack: *${packDetails.name}*`, `🆔 ID: \`${packDetails.pack_key}\``, `🧩 Enviadas: *${sendResult.sentCount} figurinha(s)*`],
|
|
886
|
+
nextSteps: [`Ver detalhes: \`${commandPrefix}pack info ${packDetails.pack_key}\`.`, `Editar: \`${commandPrefix}pack add ${packDetails.pack_key}\` ou \`${commandPrefix}pack remove ${packDetails.pack_key} <item>\`.`],
|
|
1032
887
|
}),
|
|
1033
888
|
});
|
|
1034
889
|
} else {
|
|
@@ -1039,19 +894,9 @@ export async function handlePackCommand({
|
|
|
1039
894
|
expirationMessage,
|
|
1040
895
|
text: buildActionMessage({
|
|
1041
896
|
title: 'ℹ️ *Pack enviado em modo compatível.*',
|
|
1042
|
-
explanation: [
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
],
|
|
1046
|
-
details: [
|
|
1047
|
-
`📦 Pack: *${packDetails.name}*`,
|
|
1048
|
-
`🧩 Progresso: *${sendResult.sentCount}/${sendResult.total}*`,
|
|
1049
|
-
sendResult.nativeError ? `🛠 Detalhe técnico: ${sendResult.nativeError}` : null,
|
|
1050
|
-
],
|
|
1051
|
-
nextSteps: [
|
|
1052
|
-
`Você pode continuar gerenciando: \`${commandPrefix}pack info ${packDetails.pack_key}\`.`,
|
|
1053
|
-
`Para tentar novamente no formato nativo: \`${commandPrefix}pack send ${packDetails.pack_key}\` mais tarde.`,
|
|
1054
|
-
],
|
|
897
|
+
explanation: [`O cliente não aceitou o formato nativo para *${packDetails.name}*.`, 'Enviei em modo compatível (prévia + figurinhas individuais).'],
|
|
898
|
+
details: [`📦 Pack: *${packDetails.name}*`, `🧩 Progresso: *${sendResult.sentCount}/${sendResult.total}*`, sendResult.nativeError ? `🛠 Detalhe técnico: ${sendResult.nativeError}` : null],
|
|
899
|
+
nextSteps: [`Você pode continuar gerenciando: \`${commandPrefix}pack info ${packDetails.pack_key}\`.`, `Para tentar novamente no formato nativo: \`${commandPrefix}pack send ${packDetails.pack_key}\` mais tarde.`],
|
|
1055
900
|
}),
|
|
1056
901
|
});
|
|
1057
902
|
}
|