blockmine 1.20.0 → 1.21.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 (68) hide show
  1. package/.claude/settings.local.json +13 -1
  2. package/CHANGELOG.md +15 -0
  3. package/backend/src/core/BotManager.js +2 -2
  4. package/backend/src/core/BotProcess.js +8 -0
  5. package/backend/src/core/BreakLoopSignal.js +8 -0
  6. package/backend/src/core/EventGraphManager.js +5 -0
  7. package/backend/src/core/GraphExecutionEngine.js +36 -639
  8. package/backend/src/core/NodeRegistry.js +144 -1
  9. package/backend/src/core/nodes/action_bot_look_at.js +36 -0
  10. package/backend/src/core/nodes/action_bot_set_variable.js +32 -0
  11. package/backend/src/core/nodes/action_http_request.js +98 -0
  12. package/backend/src/core/nodes/action_send_log.js +23 -0
  13. package/backend/src/core/nodes/action_send_message.js +32 -0
  14. package/backend/src/core/nodes/array_add_element.js +23 -0
  15. package/backend/src/core/nodes/array_contains.js +40 -0
  16. package/backend/src/core/nodes/array_find_index.js +23 -0
  17. package/backend/src/core/nodes/array_get_by_index.js +23 -0
  18. package/backend/src/core/nodes/array_get_random_element.js +32 -0
  19. package/backend/src/core/nodes/array_remove_by_index.js +30 -0
  20. package/backend/src/core/nodes/bot_get_position.js +20 -0
  21. package/backend/src/core/nodes/data_array_literal.js +31 -0
  22. package/backend/src/core/nodes/data_boolean_literal.js +21 -0
  23. package/backend/src/core/nodes/data_cast.js +34 -0
  24. package/backend/src/core/nodes/data_get_argument.js +23 -0
  25. package/backend/src/core/nodes/data_get_bot_look.js +14 -0
  26. package/backend/src/core/nodes/data_get_entity_field.js +18 -0
  27. package/backend/src/core/nodes/data_get_server_players.js +18 -0
  28. package/backend/src/core/nodes/data_get_user_field.js +40 -0
  29. package/backend/src/core/nodes/data_get_variable.js +23 -0
  30. package/backend/src/core/nodes/data_length.js +25 -0
  31. package/backend/src/core/nodes/data_make_object.js +31 -0
  32. package/backend/src/core/nodes/data_number_literal.js +21 -0
  33. package/backend/src/core/nodes/data_string_literal.js +34 -0
  34. package/backend/src/core/nodes/debug_log.js +16 -0
  35. package/backend/src/core/nodes/flow_branch.js +15 -0
  36. package/backend/src/core/nodes/flow_break.js +14 -0
  37. package/backend/src/core/nodes/flow_for_each.js +39 -0
  38. package/backend/src/core/nodes/flow_sequence.js +16 -0
  39. package/backend/src/core/nodes/flow_switch.js +47 -0
  40. package/backend/src/core/nodes/flow_while.js +64 -0
  41. package/backend/src/core/nodes/logic_compare.js +33 -0
  42. package/backend/src/core/nodes/logic_operation.js +35 -0
  43. package/backend/src/core/nodes/math_operation.js +31 -0
  44. package/backend/src/core/nodes/math_random_number.js +43 -0
  45. package/backend/src/core/nodes/object_create.js +40 -0
  46. package/backend/src/core/nodes/object_delete.js +26 -0
  47. package/backend/src/core/nodes/object_get.js +23 -0
  48. package/backend/src/core/nodes/object_has_key.js +30 -0
  49. package/backend/src/core/nodes/object_set.js +27 -0
  50. package/backend/src/core/nodes/string_concat.js +27 -0
  51. package/backend/src/core/nodes/string_contains.js +41 -0
  52. package/backend/src/core/nodes/string_ends_with.js +43 -0
  53. package/backend/src/core/nodes/string_equals.js +36 -0
  54. package/backend/src/core/nodes/string_length.js +36 -0
  55. package/backend/src/core/nodes/string_matches.js +39 -0
  56. package/backend/src/core/nodes/string_split.js +37 -0
  57. package/backend/src/core/nodes/string_starts_with.js +43 -0
  58. package/backend/src/core/nodes/user_check_blacklist.js +37 -0
  59. package/backend/src/core/nodes/user_get_groups.js +36 -0
  60. package/backend/src/core/nodes/user_get_permissions.js +36 -0
  61. package/backend/src/core/nodes/user_set_blacklist.js +37 -0
  62. package/frontend/dist/assets/index-B9GedHEa.js +8352 -0
  63. package/frontend/dist/assets/index-zLiy9MDx.css +1 -0
  64. package/frontend/dist/index.html +2 -2
  65. package/frontend/package.json +1 -0
  66. package/package.json +1 -1
  67. package/frontend/dist/assets/index-BFd7YoAj.css +0 -1
  68. package/frontend/dist/assets/index-CMMutadc.js +0 -8352
@@ -1,13 +1,7 @@
1
- const User = require('./UserService');
2
1
  const { PrismaClient } = require('@prisma/client');
3
2
  const prisma = new PrismaClient();
4
3
 
5
- class BreakLoopSignal extends Error {
6
- constructor() {
7
- super("Loop break signal");
8
- this.name = "BreakLoopSignal";
9
- }
10
- }
4
+ const BreakLoopSignal = require('./BreakLoopSignal');
11
5
 
12
6
  class GraphExecutionEngine {
13
7
  constructor(nodeRegistry, botManagerOrApi = null) {
@@ -102,6 +96,18 @@ class GraphExecutionEngine {
102
96
  }
103
97
 
104
98
  async executeNode(node) {
99
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
100
+ if (nodeConfig && typeof nodeConfig.executor === 'function') {
101
+ const helpers = {
102
+ resolvePinValue: this.resolvePinValue.bind(this),
103
+ traverse: this.traverse.bind(this),
104
+ memo: this.memo,
105
+ clearLoopBodyMemo: this.clearLoopBodyMemo.bind(this),
106
+ };
107
+ await nodeConfig.executor.call(this, node, this.context, helpers);
108
+ return;
109
+ }
110
+
105
111
  const execCacheKey = `${node.id}_executed`;
106
112
  if (this.memo.has(execCacheKey)) {
107
113
  return;
@@ -109,203 +115,13 @@ class GraphExecutionEngine {
109
115
  this.memo.set(execCacheKey, true);
110
116
 
111
117
  switch (node.type) {
112
- case 'user:set_blacklist': {
113
- const userObject = await this.resolvePinValue(node, 'user', null);
114
- const blacklistStatus = await this.resolvePinValue(node, 'blacklist_status', false);
115
- let updatedUser = null;
116
-
117
- if (userObject && userObject.username) {
118
- const user = await User.getUser(userObject.username, this.context.botId);
119
- if (user) {
120
- updatedUser = await prisma.user.update({
121
- where: { id: user.id },
122
- data: { isBlacklisted: blacklistStatus }
123
- });
124
- User.clearCache(user.username, this.context.botId);
125
- }
126
- }
127
- this.memo.set(`${node.id}:updated_user`, updatedUser);
128
- await this.traverse(node, 'exec');
129
- break;
130
- }
131
- case 'action:send_message': {
132
- let message = String(await this.resolvePinValue(node, 'message', ''));
133
- const chatType = await this.resolvePinValue(node, 'chat_type', this.context.typeChat);
134
- const recipient = await this.resolvePinValue(node, 'recipient', this.context.user?.username);
135
-
136
- // Парсим и заменяем переменные в формате {varName}
137
- const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
138
- const matches = [...message.matchAll(variablePattern)];
139
-
140
- for (const match of matches) {
141
- const varName = match[1];
142
- const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
143
- message = message.replace(match[0], String(varValue));
144
- }
145
118
 
146
- this.context.bot.sendMessage(chatType, message, recipient);
147
- await this.traverse(node, 'exec');
148
- break;
149
- }
150
- case 'action:server_command': {
151
- const command = await this.resolvePinValue(node, 'command', '');
152
- if (command) this.context.bot.executeCommand(command);
153
- await this.traverse(node, 'exec');
154
- break;
155
- }
156
- case 'action:send_log': {
157
- const message = await this.resolvePinValue(node, 'message', '');
158
- if (this.botManager?.appendLog && this.context?.botId) {
159
- this.botManager.appendLog(this.context.botId, `[Graph] ${message}`);
160
- } else {
161
- console.log(`[Graph Log] ${message}`);
162
- }
163
- await this.traverse(node, 'exec');
164
- break;
165
- }
166
- case 'action:bot_look_at': {
167
- const target = await this.resolvePinValue(node, 'target');
168
- const yOffset = await this.resolvePinValue(node, 'add_y', 0);
169
-
170
- if (target && this.context.bot?.lookAt) {
171
- let finalPosition;
172
- if (target.position) {
173
- finalPosition = { ...target.position };
174
- } else if (target.x !== undefined && target.y !== undefined && target.z !== undefined) {
175
- finalPosition = { ...target };
176
- }
177
-
178
- if (finalPosition) {
179
- finalPosition.y += Number(yOffset || 0);
180
- this.context.bot.lookAt(finalPosition);
181
- }
182
- }
183
- await this.traverse(node, 'exec');
184
- break;
185
- }
186
- case 'action:bot_set_variable': {
187
- const varName = await this.resolvePinValue(node, 'name', '');
188
- const varValue = await this.resolvePinValue(node, 'value');
189
- let shouldPersist = await this.resolvePinValue(node, 'persist', false);
190
-
191
- if (this.context.eventType === 'command') {
192
- shouldPersist = false;
193
- }
194
119
 
195
- if (varName) {
196
- this.context.variables[varName] = varValue;
197
- if (this.context.persistenceIntent) {
198
- this.context.persistenceIntent.set(varName, shouldPersist);
199
- }
200
- }
201
- await this.traverse(node, 'exec');
202
- break;
203
- }
204
- case 'flow:branch': {
205
- const condition = await this.resolvePinValue(node, 'condition', false);
206
- await this.traverse(node, condition ? 'exec_true' : 'exec_false');
207
- break;
208
- }
209
- case 'flow:break': {
210
- throw new BreakLoopSignal();
211
- }
212
- case 'flow:for_each': {
213
- const array = await this.resolvePinValue(node, 'array', []);
214
- if (Array.isArray(array)) {
215
- try {
216
- for (let i = 0; i < array.length; i++) {
217
- const element = array[i];
218
- this.memo.set(`${node.id}:element`, element);
219
- this.memo.set(`${node.id}:index`, i);
220
- this.clearLoopBodyMemo(node);
221
- await this.traverse(node, 'loop_body');
222
- }
223
- } catch (e) {
224
- if (e instanceof BreakLoopSignal) {
225
- } else {
226
- throw e;
227
- }
228
- }
229
- }
230
- await this.traverse(node, 'completed');
231
- break;
232
- }
233
- case 'flow:while': {
234
- let iteration = 0;
235
- const maxIterations = 1000;
236
-
237
- try {
238
- while (iteration < maxIterations) {
239
- const condition = await this.resolvePinValue(node, 'condition', false);
240
- if (!condition) break;
241
-
242
- this.memo.set(`${node.id}:iteration`, iteration);
243
- this.clearLoopBodyMemo(node);
244
- await this.traverse(node, 'loop_body');
245
- iteration++;
246
- }
247
-
248
- if (iteration >= maxIterations) {
249
- console.warn(`[GraphExecutionEngine] Цикл while достиг максимального количества итераций (${maxIterations})`);
250
- }
251
- } catch (e) {
252
- if (e instanceof BreakLoopSignal) {
253
- } else {
254
- throw e;
255
- }
256
- }
257
-
258
- await this.traverse(node, 'completed');
259
- break;
260
- }
261
- case 'flow:sequence': {
262
- const pinCount = node.data?.pinCount || 2;
263
- for (let i = 0; i < pinCount; i++) {
264
- await this.traverse(node, `exec_${i}`);
265
- }
266
- break;
267
- }
268
- case 'flow:switch': {
269
- const value = await this.resolvePinValue(node, 'value');
270
- const caseCount = node.data?.caseCount || 0;
271
- let matched = false;
272
-
273
- for (let i = 0; i < caseCount; i++) {
274
- const caseValue = node.data?.[`case_${i}`];
275
- if (caseValue !== undefined) {
276
- let isMatch = false;
277
-
278
- if (Array.isArray(value) && Array.isArray(caseValue)) {
279
- isMatch = JSON.stringify(value) === JSON.stringify(caseValue);
280
- } else if (typeof value === 'object' && typeof caseValue === 'object' && value !== null && caseValue !== null) {
281
- isMatch = JSON.stringify(value) === JSON.stringify(caseValue);
282
- } else if (typeof value === 'number' && typeof caseValue === 'number') {
283
- isMatch = value === caseValue;
284
- } else if (typeof value === 'boolean' && typeof caseValue === 'boolean') {
285
- isMatch = value === caseValue;
286
- } else {
287
- isMatch = String(value) === String(caseValue);
288
- }
289
-
290
- if (isMatch) {
291
- await this.traverse(node, `case_${i}`);
292
- matched = true;
293
- break;
294
- }
295
- }
296
- }
297
-
298
- if (!matched) {
299
- await this.traverse(node, 'default');
300
- }
301
- break;
302
- }
303
- case 'debug:log': {
304
- const value = await this.resolvePinValue(node, 'value');
305
- console.log('[Graph Debug]', value);
306
- await this.traverse(node, 'exec');
307
- break;
308
- }
120
+
121
+
122
+
123
+
124
+
309
125
  case 'string:contains':
310
126
  case 'string:matches':
311
127
  case 'string:equals': {
@@ -407,77 +223,24 @@ class GraphExecutionEngine {
407
223
  return this.memo.get(cacheKey);
408
224
  }
409
225
 
226
+ const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
227
+ if (nodeConfig && typeof nodeConfig.evaluator === 'function') {
228
+ const helpers = {
229
+ resolvePinValue: this.resolvePinValue.bind(this),
230
+ memo: this.memo,
231
+ };
232
+ const result = await nodeConfig.evaluator.call(this, node, pinId, this.context, helpers);
233
+
234
+ const isVolatile = this.isNodeVolatile(node);
235
+ if (!isVolatile) {
236
+ this.memo.set(cacheKey, result);
237
+ }
238
+ return result;
239
+ }
240
+
410
241
  let result;
411
242
 
412
243
  switch (node.type) {
413
- case 'user:check_blacklist': {
414
- const userIdentifier = await this.resolvePinValue(node, 'user', null);
415
- let isBlacklisted = false;
416
- let usernameToFind = null;
417
-
418
- if (typeof userIdentifier === 'string') {
419
- usernameToFind = userIdentifier;
420
- } else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
421
- usernameToFind = userIdentifier.username;
422
- }
423
-
424
- if (usernameToFind) {
425
- const user = await User.getUser(usernameToFind, this.context.botId);
426
- if (user) {
427
- isBlacklisted = user.isBlacklisted;
428
- }
429
- }
430
- result = isBlacklisted;
431
- break;
432
- }
433
- case 'user:get_groups': {
434
- const userIdentifier = await this.resolvePinValue(node, 'user', null);
435
- let groups = [];
436
- let usernameToFind = null;
437
-
438
- if (typeof userIdentifier === 'string') {
439
- usernameToFind = userIdentifier;
440
- } else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
441
- usernameToFind = userIdentifier.username;
442
- }
443
-
444
- if (usernameToFind) {
445
- const user = await User.getUser(usernameToFind, this.context.botId);
446
- if (user && user.groups) {
447
- groups = user.groups.map(g => g.group.name);
448
- }
449
- }
450
- result = groups;
451
- break;
452
- }
453
- case 'user:get_permissions': {
454
- const userIdentifier = await this.resolvePinValue(node, 'user', null);
455
- let permissions = [];
456
- let usernameToFind = null;
457
-
458
- if (typeof userIdentifier === 'string') {
459
- usernameToFind = userIdentifier;
460
- } else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
461
- usernameToFind = userIdentifier.username;
462
- }
463
-
464
- if (usernameToFind) {
465
- const user = await User.getUser(usernameToFind, this.context.botId);
466
- if (user && user.permissionsSet) {
467
- permissions = Array.from(user.permissionsSet);
468
- }
469
- }
470
- result = permissions;
471
- break;
472
- }
473
- case 'bot:get_position': {
474
- if (this.context.bot?.entity?.position) {
475
- result = this.context.bot.entity.position;
476
- } else {
477
- result = null;
478
- }
479
- break;
480
- }
481
244
  case 'user:set_blacklist':
482
245
  result = this.memo.get(`${node.id}:updated_user`);
483
246
  break;
@@ -506,342 +269,10 @@ class GraphExecutionEngine {
506
269
  case 'event:entityGone':
507
270
  result = this.context[pinId];
508
271
  break;
509
-
510
- case 'data:get_variable':
511
- const varName = node.data?.variableName || node.data?.selectedVariable || '';
512
- if (!varName) {
513
- console.warn('[GraphExecutionEngine] data:get_variable: не указано имя переменной', node.data);
514
- result = null;
515
- } else {
516
- result = this.context.variables.hasOwnProperty(varName) ? this.context.variables[varName] : null;
517
- }
518
- break;
519
-
520
-
521
-
522
- case 'data:get_argument': {
523
- const args = this.context.args || {};
524
- const argName = node.data?.argumentName || '';
525
- if (pinId === 'value') {
526
- result = args && argName && args[argName] !== undefined ? args[argName] : defaultValue;
527
- } else if (pinId === 'exists') {
528
- result = args && argName && args[argName] !== undefined;
529
- }
530
- break;
531
- }
532
-
533
- case 'data:length': {
534
- const data = await this.resolvePinValue(node, 'data');
535
- if (Array.isArray(data) || typeof data === 'string') {
536
- result = data.length;
537
- } else {
538
- result = 0;
539
- }
540
- break;
541
- }
542
-
543
- case 'math:operation': {
544
- const op = node.data?.operation || '+';
545
- const a = Number(await this.resolvePinValue(node, 'a', 0));
546
- const b = Number(await this.resolvePinValue(node, 'b', 0));
547
- switch (op) {
548
- case '+': result = a + b; break;
549
- case '-': result = a - b; break;
550
- case '*': result = a * b; break;
551
- case '/': result = b !== 0 ? a / b : 0; break;
552
- default: result = 0;
553
- }
554
- break;
555
- }
556
-
557
- case 'math:random_number': {
558
- const minRaw = await this.resolvePinValue(node, 'min', node.data.min ?? '0');
559
- const maxRaw = await this.resolvePinValue(node, 'max', node.data.max ?? '1');
560
-
561
- const minStr = String(minRaw);
562
- const maxStr = String(maxRaw);
563
-
564
- const min = parseFloat(minStr.replace(',', '.'));
565
- const max = parseFloat(maxStr.replace(',', '.'));
566
-
567
- if (isNaN(min) || isNaN(max)) {
568
- result = NaN;
569
- break;
570
- }
571
-
572
- const produceFloat = minStr.includes('.') || minStr.includes(',') || !Number.isInteger(min) ||
573
- maxStr.includes('.') || maxStr.includes(',') || !Number.isInteger(max);
574
-
575
- if (produceFloat) {
576
- result = Math.random() * (max - min) + min;
577
- } else {
578
- const minInt = Math.ceil(min);
579
- const maxInt = Math.floor(max);
580
- result = Math.floor(Math.random() * (maxInt - minInt + 1)) + minInt;
581
- }
582
- break;
583
- }
584
-
585
- case 'logic:operation': {
586
- const op = node.data?.operation || 'AND';
587
- const inputs = [];
588
- const pinCount = node.data?.pinCount || 2;
589
-
590
- for (let i = 0; i < pinCount; i++) {
591
- const value = await this.resolvePinValue(node, `pin_${i}`, false);
592
- inputs.push(value);
593
- }
594
-
595
- switch (op) {
596
- case 'AND': result = inputs.every(Boolean); break;
597
- case 'OR': result = inputs.some(Boolean); break;
598
- case 'NOT': result = !inputs[0]; break;
599
- default: result = false;
600
- }
601
- break;
602
- }
603
-
604
- case 'string:concat': {
605
- const pinCount = node.data?.pinCount || 2;
606
- let finalString = '';
607
- for (let i = 0; i < pinCount; i++) {
608
- const part = await this.resolvePinValue(node, `pin_${i}`, '');
609
- finalString += String(part ?? '');
610
- }
611
- result = finalString;
612
- break;
613
- }
614
-
615
- case 'data:array_literal': {
616
- const numPins = node.data?.pinCount || 0;
617
- const items = [];
618
- for (let i = 0; i < numPins; i++) {
619
- const value = await this.resolvePinValue(node, `pin_${i}`) ||
620
- node.data?.[`item_${i}`] ||
621
- node.data?.[`value_${i}`];
622
- items.push(value);
623
- }
624
- result = items;
625
- break;
626
- }
627
-
628
- case 'data:make_object': {
629
- const numPins = node.data?.pinCount || 0;
630
- const obj = {};
631
- for (let i = 0; i < numPins; i++) {
632
- const key = node.data[`key_${i}`];
633
- if (key) {
634
- obj[key] = await this.resolvePinValue(node, `value_${i}`);
635
- }
636
- }
637
- result = obj;
638
- break;
639
- }
640
-
641
- case 'data:get_entity_field': {
642
- const entity = await this.resolvePinValue(node, 'entity');
643
- result = entity ? entity[pinId] : defaultValue;
644
- break;
645
- }
646
-
647
- case 'data:cast': {
648
- const value = await this.resolvePinValue(node, 'value');
649
- const targetType = node.data?.targetType || 'String';
650
- switch (targetType) {
651
- case 'String': result = String(value ?? ''); break;
652
- case 'Number': result = Number(value); if (isNaN(result)) result = 0; break;
653
- case 'Boolean': result = ['true', '1', 'yes'].includes(String(value).toLowerCase()); break;
654
- default: result = value;
655
- }
656
- break;
657
- }
658
-
659
- case 'string:contains': {
660
- if (pinId === 'result') {
661
- const haystack = String(await this.resolvePinValue(node, 'haystack', ''));
662
- const needle = String(await this.resolvePinValue(node, 'needle', ''));
663
- const caseSensitive = await this.resolvePinValue(node, 'case_sensitive', false);
664
-
665
- if (caseSensitive) {
666
- return haystack.includes(needle);
667
- } else {
668
- return haystack.toLowerCase().includes(needle.toLowerCase());
669
- }
670
- }
671
- break;
672
- }
673
- case 'string:matches': {
674
- if (pinId === 'result') {
675
- const str = String(await this.resolvePinValue(node, 'string', ''));
676
- const regexStr = String(await this.resolvePinValue(node, 'regex', ''));
677
- try {
678
- result = new RegExp(regexStr).test(str);
679
- } catch (e) { result = false; }
680
- }
681
- break;
682
- }
683
- case 'data:string_literal':
684
- result = await this.resolvePinValue(node, 'value', '');
685
- break;
686
-
687
- case 'data:get_user_field': {
688
- const userIdentifier = await this.resolvePinValue(node, 'user');
689
- let userObject = null;
690
-
691
- if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
692
- userObject = userIdentifier;
693
- } else if (typeof userIdentifier === 'string' && userIdentifier.length > 0) {
694
- userObject = await User.getUser(userIdentifier, this.context.botId);
695
- }
696
-
697
- if (userObject) {
698
- if (pinId === 'username') {
699
- result = userObject.username;
700
- } else if (pinId === 'groups') {
701
- result = userObject.groups ? userObject.groups.map(g => g.group?.name).filter(Boolean) : [];
702
- } else if (pinId === 'permissions') {
703
- result = userObject.permissionsSet ? Array.from(userObject.permissionsSet) : [];
704
- } else if (pinId === 'isBlacklisted') {
705
- result = !!userObject.isBlacklisted;
706
- } else {
707
- result = defaultValue;
708
- }
709
- } else {
710
- result = defaultValue;
711
- }
712
- break;
713
- }
714
- case 'data:get_server_players':
715
- result = this.context.players || [];
716
- break;
717
- case 'data:get_bot_look':
718
- result = this.context.botState ? { yaw: this.context.botState.yaw, pitch: this.context.botState.pitch } : null;
719
- break;
720
-
721
- case 'array:get_random_element': {
722
- const arr = await this.resolvePinValue(node, 'array', []);
723
- if (!Array.isArray(arr) || arr.length === 0) {
724
- result = null;
725
- } else {
726
- const randomIndex = Math.floor(Math.random() * arr.length);
727
- this.memo.set(`${node.id}:index`, randomIndex);
728
- if (pinId === 'element') {
729
- result = arr[randomIndex];
730
- } else if (pinId === 'index') {
731
- result = randomIndex;
732
- }
733
- }
734
- break;
735
- }
736
- case 'array:add_element': {
737
- const arr = await this.resolvePinValue(node, 'array', []);
738
- const element = await this.resolvePinValue(node, 'element', null);
739
- result = Array.isArray(arr) ? [...arr, element] : [element];
740
- break;
741
- }
742
- case 'array:remove_by_index': {
743
- const arr = await this.resolvePinValue(node, 'array', []);
744
- const index = await this.resolvePinValue(node, 'index', -1);
745
- if (!Array.isArray(arr) || index < 0 || index >= arr.length) {
746
- result = arr || [];
747
- } else {
748
- const newArr = [...arr];
749
- newArr.splice(index, 1);
750
- result = newArr;
751
- }
752
- break;
753
- }
754
- case 'array:get_by_index': {
755
- const arr = await this.resolvePinValue(node, 'array', []);
756
- const index = await this.resolvePinValue(node, 'index', -1);
757
- result = (!Array.isArray(arr) || index < 0 || index >= arr.length) ? null : arr[index];
758
- break;
759
- }
760
- case 'array:find_index': {
761
- const arr = await this.resolvePinValue(node, 'array', []);
762
- const element = await this.resolvePinValue(node, 'element', null);
763
- result = Array.isArray(arr) ? arr.indexOf(element) : -1;
764
- break;
765
- }
766
- case 'array:contains': {
767
- const arr = await this.resolvePinValue(node, 'array', []);
768
- const element = await this.resolvePinValue(node, 'element', null);
769
- if (Array.isArray(arr)) {
770
- const index = arr.indexOf(element);
771
- this.memo.set(`${node.id}:index`, index);
772
- if (pinId === 'result') {
773
- result = index !== -1;
774
- } else if (pinId === 'index') {
775
- result = index;
776
- }
777
- } else {
778
- this.memo.set(`${node.id}:index`, -1);
779
- if (pinId === 'result') {
780
- result = false;
781
- } else if (pinId === 'index') {
782
- result = -1;
783
- }
784
- }
785
- break;
786
- }
787
-
788
-
789
- case 'object:create': {
790
- if (node.data?.advanced) {
791
- try {
792
- result = JSON.parse(node.data.jsonValue || '{}');
793
- } catch (e) {
794
- console.error('Ошибка парсинга JSON в object:create:', e);
795
- result = {};
796
- }
797
- } else {
798
- const numPins = node.data?.pinCount || 0;
799
- const obj = {};
800
- for (let i = 0; i < numPins; i++) {
801
- const key = node.data[`key_${i}`];
802
- if (key) {
803
- obj[key] = await this.resolvePinValue(node, `value_${i}`);
804
- }
805
- }
806
- result = obj;
807
- }
808
- break;
809
- }
810
- case 'object:get': {
811
- const obj = await this.resolvePinValue(node, 'object', {});
812
- const key = await this.resolvePinValue(node, 'key', '');
813
- result = obj[key] ?? defaultValue;
814
- break;
815
- }
816
- case 'object:set': {
817
- const obj = await this.resolvePinValue(node, 'object', {});
818
- const key = await this.resolvePinValue(node, 'key', '');
819
- const val = await this.resolvePinValue(node, 'value');
820
- const newObj = { ...obj };
821
- newObj[key] = val;
822
- result = newObj;
823
- break;
824
- }
825
- case 'object:delete': {
826
- const obj = await this.resolvePinValue(node, 'object', {});
827
- const key = await this.resolvePinValue(node, 'key', '');
828
- const newObj = { ...obj };
829
- delete newObj[key];
830
- result = newObj;
831
- break;
832
- }
833
- case 'object:has_key': {
834
- const obj = await this.resolvePinValue(node, 'object', {});
835
- const key = await this.resolvePinValue(node, 'key', '');
836
- const exists = obj.hasOwnProperty(key);
837
- this.memo.set(`${node.id}:value`, exists ? obj[key] : null);
838
- if (pinId === 'result') {
839
- result = exists;
840
- } else if (pinId === 'value') {
841
- result = this.memo.get(`${node.id}:value`);
842
- }
272
+ case 'event:health':
273
+ case 'event:botDied':
274
+ result = this.context[pinId];
843
275
  break;
844
- }
845
276
 
846
277
  case 'flow:for_each': {
847
278
  if (pinId === 'element') {
@@ -851,41 +282,7 @@ class GraphExecutionEngine {
851
282
  }
852
283
  break;
853
284
  }
854
- case 'flow:while': {
855
- if (pinId === 'iteration') {
856
- result = this.memo.get(`${node.id}:iteration`);
857
- }
858
- break;
859
- }
860
-
861
- case 'string:equals': {
862
- const strA = String(await this.resolvePinValue(node, 'a', ''));
863
- const strB = String(await this.resolvePinValue(node, 'b', ''));
864
- const caseSensitive = node.data?.case_sensitive ?? true;
865
- if (pinId === 'result') {
866
- result = caseSensitive ? strA === strB : strA.toLowerCase() === strB.toLowerCase();
867
- }
868
- break;
869
- }
870
285
 
871
- case 'logic:compare': {
872
- const op = node.data?.operation || '==';
873
- const valA = await this.resolvePinValue(node, 'a');
874
- const valB = await this.resolvePinValue(node, 'b');
875
- if (pinId === 'result') {
876
- switch (op) {
877
- case '==': result = valA == valB; break;
878
- case '!=': result = valA != valB; break;
879
- case '>': result = valA > valB; break;
880
- case '<': result = valA < valB; break;
881
- case '>=': result = valA >= valB; break;
882
- case '<=': result = valA <= valB; break;
883
- default: result = false;
884
- }
885
- }
886
- break;
887
- }
888
-
889
286
  default:
890
287
  result = defaultValue;
891
288
  break;