blockmine 1.0.9 → 1.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/backend/cli.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
-
3
-
4
2
  const fs = require('fs');
5
3
  const os = require('os');
6
4
  const path = require('path');
@@ -8,12 +6,6 @@ const { execSync } = require('child_process');
8
6
  const { startServer } = require('./src/server.js');
9
7
 
10
8
  const DATA_DIR = path.join(os.homedir(), '.blockmine');
11
- if (!fs.existsSync(DATA_DIR)) {
12
- console.log(`[BlockMine] Создание папки для данных: ${DATA_DIR}`);
13
- fs.mkdirSync(DATA_DIR, { recursive: true });
14
- }
15
-
16
- process.env.DATABASE_URL = `file:${path.join(DATA_DIR, 'blockmine.db')}`;
17
9
  const prismaSchemaPath = path.join(__dirname, 'prisma', 'schema.prisma');
18
10
 
19
11
  function runCommand(command) {
@@ -29,13 +21,17 @@ function runCommand(command) {
29
21
 
30
22
  async function main() {
31
23
  console.log('Запуск панели управления BlockMine...');
32
- console.log(`[BlockMine] Хранилище данных: ${DATA_DIR}`);
33
24
 
34
25
  const dbPath = path.join(DATA_DIR, 'blockmine.db');
35
26
 
36
27
  if (!fs.existsSync(dbPath)) {
37
28
  console.log('База данных не найдена. Создаем и применяем все миграции...');
38
29
  runCommand(`npx prisma migrate deploy --schema=${prismaSchemaPath}`);
30
+
31
+ console.log('Заполнение базы данных начальными данными (серверами)...');
32
+ const seedScriptPath = path.join(__dirname, 'prisma', 'seed.js');
33
+ runCommand(`node ${seedScriptPath}`);
34
+
39
35
  console.log('Первоначальная настройка базы данных завершена.');
40
36
  } else {
41
37
  console.log('Проверка и применение обновлений базы данных...');
@@ -1,12 +1,8 @@
1
1
  {
2
- "watch": [
3
- "src/"
4
- ],
5
- "ext": "js,mjs,cjs,json",
6
- "ignore": [
7
- "storage/",
8
- "prisma/dev.db",
9
- "prisma/dev.db-journal"
10
- ],
11
- "exec": "node src/server.js"
12
- }
2
+ "watch": ["src/"],
3
+ "ext": "js,mjs,cjs,json",
4
+ "ignore": ["storage/", "prisma/dev.db", "prisma/dev.db-journal"],
5
+ "exec": "node src/server.js",
6
+ "signal": "SIGINT",
7
+ "delay": 1500
8
+ }
@@ -4,10 +4,10 @@
4
4
  "description": "",
5
5
  "main": "src/server.js",
6
6
  "scripts": {
7
- "start": "node src/server.js",
8
- "dev": "nodemon src/server.js",
9
- "test": "echo \"Error: no test specified\" && exit 1"
10
- },
7
+ "start": "node src/server.js",
8
+ "dev": "nodemon",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
11
  "prisma": {
12
12
  "seed": "node prisma/seed.js"
13
13
  },
@@ -22,11 +22,12 @@ async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
22
22
  { name: "admin.*", description: "Все права администратора" },
23
23
  { name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
24
24
  { name: "user.*", description: "Все права обычного пользователя" },
25
+ { name: "user.say", description: "Доступ к простым командам" },
25
26
  { name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
26
27
  ],
27
28
  groupPermissions: {
28
- "User": ["user.*"],
29
- "Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass"]
29
+ "User": ["user.say"],
30
+ "Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
30
31
  },
31
32
  };
32
33
 
@@ -55,7 +56,10 @@ router.get('/', async (req, res) => {
55
56
  try {
56
57
  const bots = await prisma.bot.findMany({ include: { server: true }, orderBy: { createdAt: 'asc' } });
57
58
  res.json(bots);
58
- } catch (error) { res.status(500).json({ error: 'Не удалось получить список ботов' }); }
59
+ } catch (error) {
60
+ console.error("[API /api/bots] Ошибка получения списка ботов:", error);
61
+ res.status(500).json({ error: 'Не удалось получить список ботов' });
62
+ }
59
63
  });
60
64
 
61
65
  router.get('/state', (req, res) => {
@@ -8,6 +8,7 @@ router.get('/', async (req, res) => {
8
8
  const servers = await prisma.server.findMany({ orderBy: { name: 'asc' } });
9
9
  res.json(servers);
10
10
  } catch (error) {
11
+ console.error("[API /api/servers] Ошибка получения списка серверов:", error);
11
12
  res.status(500).json({ error: 'Не удалось получить список серверов' });
12
13
  }
13
14
  });
@@ -6,11 +6,34 @@ const { PrismaClient } = require('@prisma/client');
6
6
  const pidusage = require('pidusage');
7
7
  const DependencyService = require('./DependencyService');
8
8
 
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const { v4: uuidv4 } = require('uuid');
12
+ const crypto = require('crypto');
13
+
9
14
  const RealPermissionManager = require('./PermissionManager');
10
15
  const RealUserService = require('./UserService');
11
16
 
12
17
  const prisma = new PrismaClient();
13
18
  const cooldowns = new Map();
19
+ const warningCache = new Map();
20
+ const WARNING_COOLDOWN = 10 * 1000;
21
+
22
+
23
+ const TELEMETRY_ENABLED = 'true';
24
+ const STATS_SERVER_URL = 'http://185.65.200.184:3000';
25
+ let instanceId = null;
26
+ const DATA_DIR = path.join(os.homedir(), '.blockmine');
27
+
28
+ if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
29
+ const idPath = path.join(DATA_DIR, '.instance_id');
30
+ try {
31
+ instanceId = fs.readFileSync(idPath, 'utf-8');
32
+ } catch (e) {
33
+ instanceId = uuidv4();
34
+ fs.writeFileSync(idPath, instanceId, 'utf-8');
35
+ }
36
+ }
14
37
 
15
38
  class BotManager {
16
39
  constructor() {
@@ -19,6 +42,129 @@ class BotManager {
19
42
  this.resourceUsage = new Map();
20
43
 
21
44
  setInterval(() => this.updateAllResourceUsage(), 5000);
45
+
46
+
47
+ if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
48
+ setTimeout(() => this.sendHeartbeat(), 10000);
49
+ setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
50
+ }
51
+ }
52
+
53
+
54
+ _sendThrottledWarning(botId, username, warningType, message, typeChat = 'private') {
55
+ const cacheKey = `${botId}:${username}:${warningType}`;
56
+ const now = Date.now();
57
+ const lastWarning = warningCache.get(cacheKey);
58
+
59
+ if (!lastWarning || (now - lastWarning > WARNING_COOLDOWN)) {
60
+ this.sendMessageToBot(botId, message, typeChat, username);
61
+ warningCache.set(cacheKey, now);
62
+ }
63
+ }
64
+
65
+
66
+ async sendHeartbeat() {
67
+ if (!instanceId) return;
68
+
69
+ try {
70
+ const runningBots = [];
71
+ for (const botProcess of this.bots.values()) {
72
+ if (botProcess.botConfig) {
73
+ runningBots.push({
74
+ username: botProcess.botConfig.username,
75
+ serverHost: botProcess.botConfig.server.host,
76
+ serverPort: botProcess.botConfig.server.port
77
+ });
78
+ }
79
+ }
80
+
81
+ if (runningBots.length === 0) {
82
+ return;
83
+ }
84
+
85
+ console.log(`[Telemetry] Подготовка к отправке heartbeat для ${runningBots.length} ботов...`);
86
+
87
+ const challengeRes = await fetch(`${STATS_SERVER_URL}/api/challenge?uuid=${instanceId}`);
88
+ if (!challengeRes.ok) {
89
+ console.error(`[Telemetry] Сервер статистики вернул ошибку при получении challenge: ${challengeRes.statusText}`);
90
+ return;
91
+ }
92
+ const { challenge, difficulty, prefix } = await challengeRes.json();
93
+
94
+ let nonce = 0;
95
+ let hash = '';
96
+ do {
97
+ nonce++;
98
+ hash = crypto.createHash('sha256').update(prefix + challenge + nonce).digest('hex');
99
+ } while (!hash.startsWith('0'.repeat(difficulty)));
100
+
101
+ const packageJson = require('../../../package.json');
102
+ await fetch(`${STATS_SERVER_URL}/api/heartbeat`, {
103
+ method: 'POST',
104
+ headers: { 'Content-Type': 'application/json' },
105
+ body: JSON.stringify({
106
+ instanceUuid: instanceId,
107
+ appVersion: packageJson.version,
108
+ bots: runningBots,
109
+ nonce: nonce
110
+ })
111
+ });
112
+ console.log(`[Telemetry] Heartbeat успешно отправлен.`);
113
+
114
+ } catch (error) {
115
+ console.error(`[Telemetry] Не удалось отправить heartbeat: ${error.message}`);
116
+ }
117
+ }
118
+
119
+
120
+ async _syncSystemPermissions(botId) {
121
+ const systemPermissions = [
122
+ { name: "admin.*", description: "Все права администратора" },
123
+ { name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
124
+ { name: "user.*", description: "Все права обычного пользователя" },
125
+ { name: "user.say", description: "Доступ к простым командам" },
126
+ { name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
127
+ ];
128
+ const systemGroups = ["User", "Admin"];
129
+ const systemGroupPermissions = {
130
+ "User": ["user.say"],
131
+ "Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
132
+ };
133
+
134
+ console.log(`[Permission Sync] Синхронизация системных прав для бота ID ${botId}...`);
135
+
136
+ for (const perm of systemPermissions) {
137
+ await prisma.permission.upsert({
138
+ where: { botId_name: { botId, name: perm.name } },
139
+ update: { description: perm.description },
140
+ create: { ...perm, botId, owner: 'system' }
141
+ });
142
+ }
143
+
144
+ for (const groupName of systemGroups) {
145
+ await prisma.group.upsert({
146
+ where: { botId_name: { botId, name: groupName } },
147
+ update: {},
148
+ create: { name: groupName, botId, owner: 'system' }
149
+ });
150
+ }
151
+
152
+ for (const [groupName, permNames] of Object.entries(systemGroupPermissions)) {
153
+ const group = await prisma.group.findUnique({ where: { botId_name: { botId, name: groupName } } });
154
+ if (group) {
155
+ for (const permName of permNames) {
156
+ const permission = await prisma.permission.findUnique({ where: { botId_name: { botId, name: permName } } });
157
+ if (permission) {
158
+ await prisma.groupPermission.upsert({
159
+ where: { groupId_permissionId: { groupId: group.id, permissionId: permission.id } },
160
+ update: {},
161
+ create: { groupId: group.id, permissionId: permission.id }
162
+ });
163
+ }
164
+ }
165
+ }
166
+ }
167
+ console.log(`[Permission Sync] Синхронизация для бота ID ${botId} завершена.`);
22
168
  }
23
169
 
24
170
  async updateAllResourceUsage() {
@@ -102,6 +248,9 @@ class BotManager {
102
248
  return { success: false, message: 'Бот уже запущен или запускается.' };
103
249
  }
104
250
  }
251
+
252
+ await this._syncSystemPermissions(botConfig.id);
253
+
105
254
  this.logCache.set(botConfig.id, []);
106
255
  this.emitStatusUpdate(botConfig.id, 'starting', '');
107
256
 
@@ -111,27 +260,20 @@ class BotManager {
111
260
 
112
261
  const enabledPlugins = allPluginsForBot.filter(p => p.isEnabled);
113
262
 
114
- const { sortedPlugins, pluginInfo } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
263
+ const { sortedPlugins, pluginInfo, hasCriticalIssues } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
115
264
 
116
- let canStart = true;
117
- if (Object.values(pluginInfo).some(info => info.issues.length > 0)) {
118
- this.appendLog(botConfig.id, '[DependencyManager] Обнаружены проблемы с зависимостями:');
265
+ if (hasCriticalIssues) {
266
+ this.appendLog(botConfig.id, '[DependencyManager] Обнаружены критические проблемы с зависимостями:');
119
267
  for (const plugin of Object.values(pluginInfo)) {
120
268
  if (plugin.issues.length > 0) {
121
269
  this.appendLog(botConfig.id, ` - Плагин "${plugin.name}":`);
122
270
  for (const issue of plugin.issues) {
123
271
  const logMessage = ` - [${issue.type.toUpperCase()}] ${issue.message}`;
124
272
  this.appendLog(botConfig.id, logMessage);
125
- if (['missing_dependency', 'version_mismatch', 'circular_dependency'].includes(issue.type)) {
126
- canStart = false;
127
- }
128
273
  }
129
274
  }
130
275
  }
131
- }
132
-
133
- if (!canStart) {
134
- this.appendLog(botConfig.id, '[DependencyManager] [FATAL] Запуск отменен из-за критических ошибок в зависимостях.');
276
+ this.appendLog(botConfig.id, '[DependencyManager] [FATAL] Запуск отменен.');
135
277
  this.emitStatusUpdate(botConfig.id, 'stopped', 'Ошибка зависимостей плагинов.');
136
278
  return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
137
279
  }
@@ -141,6 +283,8 @@ class BotManager {
141
283
  const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
142
284
  const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
143
285
 
286
+ child.botConfig = botConfig;
287
+
144
288
  child.on('error', (err) => {
145
289
  this.appendLog(botConfig.id, `[PROCESS FATAL] КРИТИЧЕСКАЯ ОШИБКА ПРОЦЕССА: ${err.stack}`);
146
290
  });
@@ -156,6 +300,8 @@ class BotManager {
156
300
  this.emitStatusUpdate(botConfig.id, message.status);
157
301
  } else if (message.type === 'validate_and_run_command') {
158
302
  await this.handleCommandValidation(botConfig, message);
303
+ } else if (message.type === 'register_command') {
304
+ await this.handleCommandRegistration(botConfig.id, message.commandConfig);
159
305
  }
160
306
  });
161
307
 
@@ -180,41 +326,58 @@ class BotManager {
180
326
  try {
181
327
  const user = await RealUserService.getUser(username, botConfig.id, botConfig);
182
328
 
183
- if (user.isBlacklisted) {
184
- return;
185
- }
329
+ if (user.isBlacklisted) return;
186
330
 
187
- const dbCommand = await prisma.command.findUnique({ where: { botId_name: { botId: botConfig.id, name: commandName } } });
331
+ const dbCommand = await prisma.command.findFirst({
332
+ where: {
333
+ botId: botConfig.id,
334
+ OR: [ { name: commandName }, { aliases: { contains: `"${commandName}"` } } ]
335
+ }
336
+ });
188
337
 
189
- if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
190
- return;
191
- }
338
+ if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) return;
192
339
 
193
340
  const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
194
341
  if (!allowedTypes.includes(typeChat) && !user.isOwner) {
195
- const replyMessage = `Команду ${commandName} нельзя использовать в этом типе чата.`;
196
- this.sendMessageToBot(botConfig.id, replyMessage, 'private', username);
342
+ this._sendThrottledWarning(
343
+ botConfig.id,
344
+ username,
345
+ `wrong_chat:${dbCommand.name}:${typeChat}`,
346
+ `Команду ${dbCommand.name} нельзя использовать в этом типе чата.`,
347
+ typeChat
348
+ );
197
349
  return;
198
350
  }
199
351
 
200
352
  const permission = dbCommand.permissionId ? await prisma.permission.findUnique({ where: { id: dbCommand.permissionId } }) : null;
201
353
  if (permission && !user.hasPermission(permission.name)) {
202
- const replyMessage = `У вас нет прав для выполнения команды ${commandName}.`;
203
- this.sendMessageToBot(botConfig.id, replyMessage, typeChat, username);
354
+ this._sendThrottledWarning(
355
+ botConfig.id,
356
+ username,
357
+ `no_permission:${dbCommand.name}`,
358
+ `У вас нет прав для выполнения команды ${dbCommand.name}.`,
359
+ typeChat
360
+ );
204
361
  return;
205
362
  }
206
363
 
207
364
  const domain = (permission?.name || '').split('.')[0] || 'user';
208
365
  const bypassCooldownPermission = `${domain}.cooldown.bypass`;
209
366
  if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
210
- const cooldownKey = `${botConfig.id}:${commandName}:${user.id}`;
367
+ const cooldownKey = `${botConfig.id}:${dbCommand.name}:${user.id}`;
211
368
  const now = Date.now();
212
369
  const lastUsed = cooldowns.get(cooldownKey);
213
370
 
214
371
  if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
215
372
  const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
216
- const replyMessage = `Команду ${commandName} можно будет использовать через ${timeLeft} сек.`;
217
- this.sendMessageToBot(botConfig.id, replyMessage, typeChat, username);
373
+
374
+ this._sendThrottledWarning(
375
+ botConfig.id,
376
+ username,
377
+ `cooldown:${dbCommand.name}`,
378
+ `Команду ${dbCommand.name} можно будет использовать через ${timeLeft} сек.`,
379
+ typeChat
380
+ );
218
381
  return;
219
382
  }
220
383
  cooldowns.set(cooldownKey, now);
@@ -222,13 +385,55 @@ class BotManager {
222
385
 
223
386
  const child = this.bots.get(botConfig.id);
224
387
  if (child) {
225
- child.send({ type: 'execute_handler', commandName, username, args, typeChat });
388
+ child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
226
389
  }
227
390
 
228
391
  } catch (error) {
229
392
  console.error(`[BotManager] Ошибка валидации команды ${commandName}:`, error);
230
- const replyMessage = `Произошла внутренняя ошибка при выполнении команды.`;
231
- this.sendMessageToBot(botConfig.id, replyMessage, 'private', username);
393
+ this.sendMessageToBot(botConfig.id, `Произошла внутренняя ошибка при выполнении команды.`, 'private', username);
394
+ }
395
+ }
396
+
397
+ async handleCommandRegistration(botId, commandConfig) {
398
+ try {
399
+ let permissionId = null;
400
+ if (commandConfig.permissions) {
401
+ let permission = await prisma.permission.findUnique({
402
+ where: { botId_name: { botId, name: commandConfig.permissions } }
403
+ });
404
+ if (!permission) {
405
+ permission = await prisma.permission.create({
406
+ data: {
407
+ botId,
408
+ name: commandConfig.permissions,
409
+ description: `Автоматически создано для команды ${commandConfig.name}`,
410
+ owner: commandConfig.owner,
411
+ }
412
+ });
413
+ }
414
+ permissionId = permission.id;
415
+ }
416
+
417
+ const data = {
418
+ description: commandConfig.description,
419
+ aliases: JSON.stringify(commandConfig.aliases || []),
420
+ owner: commandConfig.owner,
421
+ permissionId: permissionId,
422
+ allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
423
+ cooldown: commandConfig.cooldown || 0,
424
+ };
425
+
426
+ await prisma.command.upsert({
427
+ where: { botId_name: { botId, name: commandConfig.name } },
428
+ update: data,
429
+ create: {
430
+ botId,
431
+ name: commandConfig.name,
432
+ ...data
433
+ }
434
+ });
435
+ } catch (error) {
436
+ console.error(`[BotManager] Ошибка при регистрации команды '${commandConfig.name}':`, error);
232
437
  }
233
438
  }
234
439
 
@@ -244,7 +449,6 @@ class BotManager {
244
449
  sendMessageToBot(botId, message, chatType = 'command', username = null) {
245
450
  const child = this.bots.get(botId);
246
451
  if (child) {
247
- // Отправляем объект с полной информацией
248
452
  child.send({ type: 'chat', payload: { message, chatType, username } });
249
453
  return { success: true };
250
454
  }
@@ -21,10 +21,7 @@ function sendLog(content) {
21
21
  }
22
22
  }
23
23
 
24
- /**
25
- * Анализирует входящее сообщение и отправляет запрос на валидацию команды в главный процесс.
26
- */
27
- async function handleIncomingChat(type, username, message) {
24
+ function handleIncomingCommand(type, username, message) {
28
25
  if (!message.startsWith(bot.config.prefix || '@')) return;
29
26
 
30
27
  const rawMessage = message.slice((bot.config.prefix || '@').length).trim();
@@ -38,13 +35,47 @@ async function handleIncomingChat(type, username, message) {
38
35
  if (!commandInstance) return;
39
36
 
40
37
  try {
41
- const args = parseArguments(restOfMessage);
38
+ const processedArgs = {};
39
+ const parsedArgs = parseArguments(restOfMessage);
40
+ let currentArgIndex = 0;
41
+
42
+ for (const argDef of commandInstance.args) {
43
+ if (argDef.type === 'greedy_string') {
44
+ if (currentArgIndex < parsedArgs.length) {
45
+ processedArgs[argDef.name] = parsedArgs.slice(currentArgIndex).join(' ');
46
+ currentArgIndex = parsedArgs.length;
47
+ }
48
+ } else if (currentArgIndex < parsedArgs.length) {
49
+ let value = parsedArgs[currentArgIndex];
50
+ if (argDef.type === 'number') {
51
+ const numValue = parseFloat(value);
52
+ if (isNaN(numValue)) {
53
+ bot.api.sendMessage(type, `Ошибка: Неверный тип аргумента "${argDef.description}". Ожидалось число.`, username);
54
+ return;
55
+ }
56
+ value = numValue;
57
+ }
58
+ processedArgs[argDef.name] = value;
59
+ currentArgIndex++;
60
+ }
61
+
62
+ if (processedArgs[argDef.name] === undefined) {
63
+ if (argDef.required) {
64
+ bot.api.sendMessage(type, `Ошибка: Необходимо указать: ${argDef.description}`, username);
65
+ return;
66
+ }
67
+ if (argDef.default !== undefined) {
68
+ processedArgs[argDef.name] = argDef.default;
69
+ }
70
+ }
71
+ }
72
+
42
73
  if (process.send) {
43
74
  process.send({
44
75
  type: 'validate_and_run_command',
45
76
  commandName: commandInstance.name,
46
77
  username,
47
- args,
78
+ args: processedArgs,
48
79
  typeChat: type
49
80
  });
50
81
  }
@@ -140,6 +171,24 @@ process.on('message', async (message) => {
140
171
  };
141
172
 
142
173
  bot.commands = await loadCommands();
174
+
175
+ if (process.send) {
176
+ for (const cmd of bot.commands.values()) {
177
+ process.send({
178
+ type: 'register_command',
179
+ commandConfig: {
180
+ name: cmd.name,
181
+ description: cmd.description,
182
+ aliases: cmd.aliases,
183
+ owner: cmd.owner,
184
+ permissions: cmd.permissions,
185
+ cooldown: cmd.cooldown,
186
+ allowedChatTypes: cmd.allowedChatTypes,
187
+ }
188
+ });
189
+ }
190
+ }
191
+
143
192
  await initializePlugins(bot, config.plugins);
144
193
  sendLog('[System] Все системы инициализированы.');
145
194
 
@@ -147,12 +196,13 @@ process.on('message', async (message) => {
147
196
  let messageHandledByCustomParser = false;
148
197
 
149
198
  bot.events.on('chat:message', (data) => {
150
- if (data && data.username && data.message) {
199
+ const { type, username, message } = data;
200
+ if (username && message) {
151
201
  messageHandledByCustomParser = true;
152
- handleIncomingChat(data.type, data.username, data.message);
202
+ handleIncomingCommand(type, username, message);
153
203
  }
154
204
  });
155
-
205
+
156
206
  bot.on('message', (jsonMsg) => {
157
207
  const ansiMessage = jsonMsg.toAnsi();
158
208
  if (ansiMessage.trim()) {
@@ -168,8 +218,7 @@ process.on('message', async (message) => {
168
218
  if (messageHandledByCustomParser) {
169
219
  return;
170
220
  }
171
-
172
- handleIncomingChat('chat', username, message);
221
+ handleIncomingCommand('chat', username, message);
173
222
  });
174
223
 
175
224
  bot.on('login', () => {
@@ -205,11 +254,11 @@ process.on('message', async (message) => {
205
254
  if (bot) bot.quit();
206
255
  else process.exit(0);
207
256
  } else if (message.type === 'chat') {
208
- if (bot && bot.entity) {
209
- const { message: msg, chatType, username } = message.payload;
210
- bot.messageQueue.enqueue(chatType, msg, username);
211
- }
212
- } else if (message.type === 'execute_handler') {
257
+ if (bot && bot.entity) {
258
+ const { message: msg, chatType, username } = message.payload;
259
+ bot.messageQueue.enqueue(chatType, msg, username);
260
+ }
261
+ } else if (message.type === 'execute_handler') {
213
262
  const { commandName, username, args, typeChat } = message;
214
263
  const commandInstance = bot.commands.get(commandName);
215
264
  if (commandInstance) {