@kaikybrofc/omnizap-system 2.1.8
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 +534 -0
- package/LICENSE +21 -0
- package/README.md +431 -0
- package/RELEASE-v2.1.2.md +83 -0
- package/app/config/adminIdentity.js +87 -0
- package/app/config/baileysConfig.js +693 -0
- package/app/config/groupUtils.js +388 -0
- package/app/connection/socketController.js +992 -0
- package/app/controllers/messageController.js +354 -0
- package/app/modules/adminModule/groupCommandHandlers.js +1294 -0
- package/app/modules/adminModule/groupEventHandlers.js +355 -0
- package/app/modules/aiModule/catCommand.js +1006 -0
- package/app/modules/broadcastModule/noticeCommand.js +416 -0
- package/app/modules/gameModule/diceCommand.js +67 -0
- package/app/modules/menuModule/common.js +311 -0
- package/app/modules/menuModule/menus.js +59 -0
- package/app/modules/playModule/playCommand.js +1615 -0
- package/app/modules/quoteModule/quoteCommand.js +851 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +786 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.js +2082 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +760 -0
- package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
- package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +172 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
- package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
- package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1859 -0
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +6738 -0
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
- package/app/modules/statsModule/globalRankingCommand.js +65 -0
- package/app/modules/statsModule/noMessageCommand.js +288 -0
- package/app/modules/statsModule/rankingCommand.js +60 -0
- package/app/modules/statsModule/rankingCommon.js +889 -0
- package/app/modules/stickerModule/addStickerMetadata.js +239 -0
- package/app/modules/stickerModule/convertToWebp.js +390 -0
- package/app/modules/stickerModule/stickerCommand.js +454 -0
- package/app/modules/stickerModule/stickerConvertCommand.js +156 -0
- package/app/modules/stickerModule/stickerTextCommand.js +657 -0
- package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
- package/app/modules/stickerPackModule/autoPackCollectorService.js +284 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +466 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +88 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +571 -0
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +449 -0
- package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +180 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +4078 -0
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +598 -0
- package/app/modules/stickerPackModule/stickerClassificationService.js +588 -0
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +102 -0
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +7506 -0
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1095 -0
- package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +108 -0
- package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +110 -0
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +440 -0
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +337 -0
- package/app/modules/stickerPackModule/stickerPackMessageService.js +296 -0
- package/app/modules/stickerPackModule/stickerPackRepository.js +442 -0
- package/app/modules/stickerPackModule/stickerPackService.js +788 -0
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +51 -0
- package/app/modules/stickerPackModule/stickerPackUtils.js +97 -0
- package/app/modules/stickerPackModule/stickerStorageService.js +507 -0
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +233 -0
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +205 -0
- package/app/modules/systemMetricsModule/pingCommand.js +421 -0
- package/app/modules/tiktokModule/tiktokCommand.js +798 -0
- package/app/modules/userModule/userCommand.js +1217 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +177 -0
- package/app/observability/metrics.js +734 -0
- package/app/services/captchaService.js +492 -0
- package/app/services/dbWriteQueue.js +572 -0
- package/app/services/groupMetadataService.js +279 -0
- package/app/services/lidMapService.js +663 -0
- package/app/services/messagePersistenceService.js +56 -0
- package/app/services/newsBroadcastService.js +351 -0
- package/app/services/pokeApiService.js +398 -0
- package/app/services/queueUtils.js +57 -0
- package/app/services/socketState.js +7 -0
- package/app/store/aiPromptStore.js +38 -0
- package/app/store/groupConfigStore.js +58 -0
- package/app/store/premiumUserStore.js +36 -0
- package/app/utils/antiLink/antiLinkModule.js +804 -0
- package/app/utils/http/getImageBufferModule.js +18 -0
- package/app/utils/json/jsonSanitizer.js +113 -0
- package/app/utils/json/jsonSanitizer.test.js +40 -0
- package/app/utils/logger/loggerModule.js +262 -0
- package/app/utils/systemMetrics/systemMetricsModule.js +91 -0
- package/database/index.js +2052 -0
- package/database/init.js +516 -0
- package/database/migrations/20260203_0001_sticker_packs.sql +54 -0
- package/database/migrations/20260210_0003_rpg_pokemon.sql +58 -0
- package/database/migrations/20260210_0004_rpg_shiny_biome.sql +9 -0
- package/database/migrations/20260210_0005_rpg_missions.sql +14 -0
- package/database/migrations/20260210_0006_rpg_world_pokedex_traits.sql +27 -0
- package/database/migrations/20260210_0007_rpg_raid_pvp.sql +56 -0
- package/database/migrations/20260210_0008_rpg_social_system.sql +195 -0
- package/database/migrations/20260211_0009_rpg_social_xp.sql +36 -0
- package/database/migrations/20260222_0010_remove_message_xp.sql +2 -0
- package/database/migrations/20260226_0011_sticker_asset_classification.sql +17 -0
- package/database/migrations/20260226_0012_sticker_pack_engagement.sql +16 -0
- package/database/migrations/20260226_0013_sticker_marketplace_intelligence.sql +19 -0
- package/database/migrations/20260226_0014_sticker_pack_publish_flow.sql +30 -0
- package/database/migrations/20260226_0014_sticker_worker_queues.sql +42 -0
- package/database/migrations/20260226_0015_sticker_auto_pack_curation_integrity.sql +18 -0
- package/database/migrations/20260226_0016_sticker_web_google_auth_persistence.sql +34 -0
- package/database/migrations/20260226_0017_sticker_web_admin_ban.sql +22 -0
- package/database/migrations/20260226_0018_sticker_web_admin_moderator.sql +18 -0
- package/database/migrations/20260227_0019_sticker_classification_v2_signals.sql +12 -0
- package/database/migrations/20260227_0020_semantic_theme_clusters.sql +35 -0
- package/docker-compose.yml +103 -0
- package/ecosystem.prod.config.cjs +35 -0
- package/eslint.config.js +61 -0
- package/index.js +437 -0
- package/ml/clip_classifier/Dockerfile +16 -0
- package/ml/clip_classifier/README.md +120 -0
- package/ml/clip_classifier/adaptive_scoring.py +40 -0
- package/ml/clip_classifier/classifier.py +654 -0
- package/ml/clip_classifier/embedding_store.py +481 -0
- package/ml/clip_classifier/env_loader.py +15 -0
- package/ml/clip_classifier/llm_label_expander.py +144 -0
- package/ml/clip_classifier/main.py +213 -0
- package/ml/clip_classifier/requirements.txt +10 -0
- package/ml/clip_classifier/similarity_engine.py +74 -0
- package/observability/alert-rules.yml +60 -0
- package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
- package/observability/grafana/dashboards/omnizap-overview.json +170 -0
- package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
- package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
- package/observability/loki-config.yml +38 -0
- package/observability/mysql-exporter.cnf +5 -0
- package/observability/mysql-setup.sql +46 -0
- package/observability/prometheus.yml +32 -0
- package/observability/promtail-config.yml +84 -0
- package/package.json +109 -0
- package/public/api-docs/index.html +144 -0
- package/public/css/github-project-panel.css +297 -0
- package/public/css/stickers-admin.css +1272 -0
- package/public/css/styles.css +671 -0
- package/public/index.html +1311 -0
- package/public/js/apps/apiDocsApp.js +310 -0
- package/public/js/apps/createPackApp.js +2069 -0
- package/public/js/apps/homeApp.js +396 -0
- package/public/js/apps/stickersAdminApp.js +1744 -0
- package/public/js/apps/stickersApp.js +4830 -0
- package/public/js/catalog.js +1019 -0
- package/public/js/github-panel/components/CommitList.js +34 -0
- package/public/js/github-panel/components/ErrorState.js +16 -0
- package/public/js/github-panel/components/GithubProjectPanel.js +106 -0
- package/public/js/github-panel/components/ReleaseList.js +38 -0
- package/public/js/github-panel/components/SkeletonPanel.js +22 -0
- package/public/js/github-panel/components/StatCard.js +15 -0
- package/public/js/github-panel/index.js +15 -0
- package/public/js/github-panel/useGithubRepoData.js +154 -0
- package/public/js/github-panel/vendor/react.js +11 -0
- package/public/js/runtime/react-runtime.js +19 -0
- package/public/licenca/index.html +106 -0
- package/public/stickers/admin/index.html +23 -0
- package/public/stickers/create/index.html +47 -0
- package/public/stickers/index.html +48 -0
- package/public/termos-de-uso/index.html +125 -0
- package/scripts/cache-bust.mjs +107 -0
- package/scripts/deploy.sh +458 -0
- package/scripts/github-deploy-notify.mjs +174 -0
- package/scripts/release.sh +129 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import groupConfigStore from '../../store/groupConfigStore.js';
|
|
2
|
+
import logger from '../../utils/logger/loggerModule.js';
|
|
3
|
+
import { getGroupMetadata, getGroupInviteCode, getGroupRequestParticipantsList, updateGroupRequestParticipants } from '../../config/groupUtils.js';
|
|
4
|
+
import { getJidUser, isSameJidUser, resolveBotJid } from '../../config/baileysConfig.js';
|
|
5
|
+
import { updateGroupParticipantsFromAction } from '../../services/groupMetadataService.js';
|
|
6
|
+
import { CAPTCHA_TIMEOUT_MINUTES, clearCaptchaForUser, registerCaptchaChallenge } from '../../services/captchaService.js';
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import moment from 'moment-timezone';
|
|
11
|
+
import { sendAndStore } from '../../services/messagePersistenceService.js';
|
|
12
|
+
|
|
13
|
+
const replacePlaceholders = async (message, sock, groupId) => {
|
|
14
|
+
logger.debug('Iniciando substituição de placeholders para a mensagem.', { groupId });
|
|
15
|
+
let updatedMessage = message;
|
|
16
|
+
const mentions = [];
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const metadata = await getGroupMetadata(sock, groupId);
|
|
20
|
+
logger.debug('Metadados do grupo obtidos para substituição de placeholders.', {
|
|
21
|
+
groupId,
|
|
22
|
+
subject: metadata.subject,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (updatedMessage.includes('@date')) {
|
|
26
|
+
updatedMessage = updatedMessage.replace(/@date/g, moment().format('DD/MM/YYYY HH:mm:ss'));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (updatedMessage.includes('@desc') && metadata.desc) {
|
|
30
|
+
updatedMessage = updatedMessage.replace(/@desc/g, metadata.desc);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (updatedMessage.includes('@admins') && metadata.participants) {
|
|
34
|
+
const adminJids = metadata.participants.filter((p) => p.admin === 'admin' || p.admin === 'superadmin').map((p) => p.id);
|
|
35
|
+
|
|
36
|
+
const adminNames = adminJids.map((jid) => {
|
|
37
|
+
const user = getJidUser(jid);
|
|
38
|
+
if (user) mentions.push(jid);
|
|
39
|
+
return user ? `@${user}` : 'Desconhecido';
|
|
40
|
+
});
|
|
41
|
+
updatedMessage = updatedMessage.replace(/@admins/g, adminNames.join(', '));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (updatedMessage.includes('@groupname') && metadata.subject) {
|
|
45
|
+
updatedMessage = updatedMessage.replace(/@groupname/g, metadata.subject);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (updatedMessage.includes('@membercount') && metadata.participants) {
|
|
49
|
+
updatedMessage = updatedMessage.replace(/@membercount/g, metadata.participants.length.toString());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (updatedMessage.includes('@owner') && metadata.owner) {
|
|
53
|
+
const ownerJid = metadata.owner;
|
|
54
|
+
const ownerUser = getJidUser(ownerJid);
|
|
55
|
+
if (ownerUser) {
|
|
56
|
+
mentions.push(ownerJid);
|
|
57
|
+
updatedMessage = updatedMessage.replace(/@owner/g, `@${ownerUser}`);
|
|
58
|
+
} else {
|
|
59
|
+
updatedMessage = updatedMessage.replace(/@owner/g, 'Desconhecido');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (updatedMessage.includes('@creationtime') && metadata.creation) {
|
|
64
|
+
updatedMessage = updatedMessage.replace(/@creationtime/g, moment.unix(metadata.creation).format('DD/MM/YYYY HH:mm:ss'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (updatedMessage.includes('@invitecode')) {
|
|
68
|
+
try {
|
|
69
|
+
const inviteCode = await getGroupInviteCode(sock, groupId);
|
|
70
|
+
updatedMessage = updatedMessage.replace(/@invitecode/g, inviteCode);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
logger.warn(`Não foi possível obter o código de convite para o grupo ${groupId}. O placeholder não será substituído.`, { error: e.message });
|
|
73
|
+
updatedMessage = updatedMessage.replace(/@invitecode/g, '[Código de convite não disponível]');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (updatedMessage.includes('@isrestricted')) {
|
|
78
|
+
updatedMessage = updatedMessage.replace(/@isrestricted/g, metadata.restrict ? 'Sim' : 'Não');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (updatedMessage.includes('@isannounceonly')) {
|
|
82
|
+
updatedMessage = updatedMessage.replace(/@isannounceonly/g, metadata.announce ? 'Sim' : 'Não');
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error(`Erro ao substituir placeholders para o grupo ${groupId}.`, {
|
|
86
|
+
errorMessage: error.message,
|
|
87
|
+
stack: error.stack,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
logger.debug('Substituição de placeholders finalizada.', { groupId });
|
|
91
|
+
return { updatedMessage, mentions };
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const buildCaptchaLine = (participantName) => `\n🤖 *Verificação humana*\n@${participantName}, reaja a esta mensagem ou envie qualquer mensagem em até *${CAPTCHA_TIMEOUT_MINUTES} minutos* para continuar no grupo.\n\n`;
|
|
95
|
+
|
|
96
|
+
const ACTIONS_TO_SKIP_AUTO_APPROVE = new Set(['reject', 'rejected', 'cancel', 'canceled', 'approve', 'approved', 'accept', 'accepted', 'remove', 'removed']);
|
|
97
|
+
|
|
98
|
+
const shouldAutoApproveAction = (action) => {
|
|
99
|
+
if (!action) return true;
|
|
100
|
+
return !ACTIONS_TO_SKIP_AUTO_APPROVE.has(action);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const extractJoinRequestParticipants = (payload) => {
|
|
104
|
+
const rawParticipants = [];
|
|
105
|
+
|
|
106
|
+
if (payload?.participant) rawParticipants.push(payload.participant);
|
|
107
|
+
if (payload?.participants && Array.isArray(payload.participants)) {
|
|
108
|
+
rawParticipants.push(...payload.participants);
|
|
109
|
+
}
|
|
110
|
+
if (payload?.participantsJids && Array.isArray(payload.participantsJids)) {
|
|
111
|
+
rawParticipants.push(...payload.participantsJids);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const participants = rawParticipants
|
|
115
|
+
.map((participant) => {
|
|
116
|
+
if (!participant) return null;
|
|
117
|
+
if (typeof participant === 'string') return participant;
|
|
118
|
+
return participant.id || participant.jid || participant.participant || participant.lid || null;
|
|
119
|
+
})
|
|
120
|
+
.filter(Boolean);
|
|
121
|
+
|
|
122
|
+
return Array.from(new Set(participants));
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const handleGroupUpdate = async (sock, groupId, participants, action) => {
|
|
126
|
+
logger.debug('Iniciando tratamento de evento de atualização de grupo.', {
|
|
127
|
+
groupId,
|
|
128
|
+
participants,
|
|
129
|
+
action,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
try {
|
|
134
|
+
await updateGroupParticipantsFromAction(groupId, participants, action);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
logger.error('Erro ao atualizar participantes do grupo no banco.', {
|
|
137
|
+
groupId,
|
|
138
|
+
action,
|
|
139
|
+
errorMessage: error.message,
|
|
140
|
+
stack: error.stack,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const groupConfig = await groupConfigStore.getGroupConfig(groupId);
|
|
145
|
+
logger.debug('Configurações do grupo carregadas.', { groupId, config: groupConfig });
|
|
146
|
+
|
|
147
|
+
let message = '';
|
|
148
|
+
const allMentions = [];
|
|
149
|
+
const captchaParticipants = [];
|
|
150
|
+
const captchaEnabled = Boolean(groupConfig.captchaEnabled);
|
|
151
|
+
const botJid = resolveBotJid(sock?.user?.id);
|
|
152
|
+
|
|
153
|
+
for (const participant of participants) {
|
|
154
|
+
const jid = typeof participant === 'string' ? participant : participant?.id || participant?.jid || participant?.phoneNumber || '';
|
|
155
|
+
|
|
156
|
+
const participantName = getJidUser(jid) || participant?.phoneNumber || 'user';
|
|
157
|
+
|
|
158
|
+
if (jid) allMentions.push(jid);
|
|
159
|
+
|
|
160
|
+
switch (action) {
|
|
161
|
+
case 'add':
|
|
162
|
+
{
|
|
163
|
+
const shouldRequestCaptcha = captchaEnabled && jid && (!botJid || !isSameJidUser(jid, botJid));
|
|
164
|
+
|
|
165
|
+
if (shouldRequestCaptcha) {
|
|
166
|
+
captchaParticipants.push(jid);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (groupConfig.welcomeMessageEnabled) {
|
|
170
|
+
const welcomeMsg = groupConfig.welcomeMessage || '👋 Bem-vindo(a) ao grupo @groupname, @user! 🎉';
|
|
171
|
+
let msg = welcomeMsg.replace('{participant}', `@${participantName}`);
|
|
172
|
+
msg = msg.replace(/@user/g, `@${participantName}`);
|
|
173
|
+
if (shouldRequestCaptcha) {
|
|
174
|
+
msg = `${buildCaptchaLine(participantName)}\n${msg}`;
|
|
175
|
+
}
|
|
176
|
+
message += `${msg}\n`;
|
|
177
|
+
} else if (shouldRequestCaptcha) {
|
|
178
|
+
message += `${buildCaptchaLine(participantName)}\n`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case 'remove':
|
|
183
|
+
if (jid) {
|
|
184
|
+
clearCaptchaForUser(groupId, jid, 'remove');
|
|
185
|
+
}
|
|
186
|
+
if (groupConfig.farewellMessageEnabled) {
|
|
187
|
+
const farewellMsg = groupConfig.farewellMessage || '😥 Adeus, @user! Sentiremos sua falta.';
|
|
188
|
+
let msg = farewellMsg.replace('{participant}', `@${participantName}`);
|
|
189
|
+
msg = msg.replace(/@user/g, `@${participantName}`);
|
|
190
|
+
message += `${msg}\n`;
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case 'promote':
|
|
194
|
+
if (groupConfig.welcomeMessageEnabled) {
|
|
195
|
+
message += `O usuário @${participantName} foi promovido a administrador do grupo. 🎉\n`;
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case 'demote':
|
|
199
|
+
if (groupConfig.welcomeMessageEnabled) {
|
|
200
|
+
message += `O usuário @${participantName} não é mais um administrador do grupo. ⬇️\n`;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (message) {
|
|
207
|
+
logger.debug('Mensagem de evento de grupo gerada.', { groupId, action, message });
|
|
208
|
+
let messageOptions = {};
|
|
209
|
+
let mediaPath = null;
|
|
210
|
+
|
|
211
|
+
const { updatedMessage, mentions: groupMentions } = await replacePlaceholders(message, sock, groupId);
|
|
212
|
+
message = updatedMessage;
|
|
213
|
+
const captchaMessageText = message.trim();
|
|
214
|
+
|
|
215
|
+
const finalMentions = [...new Set([...allMentions, ...groupMentions])];
|
|
216
|
+
logger.debug('Menções para a mensagem final processadas.', {
|
|
217
|
+
groupId,
|
|
218
|
+
finalMentionsCount: finalMentions.length,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (action === 'add' && groupConfig.welcomeMedia && groupConfig.welcomeMessageEnabled) {
|
|
222
|
+
mediaPath = groupConfig.welcomeMedia;
|
|
223
|
+
} else if (action === 'remove' && groupConfig.farewellMedia) {
|
|
224
|
+
mediaPath = groupConfig.farewellMedia;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (mediaPath) {
|
|
228
|
+
logger.info(`Tentando enviar mensagem com mídia para o grupo ${groupId}.`, {
|
|
229
|
+
action,
|
|
230
|
+
mediaPath,
|
|
231
|
+
});
|
|
232
|
+
const absoluteMediaPath = path.resolve(mediaPath);
|
|
233
|
+
logger.debug(`Caminho absoluto da mídia resolvido: ${absoluteMediaPath}`);
|
|
234
|
+
|
|
235
|
+
if (fs.existsSync(absoluteMediaPath)) {
|
|
236
|
+
logger.info(`Arquivo de mídia encontrado em ${absoluteMediaPath}. Preparando para enviar.`);
|
|
237
|
+
const mediaType = absoluteMediaPath.endsWith('.mp4') ? 'video' : 'image';
|
|
238
|
+
const mediaBuffer = fs.readFileSync(absoluteMediaPath);
|
|
239
|
+
|
|
240
|
+
if (mediaType === 'image') {
|
|
241
|
+
messageOptions = {
|
|
242
|
+
image: mediaBuffer,
|
|
243
|
+
caption: message.trim(),
|
|
244
|
+
mentions: finalMentions,
|
|
245
|
+
};
|
|
246
|
+
} else if (mediaType === 'video') {
|
|
247
|
+
messageOptions = {
|
|
248
|
+
video: mediaBuffer,
|
|
249
|
+
caption: message.trim(),
|
|
250
|
+
mentions: finalMentions,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
logger.warn(`Arquivo de mídia não encontrado em ${absoluteMediaPath} para o grupo ${groupId}. Ação: ${action}. Enviando apenas a mensagem de texto.`);
|
|
255
|
+
messageOptions = { text: message.trim(), mentions: finalMentions };
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
logger.debug('Nenhuma mídia configurada para este evento. Enviando apenas texto.', {
|
|
259
|
+
groupId,
|
|
260
|
+
action,
|
|
261
|
+
});
|
|
262
|
+
messageOptions = { text: message.trim(), mentions: finalMentions };
|
|
263
|
+
}
|
|
264
|
+
const sentMessage = await sendAndStore(sock, groupId, messageOptions);
|
|
265
|
+
if (action === 'add' && captchaEnabled && captchaParticipants.length > 0 && sentMessage?.key) {
|
|
266
|
+
for (const participantJid of captchaParticipants) {
|
|
267
|
+
registerCaptchaChallenge({
|
|
268
|
+
groupId,
|
|
269
|
+
participantJid,
|
|
270
|
+
messageKey: sentMessage.key,
|
|
271
|
+
messageText: captchaMessageText,
|
|
272
|
+
messageMentions: finalMentions,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
logger.info(`Mensagem de atualização de grupo enviada com sucesso para o grupo ${groupId}.`, {
|
|
277
|
+
action,
|
|
278
|
+
participants,
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
logger.debug('Nenhuma mensagem de evento de grupo para enviar.', { groupId, action });
|
|
282
|
+
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
logger.error(`Erro ao tratar atualização de grupo para o grupo ${groupId}, ação ${action}:`, {
|
|
285
|
+
errorMessage: error.message,
|
|
286
|
+
stack: error.stack,
|
|
287
|
+
error,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
export const handleGroupJoinRequest = async (sock, update) => {
|
|
293
|
+
const groupId = update?.id || update?.groupId || update?.jid;
|
|
294
|
+
const action = typeof update?.action === 'string' ? update.action.toLowerCase() : '';
|
|
295
|
+
|
|
296
|
+
logger.debug('Iniciando tratamento de solicitação de entrada no grupo.', {
|
|
297
|
+
groupId,
|
|
298
|
+
action,
|
|
299
|
+
participant: update?.participant,
|
|
300
|
+
method: update?.method,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (!groupId) {
|
|
304
|
+
logger.warn('Evento de solicitação de entrada sem groupId.', {
|
|
305
|
+
action,
|
|
306
|
+
updateKeys: update && typeof update === 'object' ? Object.keys(update) : null,
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!shouldAutoApproveAction(action)) {
|
|
312
|
+
logger.debug('Evento de solicitação ignorado pelo filtro de ação.', { groupId, action });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const groupConfig = await groupConfigStore.getGroupConfig(groupId);
|
|
318
|
+
if (!groupConfig.autoApproveRequestsEnabled) {
|
|
319
|
+
logger.debug('Auto-aprovação de solicitações desativada para o grupo.', { groupId });
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let participants = extractJoinRequestParticipants(update);
|
|
324
|
+
|
|
325
|
+
if (participants.length === 0) {
|
|
326
|
+
try {
|
|
327
|
+
const listResponse = await getGroupRequestParticipantsList(sock, groupId);
|
|
328
|
+
participants = extractJoinRequestParticipants(listResponse);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
logger.warn('Falha ao obter lista de solicitações de entrada.', {
|
|
331
|
+
groupId,
|
|
332
|
+
errorMessage: error.message,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (participants.length === 0) {
|
|
338
|
+
logger.debug('Nenhuma solicitação de entrada pendente encontrada.', { groupId });
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
await updateGroupRequestParticipants(sock, groupId, participants, 'approve');
|
|
343
|
+
logger.info('Solicitações de entrada aprovadas automaticamente.', {
|
|
344
|
+
groupId,
|
|
345
|
+
participantsCount: participants.length,
|
|
346
|
+
participants,
|
|
347
|
+
});
|
|
348
|
+
} catch (error) {
|
|
349
|
+
logger.error('Erro ao aprovar automaticamente solicitações de entrada.', {
|
|
350
|
+
groupId,
|
|
351
|
+
errorMessage: error.message,
|
|
352
|
+
stack: error.stack,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
};
|