blockmine 1.19.0 → 1.20.0

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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(netstat:*)",
5
+ "Bash(findstr:*)",
6
+ "WebSearch"
7
+ ],
8
+ "deny": [],
9
+ "ask": []
10
+ }
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # История версий
2
2
 
3
3
 
4
+ ## [1.20.0](https://github.com/blockmineJS/blockmine/compare/v1.19.1...v1.20.0) (2025-10-23)
5
+
6
+
7
+ ### ✨ Новые возможности
8
+
9
+ * если бот падает 5 раз с критической ошибкой, то перезапуск отменяется ([2ba022f](https://github.com/blockmineJS/blockmine/commit/2ba022fff038cc83bb1edfa04d3edf97fc76c255))
10
+ * action:send_message to support dynamic variables. example. Сообщение: привет, {username} ([457639d](https://github.com/blockmineJS/blockmine/commit/457639d70e7a8933f0508d93196a2be6951f1a0a))
11
+
12
+ ### [1.19.1](https://github.com/blockmineJS/blockmine/compare/v1.19.0...v1.19.1) (2025-10-01)
13
+
14
+
15
+ ### 🐛 Исправления
16
+
17
+ * изменение порядка ботов теперь корректное ([9663737](https://github.com/blockmineJS/blockmine/commit/9663737505201468dd2233bf5658be5b2442f583))
18
+ * фиксим новой вкладки прокси ([7ebdb7d](https://github.com/blockmineJS/blockmine/commit/7ebdb7d0df6c4e5cd6a9b15576cc1907ab7fcd74))
19
+
4
20
  ## [1.19.0](https://github.com/blockmineJS/blockmine/compare/v1.18.5...v1.19.0) (2025-08-23)
5
21
 
6
22
 
@@ -136,11 +136,20 @@ router.get('/', conditionalListAuth, async (req, res) => {
136
136
 
137
137
  if (botsWithoutSortOrder.length > 0) {
138
138
  console.log(`[API] Обновляем sortOrder для ${botsWithoutSortOrder.length} ботов`);
139
+
140
+ const maxSortOrder = await prisma.bot.aggregate({
141
+ _max: { sortOrder: true }
142
+ });
143
+
144
+ let nextSortOrder = (maxSortOrder._max.sortOrder || 0) + 1;
145
+
139
146
  for (const bot of botsWithoutSortOrder) {
140
147
  await prisma.bot.update({
141
148
  where: { id: bot.id },
142
- data: { sortOrder: bot.id }
149
+ data: { sortOrder: nextSortOrder }
143
150
  });
151
+ console.log(`[API] Установлен sortOrder ${nextSortOrder} для бота ${bot.id}`);
152
+ nextSortOrder++;
144
153
  }
145
154
  }
146
155
 
@@ -236,7 +245,7 @@ router.put('/bulk-proxy-update', authenticate, authorize('bot:update'), async (r
236
245
  const encryptedSettings = {
237
246
  proxyHost: proxySettings.proxyHost.trim(),
238
247
  proxyPort: parseInt(proxySettings.proxyPort),
239
- proxyUsername: proxySettings.proxyUsername ? encrypt(proxySettings.proxyUsername.trim()) : null,
248
+ proxyUsername: proxySettings.proxyUsername ? proxySettings.proxyUsername.trim() : null,
240
249
  proxyPassword: proxySettings.proxyPassword ? encrypt(proxySettings.proxyPassword) : null
241
250
  };
242
251
 
@@ -465,74 +474,56 @@ router.put('/:id', authenticate, checkBotAccess, authorize('bot:update'), async
465
474
 
466
475
  router.put('/:id/sort-order', authenticate, checkBotAccess, authorize('bot:update'), async (req, res) => {
467
476
  try {
468
- const { newPosition } = req.body;
477
+ const { newPosition, oldIndex, newIndex } = req.body;
469
478
  const botId = parseInt(req.params.id, 10);
470
479
 
471
- console.log(`[API] Запрос на изменение порядка бота ${botId} на позицию ${newPosition}`);
480
+ console.log(`[API] Запрос на изменение порядка бота ${botId}: oldIndex=${oldIndex}, newIndex=${newIndex}, newPosition=${newPosition}`);
472
481
 
473
- if (isNaN(botId) || typeof newPosition !== 'number') {
474
- console.log(`[API] Неверные параметры: botId=${botId}, newPosition=${newPosition}`);
475
- return res.status(400).json({ error: 'Неверные параметры' });
482
+ if (isNaN(botId)) {
483
+ console.log(`[API] Неверный botId: ${botId}`);
484
+ return res.status(400).json({ error: 'Неверный ID бота' });
476
485
  }
477
486
 
478
- const currentBot = await prisma.bot.findUnique({
479
- where: { id: botId },
480
- select: { sortOrder: true }
487
+ const allBots = await prisma.bot.findMany({
488
+ orderBy: { sortOrder: 'asc' },
489
+ select: { id: true, sortOrder: true }
481
490
  });
482
491
 
483
- if (!currentBot) {
492
+ console.log(`[API] Всего ботов: ${allBots.length}`);
493
+ const currentBotIndex = allBots.findIndex(bot => bot.id === botId);
494
+ if (currentBotIndex === -1) {
484
495
  console.log(`[API] Бот ${botId} не найден`);
485
496
  return res.status(404).json({ error: 'Бот не найден' });
486
497
  }
487
498
 
488
- const currentPosition = currentBot.sortOrder;
489
- console.log(`[API] Текущая позиция бота ${botId}: ${currentPosition}, новая позиция: ${newPosition}`);
490
-
491
- if (newPosition === currentPosition) {
499
+ if (newIndex < 0 || newIndex >= allBots.length) {
500
+ console.log(`[API] Неверная новая позиция: ${newIndex}`);
501
+ return res.status(400).json({ error: 'Неверная позиция' });
502
+ }
503
+
504
+ if (currentBotIndex === newIndex) {
492
505
  console.log(`[API] Позиция не изменилась для бота ${botId}`);
493
506
  return res.json({ success: true, message: 'Позиция не изменилась' });
494
507
  }
508
+ const reorderedBots = [...allBots];
509
+ const [movedBot] = reorderedBots.splice(currentBotIndex, 1);
510
+ reorderedBots.splice(newIndex, 0, movedBot);
495
511
 
496
- if (newPosition > currentPosition) {
497
- console.log(`[API] Перемещаем бота ${botId} вниз с позиции ${currentPosition} на ${newPosition}`);
498
- const updateResult = await prisma.bot.updateMany({
499
- where: {
500
- sortOrder: {
501
- gt: currentPosition,
502
- lte: newPosition
503
- }
504
- },
505
- data: {
506
- sortOrder: {
507
- decrement: 1
508
- }
509
- }
510
- });
511
- console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вниз`);
512
- } else {
513
- console.log(`[API] Перемещаем бота ${botId} вверх с позиции ${currentPosition} на ${newPosition}`);
514
- const updateResult = await prisma.bot.updateMany({
515
- where: {
516
- sortOrder: {
517
- gte: newPosition,
518
- lt: currentPosition
519
- }
520
- },
521
- data: {
522
- sortOrder: {
523
- increment: 1
524
- }
525
- }
526
- });
527
- console.log(`[API] Обновлено ${updateResult.count} ботов при перемещении вверх`);
512
+ console.log(`[API] Обновляем порядок для всех ботов`);
513
+ for (let i = 0; i < reorderedBots.length; i++) {
514
+ const bot = reorderedBots[i];
515
+ const newSortOrder = i + 1; // 1-based позиции
516
+
517
+ if (bot.sortOrder !== newSortOrder) {
518
+ await prisma.bot.update({
519
+ where: { id: bot.id },
520
+ data: { sortOrder: newSortOrder }
521
+ });
522
+ console.log(`[API] Обновлен бот ${bot.id}: sortOrder ${bot.sortOrder} -> ${newSortOrder}`);
523
+ }
528
524
  }
529
525
 
530
- await prisma.bot.update({
531
- where: { id: botId },
532
- data: { sortOrder: newPosition }
533
- });
534
-
535
- console.log(`[API] Успешно обновлен порядок бота ${botId} на позицию ${newPosition}`);
526
+ console.log(`[API] Успешно обновлен порядок бота ${botId}`);
536
527
  res.json({ success: true, message: 'Порядок ботов обновлен' });
537
528
  } catch (error) {
538
529
  console.error("[API Error] /bots sort-order PUT:", error);
@@ -588,6 +588,10 @@ router.post('/:pluginName/create-pr', resolvePluginPath, async (req, res) => {
588
588
  if (!branch) {
589
589
  return res.status(400).json({ error: 'Название ветки обязательно.' });
590
590
  }
591
+ // Validate branch name: only allow letters, numbers, dashes, underscores, dots, slashes
592
+ if (!branch.match(/^[\w\-.\/]+$/)) {
593
+ return res.status(400).json({ error: 'Некорректное имя ветки.' });
594
+ }
591
595
 
592
596
  try {
593
597
  cp.execSync('git --version');
@@ -675,10 +679,10 @@ router.post('/:pluginName/create-pr', resolvePluginPath, async (req, res) => {
675
679
 
676
680
 
677
681
  if (branchExists) {
678
- cp.execSync(`git push origin ${branch} --force`);
682
+ cp.execFileSync('git', ['push', 'origin', branch, '--force']);
679
683
  console.log(`[Plugin IDE] Ветка ${branch} обновлена`);
680
684
  } else {
681
- cp.execSync(`git push -u origin ${branch}`);
685
+ cp.execFileSync('git', ['push', '-u', 'origin', branch]);
682
686
  console.log(`[Plugin IDE] Новая ветка ${branch} создана`);
683
687
  }
684
688
 
@@ -52,6 +52,7 @@ class BotManager {
52
52
  this.playerListCache = new Map();
53
53
  this.eventGraphManager = null;
54
54
  this.uiSubscriptions = new Map();
55
+ this.crashCounters = new Map();
55
56
 
56
57
  getInstanceId();
57
58
  setInterval(() => this.updateAllResourceUsage(), 5000);
@@ -390,11 +391,16 @@ class BotManager {
390
391
  if (decryptedConfig.proxyPassword) decryptedConfig.proxyPassword = decrypt(decryptedConfig.proxyPassword);
391
392
 
392
393
  if (decryptedConfig.proxyUsername) decryptedConfig.proxyUsername = decryptedConfig.proxyUsername.trim();
393
- if (decryptedConfig.proxyPassword) decryptedConfig.proxyPassword = decryptedConfig.proxyPassword.trim();
394
394
 
395
395
  const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
396
396
  const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
397
- const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
397
+ const child = fork(botProcessPath, [], {
398
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
399
+ env: {
400
+ ...process.env,
401
+ NODE_PATH: path.resolve(__dirname, '../../../node_modules')
402
+ }
403
+ });
398
404
 
399
405
  child.botConfig = botConfig;
400
406
 
@@ -441,6 +447,7 @@ class BotManager {
441
447
  break;
442
448
  case 'bot_ready':
443
449
  this.emitStatusUpdate(botId, 'running', 'Бот успешно подключился к серверу.');
450
+ this.crashCounters.delete(botId);
444
451
  break;
445
452
  case 'validate_and_run_command':
446
453
  await this.handleCommandValidation(botConfig, message);
@@ -528,13 +535,35 @@ class BotManager {
528
535
  this.bots.delete(botId);
529
536
  this.resourceUsage.delete(botId);
530
537
  this.botConfigs.delete(botId);
531
-
538
+
532
539
  this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
533
540
  this.updateAllResourceUsage();
534
541
 
535
542
  if (code === 1) {
536
- console.log(`[BotManager] Обнаружена ошибка с кодом 1 для бота ${botId}. Попытка перезапуска через 5 секунд...`);
537
- this.appendLog(botId, `[SYSTEM] Обнаружена критическая ошибка, перезапуск через 5 секунд...`);
543
+ const MAX_RESTART_ATTEMPTS = 5;
544
+ const RESTART_COOLDOWN = 60000;
545
+
546
+ const counter = this.crashCounters.get(botId) || { count: 0, firstCrash: Date.now() };
547
+ const timeSinceFirstCrash = Date.now() - counter.firstCrash;
548
+
549
+ if (timeSinceFirstCrash > RESTART_COOLDOWN) {
550
+ counter.count = 0;
551
+ counter.firstCrash = Date.now();
552
+ }
553
+
554
+ counter.count++;
555
+ this.crashCounters.set(botId, counter);
556
+
557
+ if (counter.count >= MAX_RESTART_ATTEMPTS) {
558
+ console.log(`[BotManager] Бот ${botId} упал ${counter.count} раз подряд. Автоперезапуск остановлен.`);
559
+ this.appendLog(botId, `[SYSTEM] ❌ Обнаружено ${counter.count} критических ошибок подряд.`);
560
+ this.appendLog(botId, `[SYSTEM] 💡 Исправьте проблему и запустите бота вручную.`);
561
+ this.crashCounters.delete(botId);
562
+ return;
563
+ }
564
+
565
+ console.log(`[BotManager] Обнаружена ошибка с кодом 1 для бота ${botId}. Попытка ${counter.count}/${MAX_RESTART_ATTEMPTS}. Перезапуск через 5 секунд...`);
566
+ this.appendLog(botId, `[SYSTEM] Обнаружена критическая ошибка, перезапуск через 5 секунд... (попытка ${counter.count}/${MAX_RESTART_ATTEMPTS})`);
538
567
  setTimeout(() => {
539
568
  console.log(`[BotManager] Перезапуск бота ${botId}...`);
540
569
  this.startBot(botConfig);
@@ -187,9 +187,8 @@ process.on('message', async (message) => {
187
187
  if (config.proxyHost && config.proxyPort) {
188
188
  sendLog(`[System] Используется прокси: ${config.proxyHost}:${config.proxyPort}`);
189
189
 
190
- // Очищаем пароль от лишних символов
191
- const cleanProxyPassword = config.proxyPassword ? config.proxyPassword.trim() : null;
192
190
  const cleanProxyUsername = config.proxyUsername ? config.proxyUsername.trim() : null;
191
+ const cleanProxyPassword = config.proxyPassword || null;
193
192
 
194
193
  botOptions.connect = (client) => {
195
194
  SocksClient.createConnection({
@@ -129,9 +129,20 @@ class GraphExecutionEngine {
129
129
  break;
130
130
  }
131
131
  case 'action:send_message': {
132
- const message = String(await this.resolvePinValue(node, 'message', ''));
132
+ let message = String(await this.resolvePinValue(node, 'message', ''));
133
133
  const chatType = await this.resolvePinValue(node, 'chat_type', this.context.typeChat);
134
134
  const recipient = await this.resolvePinValue(node, 'recipient', this.context.user?.username);
135
+
136
+ // Парсим и заменяем переменные в формате {varName}
137
+ const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
138
+ const matches = [...message.matchAll(variablePattern)];
139
+
140
+ for (const match of matches) {
141
+ const varName = match[1];
142
+ const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
143
+ message = message.replace(match[0], String(varValue));
144
+ }
145
+
135
146
  this.context.bot.sendMessage(chatType, message, recipient);
136
147
  await this.traverse(node, 'exec');
137
148
  break;
@@ -299,8 +299,9 @@ class NodeRegistry {
299
299
  type: 'action:send_message',
300
300
  label: '🗣️ Отправить сообщение',
301
301
  category: 'Действия',
302
- description: 'Отправляет сообщение в чат',
302
+ description: 'Отправляет сообщение в чат. Поддерживает переменные в формате {varName}',
303
303
  graphType: all,
304
+ dynamicPins: true,
304
305
  pins: {
305
306
  inputs: [
306
307
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },