blockmine 1.13.1 → 1.14.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/CHANGELOG.md +17 -0
- package/backend/src/api/routes/bots.js +264 -19
- package/backend/src/core/BotManager.js +13 -0
- package/backend/src/core/BotProcess.js +44 -1
- package/backend/src/core/PluginLoader.js +10 -6
- package/commitlint.config.js +4 -1
- package/frontend/dist/assets/index-BOFp308w.css +1 -0
- package/frontend/dist/assets/{index-D4biDGgF.js → index-C7vFQnSR.js} +1671 -1671
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-DhU2u6V0.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# История версий
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
### [1.14.1](https://github.com/blockmineJS/blockmine/compare/v1.14.0...v1.14.1) (2025-07-19)
|
|
5
|
+
|
|
6
|
+
## [1.14.0](https://github.com/blockmineJS/blockmine/compare/v1.13.1...v1.14.0) (2025-07-19)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### ✨ Новые возможности
|
|
10
|
+
|
|
11
|
+
* [PluginUiPage] Если бот не запущен, то на этой странице попросит запустить бота ([fda603e](https://github.com/blockmineJS/blockmine/commit/fda603ee4162b32d3bac481307dc6aca2d671d07))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### 🐛 Исправления
|
|
15
|
+
|
|
16
|
+
* импорт/экспорт починен ([bad135f](https://github.com/blockmineJS/blockmine/commit/bad135fe04c7e1dc5bb0cfc67a326a3a60e3b040))
|
|
17
|
+
* обновлен интерфейс отображения установленных плагинов и улучшена логика обновления плагинов ([ba50a0a](https://github.com/blockmineJS/blockmine/commit/ba50a0aec6bf656e73fb46dc575668dbc617d5ce))
|
|
18
|
+
* теперь бот будет принудительно убит если не сможет остановится ([7770af5](https://github.com/blockmineJS/blockmine/commit/7770af5999abf108ec38ab416d8aab3fd5c804ab))
|
|
19
|
+
* улучшен интерфейс переключателя темы и исправлена логика инициализации темы при монтировании компонента ([2ed06d8](https://github.com/blockmineJS/blockmine/commit/2ed06d8ed26263521741b62318cab34cf561248e))
|
|
20
|
+
|
|
4
21
|
### [1.13.1](https://github.com/blockmineJS/blockmine/compare/v1.13.0...v1.13.1) (2025-07-18)
|
|
5
22
|
|
|
6
23
|
|
|
@@ -15,6 +15,7 @@ const pluginIdeRouter = require('./pluginIde');
|
|
|
15
15
|
const multer = require('multer');
|
|
16
16
|
const archiver = require('archiver');
|
|
17
17
|
const AdmZip = require('adm-zip');
|
|
18
|
+
const os = require('os');
|
|
18
19
|
|
|
19
20
|
const upload = multer({ storage: multer.memoryStorage() });
|
|
20
21
|
|
|
@@ -127,15 +128,22 @@ router.put('/:id', authorize('bot:update'), async (req, res) => {
|
|
|
127
128
|
dataToUpdate.proxyPassword = encrypt(proxyPassword);
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
if (serverId) {
|
|
131
|
+
if (serverId !== undefined && serverId !== '') {
|
|
131
132
|
dataToUpdate.serverId = parseInt(serverId, 10);
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
Object.keys(dataToUpdate).forEach(key => {
|
|
136
|
+
if (dataToUpdate[key] === undefined) {
|
|
137
|
+
delete dataToUpdate[key];
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
134
141
|
if (dataToUpdate.serverId) {
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
const serverIdValue = dataToUpdate.serverId;
|
|
143
|
+
delete dataToUpdate.serverId;
|
|
144
|
+
dataToUpdate.server = { connect: { id: serverIdValue } };
|
|
137
145
|
}
|
|
138
|
-
|
|
146
|
+
|
|
139
147
|
const botId = parseInt(req.params.id, 10);
|
|
140
148
|
if (isNaN(botId)) {
|
|
141
149
|
return res.status(400).json({ message: 'Неверный ID бота.' });
|
|
@@ -1011,16 +1019,22 @@ router.post('/:botId/event-graphs', authorize('management:edit'), async (req, re
|
|
|
1011
1019
|
const botId = parseInt(req.params.botId, 10);
|
|
1012
1020
|
const { name } = req.body;
|
|
1013
1021
|
|
|
1014
|
-
if (!name) {
|
|
1015
|
-
return res.status(400).json({ error: 'Имя графа обязательно' });
|
|
1022
|
+
if (!name || typeof name !== 'string' || name.trim() === '') {
|
|
1023
|
+
return res.status(400).json({ error: 'Имя графа обязательно и должно быть непустой строкой' });
|
|
1016
1024
|
}
|
|
1017
1025
|
|
|
1026
|
+
const initialGraph = {
|
|
1027
|
+
nodes: [],
|
|
1028
|
+
connections: []
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1018
1031
|
const newEventGraph = await prisma.eventGraph.create({
|
|
1019
1032
|
data: {
|
|
1020
1033
|
botId,
|
|
1021
|
-
name,
|
|
1034
|
+
name: name.trim(),
|
|
1022
1035
|
isEnabled: true,
|
|
1023
|
-
graphJson:
|
|
1036
|
+
graphJson: JSON.stringify(initialGraph),
|
|
1037
|
+
variables: '[]'
|
|
1024
1038
|
},
|
|
1025
1039
|
});
|
|
1026
1040
|
|
|
@@ -1052,10 +1066,10 @@ router.delete('/:botId/event-graphs/:graphId', authorize('management:edit'), asy
|
|
|
1052
1066
|
|
|
1053
1067
|
router.put('/:botId/event-graphs/:graphId', authorize('management:edit'), async (req, res) => {
|
|
1054
1068
|
const { botId, graphId } = req.params;
|
|
1055
|
-
const { name, isEnabled, graphJson,
|
|
1069
|
+
const { name, isEnabled, graphJson, variables } = req.body;
|
|
1056
1070
|
|
|
1057
|
-
if (!name ||
|
|
1058
|
-
return res.status(400).json({ error: '
|
|
1071
|
+
if (!name || typeof name !== 'string' || name.trim() === '') {
|
|
1072
|
+
return res.status(400).json({ error: 'Поле name обязательно и должно быть непустой строкой.' });
|
|
1059
1073
|
}
|
|
1060
1074
|
|
|
1061
1075
|
if (typeof isEnabled !== 'boolean') {
|
|
@@ -1063,18 +1077,24 @@ router.put('/:botId/event-graphs/:graphId', authorize('management:edit'), async
|
|
|
1063
1077
|
}
|
|
1064
1078
|
|
|
1065
1079
|
try {
|
|
1080
|
+
const dataToUpdate = {
|
|
1081
|
+
name: name.trim(),
|
|
1082
|
+
isEnabled,
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
if (graphJson !== undefined) {
|
|
1086
|
+
dataToUpdate.graphJson = graphJson;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (variables !== undefined) {
|
|
1090
|
+
dataToUpdate.variables = Array.isArray(variables) ? JSON.stringify(variables) : variables;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1066
1093
|
const updatedGraph = await prisma.eventGraph.update({
|
|
1067
1094
|
where: { id: parseInt(graphId), botId: parseInt(botId) },
|
|
1068
|
-
data:
|
|
1069
|
-
name,
|
|
1070
|
-
isEnabled,
|
|
1071
|
-
graphJson,
|
|
1072
|
-
eventType,
|
|
1073
|
-
}
|
|
1095
|
+
data: dataToUpdate
|
|
1074
1096
|
});
|
|
1075
1097
|
|
|
1076
|
-
botManager.eventGraphManager.updateGraph(parseInt(botId), updatedGraph);
|
|
1077
|
-
|
|
1078
1098
|
res.json(updatedGraph);
|
|
1079
1099
|
} catch (error) {
|
|
1080
1100
|
console.error(`[API Error] /event-graphs/:graphId PUT:`, error);
|
|
@@ -1261,4 +1281,229 @@ router.post('/:botId/plugins/:pluginName/action', authorize('plugin:list'), asyn
|
|
|
1261
1281
|
});
|
|
1262
1282
|
|
|
1263
1283
|
|
|
1284
|
+
router.get('/:botId/export', authorize('bot:export'), async (req, res) => {
|
|
1285
|
+
try {
|
|
1286
|
+
const botId = parseInt(req.params.botId, 10);
|
|
1287
|
+
const {
|
|
1288
|
+
includeCommands,
|
|
1289
|
+
includePermissions,
|
|
1290
|
+
includePluginFiles,
|
|
1291
|
+
includePluginDataStore,
|
|
1292
|
+
includeEventGraphs,
|
|
1293
|
+
} = req.query;
|
|
1294
|
+
|
|
1295
|
+
const bot = await prisma.bot.findUnique({ where: { id: botId } });
|
|
1296
|
+
if (!bot) {
|
|
1297
|
+
return res.status(404).json({ error: 'Bot not found' });
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
1301
|
+
res.attachment(`bot_${bot.username}_export_${new Date().toISOString()}.zip`);
|
|
1302
|
+
archive.pipe(res);
|
|
1303
|
+
|
|
1304
|
+
const botData = { ...bot };
|
|
1305
|
+
delete botData.password;
|
|
1306
|
+
delete botData.proxyPassword;
|
|
1307
|
+
archive.append(JSON.stringify(botData, null, 2), { name: 'bot.json' });
|
|
1308
|
+
|
|
1309
|
+
if (includeCommands === 'true') {
|
|
1310
|
+
const commands = await prisma.command.findMany({ where: { botId } });
|
|
1311
|
+
archive.append(JSON.stringify(commands, null, 2), { name: 'commands.json' });
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
if (includePermissions === 'true') {
|
|
1315
|
+
const users = await prisma.user.findMany({ where: { botId }, include: { groups: { include: { group: true } } } });
|
|
1316
|
+
const groups = await prisma.group.findMany({ where: { botId }, include: { permissions: { include: { permission: true } } } });
|
|
1317
|
+
const permissions = await prisma.permission.findMany({ where: { botId } });
|
|
1318
|
+
const permissionsData = { users, groups, permissions };
|
|
1319
|
+
archive.append(JSON.stringify(permissionsData, null, 2), { name: 'permissions.json' });
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if (includeEventGraphs === 'true') {
|
|
1323
|
+
const eventGraphs = await prisma.eventGraph.findMany({ where: { botId } });
|
|
1324
|
+
archive.append(JSON.stringify(eventGraphs, null, 2), { name: 'event_graphs.json' });
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
if (includePluginFiles === 'true' || includePluginDataStore === 'true') {
|
|
1328
|
+
const installedPlugins = await prisma.installedPlugin.findMany({ where: { botId } });
|
|
1329
|
+
archive.append(JSON.stringify(installedPlugins, null, 2), { name: 'plugins.json' });
|
|
1330
|
+
|
|
1331
|
+
if (includePluginFiles === 'true') {
|
|
1332
|
+
for (const plugin of installedPlugins) {
|
|
1333
|
+
const pluginPath = plugin.path;
|
|
1334
|
+
if (await fs.stat(pluginPath).then(s => s.isDirectory()).catch(() => false)) {
|
|
1335
|
+
archive.directory(pluginPath, `plugins/${plugin.name}`);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
if (includePluginDataStore === 'true') {
|
|
1340
|
+
console.log(`[Export] Экспорт PluginDataStore для бота ${botId}`);
|
|
1341
|
+
const pluginDataStore = await prisma.pluginDataStore.findMany({
|
|
1342
|
+
where: { botId: parseInt(botId) }
|
|
1343
|
+
});
|
|
1344
|
+
console.log(`[Export] Найдено записей PluginDataStore: ${pluginDataStore.length}`);
|
|
1345
|
+
if (pluginDataStore.length > 0) {
|
|
1346
|
+
archive.append(JSON.stringify(pluginDataStore, null, 2), { name: 'plugin_data_store.json' });
|
|
1347
|
+
console.log(`[Export] Данные PluginDataStore добавлены в архив`);
|
|
1348
|
+
} else {
|
|
1349
|
+
console.log(`[Export] Нет данных PluginDataStore для экспорта`);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
await archive.finalize();
|
|
1355
|
+
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
console.error('Failed to export bot:', error);
|
|
1358
|
+
res.status(500).json({ error: `Failed to export bot: ${error.message}` });
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
router.post('/import', authorize('bot:create'), upload.single('file'), async (req, res) => {
|
|
1363
|
+
if (!req.file) {
|
|
1364
|
+
return res.status(400).json({ error: 'No file uploaded.' });
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const botIdMap = new Map();
|
|
1368
|
+
|
|
1369
|
+
try {
|
|
1370
|
+
const zip = new AdmZip(req.file.buffer);
|
|
1371
|
+
const zipEntries = zip.getEntries();
|
|
1372
|
+
|
|
1373
|
+
const botDataEntry = zipEntries.find(e => e.entryName === 'bot.json');
|
|
1374
|
+
if (!botDataEntry) {
|
|
1375
|
+
return res.status(400).json({ error: 'Archive missing bot.json' });
|
|
1376
|
+
}
|
|
1377
|
+
const botData = JSON.parse(botDataEntry.getData().toString('utf8'));
|
|
1378
|
+
|
|
1379
|
+
const server = await prisma.server.findFirst();
|
|
1380
|
+
if (!server) {
|
|
1381
|
+
return res.status(500).json({ error: 'No servers configured in the target system.' });
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
let newBotName = botData.username;
|
|
1385
|
+
let counter = 1;
|
|
1386
|
+
while (await prisma.bot.findFirst({ where: { username: newBotName } })) {
|
|
1387
|
+
newBotName = `${botData.username}_imported_${counter}`;
|
|
1388
|
+
counter++;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const newBot = await prisma.bot.create({
|
|
1392
|
+
data: {
|
|
1393
|
+
...botData,
|
|
1394
|
+
id: undefined,
|
|
1395
|
+
username: newBotName,
|
|
1396
|
+
serverId: server.id,
|
|
1397
|
+
password: null,
|
|
1398
|
+
proxyPassword: null
|
|
1399
|
+
},
|
|
1400
|
+
include: { server: true }
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
botIdMap.set(botData.id, newBot.id);
|
|
1404
|
+
|
|
1405
|
+
const commandsEntry = zipEntries.find(e => e.entryName === 'commands.json');
|
|
1406
|
+
if (commandsEntry) {
|
|
1407
|
+
const commands = JSON.parse(commandsEntry.getData().toString('utf8'));
|
|
1408
|
+
for (let command of commands) {
|
|
1409
|
+
delete command.id;
|
|
1410
|
+
command.botId = newBot.id;
|
|
1411
|
+
await prisma.command.create({ data: command });
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
const permissionsEntry = zipEntries.find(e => e.entryName === 'permissions.json');
|
|
1416
|
+
if (permissionsEntry) {
|
|
1417
|
+
const { users, groups, permissions } = JSON.parse(permissionsEntry.getData().toString('utf8'));
|
|
1418
|
+
|
|
1419
|
+
await setupDefaultPermissionsForBot(newBot.id, prisma);
|
|
1420
|
+
|
|
1421
|
+
const pMap = new Map();
|
|
1422
|
+
for(let p of permissions.filter(p=>p.owner !== 'system')) {
|
|
1423
|
+
const newP = await prisma.permission.create({ data: { ...p, id: undefined, botId: newBot.id }});
|
|
1424
|
+
pMap.set(p.id, newP.id);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const gMap = new Map();
|
|
1428
|
+
for(let g of groups.filter(g=>g.owner !== 'system')) {
|
|
1429
|
+
const newG = await prisma.group.create({ data: { ...g, id: undefined, botId: newBot.id, permissions: {
|
|
1430
|
+
create: g.permissions.map(gp => ({ permissionId: pMap.get(gp.permissionId) })).filter(p=>p.permissionId)
|
|
1431
|
+
}}});
|
|
1432
|
+
gMap.set(g.id, newG.id);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
for(let u of users) {
|
|
1436
|
+
await prisma.user.create({ data: { ...u, id: undefined, botId: newBot.id, groups: {
|
|
1437
|
+
create: u.groups.map(ug => ({ groupId: gMap.get(ug.groupId) })).filter(g=>g.groupId)
|
|
1438
|
+
}}});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
const eventGraphsEntry = zipEntries.find(e => e.entryName === 'event_graphs.json');
|
|
1443
|
+
if (eventGraphsEntry) {
|
|
1444
|
+
const eventGraphs = JSON.parse(eventGraphsEntry.getData().toString('utf8'));
|
|
1445
|
+
for (let graph of eventGraphs) {
|
|
1446
|
+
delete graph.id;
|
|
1447
|
+
graph.botId = newBot.id;
|
|
1448
|
+
await prisma.eventGraph.create({ data: graph });
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
const pluginDataStoreEntry = zipEntries.find(e => e.entryName === 'plugin_data_store.json');
|
|
1453
|
+
if (pluginDataStoreEntry) {
|
|
1454
|
+
console.log(`[Import] Импорт PluginDataStore для бота ${newBot.id}`);
|
|
1455
|
+
const pluginDataStore = JSON.parse(pluginDataStoreEntry.getData().toString('utf8'));
|
|
1456
|
+
console.log(`[Import] Найдено записей PluginDataStore: ${pluginDataStore.length}`);
|
|
1457
|
+
|
|
1458
|
+
for (let dataRecord of pluginDataStore) {
|
|
1459
|
+
delete dataRecord.id;
|
|
1460
|
+
dataRecord.botId = newBot.id;
|
|
1461
|
+
await prisma.pluginDataStore.create({ data: dataRecord });
|
|
1462
|
+
}
|
|
1463
|
+
console.log(`[Import] PluginDataStore успешно импортирован`);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
const pluginsEntry = zipEntries.find(e => e.entryName === 'plugins.json');
|
|
1467
|
+
if (pluginsEntry) {
|
|
1468
|
+
const plugins = JSON.parse(pluginsEntry.getData().toString('utf8'));
|
|
1469
|
+
const pluginsDir = path.join(os.homedir(), '.blockmine', 'storage', 'plugins');
|
|
1470
|
+
const botPluginsDir = path.join(pluginsDir, newBot.username);
|
|
1471
|
+
await fs.mkdir(botPluginsDir, { recursive: true });
|
|
1472
|
+
|
|
1473
|
+
for (let pluginData of plugins) {
|
|
1474
|
+
const oldPath = pluginData.path;
|
|
1475
|
+
const pluginName = pluginData.name;
|
|
1476
|
+
const newPluginPath = path.join(botPluginsDir, pluginName);
|
|
1477
|
+
|
|
1478
|
+
delete pluginData.id;
|
|
1479
|
+
pluginData.botId = newBot.id;
|
|
1480
|
+
pluginData.path = path.resolve(newPluginPath);
|
|
1481
|
+
|
|
1482
|
+
for (const entry of zipEntries) {
|
|
1483
|
+
if (entry.entryName.startsWith(`plugins/${pluginName}/`)) {
|
|
1484
|
+
const relativePath = entry.entryName.replace(`plugins/${pluginName}/`, '');
|
|
1485
|
+
if (relativePath) {
|
|
1486
|
+
const destPath = path.join(newPluginPath, relativePath);
|
|
1487
|
+
const destDir = path.dirname(destPath);
|
|
1488
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
1489
|
+
|
|
1490
|
+
if (!entry.isDirectory) {
|
|
1491
|
+
await fs.writeFile(destPath, entry.getData());
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
await prisma.installedPlugin.create({ data: pluginData });
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
res.status(201).json(newBot);
|
|
1502
|
+
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
console.error('Failed to import bot:', error);
|
|
1505
|
+
res.status(500).json({ error: `Failed to import bot: ${error.message}` });
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1264
1509
|
module.exports = router;
|
|
@@ -727,7 +727,20 @@ class BotManager {
|
|
|
727
727
|
const child = this.bots.get(botId);
|
|
728
728
|
if (child) {
|
|
729
729
|
this.eventGraphManager.unloadGraphsForBot(botId);
|
|
730
|
+
|
|
730
731
|
child.send({ type: 'stop' });
|
|
732
|
+
|
|
733
|
+
setTimeout(() => {
|
|
734
|
+
if (!child.killed) {
|
|
735
|
+
console.log(`[BotManager] Принудительное завершение процесса бота ${botId}`);
|
|
736
|
+
try {
|
|
737
|
+
child.kill('SIGKILL');
|
|
738
|
+
} catch (error) {
|
|
739
|
+
console.error(`[BotManager] Ошибка при принудительном завершении бота ${botId}:`, error);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}, 5000);
|
|
743
|
+
|
|
731
744
|
this.botConfigs.delete(botId);
|
|
732
745
|
return { success: true };
|
|
733
746
|
}
|
|
@@ -20,6 +20,7 @@ const prisma = new PrismaClient();
|
|
|
20
20
|
const pluginUiState = new Map();
|
|
21
21
|
const pendingRequests = new Map();
|
|
22
22
|
const entityMoveThrottles = new Map();
|
|
23
|
+
let connectionTimeout = null;
|
|
23
24
|
|
|
24
25
|
function sendLog(content) {
|
|
25
26
|
if (process.send) {
|
|
@@ -202,6 +203,13 @@ process.on('message', async (message) => {
|
|
|
202
203
|
|
|
203
204
|
bot = mineflayer.createBot(botOptions);
|
|
204
205
|
|
|
206
|
+
connectionTimeout = setTimeout(() => {
|
|
207
|
+
if (bot && !bot.player) {
|
|
208
|
+
sendLog('[System] Таймаут подключения к серверу (30 секунд). Завершение работы...');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}, 30000);
|
|
212
|
+
|
|
205
213
|
bot.pluginUiState = pluginUiState;
|
|
206
214
|
|
|
207
215
|
let isReady = false;
|
|
@@ -489,6 +497,10 @@ process.on('message', async (message) => {
|
|
|
489
497
|
});
|
|
490
498
|
|
|
491
499
|
bot.on('login', () => {
|
|
500
|
+
if (connectionTimeout) {
|
|
501
|
+
clearTimeout(connectionTimeout);
|
|
502
|
+
connectionTimeout = null;
|
|
503
|
+
}
|
|
492
504
|
sendLog('[Event: login] Успешно залогинился!');
|
|
493
505
|
if (process.send) {
|
|
494
506
|
process.send({ type: 'bot_ready' });
|
|
@@ -507,9 +519,19 @@ process.on('message', async (message) => {
|
|
|
507
519
|
process.exit(0);
|
|
508
520
|
});
|
|
509
521
|
|
|
510
|
-
bot.on('error', (err) =>
|
|
522
|
+
bot.on('error', (err) => {
|
|
523
|
+
if (connectionTimeout) {
|
|
524
|
+
clearTimeout(connectionTimeout);
|
|
525
|
+
connectionTimeout = null;
|
|
526
|
+
}
|
|
527
|
+
sendLog(`[Event: error] Произошла ошибка: ${err.stack || err.message}`);
|
|
528
|
+
});
|
|
511
529
|
|
|
512
530
|
bot.on('end', (reason) => {
|
|
531
|
+
if (connectionTimeout) {
|
|
532
|
+
clearTimeout(connectionTimeout);
|
|
533
|
+
connectionTimeout = null;
|
|
534
|
+
}
|
|
513
535
|
sendLog(`[Event: end] Отключен от сервера. Причина: ${reason}`);
|
|
514
536
|
process.exit(0);
|
|
515
537
|
});
|
|
@@ -575,6 +597,10 @@ process.on('message', async (message) => {
|
|
|
575
597
|
sendLog(`[System] Error reloading configuration: ${error.message}`);
|
|
576
598
|
}
|
|
577
599
|
} else if (message.type === 'stop') {
|
|
600
|
+
if (connectionTimeout) {
|
|
601
|
+
clearTimeout(connectionTimeout);
|
|
602
|
+
connectionTimeout = null;
|
|
603
|
+
}
|
|
578
604
|
if (bot) bot.quit();
|
|
579
605
|
else process.exit(0);
|
|
580
606
|
} else if (message.type === 'chat') {
|
|
@@ -653,6 +679,23 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
653
679
|
process.exit(1);
|
|
654
680
|
});
|
|
655
681
|
|
|
682
|
+
process.on('SIGTERM', () => {
|
|
683
|
+
sendLog('[System] Получен сигнал SIGTERM. Завершение работы...');
|
|
684
|
+
if (bot) {
|
|
685
|
+
try {
|
|
686
|
+
bot.quit();
|
|
687
|
+
} catch (error) {
|
|
688
|
+
sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
process.exit(0);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
process.on('SIGKILL', () => {
|
|
695
|
+
sendLog('[System] Получен сигнал SIGKILL. Принудительное завершение...');
|
|
696
|
+
process.exit(0);
|
|
697
|
+
});
|
|
698
|
+
|
|
656
699
|
function serializeEntity(entity) {
|
|
657
700
|
if (!entity) return null;
|
|
658
701
|
return {
|
|
@@ -14,10 +14,12 @@ async function ensurePluginDependencies(pluginPath, pluginName) {
|
|
|
14
14
|
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
15
15
|
if (packageJson.dependencies && Object.keys(packageJson.dependencies).length > 0) {
|
|
16
16
|
console.log(`[PluginLoader] У плагина ${pluginName} есть зависимости, устанавливаем их...`);
|
|
17
|
+
const shell = process.platform === 'win32' ? process.env.ComSpec : '/bin/sh';
|
|
17
18
|
try {
|
|
18
|
-
execSync('npm install', {
|
|
19
|
-
cwd: pluginPath,
|
|
20
|
-
stdio: 'pipe'
|
|
19
|
+
execSync('npm install', {
|
|
20
|
+
cwd: pluginPath,
|
|
21
|
+
stdio: 'pipe',
|
|
22
|
+
shell: shell
|
|
21
23
|
});
|
|
22
24
|
console.log(`[PluginLoader] Зависимости для плагина ${pluginName} установлены`);
|
|
23
25
|
} catch (installError) {
|
|
@@ -92,10 +94,12 @@ async function initializePlugins(bot, installedPlugins = [], prisma) {
|
|
|
92
94
|
if (moduleMatch) {
|
|
93
95
|
const missingModule = moduleMatch[1];
|
|
94
96
|
sendLog(`[PluginLoader] Попытка установки недостающего модуля ${missingModule} в папку плагина ${plugin.name}`);
|
|
97
|
+
const shell = process.platform === 'win32' ? process.env.ComSpec : '/bin/sh';
|
|
95
98
|
try {
|
|
96
|
-
execSync(`npm install ${missingModule}`, {
|
|
97
|
-
cwd: plugin.path,
|
|
98
|
-
stdio: 'pipe'
|
|
99
|
+
execSync(`npm install ${missingModule}`, {
|
|
100
|
+
cwd: plugin.path,
|
|
101
|
+
stdio: 'pipe',
|
|
102
|
+
shell: shell
|
|
99
103
|
});
|
|
100
104
|
sendLog(`[PluginLoader] Модуль ${missingModule} успешно установлен в папку плагина ${plugin.name}, повторная попытка загрузки`);
|
|
101
105
|
|