blockmine 1.6.3 → 1.12.1
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 +29 -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 +201 -204
- 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-BzDKhCKk.js +8330 -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
|
@@ -1085,4 +1085,180 @@ router.put('/:botId/event-graphs/:graphId', authorize('management:edit'), async
|
|
|
1085
1085
|
router.post('/:botId/visual-editor/save', authorize('management:edit'), async (req, res) => {
|
|
1086
1086
|
});
|
|
1087
1087
|
|
|
1088
|
+
router.get('/:botId/ui-extensions', authorize('plugin:list'), async (req, res) => {
|
|
1089
|
+
try {
|
|
1090
|
+
const botId = parseInt(req.params.botId, 10);
|
|
1091
|
+
const enabledPlugins = await prisma.installedPlugin.findMany({
|
|
1092
|
+
where: { botId: botId, isEnabled: true }
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
const extensions = [];
|
|
1096
|
+
for (const plugin of enabledPlugins) {
|
|
1097
|
+
if (plugin.manifest) {
|
|
1098
|
+
try {
|
|
1099
|
+
const manifest = JSON.parse(plugin.manifest);
|
|
1100
|
+
if (manifest.uiExtensions && Array.isArray(manifest.uiExtensions)) {
|
|
1101
|
+
manifest.uiExtensions.forEach(ext => {
|
|
1102
|
+
extensions.push({
|
|
1103
|
+
pluginName: plugin.name,
|
|
1104
|
+
...ext
|
|
1105
|
+
});
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
} catch (e) {
|
|
1109
|
+
console.error(`Ошибка парсинга манифеста для плагина ${plugin.name}:`, e);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
res.json(extensions);
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
res.status(500).json({ error: 'Не удалось получить расширения интерфейса' });
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
router.get('/:botId/plugins/:pluginName/ui-content/:path', authorize('plugin:list'), async (req, res) => {
|
|
1120
|
+
const { botId, pluginName, path: uiPath } = req.params;
|
|
1121
|
+
const numericBotId = parseInt(botId, 10);
|
|
1122
|
+
|
|
1123
|
+
try {
|
|
1124
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
1125
|
+
where: { botId: numericBotId, name: pluginName, isEnabled: true }
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
if (!plugin) {
|
|
1129
|
+
return res.status(404).json({ error: `Активный плагин "${pluginName}" не найден для этого бота.` });
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
|
|
1133
|
+
const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
|
|
1134
|
+
const defaultSettings = {};
|
|
1135
|
+
|
|
1136
|
+
if (manifest.settings) {
|
|
1137
|
+
for (const key in manifest.settings) {
|
|
1138
|
+
const config = manifest.settings[key];
|
|
1139
|
+
if (config.type === 'json_file' && config.defaultPath) {
|
|
1140
|
+
const configFilePath = path.join(plugin.path, config.defaultPath);
|
|
1141
|
+
try {
|
|
1142
|
+
const fileContent = await fs.readFile(configFilePath, 'utf-8');
|
|
1143
|
+
defaultSettings[key] = JSON.parse(fileContent);
|
|
1144
|
+
} catch (e) { defaultSettings[key] = {}; }
|
|
1145
|
+
} else {
|
|
1146
|
+
try { defaultSettings[key] = JSON.parse(config.default || 'null'); }
|
|
1147
|
+
catch { defaultSettings[key] = config.default; }
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
const finalSettings = { ...defaultSettings, ...savedSettings };
|
|
1152
|
+
|
|
1153
|
+
const mainFilePath = manifest.main || 'index.js';
|
|
1154
|
+
const pluginEntryPoint = path.join(plugin.path, mainFilePath);
|
|
1155
|
+
|
|
1156
|
+
delete require.cache[require.resolve(pluginEntryPoint)];
|
|
1157
|
+
const pluginModule = require(pluginEntryPoint);
|
|
1158
|
+
|
|
1159
|
+
if (typeof pluginModule.getUiPageContent !== 'function') {
|
|
1160
|
+
return res.status(501).json({ error: `Плагин "${pluginName}" не предоставляет кастомный UI контент.` });
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const botProcess = botManager.bots.get(numericBotId);
|
|
1164
|
+
const botApi = botProcess ? botProcess.api : null;
|
|
1165
|
+
|
|
1166
|
+
const content = await pluginModule.getUiPageContent({
|
|
1167
|
+
path: uiPath,
|
|
1168
|
+
bot: botApi,
|
|
1169
|
+
botId: numericBotId,
|
|
1170
|
+
settings: finalSettings
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
if (content === null) {
|
|
1174
|
+
return res.status(404).json({ error: `Для пути "${uiPath}" не найдено содержимого в плагине "${pluginName}".` });
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
res.json(content);
|
|
1178
|
+
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
console.error(`[UI Content] Ошибка при получении контента для плагина "${pluginName}":`, error);
|
|
1181
|
+
res.status(500).json({ error: error.message || 'Внутренняя ошибка сервера.' });
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), async (req, res) => {
|
|
1187
|
+
const { botId, pluginName } = req.params;
|
|
1188
|
+
const { actionName, payload } = req.body;
|
|
1189
|
+
const numericBotId = parseInt(botId, 10);
|
|
1190
|
+
|
|
1191
|
+
if (!actionName) {
|
|
1192
|
+
return res.status(400).json({ error: 'Необходимо указать "actionName".' });
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
try {
|
|
1196
|
+
const botProcess = botManager.bots.get(numericBotId);
|
|
1197
|
+
|
|
1198
|
+
if (!botProcess) {
|
|
1199
|
+
return res.status(404).json({ error: 'Бот не найден или не запущен.' });
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const plugin = await prisma.installedPlugin.findFirst({
|
|
1203
|
+
where: { botId: numericBotId, name: pluginName, isEnabled: true }
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
if (!plugin) {
|
|
1207
|
+
return res.status(404).json({ error: `Активный плагин с таким именем "${pluginName}" не найден.` });
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
const manifest = plugin.manifest ? JSON.parse(plugin.manifest) : {};
|
|
1211
|
+
const savedSettings = plugin.settings ? JSON.parse(plugin.settings) : {};
|
|
1212
|
+
const defaultSettings = {};
|
|
1213
|
+
|
|
1214
|
+
if (manifest.settings) {
|
|
1215
|
+
for (const key in manifest.settings) {
|
|
1216
|
+
const config = manifest.settings[key];
|
|
1217
|
+
if (config.type === 'json_file' && config.defaultPath) {
|
|
1218
|
+
const configFilePath = path.join(plugin.path, config.defaultPath);
|
|
1219
|
+
try {
|
|
1220
|
+
const fileContent = await fs.readFile(configFilePath, 'utf-8');
|
|
1221
|
+
defaultSettings[key] = JSON.parse(fileContent);
|
|
1222
|
+
} catch (e) {
|
|
1223
|
+
console.error(`[Action] Не удалось прочитать defaultPath для ${pluginName}: ${e.message}`);
|
|
1224
|
+
defaultSettings[key] = {};
|
|
1225
|
+
}
|
|
1226
|
+
} else {
|
|
1227
|
+
try {
|
|
1228
|
+
defaultSettings[key] = JSON.parse(config.default || 'null');
|
|
1229
|
+
} catch {
|
|
1230
|
+
defaultSettings[key] = config.default;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
const finalSettings = { ...defaultSettings, ...savedSettings };
|
|
1236
|
+
|
|
1237
|
+
const mainFilePath = manifest.main || 'index.js';
|
|
1238
|
+
const pluginPath = path.join(plugin.path, mainFilePath);
|
|
1239
|
+
|
|
1240
|
+
delete require.cache[require.resolve(pluginPath)];
|
|
1241
|
+
const pluginModule = require(pluginPath);
|
|
1242
|
+
|
|
1243
|
+
if (typeof pluginModule.handleAction !== 'function') {
|
|
1244
|
+
return res.status(501).json({ error: `Плагин "${pluginName}" не поддерживает обработку действий.` });
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
const result = await pluginModule.handleAction({
|
|
1248
|
+
botProcess: botProcess,
|
|
1249
|
+
botId: numericBotId,
|
|
1250
|
+
action: actionName,
|
|
1251
|
+
payload: payload,
|
|
1252
|
+
settings: finalSettings
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
res.json({ success: true, message: 'Действие выполнено.', result: result || null });
|
|
1256
|
+
|
|
1257
|
+
} catch (error) {
|
|
1258
|
+
console.error(`Ошибка выполнения действия "${actionName}" для плагина "${pluginName}":`, error);
|
|
1259
|
+
res.status(500).json({ error: error.message || 'Внутренняя ошибка сервера.' });
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
|
|
1088
1264
|
module.exports = router;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
router.get('/', async (req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const file = path.resolve(__dirname, '../../../../CHANGELOG.md');
|
|
9
|
+
const data = await fs.promises.readFile(file, 'utf8');
|
|
10
|
+
res.type('text/markdown').send(data);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
res.status(500).json({ error: 'changelog_not_found' });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
module.exports = router;
|
|
@@ -121,7 +121,17 @@ router.put('/:graphId',
|
|
|
121
121
|
|
|
122
122
|
const parsedGraph = JSON.parse(graphJson);
|
|
123
123
|
const eventNodes = parsedGraph.nodes.filter(node => node.type.startsWith('event:'));
|
|
124
|
-
const eventTypes = eventNodes.map(node => node.type.split(':')[1]);
|
|
124
|
+
const eventTypes = [...new Set(eventNodes.map(node => node.type.split(':')[1]))];
|
|
125
|
+
|
|
126
|
+
const existingGraph = await prisma.eventGraph.findUnique({
|
|
127
|
+
where: { id: parseInt(graphId) },
|
|
128
|
+
include: { triggers: true }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const existingEventTypes = existingGraph?.triggers?.map(t => t.eventType) || [];
|
|
132
|
+
|
|
133
|
+
const eventTypesToDelete = existingEventTypes.filter(et => !eventTypes.includes(et));
|
|
134
|
+
const eventTypesToCreate = eventTypes.filter(et => !existingEventTypes.includes(et));
|
|
125
135
|
|
|
126
136
|
dataToUpdate.triggers = {
|
|
127
137
|
deleteMany: {},
|
|
@@ -53,6 +53,17 @@ router.post('/update/:pluginId', authenticate, authorize('plugin:update'), async
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
router.post('/:id/clear-data', authenticate, authorize('plugin:settings:edit'), async (req, res) => {
|
|
57
|
+
try {
|
|
58
|
+
const pluginId = parseInt(req.params.id);
|
|
59
|
+
await pluginManager.clearPluginData(pluginId);
|
|
60
|
+
res.status(200).json({ message: 'Данные плагина успешно очищены.' });
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(`[API Error] /plugins/:id/clear-data:`, error);
|
|
63
|
+
res.status(500).json({ error: error.message || 'Не удалось очистить данные плагина.' });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
56
67
|
router.get('/catalog/:name', async (req, res) => {
|
|
57
68
|
try {
|
|
58
69
|
const pluginName = req.params.name;
|
|
@@ -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') {
|