@omnizap-system/omnizap 2.6.1 → 2.6.2
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/.env.example +54 -9
- package/.github/workflows/ci.yml +3 -3
- package/.github/workflows/security-runner-hardening.yml +1 -1
- package/.github/workflows/security-zap-full-scan.yml +1 -0
- package/app/config/index.js +2 -0
- package/app/configParts/adminIdentity.js +5 -5
- package/app/configParts/baileysConfig.js +226 -55
- package/app/configParts/groupUtils.js +5 -0
- package/app/configParts/messagePersistenceService.js +143 -3
- package/app/configParts/sessionConfig.js +157 -0
- package/app/connection/baileysCompatibility.test.js +1 -1
- package/app/connection/groupOwnerWriteStateResolver.js +109 -0
- package/app/connection/socketController.js +625 -124
- package/app/connection/socketController.multiSession.test.js +108 -0
- package/app/controllers/messageController.js +1 -1
- package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
- package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
- package/app/controllers/messageProcessingPipeline.js +88 -9
- package/app/controllers/messageProcessingPipeline.test.js +200 -0
- package/app/modules/adminModule/AGENT.md +1 -1
- package/app/modules/adminModule/commandConfig.json +3318 -1347
- package/app/modules/adminModule/groupCommandHandlers.js +856 -14
- package/app/modules/adminModule/groupCommandHandlers.test.js +375 -9
- package/app/modules/adminModule/groupWarningRepository.js +152 -0
- package/app/modules/aiModule/AGENT.md +47 -30
- package/app/modules/aiModule/aiConfigRuntime.js +1 -0
- package/app/modules/aiModule/catCommand.js +132 -25
- package/app/modules/aiModule/commandConfig.json +114 -28
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
- package/app/modules/gameModule/AGENT.md +1 -1
- package/app/modules/gameModule/commandConfig.json +29 -0
- package/app/modules/menuModule/AGENT.md +1 -1
- package/app/modules/menuModule/commandConfig.json +45 -10
- package/app/modules/menuModule/menuCatalogService.js +190 -0
- package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
- package/app/modules/menuModule/menuDynamicService.js +511 -0
- package/app/modules/menuModule/menuDynamicService.test.js +141 -0
- package/app/modules/menuModule/menus.js +36 -5
- package/app/modules/playModule/AGENT.md +10 -5
- package/app/modules/playModule/commandConfig.json +74 -16
- package/app/modules/playModule/playCommandConstants.js +13 -7
- package/app/modules/playModule/playCommandCore.js +4 -6
- package/app/modules/playModule/{playCommandYtDlpClient.js → playCommandMediaClient.js} +684 -332
- package/app/modules/playModule/playConfigRuntime.js +5 -6
- package/app/modules/playModule/playModuleCriticalFlows.test.js +44 -59
- package/app/modules/quoteModule/AGENT.md +1 -1
- package/app/modules/quoteModule/commandConfig.json +29 -0
- package/app/modules/rpgPokemonModule/AGENT.md +1 -1
- package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
- package/app/modules/statsModule/AGENT.md +1 -1
- package/app/modules/statsModule/commandConfig.json +58 -0
- package/app/modules/stickerModule/AGENT.md +1 -1
- package/app/modules/stickerModule/commandConfig.json +145 -0
- package/app/modules/stickerPackModule/AGENT.md +1 -1
- package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
- package/app/modules/stickerPackModule/commandConfig.json +29 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +1 -1
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +78 -57
- package/app/modules/stickerPackModule/stickerPackService.js +13 -6
- package/app/modules/systemMetricsModule/AGENT.md +1 -1
- package/app/modules/systemMetricsModule/commandConfig.json +29 -0
- package/app/modules/tiktokModule/AGENT.md +1 -1
- package/app/modules/tiktokModule/commandConfig.json +29 -0
- package/app/modules/userModule/AGENT.md +1 -1
- package/app/modules/userModule/commandConfig.json +29 -0
- package/app/modules/waifuPicsModule/AGENT.md +57 -27
- package/app/modules/waifuPicsModule/commandConfig.json +87 -0
- package/app/observability/metrics.js +136 -0
- package/app/services/ai/commandConfigEnrichmentService.js +229 -47
- package/app/services/ai/geminiService.js +131 -7
- package/app/services/ai/geminiService.test.js +59 -2
- package/app/services/ai/moduleAiHelpCoreService.js +33 -4
- package/app/services/group/groupMetadataService.js +24 -1
- package/app/services/infra/dbWriteQueue.js +51 -21
- package/app/services/messaging/newsBroadcastService.js +843 -27
- package/app/services/multiSession/assignmentBalancerService.js +457 -0
- package/app/services/multiSession/groupOwnershipRepository.js +381 -0
- package/app/services/multiSession/groupOwnershipService.js +890 -0
- package/app/services/multiSession/groupOwnershipService.test.js +309 -0
- package/app/services/multiSession/sessionRegistryService.js +293 -0
- package/app/store/aiPromptStore.js +36 -19
- package/app/store/groupConfigStore.js +41 -5
- package/app/store/premiumUserStore.js +21 -7
- package/app/utils/antiLink/antiLinkModule.js +352 -16
- package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
- package/database/index.js +6 -0
- package/database/migrations/20260307_d0_hardening_down.sql +1 -1
- package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
- package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
- package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
- package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
- package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
- package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
- package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
- package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
- package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
- package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
- package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
- package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
- package/database/schema.sql +102 -1
- package/docker-compose.yml +4 -1
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
- package/docs/compliance/privacy-policy-2026-03-07.md +2 -2
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
- package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
- package/docs/security/omnizap-static-security-headers.conf +25 -0
- package/ecosystem.prod.config.cjs +31 -11
- package/index.js +52 -18
- package/observability/alert-rules.yml +20 -0
- package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
- package/observability/mysql-setup.sql +4 -4
- package/observability/system-admin-observability.md +26 -0
- package/package.json +12 -5
- package/public/comandos/commands-catalog.json +2253 -78
- package/public/js/apps/commandsReactApp.js +267 -87
- package/public/js/apps/createPackApp.js +3 -3
- package/public/js/apps/stickersApp.js +255 -103
- package/public/js/apps/termsReactApp.js +57 -8
- package/public/js/apps/userPasswordResetReactApp.js +406 -0
- package/public/js/apps/userReactApp.js +96 -47
- package/public/js/apps/userSystemAdmReactApp.js +1506 -0
- package/public/pages/politica-de-privacidade.html +1 -1
- package/public/pages/stickers.html +5 -5
- package/public/pages/termos-de-uso-texto-integral.html +1 -1
- package/public/pages/termos-de-uso.html +1 -1
- package/public/pages/user-password-reset.html +3 -4
- package/public/pages/user-systemadm.html +8 -462
- package/public/pages/user.html +1 -1
- package/scripts/clear-whatsapp-session.sh +123 -0
- package/scripts/core-ai-mode.mjs +163 -0
- package/scripts/deploy.sh +10 -0
- package/scripts/enrich-command-config-ux-openai.mjs +492 -0
- package/scripts/generate-commands-catalog.mjs +155 -0
- package/scripts/new-whatsapp-session.sh +317 -0
- package/scripts/security-web-surface-check.mjs +218 -0
- package/server/controllers/admin/adminPanelHandlers.js +253 -3
- package/server/controllers/admin/systemAdminController.js +267 -0
- package/server/controllers/sticker/stickerCatalogController.js +9 -23
- package/server/controllers/system/contactController.js +9 -17
- package/server/controllers/system/stickerCatalogSystemContext.js +27 -6
- package/server/controllers/system/systemController.js +254 -1
- package/server/controllers/userController.js +6 -0
- package/server/email/emailTemplateService.js +3 -2
- package/server/http/httpServer.js +8 -4
- package/server/middleware/securityHeaders.js +20 -1
- package/server/routes/admin/systemAdminRouter.js +6 -0
- package/server/routes/indexRouter.js +30 -6
- package/server/routes/observability/grafanaProxyRouter.js +254 -0
- package/server/routes/static/staticPageRouter.js +27 -1
- package/server/utils/publicContact.js +31 -0
- package/utils/whatsapp/contactEnv.js +39 -0
- package/vite.config.mjs +2 -1
- package/app/modules/playModule/local/installYtDlp.js +0 -25
- package/app/modules/playModule/local/ytDlpInstaller.js +0 -28
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import { createGroupOwnerWriteStateResolver } from './groupOwnerWriteStateResolver.js';
|
|
5
|
+
|
|
6
|
+
const createCache = () => {
|
|
7
|
+
const map = new Map();
|
|
8
|
+
return {
|
|
9
|
+
get: (key) => map.get(key),
|
|
10
|
+
set: (key, value) => {
|
|
11
|
+
map.set(key, value);
|
|
12
|
+
return true;
|
|
13
|
+
},
|
|
14
|
+
del: (key) => map.delete(key),
|
|
15
|
+
keys: () => Array.from(map.keys()),
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const buildCacheKey = (groupJid, sessionId) => `${sessionId}:${groupJid}`;
|
|
20
|
+
|
|
21
|
+
const normalizeSessionId = (value) => String(value || '').trim() || 'default';
|
|
22
|
+
const isGroupJid = (jid) => String(jid || '').endsWith('@g.us');
|
|
23
|
+
|
|
24
|
+
test('socketController multi-session: fencing token por assignment_version invalida writer stale', async () => {
|
|
25
|
+
let ownerState = {
|
|
26
|
+
ownerSessionId: 'session-a',
|
|
27
|
+
assignmentVersion: 1,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const resolver = createGroupOwnerWriteStateResolver({
|
|
31
|
+
buildCacheKeyImpl: buildCacheKey,
|
|
32
|
+
getOwnerImpl: async () => ownerState,
|
|
33
|
+
tryAcquireImpl: async () => ({ acquired: false, reason: 'claim_disabled' }),
|
|
34
|
+
cacheImpl: createCache(),
|
|
35
|
+
isGroupJidImpl: isGroupJid,
|
|
36
|
+
normalizeSessionIdImpl: normalizeSessionId,
|
|
37
|
+
loggerImpl: { warn: () => {} },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const first = await resolver('120363555555555555@g.us', 'session-a', {
|
|
41
|
+
allowClaim: false,
|
|
42
|
+
source: 'test_first',
|
|
43
|
+
});
|
|
44
|
+
assert.equal(first.allowed, true);
|
|
45
|
+
assert.equal(first.assignmentVersion, 1);
|
|
46
|
+
|
|
47
|
+
ownerState = {
|
|
48
|
+
ownerSessionId: 'session-a',
|
|
49
|
+
assignmentVersion: 2,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const stale = await resolver('120363555555555555@g.us', 'session-a', {
|
|
53
|
+
allowClaim: false,
|
|
54
|
+
source: 'test_stale',
|
|
55
|
+
expectedAssignmentVersion: 1,
|
|
56
|
+
enforceFence: true,
|
|
57
|
+
});
|
|
58
|
+
assert.equal(stale.allowed, false);
|
|
59
|
+
assert.equal(stale.reason, 'fence_token_mismatch');
|
|
60
|
+
assert.equal(stale.assignmentVersion, 2);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('socketController multi-session: sessão antiga perde escrita após failover de owner', async () => {
|
|
64
|
+
let ownerState = {
|
|
65
|
+
ownerSessionId: 'session-a',
|
|
66
|
+
assignmentVersion: 7,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const resolver = createGroupOwnerWriteStateResolver({
|
|
70
|
+
buildCacheKeyImpl: buildCacheKey,
|
|
71
|
+
getOwnerImpl: async () => ownerState,
|
|
72
|
+
tryAcquireImpl: async () => ({ acquired: false, reason: 'claim_disabled' }),
|
|
73
|
+
cacheImpl: createCache(),
|
|
74
|
+
isGroupJidImpl: isGroupJid,
|
|
75
|
+
normalizeSessionIdImpl: normalizeSessionId,
|
|
76
|
+
loggerImpl: { warn: () => {} },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const beforeFailover = await resolver('120363666666666666@g.us', 'session-a', {
|
|
80
|
+
allowClaim: false,
|
|
81
|
+
source: 'before_failover',
|
|
82
|
+
});
|
|
83
|
+
assert.equal(beforeFailover.allowed, true);
|
|
84
|
+
assert.equal(beforeFailover.assignmentVersion, 7);
|
|
85
|
+
|
|
86
|
+
ownerState = {
|
|
87
|
+
ownerSessionId: 'session-b',
|
|
88
|
+
assignmentVersion: 8,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const staleOwner = await resolver('120363666666666666@g.us', 'session-a', {
|
|
92
|
+
allowClaim: false,
|
|
93
|
+
source: 'after_failover_old_owner',
|
|
94
|
+
expectedAssignmentVersion: 7,
|
|
95
|
+
enforceFence: true,
|
|
96
|
+
});
|
|
97
|
+
assert.equal(staleOwner.allowed, false);
|
|
98
|
+
assert.equal(staleOwner.reason, 'owned_by_other');
|
|
99
|
+
|
|
100
|
+
const newOwner = await resolver('120363666666666666@g.us', 'session-b', {
|
|
101
|
+
allowClaim: false,
|
|
102
|
+
source: 'after_failover_new_owner',
|
|
103
|
+
expectedAssignmentVersion: 8,
|
|
104
|
+
enforceFence: true,
|
|
105
|
+
});
|
|
106
|
+
assert.equal(newOwner.allowed, true);
|
|
107
|
+
assert.equal(newOwner.assignmentVersion, 8);
|
|
108
|
+
});
|
|
@@ -4,4 +4,4 @@ import { handleMessagesThroughPipeline } from './messageProcessingPipeline.js';
|
|
|
4
4
|
* Facade do controller de mensagens.
|
|
5
5
|
* Mantem assinatura/compatibilidade enquanto delega ao pipeline modular.
|
|
6
6
|
*/
|
|
7
|
-
export const handleMessages = async (update, sock) => handleMessagesThroughPipeline(update, sock);
|
|
7
|
+
export const handleMessages = async (update, sock, options = {}) => handleMessagesThroughPipeline(update, sock, options);
|
|
@@ -42,17 +42,19 @@ export const createCommandMiddleware = ({ isAdminCommand, isKnownNonAdminCommand
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
if (COMMAND_REACT_EMOJI) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
if (COMMAND_REACT_EMOJI?.trim() && ctx?.key) {
|
|
46
|
+
sendAndStore(ctx.sock, ctx.remoteJid, {
|
|
47
|
+
react: {
|
|
48
|
+
text: COMMAND_REACT_EMOJI,
|
|
49
|
+
key: ctx.key,
|
|
50
|
+
},
|
|
51
|
+
}).catch((error) => {
|
|
52
|
+
logger.warn('Falha ao enviar reação de comando', {
|
|
53
|
+
error: error?.message,
|
|
54
|
+
jid: ctx.remoteJid,
|
|
55
|
+
messageId: ctx.key?.id,
|
|
52
56
|
});
|
|
53
|
-
}
|
|
54
|
-
logger.warn('Falha ao enviar reação de comando:', error?.message);
|
|
55
|
-
}
|
|
57
|
+
});
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
const execution = await executeMessageCommandRoute({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const createConversationMiddleware = ({ logger, resolveSenderAdminForContext, resolveSenderOwnerForContext, resolveHasGoogleLoginForContext, isUserAdmin, isAdminSenderAsync, resolveCanonicalSenderJidForContext, isWhatsAppUserLinkedToGoogleWebAccount, WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN, ensureUserHasGoogleWebLoginForCommand, executeMessageCommandRoute, isAdminCommand, runCommand, sendReply, routeConversationMessage, stopMessagePipeline }) => {
|
|
1
|
+
export const createConversationMiddleware = ({ logger, resolveSenderAdminForContext, resolveSenderOwnerForContext, resolveHasGoogleLoginForContext, isUserAdmin, isAdminSenderAsync, resolveCanonicalSenderJidForContext, isWhatsAppUserLinkedToGoogleWebAccount, WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN, ensureUserHasGoogleWebLoginForCommand, executeMessageCommandRoute, isAdminCommand, runCommand, sendReply, routeConversationMessage, stopMessagePipeline, conversationAutoReplyEnabled = true }) => {
|
|
2
2
|
const resolveToolSecurityContextForConversation = async (ctx) => {
|
|
3
3
|
if (ctx.memo.toolSecurityContext) return ctx.memo.toolSecurityContext;
|
|
4
4
|
|
|
@@ -133,6 +133,7 @@ export const createConversationMiddleware = ({ logger, resolveSenderAdminForCont
|
|
|
133
133
|
};
|
|
134
134
|
|
|
135
135
|
return async (ctx) => {
|
|
136
|
+
if (!conversationAutoReplyEnabled) return null;
|
|
136
137
|
if (ctx.isCommandMessage || ctx.isMessageFromBot || !ctx.isNotifyUpsert) return null;
|
|
137
138
|
|
|
138
139
|
try {
|
|
@@ -26,6 +26,8 @@ const createBaseContext = (overrides = {}) => ({
|
|
|
26
26
|
mediaEntries: [],
|
|
27
27
|
upsertType: 'notify',
|
|
28
28
|
isNotifyUpsert: true,
|
|
29
|
+
sessionId: 'session-default',
|
|
30
|
+
ownerSessionId: null,
|
|
29
31
|
isCommandMessage: false,
|
|
30
32
|
hasCommandPrefix: false,
|
|
31
33
|
analysisPayload: {
|
|
@@ -166,6 +168,108 @@ test('pre-processing trata trigger de iniciar login', async () => {
|
|
|
166
168
|
assert.equal(stopSpy.calls[0].metadataPatch.flow, 'whatsapp_google_login');
|
|
167
169
|
});
|
|
168
170
|
|
|
171
|
+
test('pre-processing owner gate bloqueia sessao nao-owner em modo enforce', async () => {
|
|
172
|
+
const stopSpy = createStopSpy();
|
|
173
|
+
|
|
174
|
+
const middlewares = createPreProcessingMiddlewares({
|
|
175
|
+
executeQuery: async () => [],
|
|
176
|
+
TABLES: { RPG_PLAYER: 'rpg_player' },
|
|
177
|
+
isStatusJid: () => false,
|
|
178
|
+
stopMessagePipeline: stopSpy.stopMessagePipeline,
|
|
179
|
+
handleAntiLink: async () => false,
|
|
180
|
+
ensureCommandPrefixForContext: async () => '/',
|
|
181
|
+
resolveCaptchaByMessage: async () => {},
|
|
182
|
+
maybeHandleStartLoginMessage: async () => false,
|
|
183
|
+
mergeAnalysisMetadata: (analysisPayload, patch) => {
|
|
184
|
+
analysisPayload.metadata = {
|
|
185
|
+
...(analysisPayload.metadata || {}),
|
|
186
|
+
...(patch || {}),
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
ensureGroupConfigForContext: async () => ({}),
|
|
190
|
+
resolveStickerFocusState: () => ({ enabled: false }),
|
|
191
|
+
resolveStickerFocusMessageClassification: () => ({ isThrottleCandidate: false }),
|
|
192
|
+
resolveGroupOwnerForContext: async (ctx) => {
|
|
193
|
+
ctx.ownerSessionId = 'session-owner';
|
|
194
|
+
return { ownerSessionId: 'session-owner' };
|
|
195
|
+
},
|
|
196
|
+
ownerEnforcementMode: 'enforce',
|
|
197
|
+
primarySessionId: 'session-primary',
|
|
198
|
+
isUserAdmin: async () => false,
|
|
199
|
+
canSendMessageInStickerFocus: () => ({ allowed: true, remainingMs: 0 }),
|
|
200
|
+
registerMessageUsageInStickerFocus: () => {},
|
|
201
|
+
shouldSendStickerFocusWarning: () => false,
|
|
202
|
+
sendReply: async () => {},
|
|
203
|
+
formatStickerFocusRuleLabel: () => '',
|
|
204
|
+
formatRemainingMinutesLabel: () => 1,
|
|
205
|
+
logger: { warn: () => {}, info: () => {} },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const ctx = createBaseContext({
|
|
209
|
+
sessionId: 'session-worker',
|
|
210
|
+
isGroupMessage: true,
|
|
211
|
+
remoteJid: '120363111111111111@g.us',
|
|
212
|
+
});
|
|
213
|
+
const result = await middlewares.enforceGroupOwnerMiddleware(ctx);
|
|
214
|
+
|
|
215
|
+
assert.deepEqual(result, { stop: true });
|
|
216
|
+
assert.equal(stopSpy.calls.length, 1);
|
|
217
|
+
assert.equal(stopSpy.calls[0].processingResult, 'blocked_group_owner_enforcement');
|
|
218
|
+
assert.equal(ctx.analysisPayload.metadata.owner_enforcement_result, 'blocked_non_owner');
|
|
219
|
+
assert.equal(ctx.analysisPayload.metadata.owner_session_id, 'session-owner');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('pre-processing owner gate apenas loga em modo shadow para sessao nao-owner', async () => {
|
|
223
|
+
const stopSpy = createStopSpy();
|
|
224
|
+
const infoLogs = [];
|
|
225
|
+
|
|
226
|
+
const middlewares = createPreProcessingMiddlewares({
|
|
227
|
+
executeQuery: async () => [],
|
|
228
|
+
TABLES: { RPG_PLAYER: 'rpg_player' },
|
|
229
|
+
isStatusJid: () => false,
|
|
230
|
+
stopMessagePipeline: stopSpy.stopMessagePipeline,
|
|
231
|
+
handleAntiLink: async () => false,
|
|
232
|
+
ensureCommandPrefixForContext: async () => '/',
|
|
233
|
+
resolveCaptchaByMessage: async () => {},
|
|
234
|
+
maybeHandleStartLoginMessage: async () => false,
|
|
235
|
+
mergeAnalysisMetadata: (analysisPayload, patch) => {
|
|
236
|
+
analysisPayload.metadata = {
|
|
237
|
+
...(analysisPayload.metadata || {}),
|
|
238
|
+
...(patch || {}),
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
ensureGroupConfigForContext: async () => ({}),
|
|
242
|
+
resolveStickerFocusState: () => ({ enabled: false }),
|
|
243
|
+
resolveStickerFocusMessageClassification: () => ({ isThrottleCandidate: false }),
|
|
244
|
+
resolveGroupOwnerForContext: async (ctx) => {
|
|
245
|
+
ctx.ownerSessionId = 'session-owner';
|
|
246
|
+
return { ownerSessionId: 'session-owner' };
|
|
247
|
+
},
|
|
248
|
+
ownerEnforcementMode: 'shadow',
|
|
249
|
+
primarySessionId: 'session-primary',
|
|
250
|
+
isUserAdmin: async () => false,
|
|
251
|
+
canSendMessageInStickerFocus: () => ({ allowed: true, remainingMs: 0 }),
|
|
252
|
+
registerMessageUsageInStickerFocus: () => {},
|
|
253
|
+
shouldSendStickerFocusWarning: () => false,
|
|
254
|
+
sendReply: async () => {},
|
|
255
|
+
formatStickerFocusRuleLabel: () => '',
|
|
256
|
+
formatRemainingMinutesLabel: () => 1,
|
|
257
|
+
logger: { warn: () => {}, info: (msg, payload) => infoLogs.push({ msg, payload }) },
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const ctx = createBaseContext({
|
|
261
|
+
sessionId: 'session-worker',
|
|
262
|
+
isGroupMessage: true,
|
|
263
|
+
});
|
|
264
|
+
const result = await middlewares.enforceGroupOwnerMiddleware(ctx);
|
|
265
|
+
|
|
266
|
+
assert.equal(result, null);
|
|
267
|
+
assert.equal(stopSpy.calls.length, 0);
|
|
268
|
+
assert.equal(ctx.pipelineStopped, false);
|
|
269
|
+
assert.equal(ctx.analysisPayload.metadata.owner_enforcement_result, 'shadow_non_owner');
|
|
270
|
+
assert.equal(infoLogs.length, 1);
|
|
271
|
+
});
|
|
272
|
+
|
|
169
273
|
test('conversation middleware responde e interrompe pipeline', async () => {
|
|
170
274
|
const stopSpy = createStopSpy();
|
|
171
275
|
const replies = [];
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export const createPreProcessingMiddlewares = ({ executeQuery, TABLES, isStatusJid, stopMessagePipeline, handleAntiLink, ensureCommandPrefixForContext, resolveCaptchaByMessage, maybeHandleStartLoginMessage, mergeAnalysisMetadata, ensureGroupConfigForContext, resolveStickerFocusState, resolveStickerFocusMessageClassification, resolveSenderAdminForContext, isUserAdmin, canSendMessageInStickerFocus, registerMessageUsageInStickerFocus, shouldSendStickerFocusWarning, sendReply, formatStickerFocusRuleLabel, formatRemainingMinutesLabel, logger }) => {
|
|
1
|
+
export const createPreProcessingMiddlewares = ({ executeQuery, TABLES, isStatusJid, stopMessagePipeline, handleAntiLink, ensureCommandPrefixForContext, resolveCaptchaByMessage, maybeHandleStartLoginMessage, mergeAnalysisMetadata, ensureGroupConfigForContext, resolveStickerFocusState, resolveStickerFocusMessageClassification, resolveGroupOwnerForContext, ownerEnforcementMode = 'off', primarySessionId = 'default', resolveSenderAdminForContext, isUserAdmin, canSendMessageInStickerFocus, registerMessageUsageInStickerFocus, shouldSendStickerFocusWarning, sendReply, formatStickerFocusRuleLabel, formatRemainingMinutesLabel, logger }) => {
|
|
2
|
+
const normalizedOwnerEnforcementMode = String(ownerEnforcementMode || 'off')
|
|
3
|
+
.trim()
|
|
4
|
+
.toLowerCase();
|
|
5
|
+
const effectiveOwnerEnforcementMode = normalizedOwnerEnforcementMode === 'enforce' || normalizedOwnerEnforcementMode === 'shadow' ? normalizedOwnerEnforcementMode : 'off';
|
|
6
|
+
|
|
2
7
|
const touchSenderLastSeenMiddleware = async (ctx) => {
|
|
3
8
|
if (!ctx.senderJid || isStatusJid(ctx.remoteJid)) return;
|
|
4
9
|
|
|
@@ -21,6 +26,78 @@ export const createPreProcessingMiddlewares = ({ executeQuery, TABLES, isStatusJ
|
|
|
21
26
|
});
|
|
22
27
|
};
|
|
23
28
|
|
|
29
|
+
const enforceGroupOwnerMiddleware = async (ctx) => {
|
|
30
|
+
if (!ctx.isGroupMessage) return null;
|
|
31
|
+
|
|
32
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
33
|
+
owner_enforcement_mode: effectiveOwnerEnforcementMode,
|
|
34
|
+
processing_session_id: ctx.sessionId,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (effectiveOwnerEnforcementMode === 'off') return null;
|
|
38
|
+
|
|
39
|
+
if (typeof resolveGroupOwnerForContext !== 'function') {
|
|
40
|
+
logger.warn('Middleware de owner enforcement sem resolver de owner configurado.', {
|
|
41
|
+
action: 'group_owner_enforcement_missing_resolver',
|
|
42
|
+
sessionId: ctx.sessionId,
|
|
43
|
+
groupId: ctx.remoteJid,
|
|
44
|
+
});
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ownerState = await resolveGroupOwnerForContext(ctx);
|
|
49
|
+
const ownerSessionId = String(ctx.ownerSessionId || ownerState?.ownerSessionId || '').trim() || null;
|
|
50
|
+
ctx.ownerSessionId = ownerSessionId;
|
|
51
|
+
|
|
52
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
53
|
+
owner_session_id: ownerSessionId,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!ownerSessionId) {
|
|
57
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
58
|
+
owner_enforcement_result: 'owner_not_found',
|
|
59
|
+
});
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const currentSessionId = String(ctx.sessionId || '').trim() || primarySessionId;
|
|
64
|
+
const isOwnerSession = ownerSessionId === currentSessionId;
|
|
65
|
+
if (isOwnerSession) {
|
|
66
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
67
|
+
owner_enforcement_result: 'owner_match',
|
|
68
|
+
});
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (effectiveOwnerEnforcementMode === 'shadow') {
|
|
73
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
74
|
+
owner_enforcement_result: 'shadow_non_owner',
|
|
75
|
+
});
|
|
76
|
+
logger.info('Owner enforcement (shadow): sessao nao-owner detectada no grupo.', {
|
|
77
|
+
action: 'group_owner_enforcement_shadow_non_owner',
|
|
78
|
+
groupId: ctx.remoteJid,
|
|
79
|
+
sessionId: currentSessionId,
|
|
80
|
+
ownerSessionId,
|
|
81
|
+
messageId: ctx.key?.id || null,
|
|
82
|
+
});
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
87
|
+
owner_enforcement_result: 'blocked_non_owner',
|
|
88
|
+
blocked_by: 'group_owner_enforcement',
|
|
89
|
+
});
|
|
90
|
+
logger.info('Owner enforcement: mensagem bloqueada em sessao nao-owner.', {
|
|
91
|
+
action: 'group_owner_enforcement_blocked_non_owner',
|
|
92
|
+
groupId: ctx.remoteJid,
|
|
93
|
+
sessionId: currentSessionId,
|
|
94
|
+
ownerSessionId,
|
|
95
|
+
messageId: ctx.key?.id || null,
|
|
96
|
+
isCommand: ctx.isCommandMessage,
|
|
97
|
+
});
|
|
98
|
+
return stopMessagePipeline(ctx, 'blocked_group_owner_enforcement');
|
|
99
|
+
};
|
|
100
|
+
|
|
24
101
|
const applyGroupPolicyMiddleware = async (ctx) => {
|
|
25
102
|
if (!ctx.isGroupMessage) return null;
|
|
26
103
|
|
|
@@ -129,7 +206,7 @@ export const createPreProcessingMiddlewares = ({ executeQuery, TABLES, isStatusJ
|
|
|
129
206
|
if (shouldSendStickerFocusWarning({ groupId: ctx.remoteJid, senderJid: ctx.senderJid })) {
|
|
130
207
|
try {
|
|
131
208
|
await sendReply(ctx.sock, ctx.remoteJid, ctx.messageInfo, ctx.expirationMessage, {
|
|
132
|
-
text: '🖼️ Este chat está
|
|
209
|
+
text: '🖼️ *Modo Sticker ativo!*\n\n' + 'Este chat está focado em *stickers automáticos*.\n' + '👉 Envie apenas *imagens* ou *vídeos* para gerar stickers,\n' + '👉 Ou compartilhe *stickers* normalmente.\n\n' + '⏳ *Texto e áudio estão temporariamente limitados*.\n' + `Janela atual: *${formatStickerFocusRuleLabel(stickerFocusState)}*\n` + `Tente novamente em ~${formatRemainingMinutesLabel(messageGate.remainingMs)}.\n\n` + `💡 Um admin pode liberar com: *${ctx.commandPrefix}chatwindow on*`,
|
|
133
210
|
});
|
|
134
211
|
} catch (error) {
|
|
135
212
|
logger.warn('Falha ao enviar aviso de sticker focus.', {
|
|
@@ -157,6 +234,7 @@ export const createPreProcessingMiddlewares = ({ executeQuery, TABLES, isStatusJ
|
|
|
157
234
|
return {
|
|
158
235
|
touchSenderLastSeenMiddleware,
|
|
159
236
|
ignoreUnprocessableMessageMiddleware,
|
|
237
|
+
enforceGroupOwnerMiddleware,
|
|
160
238
|
applyGroupPolicyMiddleware,
|
|
161
239
|
resolveCaptchaMiddleware,
|
|
162
240
|
handleStartLoginTriggerMiddleware,
|
|
@@ -4,7 +4,7 @@ import 'dotenv/config';
|
|
|
4
4
|
import { isAdminCommand } from '../modules/adminModule/groupCommandHandlers.js';
|
|
5
5
|
import { explicarComandoGlobal, registerGlobalHelpCommandExecution } from '../services/ai/globalModuleAiHelpService.js';
|
|
6
6
|
import { extractSupportedStickerMediaDetails, processSticker } from '../modules/stickerModule/stickerCommand.js';
|
|
7
|
-
import { detectAllMediaTypes, extractMessageContent, getExpiration, getJidServer, isGroupJid, isStatusJid, isSameJidUser, normalizeJid, normalizeWAPresence, resolveBotJid, extractSenderInfoFromMessage, resolveUserId, resolveAddressingModeFromMessageKey, resolveCanonicalWhatsAppJid, parseEnvBool, parseEnvInt } from '../config/index.js';
|
|
7
|
+
import { detectAllMediaTypes, extractMessageContent, getExpiration, getJidServer, isGroupJid, isStatusJid, isSameJidUser, normalizeJid, normalizeWAPresence, resolveBotJid, extractSenderInfoFromMessage, resolveUserId, resolveAddressingModeFromMessageKey, resolveCanonicalWhatsAppJid, parseEnvBool, parseEnvInt, getMultiSessionRuntimeConfig } from '../config/index.js';
|
|
8
8
|
import { isUserAdmin } from '../config/index.js';
|
|
9
9
|
import { isAdminSenderAsync } from '../config/index.js';
|
|
10
10
|
import { executeQuery, TABLES } from '../../database/index.js';
|
|
@@ -20,6 +20,7 @@ import { createMessageAnalysisEvent } from '../modules/analyticsModule/messageAn
|
|
|
20
20
|
import { routeConversationMessage } from '../services/ai/conversationRouterService.js';
|
|
21
21
|
import { executeMessageCommandRoute, isKnownNonAdminCommand } from '../services/ai/messageCommandExecutionService.js';
|
|
22
22
|
import { canSendMessageInStickerFocus, registerMessageUsageInStickerFocus, resolveStickerFocusMessageClassification, resolveStickerFocusState, shouldSendStickerFocusWarning } from '../services/sticker/stickerFocusService.js';
|
|
23
|
+
import { getOwner as getGroupOwner } from '../services/multiSession/groupOwnershipService.js';
|
|
23
24
|
import { createPreProcessingMiddlewares } from './messagePipeline/preProcessingMiddlewares.js';
|
|
24
25
|
import { createConversationMiddleware } from './messagePipeline/conversationMiddleware.js';
|
|
25
26
|
import { createCommandMiddleware } from './messagePipeline/commandMiddleware.js';
|
|
@@ -41,6 +42,7 @@ const MESSAGE_REPLY_PRESENCE_BEFORE = normalizeWAPresence(process.env.MESSAGE_RE
|
|
|
41
42
|
const MESSAGE_REPLY_PRESENCE_AFTER = normalizeWAPresence(process.env.MESSAGE_REPLY_PRESENCE_AFTER, 'paused');
|
|
42
43
|
const MESSAGE_REPLY_PRESENCE_DELAY_MS = parseEnvInt(process.env.MESSAGE_REPLY_PRESENCE_DELAY_MS, 280, 0, 3_000);
|
|
43
44
|
const MESSAGE_REPLY_PRESENCE_SUBSCRIBE = parseEnvBool(process.env.MESSAGE_REPLY_PRESENCE_SUBSCRIBE, true);
|
|
45
|
+
const CONVERSATIONAL_AUTO_REPLY_ENABLED = parseEnvBool(process.env.CONVERSATIONAL_AUTO_REPLY_ENABLED, false);
|
|
44
46
|
const WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN = parseEnvBool(process.env.WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN, true);
|
|
45
47
|
const SITE_ORIGIN =
|
|
46
48
|
String(process.env.SITE_ORIGIN || process.env.WHATSAPP_LOGIN_BASE_URL || 'https://omnizap.shop')
|
|
@@ -48,6 +50,19 @@ const SITE_ORIGIN =
|
|
|
48
50
|
.replace(/\/+$/, '') || 'https://omnizap.shop';
|
|
49
51
|
const SITE_LOGIN_URL = `${SITE_ORIGIN}/login/`;
|
|
50
52
|
const SITE_GROUP_LOGIN_URL = `${SITE_ORIGIN}/login`;
|
|
53
|
+
const MULTI_SESSION_RUNTIME_CONFIG = getMultiSessionRuntimeConfig();
|
|
54
|
+
const PRIMARY_SESSION_ID = String(MULTI_SESSION_RUNTIME_CONFIG?.primarySessionId || 'default').trim() || 'default';
|
|
55
|
+
const VALID_SESSION_IDS = new Set(Array.isArray(MULTI_SESSION_RUNTIME_CONFIG?.sessionIds) ? MULTI_SESSION_RUNTIME_CONFIG.sessionIds : [PRIMARY_SESSION_ID]);
|
|
56
|
+
const GROUP_OWNER_ENFORCEMENT_MODE = String(MULTI_SESSION_RUNTIME_CONFIG?.ownerEnforcementMode || 'off')
|
|
57
|
+
.trim()
|
|
58
|
+
.toLowerCase();
|
|
59
|
+
|
|
60
|
+
const normalizeSessionId = (sessionId) => {
|
|
61
|
+
const normalized = String(sessionId || '').trim();
|
|
62
|
+
if (!normalized) return PRIMARY_SESSION_ID;
|
|
63
|
+
if (VALID_SESSION_IDS.has(normalized)) return normalized;
|
|
64
|
+
return PRIMARY_SESSION_ID;
|
|
65
|
+
};
|
|
51
66
|
|
|
52
67
|
let messageAnalyticsTableMissingLogged = false;
|
|
53
68
|
const recentCommandExecutions = new Map();
|
|
@@ -170,7 +185,7 @@ const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText,
|
|
|
170
185
|
|
|
171
186
|
if (isGroupMessage) {
|
|
172
187
|
await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
|
|
173
|
-
text: '
|
|
188
|
+
text: '🔐 *Segurança dos seus dados*\n\n' + 'Para proteger sua conta, o acesso é feito apenas no *privado*.\n' + 'Envie *iniciar* no chat privado do bot para receber seu link de login seguro.\n\n' + '⚠️ Por segurança, não enviamos links de acesso em grupos.',
|
|
174
189
|
});
|
|
175
190
|
return true;
|
|
176
191
|
}
|
|
@@ -199,7 +214,7 @@ const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText,
|
|
|
199
214
|
const safeName = String(senderName || '').trim();
|
|
200
215
|
const greeting = safeName ? `Oi, *${safeName}*!` : 'Oi!';
|
|
201
216
|
await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
|
|
202
|
-
text: `${greeting}\n\n` + '
|
|
217
|
+
text: `${greeting}\n\n` + '🚀 *Bem-vindo ao OmniZap!*\n\n' + 'Para continuar, faça login com sua conta *Google* no link abaixo:\n\n' + `${loginUrl}\n\n` + '🔐 Seu número do WhatsApp será vinculado automaticamente à conta após o login.\n' + '⚠️ Use apenas este link e não compartilhe com outras pessoas.',
|
|
203
218
|
});
|
|
204
219
|
|
|
205
220
|
return true;
|
|
@@ -374,7 +389,7 @@ const ensureUserHasGoogleWebLoginForCommand = async ({ sock, messageInfo, sender
|
|
|
374
389
|
}
|
|
375
390
|
|
|
376
391
|
const loginUrl = isGroupMessage ? SITE_GROUP_LOGIN_URL : buildSiteLoginUrlForUser(resolvedCanonicalUserId || senderJid);
|
|
377
|
-
const loginMessage = isGroupMessage ?
|
|
392
|
+
const loginMessage = isGroupMessage ? '🔐 *Login necessário*\n\n' + 'Para usar os comandos do bot, você precisa estar logado com sua conta *Google*.\n\n' + 'Acesse o link abaixo para entrar:\n' + `${loginUrl}\n\n` + '⚠️ Por segurança, recomendamos abrir o link no privado.' : '🔐 *Login necessário*\n\n' + 'Para usar os comandos do bot, faça login com sua conta *Google* no link abaixo:\n\n' + `${loginUrl}\n\n` + '✅ Após entrar, volte aqui e envie o comando novamente\n' + `(ex.: *${commandPrefix}menu*).`;
|
|
378
393
|
|
|
379
394
|
await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
|
|
380
395
|
text: loginMessage,
|
|
@@ -459,6 +474,43 @@ const resolveSenderOwnerForContext = async (ctx) => {
|
|
|
459
474
|
return ctx.memo.senderOwnerPromise;
|
|
460
475
|
};
|
|
461
476
|
|
|
477
|
+
const resolveGroupOwnerForContext = async (ctx, { bypassCache = false } = {}) => {
|
|
478
|
+
if (!ctx.isGroupMessage) return null;
|
|
479
|
+
|
|
480
|
+
if (!bypassCache && ctx.memo.groupOwnerPromise) {
|
|
481
|
+
return ctx.memo.groupOwnerPromise;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const fetchOwnerPromise = (async () => {
|
|
485
|
+
try {
|
|
486
|
+
const ownerState = await getGroupOwner(ctx.remoteJid, { bypassCache });
|
|
487
|
+
const ownerSessionId = String(ownerState?.ownerSessionId || '').trim() || null;
|
|
488
|
+
ctx.ownerSessionId = ownerSessionId;
|
|
489
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
490
|
+
owner_session_id: ownerSessionId,
|
|
491
|
+
});
|
|
492
|
+
return ownerState;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
logger.warn('Falha ao resolver owner de grupo para roteamento de sessão.', {
|
|
495
|
+
action: 'group_owner_resolution_failed',
|
|
496
|
+
sessionId: ctx.sessionId,
|
|
497
|
+
groupId: ctx.remoteJid,
|
|
498
|
+
error: error?.message,
|
|
499
|
+
});
|
|
500
|
+
ctx.ownerSessionId = null;
|
|
501
|
+
mergeAnalysisMetadata(ctx.analysisPayload, {
|
|
502
|
+
owner_resolution_error: String(error?.code || error?.name || 'lookup_failed')
|
|
503
|
+
.trim()
|
|
504
|
+
.toLowerCase(),
|
|
505
|
+
});
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
})();
|
|
509
|
+
|
|
510
|
+
ctx.memo.groupOwnerPromise = fetchOwnerPromise;
|
|
511
|
+
return fetchOwnerPromise;
|
|
512
|
+
};
|
|
513
|
+
|
|
462
514
|
const resolveHasGoogleLoginForContext = async (ctx) => {
|
|
463
515
|
if (ctx.isMessageFromBot || !WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN) {
|
|
464
516
|
return undefined;
|
|
@@ -476,10 +528,11 @@ const resolveHasGoogleLoginForContext = async (ctx) => {
|
|
|
476
528
|
return ctx.memo.hasGoogleLoginPromise;
|
|
477
529
|
};
|
|
478
530
|
|
|
479
|
-
const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyUpsert, sock }) => {
|
|
531
|
+
const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyUpsert, sock, sessionId }) => {
|
|
480
532
|
const key = messageInfo?.key || {};
|
|
481
533
|
const remoteJid = key?.remoteJid;
|
|
482
534
|
if (!remoteJid) return null;
|
|
535
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
483
536
|
|
|
484
537
|
const isGroupMessage = isGroupJid(remoteJid);
|
|
485
538
|
const extractedText = extractMessageContent(messageInfo);
|
|
@@ -505,6 +558,7 @@ const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyU
|
|
|
505
558
|
.slice(0, 10);
|
|
506
559
|
|
|
507
560
|
const analysisPayload = {
|
|
561
|
+
sessionId: normalizedSessionId,
|
|
508
562
|
messageId: key?.id || null,
|
|
509
563
|
chatId: remoteJid || null,
|
|
510
564
|
senderId: senderJid || null,
|
|
@@ -530,6 +584,8 @@ const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyU
|
|
|
530
584
|
upsert_type: upsertType,
|
|
531
585
|
is_notify_upsert: isNotifyUpsert,
|
|
532
586
|
is_history_append: upsertType === 'append',
|
|
587
|
+
session_id: normalizedSessionId,
|
|
588
|
+
owner_session_id: null,
|
|
533
589
|
addressing_mode: addressingMode || null,
|
|
534
590
|
participant_alt: key?.participantAlt || null,
|
|
535
591
|
remote_jid_alt: key?.remoteJidAlt || null,
|
|
@@ -550,6 +606,8 @@ const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyU
|
|
|
550
606
|
botJidCandidates,
|
|
551
607
|
botJid,
|
|
552
608
|
isMessageFromBot,
|
|
609
|
+
sessionId: normalizedSessionId,
|
|
610
|
+
ownerSessionId: null,
|
|
553
611
|
commandPrefix: DEFAULT_COMMAND_PREFIX,
|
|
554
612
|
groupConfig: null,
|
|
555
613
|
groupConfigLoaded: false,
|
|
@@ -564,7 +622,7 @@ const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyU
|
|
|
564
622
|
};
|
|
565
623
|
};
|
|
566
624
|
|
|
567
|
-
const { touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware } = createPreProcessingMiddlewares({
|
|
625
|
+
const { touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, enforceGroupOwnerMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware } = createPreProcessingMiddlewares({
|
|
568
626
|
executeQuery,
|
|
569
627
|
TABLES,
|
|
570
628
|
isStatusJid,
|
|
@@ -577,6 +635,9 @@ const { touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, app
|
|
|
577
635
|
ensureGroupConfigForContext,
|
|
578
636
|
resolveStickerFocusState,
|
|
579
637
|
resolveStickerFocusMessageClassification,
|
|
638
|
+
resolveGroupOwnerForContext,
|
|
639
|
+
ownerEnforcementMode: GROUP_OWNER_ENFORCEMENT_MODE,
|
|
640
|
+
primarySessionId: PRIMARY_SESSION_ID,
|
|
580
641
|
resolveSenderAdminForContext,
|
|
581
642
|
isUserAdmin,
|
|
582
643
|
canSendMessageInStickerFocus,
|
|
@@ -605,6 +666,7 @@ const routeConversationMiddleware = createConversationMiddleware({
|
|
|
605
666
|
sendReply,
|
|
606
667
|
routeConversationMessage,
|
|
607
668
|
stopMessagePipeline,
|
|
669
|
+
conversationAutoReplyEnabled: CONVERSATIONAL_AUTO_REPLY_ENABLED,
|
|
608
670
|
});
|
|
609
671
|
|
|
610
672
|
const executeCommandMiddleware = createCommandMiddleware({
|
|
@@ -642,7 +704,7 @@ const runPostProcessingMiddleware = createPostProcessingMiddleware({
|
|
|
642
704
|
normalizeAnalysisErrorCode,
|
|
643
705
|
});
|
|
644
706
|
|
|
645
|
-
const MESSAGE_PIPELINE_MIDDLEWARES = [touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware, routeConversationMiddleware, executeCommandMiddleware, runPostProcessingMiddleware];
|
|
707
|
+
const MESSAGE_PIPELINE_MIDDLEWARES = [touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, enforceGroupOwnerMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware, routeConversationMiddleware, executeCommandMiddleware, runPostProcessingMiddleware];
|
|
646
708
|
|
|
647
709
|
const runMessagePipeline = async (ctx) => {
|
|
648
710
|
for (const middleware of MESSAGE_PIPELINE_MIDDLEWARES) {
|
|
@@ -657,7 +719,8 @@ const runMessagePipeline = async (ctx) => {
|
|
|
657
719
|
*
|
|
658
720
|
* @param {Object} update - Objeto contendo a atualização do WhatsApp.
|
|
659
721
|
*/
|
|
660
|
-
export const handleMessagesThroughPipeline = async (update, sock) => {
|
|
722
|
+
export const handleMessagesThroughPipeline = async (update, sock, options = {}) => {
|
|
723
|
+
const sessionId = normalizeSessionId(options?.sessionId);
|
|
661
724
|
if (update.messages && Array.isArray(update.messages)) {
|
|
662
725
|
try {
|
|
663
726
|
const upsertType = update?.type || null;
|
|
@@ -669,6 +732,7 @@ export const handleMessagesThroughPipeline = async (update, sock) => {
|
|
|
669
732
|
upsertType,
|
|
670
733
|
isNotifyUpsert,
|
|
671
734
|
sock,
|
|
735
|
+
sessionId,
|
|
672
736
|
});
|
|
673
737
|
if (!context) continue;
|
|
674
738
|
|
|
@@ -678,19 +742,34 @@ export const handleMessagesThroughPipeline = async (update, sock) => {
|
|
|
678
742
|
context.analysisPayload.processingResult = 'error';
|
|
679
743
|
context.analysisPayload.errorCode = normalizeAnalysisErrorCode(messageError);
|
|
680
744
|
logger.error('Erro ao processar mensagem individual:', {
|
|
745
|
+
sessionId,
|
|
746
|
+
ownerSessionId: context.ownerSessionId || null,
|
|
681
747
|
error: messageError?.message,
|
|
682
748
|
messageId: context.key?.id || null,
|
|
683
749
|
remoteJid: context.remoteJid,
|
|
684
750
|
});
|
|
685
751
|
} finally {
|
|
752
|
+
logger.debug('Mensagem processada pelo pipeline.', {
|
|
753
|
+
action: 'message_pipeline_processed',
|
|
754
|
+
sessionId: context.sessionId,
|
|
755
|
+
ownerSessionId: context.ownerSessionId || null,
|
|
756
|
+
messageId: context.key?.id || null,
|
|
757
|
+
remoteJid: context.remoteJid,
|
|
758
|
+
result: context.analysisPayload.processingResult,
|
|
759
|
+
isCommand: context.analysisPayload.isCommand,
|
|
760
|
+
});
|
|
686
761
|
persistMessageAnalysisEvent(context.analysisPayload);
|
|
687
762
|
}
|
|
688
763
|
}
|
|
689
764
|
} catch (error) {
|
|
690
|
-
logger.error('Erro ao processar mensagens:',
|
|
765
|
+
logger.error('Erro ao processar mensagens:', {
|
|
766
|
+
sessionId,
|
|
767
|
+
error: error?.message,
|
|
768
|
+
});
|
|
691
769
|
}
|
|
692
770
|
} else {
|
|
693
771
|
logger.info('🔄 Processando evento recebido:', {
|
|
772
|
+
sessionId,
|
|
694
773
|
eventType: update?.type || 'unknown',
|
|
695
774
|
eventData: update,
|
|
696
775
|
});
|