blockmine 1.6.3 → 1.13.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.
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.versionrc.json +17 -0
- package/CHANGELOG.md +36 -0
- package/README.md +1 -1
- package/backend/package.json +1 -0
- package/backend/prisma/migrations/20250718181335_add_plugin_data_store/migration.sql +14 -0
- package/backend/prisma/schema.prisma +17 -2
- package/backend/src/api/routes/auth.js +140 -0
- package/backend/src/api/routes/bots.js +176 -0
- package/backend/src/api/routes/changelog.js +16 -0
- package/backend/src/api/routes/eventGraphs.js +11 -1
- package/backend/src/api/routes/plugins.js +11 -0
- package/backend/src/core/BotManager.js +92 -40
- package/backend/src/core/BotProcess.js +44 -24
- package/backend/src/core/EventGraphManager.js +29 -5
- package/backend/src/core/GraphExecutionEngine.js +54 -12
- package/backend/src/core/MessageQueue.js +10 -1
- package/backend/src/core/NodeRegistry.js +2 -1
- package/backend/src/core/PluginLoader.js +72 -8
- package/backend/src/core/PluginManager.js +19 -0
- package/backend/src/plugins/PluginStore.js +87 -0
- package/backend/src/real-time/socketHandler.js +11 -3
- package/backend/src/server.js +2 -0
- package/backend/temp_migration.sql +0 -0
- package/commitlint.config.js +3 -0
- package/frontend/dist/assets/index-CHwi1QN9.js +8331 -0
- package/frontend/dist/assets/index-DhU2u6V0.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package.json +6 -0
- package/package.json +20 -4
- package/frontend/dist/assets/index-CIDmlKtb.js +0 -8203
- package/frontend/dist/assets/index-DF3i-W3m.css +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const { fork } = require('child_process');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { getIO } = require('../real-time/socketHandler');
|
|
4
3
|
const prisma = require('../lib/prisma');
|
|
5
4
|
const pidusage = require('pidusage');
|
|
6
5
|
const DependencyService = require('./DependencyService');
|
|
@@ -12,8 +11,6 @@ const crypto = require('crypto');
|
|
|
12
11
|
const { decrypt } = require('./utils/crypto');
|
|
13
12
|
const EventGraphManager = require('./EventGraphManager');
|
|
14
13
|
const nodeRegistry = require('./NodeRegistry');
|
|
15
|
-
const GraphExecutionEngine = require('./GraphExecutionEngine');
|
|
16
|
-
|
|
17
14
|
const UserService = require('./UserService');
|
|
18
15
|
|
|
19
16
|
const cooldowns = new Map();
|
|
@@ -53,8 +50,8 @@ class BotManager {
|
|
|
53
50
|
this.nodeRegistry = nodeRegistry;
|
|
54
51
|
this.pendingPlayerListRequests = new Map();
|
|
55
52
|
this.playerListCache = new Map();
|
|
56
|
-
this.graphEngine = new GraphExecutionEngine(this.nodeRegistry, this);
|
|
57
53
|
this.eventGraphManager = null;
|
|
54
|
+
this.uiSubscriptions = new Map();
|
|
58
55
|
|
|
59
56
|
getInstanceId();
|
|
60
57
|
setInterval(() => this.updateAllResourceUsage(), 5000);
|
|
@@ -69,6 +66,55 @@ class BotManager {
|
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
|
|
69
|
+
subscribeToPluginUi(botId, pluginName, socket) {
|
|
70
|
+
if (!this.uiSubscriptions.has(botId)) {
|
|
71
|
+
this.uiSubscriptions.set(botId, new Map());
|
|
72
|
+
}
|
|
73
|
+
const botSubscriptions = this.uiSubscriptions.get(botId);
|
|
74
|
+
|
|
75
|
+
if (!botSubscriptions.has(pluginName)) {
|
|
76
|
+
botSubscriptions.set(pluginName, new Set());
|
|
77
|
+
}
|
|
78
|
+
const pluginSubscribers = botSubscriptions.get(pluginName);
|
|
79
|
+
|
|
80
|
+
pluginSubscribers.add(socket);
|
|
81
|
+
console.log(`[UI Sub] Сокет ${socket.id} подписался на ${pluginName} для бота ${botId}. Всего подписчиков: ${pluginSubscribers.size}`);
|
|
82
|
+
|
|
83
|
+
const botProcess = this.bots.get(botId);
|
|
84
|
+
if (botProcess && !botProcess.killed) {
|
|
85
|
+
botProcess.send({ type: 'plugin:ui:start-updates', pluginName });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
unsubscribeFromPluginUi(botId, pluginName, socket) {
|
|
90
|
+
const botSubscriptions = this.uiSubscriptions.get(botId);
|
|
91
|
+
if (!botSubscriptions) return;
|
|
92
|
+
|
|
93
|
+
const pluginSubscribers = botSubscriptions.get(pluginName);
|
|
94
|
+
if (!pluginSubscribers) return;
|
|
95
|
+
|
|
96
|
+
pluginSubscribers.delete(socket);
|
|
97
|
+
console.log(`[UI Sub] Сокет ${socket.id} отписался от ${pluginName} для бота ${botId}. Осталось: ${pluginSubscribers.size}`);
|
|
98
|
+
|
|
99
|
+
if (pluginSubscribers.size === 0) {
|
|
100
|
+
const botProcess = this.bots.get(botId);
|
|
101
|
+
if (botProcess && !botProcess.killed) {
|
|
102
|
+
botProcess.send({ type: 'plugin:ui:stop-updates', pluginName });
|
|
103
|
+
}
|
|
104
|
+
botSubscriptions.delete(pluginName);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
handleSocketDisconnect(socket) {
|
|
109
|
+
this.uiSubscriptions.forEach((botSubscriptions, botId) => {
|
|
110
|
+
botSubscriptions.forEach((pluginSubscribers, pluginName) => {
|
|
111
|
+
if (pluginSubscribers.has(socket)) {
|
|
112
|
+
this.unsubscribeFromPluginUi(botId, pluginName, socket);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
72
118
|
async loadConfigForBot(botId) {
|
|
73
119
|
console.log(`[BotManager] Caching configuration for bot ID ${botId}...`);
|
|
74
120
|
try {
|
|
@@ -108,6 +154,7 @@ class BotManager {
|
|
|
108
154
|
}
|
|
109
155
|
|
|
110
156
|
reloadBotConfigInRealTime(botId) {
|
|
157
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
111
158
|
this.invalidateConfigCache(botId);
|
|
112
159
|
const child = this.bots.get(botId);
|
|
113
160
|
if (child && !child.killed) {
|
|
@@ -214,6 +261,7 @@ class BotManager {
|
|
|
214
261
|
}
|
|
215
262
|
|
|
216
263
|
async updateAllResourceUsage() {
|
|
264
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
217
265
|
if (this.bots.size === 0) {
|
|
218
266
|
if (this.resourceUsage.size > 0) {
|
|
219
267
|
this.resourceUsage.clear();
|
|
@@ -270,12 +318,13 @@ class BotManager {
|
|
|
270
318
|
}
|
|
271
319
|
|
|
272
320
|
emitStatusUpdate(botId, status, message = null) {
|
|
273
|
-
|
|
321
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
274
322
|
if (message) this.appendLog(botId, `[SYSTEM] ${message}`);
|
|
275
323
|
getIO().emit('bot:status', { botId, status, message });
|
|
276
324
|
}
|
|
277
325
|
|
|
278
326
|
appendLog(botId, logContent) {
|
|
327
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
279
328
|
const logEntry = {
|
|
280
329
|
id: Date.now() + Math.random(),
|
|
281
330
|
content: logContent,
|
|
@@ -333,6 +382,17 @@ class BotManager {
|
|
|
333
382
|
|
|
334
383
|
child.botConfig = botConfig;
|
|
335
384
|
|
|
385
|
+
child.api = {
|
|
386
|
+
sendMessage: (type, message, username) => {
|
|
387
|
+
if (!child.killed) {
|
|
388
|
+
child.send({ type: 'chat', payload: { message, chatType: type, username } });
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
sendLog: (message) => {
|
|
392
|
+
this.appendLog(botConfig.id, message);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
336
396
|
child.on('message', async (message) => {
|
|
337
397
|
const botId = botConfig.id;
|
|
338
398
|
try {
|
|
@@ -342,6 +402,21 @@ class BotManager {
|
|
|
342
402
|
this.eventGraphManager.handleEvent(botId, message.eventType, message.args);
|
|
343
403
|
}
|
|
344
404
|
break;
|
|
405
|
+
case 'plugin:data': {
|
|
406
|
+
const { plugin: pluginName, payload } = message;
|
|
407
|
+
const botSubscriptions = this.uiSubscriptions.get(botId);
|
|
408
|
+
if (!botSubscriptions) break;
|
|
409
|
+
|
|
410
|
+
const pluginSubscribers = botSubscriptions.get(pluginName);
|
|
411
|
+
if (pluginSubscribers && pluginSubscribers.size > 0) {
|
|
412
|
+
pluginSubscribers.forEach(socket => {
|
|
413
|
+
socket.emit('plugin:ui:dataUpdate', payload);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case 'plugin:stopped':
|
|
419
|
+
break;
|
|
345
420
|
case 'log':
|
|
346
421
|
this.appendLog(botId, message.content);
|
|
347
422
|
break;
|
|
@@ -444,6 +519,7 @@ class BotManager {
|
|
|
444
519
|
await this.eventGraphManager.loadGraphsForBot(botConfig.id);
|
|
445
520
|
|
|
446
521
|
this.triggerHeartbeat();
|
|
522
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
447
523
|
getIO().emit('bot:status', { botId: botConfig.id, status: 'starting' });
|
|
448
524
|
return child;
|
|
449
525
|
}
|
|
@@ -505,41 +581,17 @@ class BotManager {
|
|
|
505
581
|
cooldowns.set(cooldownKey, now);
|
|
506
582
|
}
|
|
507
583
|
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
sendMessage: (type, message, recipient) => {
|
|
516
|
-
this.sendMessageToBot(botId, message, type, recipient);
|
|
517
|
-
},
|
|
518
|
-
executeCommand: (command) => {
|
|
519
|
-
this.sendServerCommandToBot(botId, command);
|
|
520
|
-
},
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
const graphContext = {
|
|
524
|
-
bot: botAPI,
|
|
525
|
-
botId: botId,
|
|
526
|
-
user,
|
|
527
|
-
args,
|
|
528
|
-
typeChat,
|
|
529
|
-
players,
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
try {
|
|
533
|
-
const resultContext = await this.graphEngine.execute(graph, graphContext, 'command');
|
|
534
|
-
} catch (e) {
|
|
535
|
-
console.error(`[BotManager] Ошибка выполнения визуальной команды '${commandName}':`, e);
|
|
536
|
-
this.sendMessageToBot(botId, 'Произошла внутренняя ошибка при выполнении команды.', 'private', username);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
} else {
|
|
540
|
-
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
584
|
+
if (this.eventGraphManager) {
|
|
585
|
+
this.eventGraphManager.handleEvent(botId, 'command', {
|
|
586
|
+
commandName: dbCommand.name,
|
|
587
|
+
user: { username },
|
|
588
|
+
args,
|
|
589
|
+
typeChat
|
|
590
|
+
});
|
|
541
591
|
}
|
|
542
592
|
|
|
593
|
+
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
594
|
+
|
|
543
595
|
} catch (error) {
|
|
544
596
|
console.error(`[BotManager] Command validation error for botId: ${botId}`, {
|
|
545
597
|
command: commandName, user: username, error: error.message, stack: error.stack
|
|
@@ -685,7 +737,7 @@ class BotManager {
|
|
|
685
737
|
sendMessageToBot(botId, message, chatType = 'command', username = null) {
|
|
686
738
|
const child = this.bots.get(botId);
|
|
687
739
|
if (child) {
|
|
688
|
-
child.
|
|
740
|
+
child.api.sendMessage(chatType, message, username);
|
|
689
741
|
return { success: true };
|
|
690
742
|
}
|
|
691
743
|
return { success: false, message: 'Бот не найден или не запущен' };
|
|
@@ -786,4 +838,4 @@ class BotManager {
|
|
|
786
838
|
}
|
|
787
839
|
}
|
|
788
840
|
|
|
789
|
-
module.exports = new BotManager();
|
|
841
|
+
module.exports = new BotManager();
|
|
@@ -16,6 +16,8 @@ const UserService = require('./UserService');
|
|
|
16
16
|
const PermissionManager = require('./ipc/PermissionManager.stub.js');
|
|
17
17
|
|
|
18
18
|
let bot = null;
|
|
19
|
+
const prisma = new PrismaClient();
|
|
20
|
+
const pluginUiState = new Map();
|
|
19
21
|
const pendingRequests = new Map();
|
|
20
22
|
const entityMoveThrottles = new Map();
|
|
21
23
|
|
|
@@ -34,8 +36,7 @@ function sendEvent(eventName, eventArgs) {
|
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
async function fetchNewConfig(botId) {
|
|
38
|
-
const prisma = new PrismaClient();
|
|
39
|
+
async function fetchNewConfig(botId, prisma) {
|
|
39
40
|
try {
|
|
40
41
|
const botData = await prisma.bot.findUnique({
|
|
41
42
|
where: { id: botId },
|
|
@@ -55,8 +56,6 @@ async function fetchNewConfig(botId) {
|
|
|
55
56
|
} catch (error) {
|
|
56
57
|
sendLog(`[fetchNewConfig] Error: ${error.message}`);
|
|
57
58
|
return null;
|
|
58
|
-
} finally {
|
|
59
|
-
await prisma.$disconnect();
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -129,7 +128,17 @@ function handleIncomingCommand(type, username, message) {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
process.on('message', async (message) => {
|
|
132
|
-
|
|
131
|
+
if (message.type === 'plugin:ui:start-updates') {
|
|
132
|
+
const { pluginName } = message;
|
|
133
|
+
const state = pluginUiState.get(pluginName);
|
|
134
|
+
if (state && process.send) {
|
|
135
|
+
process.send({
|
|
136
|
+
type: 'plugin:data',
|
|
137
|
+
plugin: pluginName,
|
|
138
|
+
payload: state
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} else if (message.type === 'user_action_response') {
|
|
133
142
|
if (pendingRequests.has(message.requestId)) {
|
|
134
143
|
const { resolve, reject } = pendingRequests.get(message.requestId);
|
|
135
144
|
if (message.error) {
|
|
@@ -193,6 +202,8 @@ process.on('message', async (message) => {
|
|
|
193
202
|
|
|
194
203
|
bot = mineflayer.createBot(botOptions);
|
|
195
204
|
|
|
205
|
+
bot.pluginUiState = pluginUiState;
|
|
206
|
+
|
|
196
207
|
let isReady = false;
|
|
197
208
|
|
|
198
209
|
bot.events = new EventEmitter();
|
|
@@ -214,8 +225,7 @@ process.on('message', async (message) => {
|
|
|
214
225
|
registerGroup: (groupConfig) => PermissionManager.registerGroup(bot.config.id, groupConfig),
|
|
215
226
|
addPermissionsToGroup: (groupName, permissionNames) => PermissionManager.addPermissionsToGroup(bot.config.id, groupName, permissionNames),
|
|
216
227
|
installedPlugins: installedPluginNames,
|
|
217
|
-
registerCommand: async (command) => {
|
|
218
|
-
const prisma = new PrismaClient();
|
|
228
|
+
registerCommand: async (command) => {
|
|
219
229
|
try {
|
|
220
230
|
let permissionId = null;
|
|
221
231
|
if (command.permissions) {
|
|
@@ -231,7 +241,7 @@ process.on('message', async (message) => {
|
|
|
231
241
|
if (permission) {
|
|
232
242
|
permissionId = permission.id;
|
|
233
243
|
} else {
|
|
234
|
-
sendLog(`[API] Внимание: право "${command.permissions}" не найдено для команды "${command.name}". Команда будет создана без привязанного права.`);
|
|
244
|
+
sendLog(`[API] Внимание: право \"${command.permissions}\" не найдено для команды \"${command.name}\". Команда будет создана без привязанного права.`);
|
|
235
245
|
}
|
|
236
246
|
}
|
|
237
247
|
|
|
@@ -281,7 +291,7 @@ process.on('message', async (message) => {
|
|
|
281
291
|
}
|
|
282
292
|
});
|
|
283
293
|
}
|
|
284
|
-
sendLog(`[API] Команда "${command.name}" от плагина "${command.owner}" зарегистрирована в процессе.`);
|
|
294
|
+
sendLog(`[API] Команда \"${command.name}\" от плагина \"${command.owner}\" зарегистрирована в процессе.`);
|
|
285
295
|
|
|
286
296
|
if (!bot.commands) bot.commands = new Map();
|
|
287
297
|
bot.commands.set(command.name, command);
|
|
@@ -292,8 +302,6 @@ process.on('message', async (message) => {
|
|
|
292
302
|
}
|
|
293
303
|
} catch (error) {
|
|
294
304
|
sendLog(`[API] Ошибка при регистрации команды: ${error.message}`);
|
|
295
|
-
} finally {
|
|
296
|
-
await prisma.$disconnect();
|
|
297
305
|
}
|
|
298
306
|
},
|
|
299
307
|
performUserAction: (username, action, data = {}) => {
|
|
@@ -331,6 +339,20 @@ process.on('message', async (message) => {
|
|
|
331
339
|
if (bot && position) {
|
|
332
340
|
bot.lookAt(position);
|
|
333
341
|
}
|
|
342
|
+
},
|
|
343
|
+
sendUiUpdate: (pluginName, stateUpdate) => {
|
|
344
|
+
const currentState = pluginUiState.get(pluginName) || {};
|
|
345
|
+
const newState = { ...currentState, ...stateUpdate };
|
|
346
|
+
pluginUiState.set(pluginName, newState);
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if (process.send) {
|
|
350
|
+
process.send({
|
|
351
|
+
type: 'plugin:data',
|
|
352
|
+
plugin: pluginName,
|
|
353
|
+
payload: newState
|
|
354
|
+
});
|
|
355
|
+
}
|
|
334
356
|
}
|
|
335
357
|
};
|
|
336
358
|
|
|
@@ -346,9 +368,7 @@ process.on('message', async (message) => {
|
|
|
346
368
|
|
|
347
369
|
bot.commands = await loadCommands();
|
|
348
370
|
|
|
349
|
-
const prisma = new PrismaClient();
|
|
350
371
|
const dbCommands = await prisma.command.findMany({ where: { botId: config.id } });
|
|
351
|
-
await prisma.$disconnect();
|
|
352
372
|
|
|
353
373
|
for (const dbCommand of dbCommands) {
|
|
354
374
|
if (!dbCommand.isEnabled) {
|
|
@@ -406,7 +426,7 @@ process.on('message', async (message) => {
|
|
|
406
426
|
}
|
|
407
427
|
}
|
|
408
428
|
|
|
409
|
-
await initializePlugins(bot, config.plugins);
|
|
429
|
+
await initializePlugins(bot, config.plugins, prisma);
|
|
410
430
|
sendLog('[System] Все системы инициализированы.');
|
|
411
431
|
|
|
412
432
|
let messageHandledByCustomParser = false;
|
|
@@ -540,13 +560,13 @@ process.on('message', async (message) => {
|
|
|
540
560
|
} else if (message.type === 'config:reload') {
|
|
541
561
|
sendLog('[System] Received config:reload command. Reloading configuration...');
|
|
542
562
|
try {
|
|
543
|
-
const newConfig = await fetchNewConfig(bot.config.id);
|
|
563
|
+
const newConfig = await fetchNewConfig(bot.config.id, prisma);
|
|
544
564
|
if (newConfig) {
|
|
545
565
|
bot.config = { ...bot.config, ...newConfig };
|
|
546
566
|
const newCommands = await loadCommands();
|
|
547
567
|
const newPlugins = bot.config.plugins;
|
|
548
568
|
bot.commands = newCommands;
|
|
549
|
-
await initializePlugins(bot, newPlugins,
|
|
569
|
+
await initializePlugins(bot, newPlugins, prisma);
|
|
550
570
|
sendLog('[System] Bot configuration and plugins reloaded successfully.');
|
|
551
571
|
} else {
|
|
552
572
|
sendLog('[System] Failed to fetch new configuration.');
|
|
@@ -610,14 +630,14 @@ process.on('message', async (message) => {
|
|
|
610
630
|
}
|
|
611
631
|
} else if (message.type === 'plugins:reload') {
|
|
612
632
|
sendLog('[System] Получена команда на перезагрузку плагинов...');
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
633
|
+
const newConfig = await fetchNewConfig(bot.config.id, prisma);
|
|
634
|
+
if (newConfig) {
|
|
635
|
+
bot.config.plugins = newConfig.installedPlugins;
|
|
636
|
+
bot.commands.clear();
|
|
637
|
+
await loadCommands(bot, newConfig.commands);
|
|
638
|
+
await initializePlugins(bot, newConfig.installedPlugins, prisma);
|
|
639
|
+
sendLog('[System] Плагины успешно перезагружены.');
|
|
640
|
+
} else {
|
|
621
641
|
sendLog('[System] Не удалось получить новую конфигурацию для перезагрузки плагинов.');
|
|
622
642
|
}
|
|
623
643
|
} else if (message.type === 'server_command') {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
const { PrismaClient } = require('@prisma/client');
|
|
2
|
+
const GraphExecutionEngine = require('./GraphExecutionEngine');
|
|
3
|
+
const nodeRegistry = require('./NodeRegistry');
|
|
2
4
|
|
|
3
5
|
const prisma = new PrismaClient();
|
|
4
6
|
|
|
5
7
|
class EventGraphManager {
|
|
6
8
|
constructor(botManager) {
|
|
7
9
|
this.botManager = botManager;
|
|
8
|
-
this.graphEngine = botManager
|
|
10
|
+
this.graphEngine = new GraphExecutionEngine(nodeRegistry, botManager);
|
|
9
11
|
this.activeGraphs = new Map();
|
|
10
|
-
// Хранилище для состояний (переменных) каждого графа
|
|
11
12
|
this.graphStates = new Map();
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -26,7 +27,6 @@ class EventGraphManager {
|
|
|
26
27
|
const parsedGraph = JSON.parse(graph.graphJson);
|
|
27
28
|
if (!parsedGraph.nodes) continue;
|
|
28
29
|
|
|
29
|
-
// Инициализация начального состояния переменных для этого графа
|
|
30
30
|
const initialState = {};
|
|
31
31
|
if (graph.variables) {
|
|
32
32
|
try {
|
|
@@ -56,6 +56,7 @@ class EventGraphManager {
|
|
|
56
56
|
name: graph.name,
|
|
57
57
|
nodes: parsedGraph.nodes,
|
|
58
58
|
connections: parsedGraph.connections,
|
|
59
|
+
variables: parsedGraph.variables || [],
|
|
59
60
|
});
|
|
60
61
|
}
|
|
61
62
|
} catch (e) {
|
|
@@ -69,7 +70,6 @@ class EventGraphManager {
|
|
|
69
70
|
|
|
70
71
|
unloadGraphsForBot(botId) {
|
|
71
72
|
this.activeGraphs.delete(botId);
|
|
72
|
-
// Также очищаем состояния при выгрузке
|
|
73
73
|
for (const key of this.graphStates.keys()) {
|
|
74
74
|
if (key.startsWith(`${botId}-`)) {
|
|
75
75
|
this.graphStates.delete(key);
|
|
@@ -120,7 +120,25 @@ class EventGraphManager {
|
|
|
120
120
|
initialContext.botId = botId;
|
|
121
121
|
initialContext.players = players;
|
|
122
122
|
initialContext.botState = eventArgs.botState || {};
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
const savedVariables = { ...(this.graphStates.get(stateKey) || {}) };
|
|
125
|
+
|
|
126
|
+
if (graph.variables && Array.isArray(graph.variables)) {
|
|
127
|
+
for (const v of graph.variables) {
|
|
128
|
+
if (!savedVariables.hasOwnProperty(v.name)) {
|
|
129
|
+
let val;
|
|
130
|
+
switch (v.type) {
|
|
131
|
+
case 'number': val = Number(v.value) || 0; break;
|
|
132
|
+
case 'boolean': val = v.value === 'true'; break;
|
|
133
|
+
case 'array': try { val = Array.isArray(JSON.parse(v.value)) ? JSON.parse(v.value) : []; } catch { val = []; } break;
|
|
134
|
+
default: val = v.value;
|
|
135
|
+
}
|
|
136
|
+
savedVariables[v.name] = val;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
initialContext.variables = savedVariables;
|
|
124
142
|
|
|
125
143
|
try {
|
|
126
144
|
const finalContext = await this.graphEngine.execute(graph, initialContext, eventType);
|
|
@@ -162,6 +180,12 @@ class EventGraphManager {
|
|
|
162
180
|
case 'entityGone':
|
|
163
181
|
context.entity = args.entity;
|
|
164
182
|
break;
|
|
183
|
+
case 'command':
|
|
184
|
+
context.command_name = args.commandName;
|
|
185
|
+
context.user = args.user;
|
|
186
|
+
context.args = args.args;
|
|
187
|
+
context.chat_type = args.typeChat;
|
|
188
|
+
break;
|
|
165
189
|
}
|
|
166
190
|
return context;
|
|
167
191
|
}
|
|
@@ -219,6 +219,34 @@ class GraphExecutionEngine {
|
|
|
219
219
|
await this.traverse(node, 'completed');
|
|
220
220
|
break;
|
|
221
221
|
}
|
|
222
|
+
case 'flow:while': {
|
|
223
|
+
let iteration = 0;
|
|
224
|
+
const maxIterations = 1000;
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
while (iteration < maxIterations) {
|
|
228
|
+
const condition = await this.resolvePinValue(node, 'condition', false);
|
|
229
|
+
if (!condition) break;
|
|
230
|
+
|
|
231
|
+
this.memo.set(`${node.id}:iteration`, iteration);
|
|
232
|
+
this.clearLoopBodyMemo(node);
|
|
233
|
+
await this.traverse(node, 'loop_body');
|
|
234
|
+
iteration++;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (iteration >= maxIterations) {
|
|
238
|
+
console.warn(`[GraphExecutionEngine] Цикл while достиг максимального количества итераций (${maxIterations})`);
|
|
239
|
+
}
|
|
240
|
+
} catch (e) {
|
|
241
|
+
if (e instanceof BreakLoopSignal) {
|
|
242
|
+
} else {
|
|
243
|
+
throw e;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
await this.traverse(node, 'completed');
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
222
250
|
case 'flow:sequence': {
|
|
223
251
|
const pinCount = node.data?.pinCount || 2;
|
|
224
252
|
for (let i = 0; i < pinCount; i++) {
|
|
@@ -375,7 +403,7 @@ class GraphExecutionEngine {
|
|
|
375
403
|
case 'event:command':
|
|
376
404
|
if (pinId === 'args') result = this.context.args || {};
|
|
377
405
|
else if (pinId === 'user') result = this.context.user || {};
|
|
378
|
-
else if (pinId === 'chat_type') result = this.context.typeChat || '
|
|
406
|
+
else if (pinId === 'chat_type') result = this.context.typeChat || 'chat';
|
|
379
407
|
else result = this.context[pinId];
|
|
380
408
|
break;
|
|
381
409
|
case 'event:chat':
|
|
@@ -399,8 +427,13 @@ class GraphExecutionEngine {
|
|
|
399
427
|
break;
|
|
400
428
|
|
|
401
429
|
case 'data:get_variable':
|
|
402
|
-
const varName = node.data?.variableName || '';
|
|
403
|
-
|
|
430
|
+
const varName = node.data?.variableName || node.data?.selectedVariable || '';
|
|
431
|
+
if (!varName) {
|
|
432
|
+
console.warn('[GraphExecutionEngine] data:get_variable: не указано имя переменной', node.data);
|
|
433
|
+
result = null;
|
|
434
|
+
} else {
|
|
435
|
+
result = this.context.variables.hasOwnProperty(varName) ? this.context.variables[varName] : null;
|
|
436
|
+
}
|
|
404
437
|
break;
|
|
405
438
|
|
|
406
439
|
|
|
@@ -471,17 +504,13 @@ class GraphExecutionEngine {
|
|
|
471
504
|
case 'logic:operation': {
|
|
472
505
|
const op = node.data?.operation || 'AND';
|
|
473
506
|
const inputs = [];
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (inputs.length === 0) {
|
|
480
|
-
inputs.push(await this.resolvePinValue(node, 'a', false));
|
|
481
|
-
inputs.push(await this.resolvePinValue(node, 'b', false));
|
|
507
|
+
const pinCount = node.data?.pinCount || 2;
|
|
508
|
+
|
|
509
|
+
for (let i = 0; i < pinCount; i++) {
|
|
510
|
+
const value = await this.resolvePinValue(node, `pin_${i}`, false);
|
|
511
|
+
inputs.push(value);
|
|
482
512
|
}
|
|
483
513
|
|
|
484
|
-
|
|
485
514
|
switch (op) {
|
|
486
515
|
case 'AND': result = inputs.every(Boolean); break;
|
|
487
516
|
case 'OR': result = inputs.some(Boolean); break;
|
|
@@ -738,6 +767,12 @@ class GraphExecutionEngine {
|
|
|
738
767
|
}
|
|
739
768
|
break;
|
|
740
769
|
}
|
|
770
|
+
case 'flow:while': {
|
|
771
|
+
if (pinId === 'iteration') {
|
|
772
|
+
result = this.memo.get(`${node.id}:iteration`);
|
|
773
|
+
}
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
741
776
|
|
|
742
777
|
case 'string:equals': {
|
|
743
778
|
const strA = String(await this.resolvePinValue(node, 'a', ''));
|
|
@@ -798,6 +833,13 @@ class GraphExecutionEngine {
|
|
|
798
833
|
|
|
799
834
|
return false;
|
|
800
835
|
}
|
|
836
|
+
|
|
837
|
+
hasConnection(node, pinId) {
|
|
838
|
+
if (!this.activeGraph || !this.activeGraph.connections) return false;
|
|
839
|
+
return this.activeGraph.connections.some(conn =>
|
|
840
|
+
conn.targetNodeId === node.id && conn.targetPinId === pinId
|
|
841
|
+
);
|
|
842
|
+
}
|
|
801
843
|
}
|
|
802
844
|
|
|
803
845
|
module.exports = GraphExecutionEngine;
|
|
@@ -33,7 +33,16 @@ class MessageQueue {
|
|
|
33
33
|
enqueue(chatType, message, username = null) {
|
|
34
34
|
const typeConfig = this.chatTypes[chatType];
|
|
35
35
|
if (!typeConfig) return;
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
if (Array.isArray(message)) {
|
|
38
|
+
for (const msg of message) {
|
|
39
|
+
if (typeof msg === 'string' && msg.trim().length > 0) {
|
|
40
|
+
this._enqueue({ type: 'simple', chatType, ...typeConfig, message: msg, username });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else if (typeof message === 'string') {
|
|
44
|
+
this._enqueue({ type: 'simple', chatType, ...typeConfig, message, username });
|
|
45
|
+
}
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
enqueueAndWait(command, patterns, timeout) {
|
|
@@ -102,11 +102,12 @@ class NodeRegistry {
|
|
|
102
102
|
label: '▶️ При выполнении команды',
|
|
103
103
|
category: 'События',
|
|
104
104
|
description: 'Стартовая точка для графа команды.',
|
|
105
|
-
graphType:
|
|
105
|
+
graphType: 'all',
|
|
106
106
|
pins: {
|
|
107
107
|
inputs: [],
|
|
108
108
|
outputs: [
|
|
109
109
|
{ id: 'exec', name: 'Выполнить', type: 'Exec' },
|
|
110
|
+
{ id: 'command_name', name: 'Имя команды', type: 'String' },
|
|
110
111
|
{ id: 'user', name: 'Пользователь', type: 'User' },
|
|
111
112
|
{ id: 'args', name: 'Аргументы', type: 'Object' },
|
|
112
113
|
{ id: 'chat_type', name: 'Тип чата', type: 'String' }
|