blockmine 1.4.1 → 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.
@@ -4,7 +4,7 @@
4
4
  "description": "",
5
5
  "main": "src/server.js",
6
6
  "scripts": {
7
- "start": "node src/server.js",
7
+ "start": "node ../backend/cli.js",
8
8
  "dev": "nodemon",
9
9
  "test": "echo \"Error: no test specified\" && exit 1"
10
10
  },
@@ -14,25 +14,8 @@
14
14
  "keywords": [],
15
15
  "author": "",
16
16
  "license": "ISC",
17
- "dependencies": {
18
- "@prisma/client": "^5.14.0",
19
- "adm-zip": "^0.5.16",
20
- "archiver": "^7.0.1",
21
- "bcryptjs": "^3.0.2",
22
- "cron-parser": "^5.3.0",
23
- "express": "^4.19.2",
24
- "fs-extra": "^11.3.0",
25
- "mineflayer": "^4.20.1",
26
- "multer": "^2.0.1",
27
- "node-cron": "^4.1.0",
28
- "pidusage": "^3.0.2",
29
- "prisma": "^5.14.0",
30
- "semver": "^7.6.2",
31
- "socket.io": "^4.7.5",
32
- "socks": "^2.8.5"
33
- },
17
+ "dependencies": {},
34
18
  "devDependencies": {
35
- "nodemon": "^3.1.2",
36
- "prisma": "^5.14.0"
19
+ "nodemon": "^3.1.2"
37
20
  }
38
- }
21
+ }
@@ -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
- Array.from(bot.commands.values()).find(cmd => cmd.aliases.includes(commandName));
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 === 'start') {
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
- name: cmd.name,
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);
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
-
2
1
  {
3
2
  "name": "blockmine",
4
- "version": "1.4.1",
3
+ "version": "1.4.4",
5
4
  "description": "Мощная панель управления ботами для Майнкрафта.",
6
5
  "author": "merka",
7
6
  "license": "MIT",
@@ -26,12 +25,27 @@
26
25
  ],
27
26
  "scripts": {
28
27
  "dev": "concurrently \"npm run dev --workspace=backend\" \"npm run dev --workspace=frontend\"",
29
- "postinstall": "npm install --workspaces --ignore-scripts && npx prisma generate --schema=./backend/prisma/schema.prisma",
28
+ "postinstall": "npm install --workspace=frontend --ignore-scripts && prisma generate --schema=./backend/prisma/schema.prisma",
30
29
  "build": "npm run build --workspace=frontend",
31
- "prepublishOnly": "npm run build && npx prisma generate --schema=./backend/prisma/schema.prisma"
30
+ "prepublishOnly": "npm run build"
32
31
  },
33
32
  "dependencies": {
34
- "prisma": "^5.14.0"
33
+ "@prisma/client": "^5.14.0",
34
+ "adm-zip": "^0.5.16",
35
+ "archiver": "^7.0.1",
36
+ "bcryptjs": "^2.4.3",
37
+ "cron-parser": "^5.3.0",
38
+ "express": "^4.19.2",
39
+ "fs-extra": "^11.3.0",
40
+ "jsonwebtoken": "^9.0.2",
41
+ "mineflayer": "^4.20.1",
42
+ "multer": "^2.0.1",
43
+ "node-cron": "^4.1.0",
44
+ "pidusage": "^3.0.2",
45
+ "prisma": "^5.14.0",
46
+ "semver": "^7.6.2",
47
+ "socket.io": "^4.7.5",
48
+ "socks": "^2.8.5"
35
49
  },
36
50
  "devDependencies": {
37
51
  "concurrently": "^8.2.2"