blockmine 1.14.0 → 1.15.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.
@@ -4,6 +4,8 @@ const { PrismaClient } = require('@prisma/client');
4
4
  const { authorize } = require('../middleware/auth');
5
5
  const logger = require('../../lib/logger');
6
6
  const crypto = require('crypto');
7
+ const fse = require('fs-extra');
8
+ const path = require('path');
7
9
 
8
10
  const prisma = new PrismaClient();
9
11
  const router = express.Router({ mergeParams: true });
@@ -14,7 +16,17 @@ router.get('/',
14
16
  const { botId } = req.params;
15
17
  const eventGraphs = await prisma.eventGraph.findMany({
16
18
  where: { botId: parseInt(botId) },
17
- include: { triggers: true },
19
+ include: {
20
+ triggers: true,
21
+ pluginOwner: {
22
+ select: {
23
+ id: true,
24
+ name: true,
25
+ version: true,
26
+ sourceType: true
27
+ }
28
+ }
29
+ },
18
30
  orderBy: { createdAt: 'desc' }
19
31
  });
20
32
 
@@ -46,21 +58,34 @@ router.post('/',
46
58
  }
47
59
 
48
60
  const { botId } = req.params;
49
- const { name } = req.body;
50
-
51
- const initialGraph = {
61
+ const { name, graphJson, variables, isEnabled = true } = req.body;
62
+
63
+ let graphJsonString;
64
+ if (graphJson) {
65
+ if (typeof graphJson === 'string') {
66
+ graphJsonString = graphJson;
67
+ } else {
68
+ graphJsonString = JSON.stringify(graphJson);
69
+ }
70
+ } else {
71
+ graphJsonString = JSON.stringify({
52
72
  nodes: [],
53
73
  connections: []
54
- };
74
+ });
75
+ }
55
76
 
56
77
  try {
57
78
  const newGraph = await prisma.eventGraph.create({
58
79
  data: {
59
80
  botId: parseInt(botId),
60
- name,
61
- graphJson: JSON.stringify(initialGraph)
81
+ name: name.trim(),
82
+ isEnabled: isEnabled,
83
+ graphJson: graphJsonString,
84
+ variables: variables || '[]',
85
+ pluginOwnerId: req.body.pluginOwnerId || null
62
86
  }
63
87
  });
88
+
64
89
  res.status(201).json(newGraph);
65
90
  } catch (error) {
66
91
  if (error.code === 'P2002') {
@@ -84,8 +109,20 @@ router.get('/:graphId',
84
109
  const { graphId } = req.params;
85
110
  const eventGraph = await prisma.eventGraph.findUnique({
86
111
  where: { id: parseInt(graphId) },
87
- include: { triggers: true }
112
+ include: {
113
+ triggers: true,
114
+ pluginOwner: {
115
+ select: {
116
+ id: true,
117
+ name: true,
118
+ version: true,
119
+ sourceType: true
120
+ }
121
+ }
122
+ }
88
123
  });
124
+
125
+
89
126
  if (!eventGraph) {
90
127
  return res.status(404).json({ error: 'Event graph not found' });
91
128
  }
@@ -109,12 +146,15 @@ router.put('/:graphId',
109
146
  }
110
147
 
111
148
  const { graphId } = req.params;
112
- const { name, isEnabled, graphJson, variables } = req.body;
149
+ const { name, isEnabled, graphJson, variables, pluginOwnerId } = req.body;
150
+
151
+
113
152
 
114
153
  try {
115
154
  const dataToUpdate = {};
116
155
  if (name !== undefined) dataToUpdate.name = name;
117
156
  if (isEnabled !== undefined) dataToUpdate.isEnabled = isEnabled;
157
+ if (pluginOwnerId !== undefined) dataToUpdate.pluginOwnerId = pluginOwnerId;
118
158
 
119
159
  if (graphJson !== undefined) {
120
160
  dataToUpdate.graphJson = graphJson;
@@ -149,6 +189,25 @@ router.put('/:graphId',
149
189
  include: { triggers: true },
150
190
  });
151
191
 
192
+ if (graphJson && updatedGraph.pluginOwnerId) {
193
+ try {
194
+ const plugin = await prisma.installedPlugin.findUnique({
195
+ where: { id: updatedGraph.pluginOwnerId }
196
+ });
197
+
198
+ if (plugin) {
199
+ const graphDir = path.join(plugin.path, 'graph');
200
+ await fse.mkdir(graphDir, { recursive: true });
201
+
202
+ const graphFile = path.join(graphDir, `${updatedGraph.name}.json`);
203
+ await fse.writeJson(graphFile, JSON.parse(graphJson), { spaces: 2 });
204
+ console.log(`[API] Граф события ${updatedGraph.name} сохранен в ${graphFile}`);
205
+ }
206
+ } catch (error) {
207
+ console.error(`[API] Ошибка сохранения графа события в папку плагина:`, error);
208
+ }
209
+ }
210
+
152
211
  res.json(updatedGraph);
153
212
  } catch (error) {
154
213
  logger.error(error, "--- ERROR CAUGHT IN EVENT GRAPH UPDATE ---");
@@ -64,6 +64,89 @@ router.post('/:id/clear-data', authenticate, authorize('plugin:settings:edit'),
64
64
  }
65
65
  });
66
66
 
67
+ router.get('/:id/info', authenticate, authorize('plugin:list'), async (req, res) => {
68
+ try {
69
+ const pluginId = parseInt(req.params.id);
70
+
71
+ const plugin = await prisma.installedPlugin.findUnique({
72
+ where: { id: pluginId },
73
+ include: {
74
+ commands: {
75
+ select: {
76
+ id: true,
77
+ name: true,
78
+ description: true,
79
+ isEnabled: true,
80
+ isVisual: true,
81
+ owner: true
82
+ }
83
+ },
84
+ eventGraphs: {
85
+ select: {
86
+ id: true,
87
+ name: true,
88
+ isEnabled: true,
89
+ createdAt: true,
90
+ updatedAt: true
91
+ }
92
+ }
93
+ }
94
+ });
95
+
96
+ if (!plugin) {
97
+ return res.status(404).json({ error: 'Плагин не найден.' });
98
+ }
99
+
100
+ res.json(plugin);
101
+ } catch (error) {
102
+ console.error(`[API Error] /plugins/:id/info:`, error);
103
+ res.status(500).json({ error: 'Не удалось получить информацию о плагине.' });
104
+ }
105
+ });
106
+
107
+ router.get('/bot/:botId', authenticate, authorize('plugin:list'), async (req, res) => {
108
+ try {
109
+ const botId = parseInt(req.params.botId);
110
+
111
+ const plugins = await prisma.installedPlugin.findMany({
112
+ where: { botId },
113
+ select: {
114
+ id: true,
115
+ name: true,
116
+ version: true,
117
+ description: true,
118
+ sourceType: true,
119
+ isEnabled: true,
120
+ commands: {
121
+ select: {
122
+ id: true,
123
+ name: true,
124
+ description: true,
125
+ isEnabled: true,
126
+ isVisual: true,
127
+ owner: true
128
+ }
129
+ },
130
+ eventGraphs: {
131
+ select: {
132
+ id: true,
133
+ name: true,
134
+ isEnabled: true,
135
+ createdAt: true,
136
+ updatedAt: true
137
+ }
138
+ }
139
+ },
140
+ orderBy: { name: 'asc' }
141
+ });
142
+
143
+ res.json(plugins);
144
+ } catch (error) {
145
+ console.error(`[API Error] /plugins/bot/:botId:`, error);
146
+ res.status(500).json({ error: 'Не удалось получить список плагинов.' });
147
+ }
148
+ });
149
+
67
150
  router.get('/catalog/:name', async (req, res) => {
68
151
  try {
69
152
  const pluginName = req.params.name;
@@ -253,6 +253,20 @@ process.on('message', async (message) => {
253
253
  }
254
254
  }
255
255
 
256
+ let pluginOwnerId = null;
257
+ if (command.owner && command.owner.startsWith('plugin:')) {
258
+ const pluginName = command.owner.replace('plugin:', '');
259
+ const plugin = await prisma.installedPlugin.findFirst({
260
+ where: {
261
+ botId: bot.config.id,
262
+ name: pluginName
263
+ }
264
+ });
265
+ if (plugin) {
266
+ pluginOwnerId = plugin.id;
267
+ }
268
+ }
269
+
256
270
  const commandData = {
257
271
  botId: bot.config.id,
258
272
  name: command.name,
@@ -264,6 +278,7 @@ process.on('message', async (message) => {
264
278
  aliases: JSON.stringify(command.aliases || []),
265
279
  allowedChatTypes: JSON.stringify(command.allowedChatTypes || ['chat', 'private']),
266
280
  argumentsJson: JSON.stringify(command.args || []),
281
+ pluginOwnerId: pluginOwnerId,
267
282
  };
268
283
 
269
284
  await prisma.command.upsert({
@@ -339,6 +354,69 @@ process.on('message', async (message) => {
339
354
  }, 10000);
340
355
  });
341
356
  },
357
+ registerEventGraph: async (graphData) => {
358
+ try {
359
+ let pluginOwnerId = null;
360
+ if (graphData.owner && graphData.owner.startsWith('plugin:')) {
361
+ const pluginName = graphData.owner.replace('plugin:', '');
362
+ const plugin = await prisma.installedPlugin.findFirst({
363
+ where: {
364
+ botId: bot.config.id,
365
+ name: pluginName
366
+ }
367
+ });
368
+ if (plugin) {
369
+ pluginOwnerId = plugin.id;
370
+ }
371
+ }
372
+
373
+ const graphDataToSave = {
374
+ botId: bot.config.id,
375
+ name: graphData.name,
376
+ isEnabled: graphData.isEnabled !== undefined ? graphData.isEnabled : true,
377
+ graphJson: graphData.graphJson || JSON.stringify({ nodes: [], connections: [] }),
378
+ variables: JSON.stringify(graphData.variables || []),
379
+ pluginOwnerId: pluginOwnerId,
380
+ };
381
+
382
+ const eventGraph = await prisma.eventGraph.upsert({
383
+ where: {
384
+ botId_name: {
385
+ botId: bot.config.id,
386
+ name: graphData.name
387
+ }
388
+ },
389
+ update: {
390
+ isEnabled: graphDataToSave.isEnabled,
391
+ graphJson: graphDataToSave.graphJson,
392
+ variables: graphDataToSave.variables,
393
+ pluginOwnerId: graphDataToSave.pluginOwnerId,
394
+ },
395
+ create: graphDataToSave,
396
+ });
397
+
398
+ if (graphData.triggers && Array.isArray(graphData.triggers)) {
399
+ await prisma.eventTrigger.deleteMany({
400
+ where: { graphId: eventGraph.id }
401
+ });
402
+
403
+ if (graphData.triggers.length > 0) {
404
+ await prisma.eventTrigger.createMany({
405
+ data: graphData.triggers.map(eventType => ({
406
+ graphId: eventGraph.id,
407
+ eventType
408
+ }))
409
+ });
410
+ }
411
+ }
412
+
413
+ sendLog(`[API] Граф события "${graphData.name}" от плагина "${graphData.owner}" зарегистрирован.`);
414
+ return eventGraph;
415
+ } catch (error) {
416
+ sendLog(`[API] Ошибка при регистрации графа события: ${error.message}`);
417
+ throw error;
418
+ }
419
+ },
342
420
  executeCommand: (command) => {
343
421
  sendLog(`[Graph] Выполнение серверной команды: ${command}`);
344
422
  bot.chat(command);
@@ -676,7 +754,13 @@ process.on('message', async (message) => {
676
754
  process.on('unhandledRejection', (reason, promise) => {
677
755
  const errorMsg = `[FATAL] Необработанная ошибка процесса: ${reason?.stack || reason}`;
678
756
  sendLog(errorMsg);
679
- process.exit(1);
757
+ setTimeout(() => process.exit(1), 100);
758
+ });
759
+
760
+ process.on('uncaughtException', (error) => {
761
+ const errorMsg = `[FATAL] Необработанное исключение: ${error.stack || error.message}`;
762
+ sendLog(errorMsg);
763
+ setTimeout(() => process.exit(1), 100);
680
764
  });
681
765
 
682
766
  process.on('SIGTERM', () => {
@@ -688,14 +772,26 @@ process.on('SIGTERM', () => {
688
772
  sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
689
773
  }
690
774
  }
691
- process.exit(0);
775
+ setTimeout(() => process.exit(0), 100);
692
776
  });
693
777
 
694
- process.on('SIGKILL', () => {
695
- sendLog('[System] Получен сигнал SIGKILL. Принудительное завершение...');
696
- process.exit(0);
778
+ process.on('SIGINT', () => {
779
+ sendLog('[System] Получен сигнал SIGINT. Завершение работы...');
780
+ if (bot) {
781
+ try {
782
+ bot.quit();
783
+ } catch (error) {
784
+ sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
785
+ }
786
+ }
787
+ setTimeout(() => process.exit(0), 100);
697
788
  });
698
789
 
790
+ // process.on('SIGKILL', () => {
791
+ // sendLog('[System] Получен сигнал SIGKILL. Принудительное завершение...');
792
+ // process.exit(0);
793
+ // });
794
+
699
795
  function serializeEntity(entity) {
700
796
  if (!entity) return null;
701
797
  return {
@@ -266,6 +266,41 @@ class GraphExecutionEngine {
266
266
  await this.traverse(node, 'exec');
267
267
  break;
268
268
  }
269
+ case 'array:get_random_element': {
270
+ await this.traverse(node, 'element');
271
+ break;
272
+ }
273
+ case 'array:add_element':
274
+ case 'array:remove_by_index':
275
+ case 'array:get_by_index':
276
+ case 'array:find_index':
277
+ case 'array:contains': {
278
+ await this.traverse(node, 'result');
279
+ break;
280
+ }
281
+ case 'data:array_literal':
282
+ case 'data:make_object':
283
+ case 'data:get_variable':
284
+ case 'data:get_argument':
285
+ case 'data:length':
286
+ case 'data:get_entity_field':
287
+ case 'data:cast':
288
+ case 'data:string_literal':
289
+ case 'data:get_user_field':
290
+ case 'data:get_server_players':
291
+ case 'data:get_bot_look':
292
+ case 'math:operation':
293
+ case 'math:random_number':
294
+ case 'logic:operation':
295
+ case 'string:concat':
296
+ case 'object:create':
297
+ case 'object:get':
298
+ case 'object:set':
299
+ case 'object:delete':
300
+ case 'object:has_key': {
301
+ await this.traverse(node, 'value');
302
+ break;
303
+ }
269
304
  }
270
305
  }
271
306
 
@@ -535,7 +570,10 @@ class GraphExecutionEngine {
535
570
  const numPins = node.data?.pinCount || 0;
536
571
  const items = [];
537
572
  for (let i = 0; i < numPins; i++) {
538
- items.push(await this.resolvePinValue(node, `pin_${i}`));
573
+ const value = await this.resolvePinValue(node, `pin_${i}`) ||
574
+ node.data?.[`item_${i}`] ||
575
+ node.data?.[`value_${i}`];
576
+ items.push(value);
539
577
  }
540
578
  result = items;
541
579
  break;