blockmine 1.25.0 → 1.27.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.
Files changed (164) hide show
  1. package/CHANGELOG.md +46 -3
  2. package/backend/cli.js +1 -1
  3. package/backend/package.json +2 -2
  4. package/backend/prisma/migrations/20260328173000_add_plugin_source_ref/migration.sql +2 -0
  5. package/backend/prisma/migrations/migration_lock.toml +2 -2
  6. package/backend/prisma/schema.prisma +2 -0
  7. package/backend/src/api/routes/apiKeys.js +8 -0
  8. package/backend/src/api/routes/bots.js +258 -9
  9. package/backend/src/api/routes/eventGraphs.js +151 -1
  10. package/backend/src/api/routes/health.js +38 -0
  11. package/backend/src/api/routes/nodeRegistry.js +63 -0
  12. package/backend/src/api/routes/plugins.js +254 -29
  13. package/backend/src/container.js +11 -8
  14. package/backend/src/core/BotCommandLoader.js +161 -0
  15. package/backend/src/core/BotConnection.js +125 -0
  16. package/backend/src/core/BotEventHandlers.js +234 -0
  17. package/backend/src/core/BotIPCHandler.js +445 -0
  18. package/backend/src/core/BotManager.js +15 -7
  19. package/backend/src/core/BotProcess.js +75 -142
  20. package/backend/src/core/EventGraphManager.js +7 -3
  21. package/backend/src/core/GraphDebugHandler.js +229 -0
  22. package/backend/src/core/GraphDebugIPC.js +117 -0
  23. package/backend/src/core/GraphExecutionEngine.js +545 -978
  24. package/backend/src/core/GraphTraversal.js +80 -0
  25. package/backend/src/core/GraphValidation.js +73 -0
  26. package/backend/src/core/NodeDefinition.js +138 -0
  27. package/backend/src/core/NodeRegistry.js +153 -141
  28. package/backend/src/core/PluginManager.js +272 -31
  29. package/backend/src/core/RewindSignal.js +9 -0
  30. package/backend/src/core/config/ConfigValidator.js +72 -0
  31. package/backend/src/core/config/FeatureFlags.js +52 -0
  32. package/backend/src/core/config/__tests__/ConfigValidator.test.js +232 -0
  33. package/backend/src/core/domain/entities/Bot.js +39 -0
  34. package/backend/src/core/domain/entities/Command.js +41 -0
  35. package/backend/src/core/domain/entities/EventGraph.js +39 -0
  36. package/backend/src/core/domain/entities/Plugin.js +45 -0
  37. package/backend/src/core/domain/entities/User.js +40 -0
  38. package/backend/src/core/domain/services/DependencyResolver.js +168 -0
  39. package/backend/src/core/domain/services/GraphValidator.js +117 -0
  40. package/backend/src/core/domain/services/PermissionChecker.js +34 -0
  41. package/backend/src/core/domain/services/__tests__/DependencyResolver.test.js +126 -0
  42. package/backend/src/core/domain/valueObjects/BotConfig.js +27 -0
  43. package/backend/src/core/domain/valueObjects/DependencyGraph.js +86 -0
  44. package/backend/src/core/domain/valueObjects/PluginManifest.js +36 -0
  45. package/backend/src/core/errors/BaseError.js +29 -0
  46. package/backend/src/core/errors/ErrorHandler.js +81 -0
  47. package/backend/src/core/errors/__tests__/ErrorHandler.test.js +188 -0
  48. package/backend/src/core/errors/index.js +68 -0
  49. package/backend/src/core/infrastructure/BatchingUtility.js +66 -0
  50. package/backend/src/core/infrastructure/CircuitBreaker.js +103 -0
  51. package/backend/src/core/infrastructure/ConnectionPool.js +81 -0
  52. package/backend/src/core/infrastructure/RateLimiter.js +64 -0
  53. package/backend/src/core/infrastructure/__tests__/BatchingUtility.test.js +86 -0
  54. package/backend/src/core/infrastructure/__tests__/CircuitBreaker.test.js +156 -0
  55. package/backend/src/core/infrastructure/__tests__/ConnectionPool.test.js +146 -0
  56. package/backend/src/core/infrastructure/__tests__/RateLimiter.test.js +171 -0
  57. package/backend/src/core/ipc/botApiFactory.js +72 -0
  58. package/backend/src/core/ipc/ipcMessageTypes.js +115 -0
  59. package/backend/src/core/logging/AuditLogger.js +61 -0
  60. package/backend/src/core/logging/StructuredLogger.js +80 -0
  61. package/backend/src/core/logging/__tests__/StructuredLogger.test.js +213 -0
  62. package/backend/src/core/logging/index.js +7 -0
  63. package/backend/src/core/metrics/MetricsCollector.js +104 -0
  64. package/backend/src/core/metrics/__tests__/MetricsCollector.test.js +131 -0
  65. package/backend/src/core/node-registries/actionsNodes.js +191 -0
  66. package/backend/src/core/node-registries/arraysNodes.js +152 -0
  67. package/backend/src/core/node-registries/botNodes.js +48 -0
  68. package/backend/src/core/node-registries/containerNodes.js +141 -0
  69. package/backend/src/core/node-registries/dataNodes.js +284 -0
  70. package/backend/src/core/node-registries/debugNodes.js +23 -0
  71. package/backend/src/core/node-registries/eventsNodes.js +223 -0
  72. package/backend/src/core/node-registries/flowNodes.js +151 -0
  73. package/backend/src/core/node-registries/furnaceNodes.js +123 -0
  74. package/backend/src/core/node-registries/index.js +108 -0
  75. package/backend/src/core/node-registries/inventory.js +102 -106
  76. package/backend/src/core/node-registries/logicNodes.js +54 -0
  77. package/backend/src/core/node-registries/mathNodes.js +38 -0
  78. package/backend/src/core/node-registries/navigationNodes.js +109 -0
  79. package/backend/src/core/node-registries/objectsNodes.js +90 -0
  80. package/backend/src/core/node-registries/stringsNodes.js +165 -0
  81. package/backend/src/core/node-registries/timeNodes.js +105 -0
  82. package/backend/src/core/node-registries/typeNodes.js +22 -0
  83. package/backend/src/core/node-registries/usersNodes.js +126 -0
  84. package/backend/src/core/nodes/arrays/shuffle.js +14 -0
  85. package/backend/src/core/nodes/bot/get_name.js +8 -0
  86. package/backend/src/core/nodes/bot/stop_bot.js +5 -0
  87. package/backend/src/core/nodes/container/open.js +101 -111
  88. package/backend/src/core/nodes/data/store_read.js +26 -0
  89. package/backend/src/core/nodes/data/store_write.js +23 -0
  90. package/backend/src/core/nodes/event/call_event.js +31 -0
  91. package/backend/src/core/nodes/event/custom_event.js +8 -0
  92. package/backend/src/core/nodes/flow/timer.js +35 -0
  93. package/backend/src/core/nodes/inventory/drop.js +73 -65
  94. package/backend/src/core/nodes/inventory/equip.js +54 -45
  95. package/backend/src/core/nodes/inventory/select_slot.js +48 -46
  96. package/backend/src/core/nodes/navigation/follow.js +54 -51
  97. package/backend/src/core/nodes/navigation/go_to.js +41 -53
  98. package/backend/src/core/nodes/navigation/go_to_entity.js +65 -69
  99. package/backend/src/core/nodes/navigation/go_to_player.js +65 -70
  100. package/backend/src/core/nodes/navigation/stop.js +17 -26
  101. package/backend/src/core/nodes/users/add_to_group.js +24 -0
  102. package/backend/src/core/nodes/users/check_permission.js +26 -0
  103. package/backend/src/core/nodes/users/remove_from_group.js +24 -0
  104. package/backend/src/core/services/BotIPCMessageRouter.js +337 -0
  105. package/backend/src/core/services/BotLifecycleService.js +41 -632
  106. package/backend/src/core/services/CacheManager.js +83 -23
  107. package/backend/src/core/services/CrashRestartManager.js +42 -0
  108. package/backend/src/core/services/DebugSessionManager.js +114 -12
  109. package/backend/src/core/services/EventGraphService.js +69 -0
  110. package/backend/src/core/services/MinecraftBotManager.js +9 -1
  111. package/backend/src/core/services/PluginManagementService.js +84 -0
  112. package/backend/src/core/services/TestModeContext.js +65 -0
  113. package/backend/src/core/services/__tests__/CacheManager.test.js +168 -0
  114. package/backend/src/core/services.js +1 -11
  115. package/backend/src/core/validation/InputValidator.js +167 -0
  116. package/backend/src/core/validation/__tests__/InputValidator.test.js +296 -0
  117. package/backend/src/real-time/botApi/index.js +1 -1
  118. package/backend/src/real-time/socketHandler.js +26 -0
  119. package/backend/src/server.js +10 -5
  120. package/frontend/dist/assets/{browser-ponyfill-DN7pwmHT.js → browser-ponyfill-D8y0Ty7C.js} +1 -1
  121. package/frontend/dist/assets/index-CFJLS0dk.css +32 -0
  122. package/frontend/dist/assets/{index-LSy71uwm.js → index-D91UGNMG.js} +1880 -1881
  123. package/frontend/dist/index.html +2 -2
  124. package/frontend/dist/locales/en/bots.json +4 -1
  125. package/frontend/dist/locales/en/common.json +7 -1
  126. package/frontend/dist/locales/en/login.json +2 -0
  127. package/frontend/dist/locales/en/management.json +79 -1
  128. package/frontend/dist/locales/en/nodes.json +59 -4
  129. package/frontend/dist/locales/en/plugin-detail.json +24 -4
  130. package/frontend/dist/locales/en/plugins.json +226 -7
  131. package/frontend/dist/locales/en/setup.json +2 -0
  132. package/frontend/dist/locales/en/sidebar.json +171 -3
  133. package/frontend/dist/locales/en/visual-editor.json +230 -31
  134. package/frontend/dist/locales/ru/bots.json +4 -1
  135. package/frontend/dist/locales/ru/login.json +2 -0
  136. package/frontend/dist/locales/ru/management.json +79 -1
  137. package/frontend/dist/locales/ru/minecraft-viewer.json +3 -0
  138. package/frontend/dist/locales/ru/nodes.json +105 -51
  139. package/frontend/dist/locales/ru/plugins.json +103 -4
  140. package/frontend/dist/locales/ru/setup.json +2 -0
  141. package/frontend/dist/locales/ru/sidebar.json +171 -3
  142. package/frontend/dist/locales/ru/visual-editor.json +232 -33
  143. package/frontend/package.json +2 -0
  144. package/nul +12 -0
  145. package/package.json +3 -3
  146. package/backend/package-lock.json +0 -6801
  147. package/backend/src/core/node-registries/actions.js +0 -202
  148. package/backend/src/core/node-registries/arrays.js +0 -155
  149. package/backend/src/core/node-registries/bot.js +0 -23
  150. package/backend/src/core/node-registries/container.js +0 -162
  151. package/backend/src/core/node-registries/data.js +0 -290
  152. package/backend/src/core/node-registries/debug.js +0 -26
  153. package/backend/src/core/node-registries/events.js +0 -201
  154. package/backend/src/core/node-registries/flow.js +0 -139
  155. package/backend/src/core/node-registries/furnace.js +0 -143
  156. package/backend/src/core/node-registries/logic.js +0 -62
  157. package/backend/src/core/node-registries/math.js +0 -42
  158. package/backend/src/core/node-registries/navigation.js +0 -111
  159. package/backend/src/core/node-registries/objects.js +0 -98
  160. package/backend/src/core/node-registries/strings.js +0 -187
  161. package/backend/src/core/node-registries/time.js +0 -113
  162. package/backend/src/core/node-registries/type.js +0 -25
  163. package/backend/src/core/node-registries/users.js +0 -79
  164. package/frontend/dist/assets/index-SfhKxI4-.css +0 -32
@@ -1,70 +1,65 @@
1
- const { goals } = require('mineflayer-pathfinder');
2
-
3
- /**
4
- * navigation:go_to_player - Идти к игроку
5
- */
6
- async function evaluate(node, pinId, context, helpers) {
7
- const bot = context.bot;
8
-
9
- if (pinId === 'exec' || pinId === 'exec_failed') {
10
- return true;
11
- }
12
-
13
- if (!bot?.pathfinder) {
14
- if (pinId === 'success') return false;
15
- if (pinId === 'playerPosition') return null;
16
- return null;
17
- }
18
-
19
- const playerName = await helpers.resolvePinValue(node, 'playerName');
20
- const range = (await helpers.resolvePinValue(node, 'range')) || 2;
21
-
22
- if (!playerName) {
23
- if (pinId === 'success') return false;
24
- if (pinId === 'playerPosition') return null;
25
- context._nextExecPin = 'exec_failed';
26
- return null;
27
- }
28
-
29
- try {
30
- const player = bot.players[playerName];
31
-
32
- if (!player?.entity) {
33
- console.error(`[navigation:go_to_player] Player "${playerName}" not found or not visible`);
34
- if (pinId === 'success') return false;
35
- if (pinId === 'playerPosition') return null;
36
- context._nextExecPin = 'exec_failed';
37
- return null;
38
- }
39
-
40
- const playerPos = player.entity.position;
41
-
42
- if (pinId === 'playerPosition') {
43
- return {
44
- x: playerPos.x,
45
- y: playerPos.y,
46
- z: playerPos.z
47
- };
48
- }
49
-
50
- const goal = new goals.GoalNear(playerPos.x, playerPos.y, playerPos.z, range);
51
- await bot.pathfinder.goto(goal);
52
-
53
- if (pinId === 'success') {
54
- return true;
55
- }
56
-
57
- context._nextExecPin = 'exec';
58
- } catch (error) {
59
- console.error('[navigation:go_to_player] Error:', error.message);
60
-
61
- if (pinId === 'success') return false;
62
- if (pinId === 'playerPosition') return null;
63
-
64
- context._nextExecPin = 'exec_failed';
65
- }
66
-
67
- return null;
68
- }
69
-
70
- module.exports = { evaluate };
1
+ const { goals } = require('mineflayer-pathfinder');
2
+
3
+ async function execute(node, context, helpers) {
4
+ const { resolvePinValue, traverse, memo } = helpers;
5
+ const bot = context.bot;
6
+
7
+ if (!bot?.pathfinder) {
8
+ memo.set(`${node.id}:success`, false);
9
+ await traverse(node, 'exec_failed');
10
+ return;
11
+ }
12
+
13
+ const playerName = await resolvePinValue(node, 'playerName', node.data?.playerName);
14
+ const range = await resolvePinValue(node, 'range', node.data?.range) ?? 3;
15
+
16
+ if (!playerName) {
17
+ memo.set(`${node.id}:success`, false);
18
+ await traverse(node, 'exec_failed');
19
+ return;
20
+ }
21
+
22
+ try {
23
+ const player = bot.players[playerName];
24
+
25
+ if (!player?.entity) {
26
+ console.error(`[navigation:go_to_player] Player "${playerName}" not found or not visible`);
27
+ memo.set(`${node.id}:success`, false);
28
+ await traverse(node, 'exec_failed');
29
+ return;
30
+ }
31
+
32
+ const playerPos = player.entity.position;
33
+
34
+ memo.set(`${node.id}:playerPosition`, {
35
+ x: playerPos.x,
36
+ y: playerPos.y,
37
+ z: playerPos.z
38
+ });
39
+
40
+ const goal = new goals.GoalNear(playerPos.x, playerPos.y, playerPos.z, Number(range));
41
+ await bot.pathfinder.goto(goal);
42
+
43
+ memo.set(`${node.id}:success`, true);
44
+ await traverse(node, 'exec');
45
+ } catch (error) {
46
+ console.error('[navigation:go_to_player] Error:', error.message);
47
+ memo.set(`${node.id}:success`, false);
48
+ await traverse(node, 'exec_failed');
49
+ }
50
+ }
51
+
52
+ async function evaluate(node, pinId, context, helpers) {
53
+ const { memo } = helpers;
54
+
55
+ if (pinId === 'success') {
56
+ return memo.get(`${node.id}:success`) ?? false;
57
+ }
58
+ if (pinId === 'playerPosition') {
59
+ return memo.get(`${node.id}:playerPosition`) ?? null;
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ module.exports = { execute, evaluate };
@@ -1,26 +1,17 @@
1
- /**
2
- * navigation:stop - Остановить движение бота
3
- */
4
- async function evaluate(node, pinId, context, helpers) {
5
- const bot = context.bot;
6
-
7
- if (pinId === 'exec') {
8
- return true;
9
- }
10
-
11
- if (!bot?.pathfinder) {
12
- return null;
13
- }
14
-
15
- try {
16
- bot.pathfinder.setGoal(null);
17
-
18
- bot.clearControlStates();
19
- } catch (error) {
20
- console.error('[navigation:stop] Error:', error.message);
21
- }
22
-
23
- return null;
24
- }
25
-
26
- module.exports = { evaluate };
1
+ async function execute(node, context, helpers) {
2
+ const { traverse } = helpers;
3
+ const bot = context.bot;
4
+
5
+ try {
6
+ if (bot?.pathfinder) {
7
+ bot.pathfinder.setGoal(null);
8
+ bot.clearControlStates();
9
+ }
10
+ await traverse(node, 'exec');
11
+ } catch (error) {
12
+ console.error('[navigation:stop] Error:', error.message);
13
+ await traverse(node, 'exec');
14
+ }
15
+ }
16
+
17
+ module.exports = { execute };
@@ -0,0 +1,24 @@
1
+ const User = require('../../UserService');
2
+
3
+ async function execute(node, context, helpers) {
4
+ const { resolvePinValue, traverse } = helpers;
5
+
6
+ const userIdentifier = await resolvePinValue(node, 'user', null);
7
+ const groupName = await resolvePinValue(node, 'group', node.data?.group || '');
8
+
9
+ let username = null;
10
+ if (typeof userIdentifier === 'string') username = userIdentifier;
11
+ else if (userIdentifier?.username) username = userIdentifier.username;
12
+
13
+ if (username && groupName) {
14
+ const user = await User.getUser(username, context.botId);
15
+ if (user) {
16
+ await user.addGroup(groupName);
17
+ User.clearCache(username, context.botId);
18
+ }
19
+ }
20
+
21
+ await traverse(node, 'exec');
22
+ }
23
+
24
+ module.exports = { execute };
@@ -0,0 +1,26 @@
1
+ const User = require('../../UserService');
2
+
3
+ async function evaluate(node, pinId, context, helpers) {
4
+ if (pinId !== 'has_permission') return null;
5
+ const { resolvePinValue } = helpers;
6
+
7
+ const userIdentifier = await resolvePinValue(node, 'user', null);
8
+ const permission = await resolvePinValue(node, 'permission', node.data?.permission || '');
9
+
10
+ let username = null;
11
+ if (typeof userIdentifier === 'string') username = userIdentifier;
12
+ else if (userIdentifier?.username) username = userIdentifier.username;
13
+
14
+ if (!username || !permission) return false;
15
+
16
+ const user = await User.getUser(username, context.botId);
17
+ if (!user) return false;
18
+
19
+ if (user.permissionsSet) {
20
+ return user.permissionsSet.has('*') || user.permissionsSet.has(permission);
21
+ }
22
+
23
+ return false;
24
+ }
25
+
26
+ module.exports = { evaluate };
@@ -0,0 +1,24 @@
1
+ const User = require('../../UserService');
2
+
3
+ async function execute(node, context, helpers) {
4
+ const { resolvePinValue, traverse } = helpers;
5
+
6
+ const userIdentifier = await resolvePinValue(node, 'user', null);
7
+ const groupName = await resolvePinValue(node, 'group', node.data?.group || '');
8
+
9
+ let username = null;
10
+ if (typeof userIdentifier === 'string') username = userIdentifier;
11
+ else if (userIdentifier?.username) username = userIdentifier.username;
12
+
13
+ if (username && groupName) {
14
+ const user = await User.getUser(username, context.botId);
15
+ if (user) {
16
+ await user.removeGroup(groupName);
17
+ User.clearCache(username, context.botId);
18
+ }
19
+ }
20
+
21
+ await traverse(node, 'exec');
22
+ }
23
+
24
+ module.exports = { execute };
@@ -0,0 +1,337 @@
1
+ class BotIPCMessageRouter {
2
+ constructor(deps) {
3
+ this.eventGraphManager = deps.eventGraphManager;
4
+ this.commandExecutionService = deps.commandExecutionService;
5
+ this.processManager = deps.processManager;
6
+ this.logger = deps.logger;
7
+ this.crashRestartManager = deps.crashRestartManager;
8
+ this.appendLog = deps.appendLog;
9
+ this.emitStatusUpdate = deps.emitStatusUpdate;
10
+ this.restartBot = deps.restartBot;
11
+ this.stopBot = deps.stopBot;
12
+ this.getBotConfig = deps.getBotConfig;
13
+ }
14
+
15
+ attachToChild(child, botConfig) {
16
+ const botId = botConfig.id;
17
+
18
+ child.on('message', async (message) => {
19
+ try {
20
+ await this._routeMessage(botId, child, message);
21
+ } catch (error) {
22
+ this.appendLog(botId, `[SYSTEM-ERROR] Критическая ошибка в обработчике: ${error.stack}`);
23
+ this.logger.error({ botId, error }, 'Критическая ошибка в обработчике сообщений');
24
+ }
25
+ });
26
+
27
+ child.on('error', (err) => this.appendLog(botId, `[PROCESS FATAL] ${err.stack}`));
28
+ child.stdout.on('data', (data) => console.log(data.toString()));
29
+ child.stderr.on('data', (data) => this.appendLog(botId, `[STDERR] ${data.toString()}`));
30
+
31
+ child.on('exit', (code, signal) => {
32
+ this._handleExit(botId, botConfig, code, signal);
33
+ });
34
+ }
35
+
36
+ async _routeMessage(botId, child, message) {
37
+ const handlers = {
38
+ 'event': () => this._handleEvent(botId, message),
39
+ 'plugin:data': () => this._handlePluginData(botId, message),
40
+ 'send_websocket_message': () => this._handleWebSocketMessage(message),
41
+ 'log': () => this._handleLog(botId, message),
42
+ 'plugin-log': () => this._handlePluginLog(message),
43
+ 'status': () => this._handleStatus(botId, message),
44
+ 'bot_ready': () => this._handleBotReady(botId),
45
+ 'validate_and_run_command': () => this._handleCommandValidation(botId, child, message),
46
+ 'request_user_action': () => this._handleUserAction(botId, child, message),
47
+ 'get_player_list_response': () => this._handlePlayerListResponse(message),
48
+ 'get_nearby_entities_response': () => this._handleNearbyEntitiesResponse(message),
49
+ 'execute_command_response': () => this._handleCommandResponse(message),
50
+ 'register_command': () => this._handleRegisterCommand(botId, message),
51
+ 'register_permissions': () => this._handlePermissions(botId, message),
52
+ 'register_group': () => this._handleGroup(botId, message),
53
+ 'add_permissions_to_group': () => this._handleAddPermissions(botId, message),
54
+ 'trace:completed': () => this._handleTrace(botId, message),
55
+ 'debug:check_breakpoint': () => this._handleBreakpoint(botId, child, message),
56
+ 'debug:check_step_mode': () => this._handleStepMode(botId, child, message),
57
+ 'update_credentials': () => this._handleUpdateCredentials(botId, child, message),
58
+ 'restart_bot': () => this._handleRestartBot(botId, child, message),
59
+ 'stop': () => this._handleStopBot(botId),
60
+ 'change_credentials': () => this._handleChangeCredentials(botId, child, message),
61
+ };
62
+
63
+ const handler = handlers[message.type];
64
+ if (handler) {
65
+ await handler();
66
+ }
67
+ }
68
+
69
+ _handleEvent(botId, message) {
70
+ if (message.eventType === 'raw_message') {
71
+ try {
72
+ const { getIOSafe } = require('../../real-time/socketHandler');
73
+ const { broadcastToApiClients } = require('../../real-time/botApi');
74
+ broadcastToApiClients(getIOSafe(), botId, 'chat:raw_message', {
75
+ raw_message: message.args.rawText || message.args.raw_message,
76
+ json: message.args.json
77
+ });
78
+ } catch (e) {}
79
+ }
80
+
81
+ try {
82
+ const { broadcastToPanelNamespace } = require('../../real-time/panelNamespace');
83
+ broadcastToPanelNamespace(botId, 'bot:event', {
84
+ botId,
85
+ eventType: message.eventType,
86
+ data: message.args || {},
87
+ timestamp: new Date().toISOString()
88
+ });
89
+ } catch (e) {}
90
+
91
+ if (this.eventGraphManager) {
92
+ this.eventGraphManager.handleEvent(botId, message.eventType, message.args);
93
+ }
94
+ }
95
+
96
+ _handlePluginData(botId, message) {
97
+ const { plugin: pluginName, payload } = message;
98
+ const pluginSubscribers = this.processManager.getPluginSubscribers(botId, pluginName);
99
+ if (pluginSubscribers && pluginSubscribers.size > 0) {
100
+ pluginSubscribers.forEach(socket => socket.emit('plugin:ui:dataUpdate', payload));
101
+ }
102
+ }
103
+
104
+ _handlePluginLog(logData) {
105
+ const { getIOSafe, addPluginLogToBuffer } = require('../../real-time/socketHandler');
106
+ const { botId, pluginName } = logData;
107
+ addPluginLogToBuffer(botId, pluginName, logData);
108
+ const io = getIOSafe();
109
+ if (io) {
110
+ io.to(`plugin-logs:${botId}:${pluginName}`).emit('plugin-log', logData);
111
+ }
112
+ }
113
+
114
+ _handleWebSocketMessage(message) {
115
+ const { getIOSafe } = require('../../real-time/socketHandler');
116
+ const { botId, message: msg } = message.payload;
117
+ getIOSafe().to(`bot_${botId}`).emit('bot:message', { message: msg });
118
+ }
119
+
120
+ _handleLog(botId, message) {
121
+ this.appendLog(botId, message.content);
122
+ }
123
+
124
+ _handleStatus(botId, message) {
125
+ this.emitStatusUpdate(botId, message.status);
126
+ }
127
+
128
+ _handleBotReady(botId) {
129
+ this.crashRestartManager.resetCounter(botId);
130
+ this.emitStatusUpdate(botId, 'running', 'Бот успешно подключился к серверу.');
131
+
132
+ try {
133
+ const { getIOSafe } = require('../../real-time/socketHandler');
134
+ const { broadcastBotStatus } = require('../../real-time/botApi');
135
+ broadcastBotStatus(getIOSafe(), botId, true);
136
+ } catch (e) {}
137
+
138
+ if (this.eventGraphManager) {
139
+ this.eventGraphManager.handleEvent(botId, 'botStartup', {});
140
+ }
141
+ }
142
+
143
+ async _handleCommandValidation(botId, child, message) {
144
+ if (this.commandExecutionService) {
145
+ const botConfig = child.botConfig;
146
+ if (botConfig) {
147
+ await this.commandExecutionService.handleCommandValidation(botConfig, message);
148
+ }
149
+ }
150
+ }
151
+
152
+ async _handleUserAction(botId, child, message) {
153
+ const { requestId, payload } = message;
154
+ const { targetUsername, action, data } = payload;
155
+ const UserService = require('../../core/UserService');
156
+ const PermissionManager = require('../../core/PermissionManager');
157
+
158
+ try {
159
+ const botConfig = child.botConfig;
160
+ const user = await UserService.getUser(targetUsername, botId, botConfig);
161
+ if (!user) throw new Error(`Пользователь ${targetUsername} не найден.`);
162
+
163
+ let result;
164
+ switch (action) {
165
+ case 'addGroup': result = await user.addGroup(data.group); break;
166
+ case 'removeGroup': result = await user.removeGroup(data.group); break;
167
+ case 'getGroups': result = user.groups ? user.groups.map(g => g.group.name) : []; break;
168
+ case 'getPermissions': result = Array.from(user.permissionsSet); break;
169
+ case 'isBlacklisted': result = user.isBlacklisted; break;
170
+ case 'setBlacklisted': result = await user.setBlacklist(data.value); break;
171
+ default: throw new Error(`Неизвестное действие: ${action}`);
172
+ }
173
+
174
+ child.send({ type: 'user_action_response', requestId, payload: result });
175
+ } catch (error) {
176
+ this.logger.error({ botId, action, username: targetUsername, error }, 'Ошибка действия пользователя');
177
+ child.send({ type: 'user_action_response', requestId, error: error.message });
178
+ }
179
+ }
180
+
181
+ _handlePlayerListResponse(message) {
182
+ this.processManager.resolvePlayerListRequest(message.requestId, message.payload.players);
183
+ }
184
+
185
+ _handleNearbyEntitiesResponse(message) {
186
+ this.processManager.resolveNearbyEntitiesRequest(message.requestId, message.payload.entities);
187
+ }
188
+
189
+ _handleCommandResponse(message) {
190
+ this.processManager.resolveCommandRequest(message.requestId, message.result, message.error);
191
+ }
192
+
193
+ async _handleRegisterCommand(botId, message) {
194
+ if (this.commandExecutionService) {
195
+ await this.commandExecutionService.handleCommandRegistration(botId, message.commandConfig);
196
+ }
197
+ }
198
+
199
+ async _handlePermissions(botId, message) {
200
+ const PermissionManager = require('../../core/PermissionManager');
201
+ await PermissionManager.registerPermissions(botId, message.permissions);
202
+ }
203
+
204
+ async _handleGroup(botId, message) {
205
+ const PermissionManager = require('../../core/PermissionManager');
206
+ await PermissionManager.registerGroup(botId, message.groupConfig);
207
+ }
208
+
209
+ async _handleAddPermissions(botId, message) {
210
+ const PermissionManager = require('../../core/PermissionManager');
211
+ await PermissionManager.addPermissionsToGroup(botId, message.groupName, message.permissionNames);
212
+ }
213
+
214
+ async _handleTrace(botId, message) {
215
+ const { getTraceCollector } = require('./TraceCollectorService');
216
+ await getTraceCollector()._storeCompletedTrace(message.trace);
217
+ }
218
+
219
+ async _handleBreakpoint(botId, child, message) {
220
+ const { requestId, payload } = message;
221
+ const { getGlobalDebugManager } = require('./DebugSessionManager');
222
+ const debugManager = getGlobalDebugManager();
223
+ const debugState = debugManager.get(payload.graphId);
224
+
225
+ if (!debugState || !debugState.breakpoints.get(payload.nodeId)?.enabled) {
226
+ child.send({ type: 'debug:breakpoint_response', requestId, overrides: null });
227
+ return;
228
+ }
229
+
230
+ const bp = debugState.breakpoints.get(payload.nodeId);
231
+ bp.hitCount++;
232
+
233
+ const overrides = await debugState.pause({ ...payload, breakpoint: { condition: bp.condition, hitCount: bp.hitCount } });
234
+ child.send({ type: 'debug:breakpoint_response', requestId, overrides: overrides || null });
235
+ }
236
+
237
+ async _handleStepMode(botId, child, message) {
238
+ const { requestId, payload } = message;
239
+ const { getGlobalDebugManager } = require('./DebugSessionManager');
240
+ const debugManager = getGlobalDebugManager();
241
+ const debugState = debugManager.get(payload.graphId);
242
+
243
+ if (!debugState || !debugState.shouldStepPause(payload.nodeId)) {
244
+ child.send({ type: 'debug:breakpoint_response', requestId, overrides: null });
245
+ return;
246
+ }
247
+
248
+ const overrides = await debugState.pause(payload);
249
+ child.send({ type: 'debug:breakpoint_response', requestId, overrides: overrides || null });
250
+ }
251
+
252
+ async _handleUpdateCredentials(botId, child, message) {
253
+ const { requestId, payload } = message;
254
+ const { username, password } = payload;
255
+ const { encrypt } = require('../../core/utils/crypto');
256
+ const botRepo = require('../../repositories/BotRepository');
257
+
258
+ if (!username?.trim()) {
259
+ child.send({ type: 'credentials_operation_response', requestId, error: 'Empty username' });
260
+ return;
261
+ }
262
+
263
+ const data = { username };
264
+ if (password?.trim()) data.password = encrypt(password);
265
+
266
+ await botRepo.update(botId, data);
267
+
268
+ if (child.botConfig) {
269
+ child.botConfig.username = username;
270
+ if (data.password) child.botConfig.password = data.password;
271
+ }
272
+
273
+ this.appendLog(botId, `[API] Credentials обновлены: ${username}`);
274
+ child.send({ type: 'credentials_operation_response', requestId, payload: { success: true } });
275
+ }
276
+
277
+ async _handleStopBot(botId) {
278
+ if (this.stopBot) {
279
+ await this.stopBot(botId);
280
+ }
281
+ }
282
+
283
+ async _handleRestartBot(botId, child, message) {
284
+ const { requestId } = message;
285
+ child.send({ type: 'credentials_operation_response', requestId, payload: { success: true } });
286
+
287
+ setTimeout(() => {
288
+ const config = { ...child.botConfig };
289
+ this.restartBot(botId, config);
290
+ }, 3000);
291
+ }
292
+
293
+ async _handleChangeCredentials(botId, child, message) {
294
+ const { requestId, payload } = message;
295
+ const { username, password } = payload;
296
+ const { encrypt } = require('../../core/utils/crypto');
297
+ const botRepo = require('../../repositories/BotRepository');
298
+
299
+ if (!username?.trim()) {
300
+ child.send({ type: 'credentials_operation_response', requestId, error: 'Empty username' });
301
+ return;
302
+ }
303
+
304
+ const data = { username };
305
+ if (password?.trim()) data.password = encrypt(password);
306
+
307
+ await botRepo.update(botId, data);
308
+
309
+ if (child.botConfig) {
310
+ child.botConfig.username = username;
311
+ if (data.password) child.botConfig.password = data.password;
312
+ }
313
+
314
+ const config = { ...child.botConfig };
315
+ this.appendLog(botId, `[API] Credentials изменены: ${username}, рестарт...`);
316
+ child.send({ type: 'credentials_operation_response', requestId, payload: { success: true } });
317
+
318
+ setTimeout(() => this.restartBot(botId, config), 3000);
319
+ }
320
+
321
+ _handleExit(botId, botConfig, code, signal) {
322
+ this.processManager.remove(botId);
323
+ this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
324
+
325
+ try {
326
+ const { getIOSafe } = require('../../real-time/socketHandler');
327
+ const { broadcastBotStatus } = require('../../real-time/botApi');
328
+ broadcastBotStatus(getIOSafe(), botId, false);
329
+ } catch (e) {}
330
+
331
+ if (code === 1) {
332
+ this.crashRestartManager.handleCrash(botId, botConfig, (msg) => this.appendLog(botId, msg));
333
+ }
334
+ }
335
+ }
336
+
337
+ module.exports = BotIPCMessageRouter;