blockmine 1.4.3 → 1.4.4
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/backend/src/core/BotManager.js +40 -25
- package/backend/src/core/BotProcess.js +45 -17
- package/package.json +1 -1
|
@@ -5,7 +5,6 @@ const { PrismaClient } = require('@prisma/client');
|
|
|
5
5
|
const pidusage = require('pidusage');
|
|
6
6
|
const DependencyService = require('./DependencyService');
|
|
7
7
|
const config = require('../config');
|
|
8
|
-
|
|
9
8
|
const fs = require('fs');
|
|
10
9
|
const os = require('os');
|
|
11
10
|
const { v4: uuidv4 } = require('uuid');
|
|
@@ -35,8 +34,6 @@ class BotManager {
|
|
|
35
34
|
this.botConfigs = new Map();
|
|
36
35
|
|
|
37
36
|
setInterval(() => this.updateAllResourceUsage(), 5000);
|
|
38
|
-
|
|
39
|
-
|
|
40
37
|
if (config.telemetry?.enabled) {
|
|
41
38
|
setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
|
|
42
39
|
}
|
|
@@ -49,13 +46,11 @@ class BotManager {
|
|
|
49
46
|
prisma.command.findMany({ where: { botId } }),
|
|
50
47
|
prisma.permission.findMany({ where: { botId } }),
|
|
51
48
|
]);
|
|
52
|
-
|
|
53
49
|
const config = {
|
|
54
50
|
commands: new Map(commands.map(cmd => [cmd.name, cmd])),
|
|
55
51
|
permissionsById: new Map(permissions.map(p => [p.id, p])),
|
|
56
52
|
commandAliases: new Map()
|
|
57
53
|
};
|
|
58
|
-
|
|
59
54
|
for (const cmd of commands) {
|
|
60
55
|
const aliases = JSON.parse(cmd.aliases || '[]');
|
|
61
56
|
for (const alias of aliases) {
|
|
@@ -81,7 +76,6 @@ class BotManager {
|
|
|
81
76
|
|
|
82
77
|
triggerHeartbeat() {
|
|
83
78
|
if (!config.telemetry?.enabled) return;
|
|
84
|
-
|
|
85
79
|
if (this.heartbeatDebounceTimer) {
|
|
86
80
|
clearTimeout(this.heartbeatDebounceTimer);
|
|
87
81
|
}
|
|
@@ -105,7 +99,6 @@ class BotManager {
|
|
|
105
99
|
|
|
106
100
|
async sendHeartbeat() {
|
|
107
101
|
if (!config.telemetry?.enabled) return;
|
|
108
|
-
|
|
109
102
|
if (!instanceId) return;
|
|
110
103
|
|
|
111
104
|
try {
|
|
@@ -131,7 +124,6 @@ class BotManager {
|
|
|
131
124
|
return;
|
|
132
125
|
}
|
|
133
126
|
const { challenge, difficulty, prefix } = await challengeRes.json();
|
|
134
|
-
|
|
135
127
|
let nonce = 0;
|
|
136
128
|
let hash = '';
|
|
137
129
|
do {
|
|
@@ -150,7 +142,6 @@ class BotManager {
|
|
|
150
142
|
nonce: nonce
|
|
151
143
|
})
|
|
152
144
|
});
|
|
153
|
-
|
|
154
145
|
} catch (error) {
|
|
155
146
|
console.error(`[Telemetry] Не удалось отправить heartbeat: ${error.message}`);
|
|
156
147
|
}
|
|
@@ -170,7 +161,6 @@ class BotManager {
|
|
|
170
161
|
"User": ["user.say"],
|
|
171
162
|
"Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
|
|
172
163
|
};
|
|
173
|
-
|
|
174
164
|
console.log(`[Permission Sync] Синхронизация системных прав для бота ID ${botId}...`);
|
|
175
165
|
|
|
176
166
|
for (const perm of systemPermissions) {
|
|
@@ -218,7 +208,6 @@ class BotManager {
|
|
|
218
208
|
|
|
219
209
|
const pids = Array.from(this.bots.values()).map(child => child.pid).filter(Boolean);
|
|
220
210
|
if (pids.length === 0) return;
|
|
221
|
-
|
|
222
211
|
try {
|
|
223
212
|
const stats = await pidusage(pids);
|
|
224
213
|
const usageData = [];
|
|
@@ -237,7 +226,6 @@ class BotManager {
|
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
228
|
getIO().emit('bots:usage', usageData);
|
|
240
|
-
|
|
241
229
|
} catch (error) {
|
|
242
230
|
}
|
|
243
231
|
}
|
|
@@ -291,18 +279,15 @@ class BotManager {
|
|
|
291
279
|
|
|
292
280
|
await this._syncSystemPermissions(botConfig.id);
|
|
293
281
|
await this.loadConfigForBot(botConfig.id);
|
|
294
|
-
|
|
295
282
|
this.logCache.set(botConfig.id, []);
|
|
296
283
|
this.emitStatusUpdate(botConfig.id, 'starting', '');
|
|
297
284
|
|
|
298
285
|
const allPluginsForBot = await prisma.installedPlugin.findMany({
|
|
299
286
|
where: { botId: botConfig.id },
|
|
300
287
|
});
|
|
301
|
-
|
|
302
288
|
const enabledPlugins = allPluginsForBot.filter(p => p.isEnabled);
|
|
303
289
|
|
|
304
290
|
const { sortedPlugins, pluginInfo, hasCriticalIssues } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
|
|
305
|
-
|
|
306
291
|
if (hasCriticalIssues) {
|
|
307
292
|
this.appendLog(botConfig.id, '[DependencyManager] Обнаружены критические проблемы с зависимостями:');
|
|
308
293
|
for (const plugin of Object.values(pluginInfo)) {
|
|
@@ -328,20 +313,16 @@ class BotManager {
|
|
|
328
313
|
}
|
|
329
314
|
|
|
330
315
|
const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
|
|
331
|
-
|
|
332
316
|
const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
|
|
333
317
|
const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
|
|
334
318
|
|
|
335
319
|
child.botConfig = botConfig;
|
|
336
|
-
|
|
337
320
|
child.on('error', (err) => {
|
|
338
321
|
this.appendLog(botConfig.id, `[PROCESS FATAL] КРИТИЧЕСКАЯ ОШИБКА ПРОЦЕССА: ${err.stack}`);
|
|
339
322
|
});
|
|
340
|
-
|
|
341
323
|
child.stderr.on('data', (data) => {
|
|
342
324
|
this.appendLog(botConfig.id, `[STDERR] ${data.toString()}`);
|
|
343
325
|
});
|
|
344
|
-
|
|
345
326
|
child.on('message', async (message) => {
|
|
346
327
|
if (message.type === 'log') {
|
|
347
328
|
this.appendLog(botConfig.id, message.content);
|
|
@@ -351,9 +332,48 @@ class BotManager {
|
|
|
351
332
|
await this.handleCommandValidation(botConfig, message);
|
|
352
333
|
} else if (message.type === 'register_command') {
|
|
353
334
|
await this.handleCommandRegistration(botConfig.id, message.commandConfig);
|
|
335
|
+
} else if (message.type === 'request_user_action') {
|
|
336
|
+
const { requestId, payload } = message;
|
|
337
|
+
const { targetUsername, action, data } = payload;
|
|
338
|
+
const botId = botConfig.id;
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const targetUser = await RealUserService.getUser(targetUsername, botId);
|
|
342
|
+
let replyPayload = {};
|
|
343
|
+
|
|
344
|
+
switch (action) {
|
|
345
|
+
case 'toggle_blacklist': {
|
|
346
|
+
const isCurrentlyBlacklisted = targetUser.isBlacklisted;
|
|
347
|
+
await targetUser.setBlacklist(!isCurrentlyBlacklisted);
|
|
348
|
+
replyPayload.newStatus = !isCurrentlyBlacklisted;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case 'toggle_group': {
|
|
353
|
+
if (!data.groupName) throw new Error('Название группы не указано.');
|
|
354
|
+
const hasGroup = targetUser.hasGroup(data.groupName);
|
|
355
|
+
if (hasGroup) {
|
|
356
|
+
await targetUser.removeGroup(data.groupName);
|
|
357
|
+
replyPayload.actionTaken = 'removed';
|
|
358
|
+
} else {
|
|
359
|
+
await targetUser.addGroup(data.groupName);
|
|
360
|
+
replyPayload.actionTaken = 'added';
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
default:
|
|
366
|
+
throw new Error(`Неизвестное действие: ${action}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
child.send({ type: 'user_action_response', requestId, payload: replyPayload });
|
|
370
|
+
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error(`[BotManager] Ошибка выполнения действия '${action}' для пользователя '${targetUsername}':`, error);
|
|
373
|
+
child.send({ type: 'user_action_response', requestId, error: error.message });
|
|
374
|
+
}
|
|
354
375
|
}
|
|
355
376
|
});
|
|
356
|
-
|
|
357
377
|
child.on('exit', (code, signal) => {
|
|
358
378
|
const botId = botConfig.id;
|
|
359
379
|
this.bots.delete(botId);
|
|
@@ -362,7 +382,6 @@ class BotManager {
|
|
|
362
382
|
this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
|
|
363
383
|
this.updateAllResourceUsage();
|
|
364
384
|
});
|
|
365
|
-
|
|
366
385
|
this.bots.set(botConfig.id, child);
|
|
367
386
|
child.send({ type: 'start', config: fullBotConfig });
|
|
368
387
|
|
|
@@ -433,7 +452,6 @@ class BotManager {
|
|
|
433
452
|
}
|
|
434
453
|
|
|
435
454
|
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
436
|
-
|
|
437
455
|
} catch (error) {
|
|
438
456
|
console.error(`[BotManager] Command validation error for botId: ${botId}`, {
|
|
439
457
|
command: commandName,
|
|
@@ -477,18 +495,15 @@ class BotManager {
|
|
|
477
495
|
allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
|
|
478
496
|
cooldown: commandConfig.cooldown || 0,
|
|
479
497
|
};
|
|
480
|
-
|
|
481
498
|
const updateData = {
|
|
482
499
|
description: commandConfig.description,
|
|
483
500
|
owner: commandConfig.owner,
|
|
484
501
|
};
|
|
485
|
-
|
|
486
502
|
await prisma.command.upsert({
|
|
487
503
|
where: { botId_name: { botId, name: commandConfig.name } },
|
|
488
504
|
update: updateData,
|
|
489
505
|
create: createData,
|
|
490
506
|
});
|
|
491
|
-
|
|
492
507
|
this.invalidateConfigCache(botId);
|
|
493
508
|
|
|
494
509
|
} catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
1
|
const mineflayer = require('mineflayer');
|
|
3
2
|
const { SocksClient } = require('socks');
|
|
4
3
|
const EventEmitter = require('events');
|
|
4
|
+
const { v4: uuidv4 } = require('uuid');
|
|
5
5
|
const { loadCommands } = require('./system/CommandRegistry');
|
|
6
6
|
const { initializePlugins } = require('./PluginLoader');
|
|
7
7
|
const MessageQueue = require('./MessageQueue');
|
|
@@ -12,6 +12,7 @@ const UserService = require('./ipc/UserService.stub.js');
|
|
|
12
12
|
const PermissionManager = require('./ipc/PermissionManager.stub.js');
|
|
13
13
|
|
|
14
14
|
let bot = null;
|
|
15
|
+
const pendingRequests = new Map();
|
|
15
16
|
|
|
16
17
|
function sendLog(content) {
|
|
17
18
|
if (process.send) {
|
|
@@ -29,8 +30,8 @@ function handleIncomingCommand(type, username, message) {
|
|
|
29
30
|
const commandName = commandParts.shift().toLowerCase();
|
|
30
31
|
const restOfMessage = commandParts.join(' ');
|
|
31
32
|
|
|
32
|
-
const commandInstance = bot.commands.get(commandName) ||
|
|
33
|
-
|
|
33
|
+
const commandInstance = bot.commands.get(commandName) ||
|
|
34
|
+
Array.from(bot.commands.values()).find(cmd => cmd.aliases.includes(commandName));
|
|
34
35
|
|
|
35
36
|
if (!commandInstance) return;
|
|
36
37
|
|
|
@@ -90,7 +91,17 @@ function handleIncomingCommand(type, username, message) {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
process.on('message', async (message) => {
|
|
93
|
-
if (message.type === '
|
|
94
|
+
if (message.type === 'user_action_response') {
|
|
95
|
+
if (pendingRequests.has(message.requestId)) {
|
|
96
|
+
const { resolve, reject } = pendingRequests.get(message.requestId);
|
|
97
|
+
if (message.error) {
|
|
98
|
+
reject(new Error(message.error));
|
|
99
|
+
} else {
|
|
100
|
+
resolve(message.payload);
|
|
101
|
+
}
|
|
102
|
+
pendingRequests.delete(message.requestId);
|
|
103
|
+
}
|
|
104
|
+
} else if (message.type === 'start') {
|
|
94
105
|
const config = message.config;
|
|
95
106
|
sendLog(`[System] Получена команда на запуск бота ${config.username}...`);
|
|
96
107
|
try {
|
|
@@ -130,18 +141,17 @@ process.on('message', async (message) => {
|
|
|
130
141
|
}
|
|
131
142
|
} else {
|
|
132
143
|
sendLog(`[System] Прокси не настроен, используется прямое подключение.`);
|
|
133
|
-
|
|
144
|
+
}
|
|
134
145
|
|
|
135
146
|
bot = mineflayer.createBot(botOptions);
|
|
136
147
|
|
|
137
148
|
bot.events = new EventEmitter();
|
|
138
|
-
bot.events.setMaxListeners(30);
|
|
149
|
+
bot.events.setMaxListeners(30);
|
|
139
150
|
bot.config = config;
|
|
140
151
|
bot.sendLog = sendLog;
|
|
141
152
|
bot.messageQueue = new MessageQueue(bot);
|
|
142
153
|
|
|
143
154
|
const installedPluginNames = config.plugins.map(p => p.name);
|
|
144
|
-
|
|
145
155
|
bot.api = {
|
|
146
156
|
Command: Command,
|
|
147
157
|
events: bot.events,
|
|
@@ -172,17 +182,43 @@ process.on('message', async (message) => {
|
|
|
172
182
|
});
|
|
173
183
|
}
|
|
174
184
|
sendLog(`[API] Команда "${commandInstance.name}" от плагина "${commandInstance.owner}" зарегистрирована в процессе.`);
|
|
185
|
+
},
|
|
186
|
+
performUserAction: (username, action, data = {}) => {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const requestId = uuidv4();
|
|
189
|
+
pendingRequests.set(requestId, { resolve, reject });
|
|
190
|
+
|
|
191
|
+
if (process.send) {
|
|
192
|
+
process.send({
|
|
193
|
+
type: 'request_user_action',
|
|
194
|
+
requestId,
|
|
195
|
+
payload: {
|
|
196
|
+
targetUsername: username,
|
|
197
|
+
action,
|
|
198
|
+
data
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
} else {
|
|
202
|
+
reject(new Error('IPC channel is not available.'));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
setTimeout(() => {
|
|
206
|
+
if (pendingRequests.has(requestId)) {
|
|
207
|
+
reject(new Error('Request to main process timed out.'));
|
|
208
|
+
pendingRequests.delete(requestId);
|
|
209
|
+
}
|
|
210
|
+
}, 5000);
|
|
211
|
+
});
|
|
175
212
|
}
|
|
176
213
|
};
|
|
177
214
|
|
|
178
215
|
bot.commands = await loadCommands();
|
|
179
|
-
|
|
180
216
|
if (process.send) {
|
|
181
217
|
for (const cmd of bot.commands.values()) {
|
|
182
218
|
process.send({
|
|
183
219
|
type: 'register_command',
|
|
184
220
|
commandConfig: {
|
|
185
|
-
|
|
221
|
+
name: cmd.name,
|
|
186
222
|
description: cmd.description,
|
|
187
223
|
aliases: cmd.aliases,
|
|
188
224
|
owner: cmd.owner,
|
|
@@ -207,7 +243,6 @@ process.on('message', async (message) => {
|
|
|
207
243
|
handleIncomingCommand(type, username, message);
|
|
208
244
|
}
|
|
209
245
|
});
|
|
210
|
-
|
|
211
246
|
bot.on('message', (jsonMsg) => {
|
|
212
247
|
const ansiMessage = jsonMsg.toAnsi();
|
|
213
248
|
if (ansiMessage.trim()) {
|
|
@@ -218,39 +253,32 @@ process.on('message', async (message) => {
|
|
|
218
253
|
const rawMessageText = jsonMsg.toString();
|
|
219
254
|
bot.events.emit('core:raw_message', rawMessageText, jsonMsg);
|
|
220
255
|
});
|
|
221
|
-
|
|
222
256
|
bot.on('chat', (username, message) => {
|
|
223
257
|
if (messageHandledByCustomParser) {
|
|
224
258
|
return;
|
|
225
259
|
}
|
|
226
260
|
handleIncomingCommand('chat', username, message);
|
|
227
261
|
});
|
|
228
|
-
|
|
229
262
|
bot.on('login', () => {
|
|
230
263
|
sendLog('[Event: login] Успешно залогинился!');
|
|
231
264
|
if (process.send) {
|
|
232
265
|
process.send({ type: 'status', status: 'running' });
|
|
233
266
|
}
|
|
234
267
|
});
|
|
235
|
-
|
|
236
268
|
bot.on('spawn', () => {
|
|
237
269
|
sendLog('[Event: spawn] Бот заспавнился в мире. Полностью готов к работе!');
|
|
238
270
|
});
|
|
239
|
-
|
|
240
271
|
bot.on('kicked', (reason) => {
|
|
241
272
|
let reasonText;
|
|
242
273
|
try { reasonText = JSON.parse(reason).text || reason; } catch (e) { reasonText = reason; }
|
|
243
274
|
sendLog(`[Event: kicked] Меня кикнули. Причина: ${reasonText}.`);
|
|
244
275
|
process.exit(0);
|
|
245
276
|
});
|
|
246
|
-
|
|
247
277
|
bot.on('error', (err) => sendLog(`[Event: error] Произошла ошибка: ${err.stack || err.message}`));
|
|
248
|
-
|
|
249
278
|
bot.on('end', (reason) => {
|
|
250
279
|
sendLog(`[Event: end] Отключен от сервера. Причина: ${reason}`);
|
|
251
280
|
process.exit(0);
|
|
252
281
|
});
|
|
253
|
-
|
|
254
282
|
} catch (err) {
|
|
255
283
|
sendLog(`[CRITICAL] Критическая ошибка при создании бота: ${err.stack}`);
|
|
256
284
|
process.exit(1);
|