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
@@ -232,6 +232,7 @@ class NodeRegistry {
232
232
  category: 'Поток',
233
233
  description: 'if/else логика',
234
234
  graphType: all,
235
+ executor: require('./nodes/flow_branch').execute,
235
236
  pins: {
236
237
  inputs: [
237
238
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -250,6 +251,7 @@ class NodeRegistry {
250
251
  category: 'Поток',
251
252
  description: 'Выполняет действия по очереди',
252
253
  graphType: all,
254
+ executor: require('./nodes/flow_sequence').execute,
253
255
  pins: {
254
256
  inputs: [
255
257
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true }
@@ -267,6 +269,7 @@ class NodeRegistry {
267
269
  category: 'Поток',
268
270
  description: 'Выполняет "Тело цикла" для каждого элемента в "Массиве".',
269
271
  graphType: all,
272
+ executor: require('./nodes/flow_for_each').execute,
270
273
  pins: {
271
274
  inputs: [
272
275
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -281,12 +284,34 @@ class NodeRegistry {
281
284
  }
282
285
  });
283
286
 
287
+ this.registerNodeType({
288
+ type: 'flow:while',
289
+ label: '🔁 Цикл While',
290
+ category: 'Поток',
291
+ description: 'Выполняет "Тело цикла" пока условие истинно.',
292
+ graphType: all,
293
+ executor: require('./nodes/flow_while').execute,
294
+ evaluator: require('./nodes/flow_while').evaluate,
295
+ pins: {
296
+ inputs: [
297
+ { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
298
+ { id: 'condition', name: 'Условие', type: 'Boolean', required: true }
299
+ ],
300
+ outputs: [
301
+ { id: 'loop_body', name: 'Тело цикла', type: 'Exec' },
302
+ { id: 'iteration', name: 'Итерация', type: 'Number' },
303
+ { id: 'completed', name: 'Завершено', type: 'Exec' }
304
+ ]
305
+ }
306
+ });
307
+
284
308
  this.registerNodeType({
285
309
  type: 'flow:break',
286
310
  label: '🛑 Выйти из цикла',
287
311
  category: 'Поток',
288
312
  description: 'Немедленно прерывает выполнение цикла (For Each Loop) и передает управление на его выход Completed.',
289
313
  graphType: all,
314
+ executor: require('./nodes/flow_break').execute,
290
315
  pins: {
291
316
  inputs: [
292
317
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true }
@@ -302,6 +327,7 @@ class NodeRegistry {
302
327
  description: 'Отправляет сообщение в чат. Поддерживает переменные в формате {varName}',
303
328
  graphType: all,
304
329
  dynamicPins: true,
330
+ executor: require('./nodes/action_send_message').execute,
305
331
  pins: {
306
332
  inputs: [
307
333
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -321,6 +347,7 @@ class NodeRegistry {
321
347
  category: 'Действия',
322
348
  description: 'Отправляет сообщение в консоль на странице бота.',
323
349
  graphType: all,
350
+ executor: require('./nodes/action_send_log').execute,
324
351
  pins: {
325
352
  inputs: [
326
353
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -338,6 +365,7 @@ class NodeRegistry {
338
365
  category: 'Действия',
339
366
  description: 'Поворачивает голову бота в сторону координат или сущности.',
340
367
  graphType: all,
368
+ executor: require('./nodes/action_bot_look_at').execute,
341
369
  pins: {
342
370
  inputs: [
343
371
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -356,6 +384,7 @@ class NodeRegistry {
356
384
  category: 'Действия',
357
385
  description: 'Сохраняет значение в переменную графа.',
358
386
  graphType: all,
387
+ executor: require('./nodes/action_bot_set_variable').execute,
359
388
  pins: {
360
389
  inputs: [
361
390
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -369,12 +398,41 @@ class NodeRegistry {
369
398
  }
370
399
  });
371
400
 
401
+ this.registerNodeType({
402
+ type: 'action:http_request',
403
+ label: '🌐 HTTP-запрос',
404
+ category: 'Действия',
405
+ description: 'Выполняет HTTP-запрос (GET, POST, PUT, DELETE и т.д.) и возвращает ответ.',
406
+ graphType: all,
407
+ executor: require('./nodes/action_http_request').execute,
408
+ pins: {
409
+ inputs: [
410
+ { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
411
+ { id: 'url', name: 'URL', type: 'String', required: true },
412
+ { id: 'method', name: 'Метод', type: 'String', required: false },
413
+ { id: 'headers', name: 'Заголовки (JSON)', type: 'String', required: false },
414
+ { id: 'body', name: 'Тело (JSON)', type: 'Wildcard', required: false },
415
+ { id: 'timeout', name: 'Таймаут (мс)', type: 'Number', required: false }
416
+ ],
417
+ outputs: [
418
+ { id: 'exec', name: 'Успех', type: 'Exec' },
419
+ { id: 'exec_error', name: 'Ошибка', type: 'Exec' },
420
+ { id: 'status', name: 'Статус', type: 'Number' },
421
+ { id: 'response', name: 'Ответ', type: 'Wildcard' },
422
+ { id: 'response_headers', name: 'Заголовки ответа', type: 'Object' },
423
+ { id: 'success', name: 'Успешно', type: 'Boolean' },
424
+ { id: 'error', name: 'Ошибка', type: 'String' }
425
+ ]
426
+ }
427
+ });
428
+
372
429
  this.registerNodeType({
373
430
  type: 'data:get_argument',
374
431
  label: '📥 Получить аргумент',
375
432
  category: 'Данные',
376
433
  description: 'Получает значение аргумента команды по его имени.',
377
434
  graphType: command,
435
+ evaluator: require('./nodes/data_get_argument').evaluate,
378
436
  data: {
379
437
  argumentName: {
380
438
  type: 'argument',
@@ -396,6 +454,7 @@ class NodeRegistry {
396
454
  category: 'Данные',
397
455
  description: 'Получает значение переменной графа.',
398
456
  graphType: all,
457
+ evaluator: require('./nodes/data_get_variable').evaluate,
399
458
  pins: {
400
459
  inputs: [],
401
460
  outputs: [
@@ -410,6 +469,7 @@ class NodeRegistry {
410
469
  category: 'Данные',
411
470
  description: 'Получает определенное поле из объекта сущности (например, "position.x", "username").',
412
471
  graphType: all,
472
+ evaluator: require('./nodes/data_get_entity_field').evaluate,
413
473
  pins: {
414
474
  inputs: [
415
475
  { id: 'entity', name: 'Сущность', type: 'Object', required: true },
@@ -427,8 +487,10 @@ class NodeRegistry {
427
487
  type: 'data:string_literal',
428
488
  label: '📜 Строка',
429
489
  category: 'Данные',
430
- description: 'Простое текстовое значение.',
490
+ description: 'Текстовое значение с поддержкой переменных. Используйте {имя} для вставки значений.',
431
491
  graphType: all,
492
+ dynamicPins: true,
493
+ evaluator: require('./nodes/data_string_literal').evaluate,
432
494
  pins: {
433
495
  inputs: [],
434
496
  outputs: [
@@ -443,6 +505,7 @@ class NodeRegistry {
443
505
  category: 'Данные',
444
506
  description: 'Простое числовое значение.',
445
507
  graphType: all,
508
+ evaluator: require('./nodes/data_number_literal').evaluate,
446
509
  pins: {
447
510
  inputs: [
448
511
  { id: 'value', name: 'Значение', type: 'Number', required: true }
@@ -459,6 +522,7 @@ class NodeRegistry {
459
522
  category: 'Данные',
460
523
  description: 'Значение Истина/Ложь.',
461
524
  graphType: all,
525
+ evaluator: require('./nodes/data_boolean_literal').evaluate,
462
526
  pins: {
463
527
  inputs: [
464
528
  { id: 'value', name: 'Значение', type: 'Boolean', required: true }
@@ -476,6 +540,7 @@ class NodeRegistry {
476
540
  description: 'Создает массив из элементов.',
477
541
  graphType: all,
478
542
  dynamicPins: true,
543
+ evaluator: require('./nodes/data_array_literal').evaluate,
479
544
  pins: {
480
545
  inputs: [],
481
546
  outputs: [
@@ -491,6 +556,7 @@ class NodeRegistry {
491
556
  description: 'Создает JSON-объект из пар ключ-значение.',
492
557
  graphType: all,
493
558
  dynamicPins: true,
559
+ evaluator: require('./nodes/data_make_object').evaluate,
494
560
  pins: {
495
561
  inputs: [],
496
562
  outputs: [
@@ -505,6 +571,7 @@ class NodeRegistry {
505
571
  category: 'Данные',
506
572
  description: 'Приводит входящее значение к указанному целевому типу.',
507
573
  graphType: all,
574
+ evaluator: require('./nodes/data_cast').evaluate,
508
575
  pins: {
509
576
  inputs: [
510
577
  { id: 'value', name: 'Значение', type: 'Wildcard', required: true }
@@ -521,6 +588,7 @@ class NodeRegistry {
521
588
  category: 'Массив',
522
589
  graphType: 'all',
523
590
  description: 'Возвращает количество элементов в массиве или длину строки.',
591
+ evaluator: require('./nodes/data_length').evaluate,
524
592
  pins: {
525
593
  inputs: [
526
594
  { id: 'data', name: 'Массив или Строка', type: 'Any', required: true }
@@ -537,6 +605,8 @@ class NodeRegistry {
537
605
  category: 'Строки',
538
606
  description: 'Проверяет, содержит ли одна строка другую.',
539
607
  graphType: all,
608
+ executor: require('./nodes/string_contains').execute,
609
+ evaluator: require('./nodes/string_contains').evaluate,
540
610
  pins: {
541
611
  inputs: [
542
612
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -551,12 +621,35 @@ class NodeRegistry {
551
621
  }
552
622
  });
553
623
 
624
+ this.registerNodeType({
625
+ type: 'string:matches',
626
+ label: '🔎 Строка: Совпадает с RegEx',
627
+ category: 'Строки',
628
+ description: 'Проверяет, совпадает ли строка с регулярным выражением.',
629
+ graphType: all,
630
+ executor: require('./nodes/string_matches').execute,
631
+ evaluator: require('./nodes/string_matches').evaluate,
632
+ pins: {
633
+ inputs: [
634
+ { id: 'exec', name: 'Exec', type: 'Exec', required: true },
635
+ { id: 'string', name: 'Строка', type: 'String', required: true },
636
+ { id: 'regex', name: 'RegEx', type: 'String', required: true }
637
+ ],
638
+ outputs: [
639
+ { id: 'exec', name: 'Exec', type: 'Exec' },
640
+ { id: 'result', name: 'Результат', type: 'Boolean' }
641
+ ]
642
+ }
643
+ });
644
+
554
645
  this.registerNodeType({
555
646
  type: 'string:equals',
556
647
  label: 'Строка: Равно',
557
648
  category: 'Строки',
558
649
  description: 'Проверяет, равны ли строки (с учетом/без учета регистра).',
559
650
  graphType: all,
651
+ executor: require('./nodes/string_equals').execute,
652
+ evaluator: require('./nodes/string_equals').evaluate,
560
653
  pins: {
561
654
  inputs: [
562
655
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -577,6 +670,8 @@ class NodeRegistry {
577
670
  category: 'Строки',
578
671
  description: 'Проверяет, начинается ли строка с указанной подстроки.',
579
672
  graphType: all,
673
+ executor: require('./nodes/string_starts_with').execute,
674
+ evaluator: require('./nodes/string_starts_with').evaluate,
580
675
  pins: {
581
676
  inputs: [
582
677
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -597,6 +692,8 @@ class NodeRegistry {
597
692
  category: 'Строки',
598
693
  description: 'Проверяет, заканчивается ли строка указанной подстрокой.',
599
694
  graphType: all,
695
+ executor: require('./nodes/string_ends_with').execute,
696
+ evaluator: require('./nodes/string_ends_with').evaluate,
600
697
  pins: {
601
698
  inputs: [
602
699
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -617,6 +714,8 @@ class NodeRegistry {
617
714
  category: 'Строки',
618
715
  description: 'Возвращает количество символов в строке.',
619
716
  graphType: all,
717
+ executor: require('./nodes/string_length').execute,
718
+ evaluator: require('./nodes/string_length').evaluate,
620
719
  pins: {
621
720
  inputs: [
622
721
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -635,6 +734,8 @@ class NodeRegistry {
635
734
  category: 'Строки',
636
735
  description: 'Разделяет строку на массив подстрок по разделителю.',
637
736
  graphType: all,
737
+ executor: require('./nodes/string_split').execute,
738
+ evaluator: require('./nodes/string_split').evaluate,
638
739
  pins: {
639
740
  inputs: [
640
741
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -655,6 +756,7 @@ class NodeRegistry {
655
756
  description: 'Объединяет две или более строки в одну.',
656
757
  graphType: all,
657
758
  dynamicPins: true,
759
+ evaluator: require('./nodes/string_concat').evaluate,
658
760
  pins: {
659
761
  inputs: [],
660
762
  outputs: [
@@ -669,6 +771,7 @@ class NodeRegistry {
669
771
  category: 'Математика',
670
772
  description: 'Выполняет математическую операцию над двумя числами.',
671
773
  graphType: all,
774
+ evaluator: require('./nodes/math_operation').evaluate,
672
775
  pins: {
673
776
  inputs: [
674
777
  { id: 'a', name: 'A', type: 'Number', required: true },
@@ -687,6 +790,7 @@ class NodeRegistry {
687
790
  description: 'Выполняет логическую операцию. Для НЕ (NOT) используется только вход А.',
688
791
  graphType: all,
689
792
  dynamicPins: true,
793
+ evaluator: require('./nodes/logic_operation').evaluate,
690
794
  pins: {
691
795
  inputs: [
692
796
  { id: 'a', name: 'A', type: 'Boolean', required: true },
@@ -704,6 +808,7 @@ class NodeRegistry {
704
808
  category: 'Отладка',
705
809
  description: 'Выводит значение в консоль терминала, где запущен бот.',
706
810
  graphType: all,
811
+ executor: require('./nodes/debug_log').execute,
707
812
  pins: {
708
813
  inputs: [
709
814
  { id: 'exec', name: 'Exec', type: 'Exec' },
@@ -721,6 +826,7 @@ class NodeRegistry {
721
826
  category: 'Математика',
722
827
  graphType: 'all',
723
828
  description: 'Генерирует случайное число в заданном диапазоне.',
829
+ evaluator: require('./nodes/math_random_number').evaluate,
724
830
  pins: {
725
831
  inputs: [
726
832
  { id: 'min', name: 'Мин', type: 'Number' },
@@ -736,6 +842,7 @@ class NodeRegistry {
736
842
  category: 'Массив',
737
843
  graphType: 'all',
738
844
  description: 'Возвращает случайный элемент из массива и его индекс.',
845
+ evaluator: require('./nodes/array_get_random_element').evaluate,
739
846
  pins: {
740
847
  inputs: [
741
848
  { id: 'array', name: 'Массив', type: 'Array', required: true }
@@ -753,6 +860,7 @@ class NodeRegistry {
753
860
  category: 'Массив',
754
861
  description: 'Проверяет, содержит ли массив указанный элемент и возвращает его индекс.',
755
862
  graphType: all,
863
+ evaluator: require('./nodes/array_contains').evaluate,
756
864
  pins: {
757
865
  inputs: [
758
866
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -771,6 +879,7 @@ class NodeRegistry {
771
879
  category: 'Массив',
772
880
  description: 'Получает элемент массива по его индексу.',
773
881
  graphType: all,
882
+ evaluator: require('./nodes/array_get_by_index').evaluate,
774
883
  pins: {
775
884
  inputs: [
776
885
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -788,6 +897,7 @@ class NodeRegistry {
788
897
  category: 'Массив',
789
898
  description: 'Добавляет элемент в конец массива.',
790
899
  graphType: all,
900
+ evaluator: require('./nodes/array_add_element').evaluate,
791
901
  pins: {
792
902
  inputs: [
793
903
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -805,6 +915,7 @@ class NodeRegistry {
805
915
  category: 'Массив',
806
916
  description: 'Удаляет элемент из массива по его индексу.',
807
917
  graphType: all,
918
+ evaluator: require('./nodes/array_remove_by_index').evaluate,
808
919
  pins: {
809
920
  inputs: [
810
921
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -822,6 +933,7 @@ class NodeRegistry {
822
933
  category: 'Массив',
823
934
  description: 'Находит индекс элемента в массиве (или -1 если не найден).',
824
935
  graphType: all,
936
+ evaluator: require('./nodes/array_find_index').evaluate,
825
937
  pins: {
826
938
  inputs: [
827
939
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -840,6 +952,7 @@ class NodeRegistry {
840
952
  description: 'Создает объект из пар ключ-значение.',
841
953
  graphType: all,
842
954
  dynamicPins: true,
955
+ evaluator: require('./nodes/object_create').evaluate,
843
956
  pins: {
844
957
  inputs: [],
845
958
  outputs: [
@@ -854,6 +967,7 @@ class NodeRegistry {
854
967
  category: 'Объект',
855
968
  description: 'Получает значение по ключу из объекта.',
856
969
  graphType: all,
970
+ evaluator: require('./nodes/object_get').evaluate,
857
971
  pins: {
858
972
  inputs: [
859
973
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -871,6 +985,7 @@ class NodeRegistry {
871
985
  category: 'Объект',
872
986
  description: 'Добавляет или изменяет значение по ключу в объекте.',
873
987
  graphType: all,
988
+ evaluator: require('./nodes/object_set').evaluate,
874
989
  pins: {
875
990
  inputs: [
876
991
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -889,6 +1004,7 @@ class NodeRegistry {
889
1004
  category: 'Объект',
890
1005
  description: 'Удаляет ключ из объекта.',
891
1006
  graphType: all,
1007
+ evaluator: require('./nodes/object_delete').evaluate,
892
1008
  pins: {
893
1009
  inputs: [
894
1010
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -906,6 +1022,7 @@ class NodeRegistry {
906
1022
  category: 'Объект',
907
1023
  description: 'Проверяет наличие ключа в объекте и возвращает значение.',
908
1024
  graphType: all,
1025
+ evaluator: require('./nodes/object_has_key').evaluate,
909
1026
  pins: {
910
1027
  inputs: [
911
1028
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -924,6 +1041,7 @@ class NodeRegistry {
924
1041
  category: 'Данные',
925
1042
  graphType: 'all',
926
1043
  description: 'Возвращает массив с именами всех игроков на сервере.',
1044
+ evaluator: require('./nodes/data_get_server_players').evaluate,
927
1045
  pins: {
928
1046
  inputs: [],
929
1047
  outputs: [
@@ -938,6 +1056,7 @@ class NodeRegistry {
938
1056
  category: 'Логика',
939
1057
  description: 'Сравнивает два значения.',
940
1058
  graphType: all,
1059
+ evaluator: require('./nodes/logic_compare').evaluate,
941
1060
  pins: {
942
1061
  inputs: [
943
1062
  { id: 'a', name: 'A', type: 'Wildcard' },
@@ -955,6 +1074,7 @@ class NodeRegistry {
955
1074
  category: 'Бот',
956
1075
  description: 'Возвращает текущую позицию бота в мире.',
957
1076
  graphType: all,
1077
+ evaluator: require('./nodes/bot_get_position').evaluate,
958
1078
  pins: {
959
1079
  inputs: [],
960
1080
  outputs: [
@@ -970,6 +1090,7 @@ class NodeRegistry {
970
1090
  category: 'Пользователи',
971
1091
  description: 'Проверяет, находится ли пользователь в черном списке.',
972
1092
  graphType: all,
1093
+ evaluator: require('./nodes/user_check_blacklist').evaluate,
973
1094
  pins: {
974
1095
  inputs: [
975
1096
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -986,6 +1107,7 @@ class NodeRegistry {
986
1107
  category: 'Пользователи',
987
1108
  description: 'Добавляет или убирает пользователя из черного списка.',
988
1109
  graphType: all,
1110
+ executor: require('./nodes/user_set_blacklist').execute,
989
1111
  pins: {
990
1112
  inputs: [
991
1113
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -1006,6 +1128,7 @@ class NodeRegistry {
1006
1128
  category: 'Пользователь',
1007
1129
  description: 'Возвращает массив названий групп, в которых состоит пользователь.',
1008
1130
  graphType: all,
1131
+ evaluator: require('./nodes/user_get_groups').evaluate,
1009
1132
  pins: {
1010
1133
  inputs: [
1011
1134
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1022,6 +1145,7 @@ class NodeRegistry {
1022
1145
  category: 'Пользователь',
1023
1146
  description: 'Возвращает массив прав пользователя.',
1024
1147
  graphType: all,
1148
+ evaluator: require('./nodes/user_get_permissions').evaluate,
1025
1149
  pins: {
1026
1150
  inputs: [
1027
1151
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1038,6 +1162,7 @@ class NodeRegistry {
1038
1162
  category: 'Данные',
1039
1163
  description: 'Получает различные данные из объекта пользователя.',
1040
1164
  graphType: all,
1165
+ evaluator: require('./nodes/data_get_user_field').evaluate,
1041
1166
  pins: {
1042
1167
  inputs: [
1043
1168
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1065,6 +1190,23 @@ class NodeRegistry {
1065
1190
  }
1066
1191
  });
1067
1192
 
1193
+ this.registerNodeType({
1194
+ type: 'event:health',
1195
+ label: '❤️ Здоровье/Голод изменилось',
1196
+ category: 'События',
1197
+ description: 'Срабатывает при изменении здоровья, голода или насыщения бота.',
1198
+ graphType: event,
1199
+ pins: {
1200
+ inputs: [],
1201
+ outputs: [
1202
+ { id: 'exec', name: 'Выполнить', type: 'Exec' },
1203
+ { id: 'health', name: 'Здоровье', type: 'Number' },
1204
+ { id: 'food', name: 'Голод', type: 'Number' },
1205
+ { id: 'saturation', name: 'Насыщение', type: 'Number' }
1206
+ ]
1207
+ }
1208
+ });
1209
+
1068
1210
  this.registerNodeType({
1069
1211
  type: 'flow:switch',
1070
1212
  label: '🔄 Switch (свитч)',
@@ -1072,6 +1214,7 @@ class NodeRegistry {
1072
1214
  description: 'Выполняет разные действия в зависимости от значения. Автоматически определяет тип сравнения.',
1073
1215
  graphType: all,
1074
1216
  dynamicPins: true,
1217
+ executor: require('./nodes/flow_switch').execute,
1075
1218
  pins: {
1076
1219
  inputs: [
1077
1220
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @param {object} node - Экземпляр узла из графа.
3
+ * @param {object} context - Контекст выполнения графа.
4
+ * @param {object} helpers - Вспомогательные функции движка.
5
+ * @param {function} helpers.resolvePinValue - Функция для получения значения с пина.
6
+ * @param {function} helpers.traverse - Функция для перехода к следующему узлу.
7
+ */
8
+ async function execute(node, context, helpers) {
9
+ const { resolvePinValue, traverse } = helpers;
10
+
11
+ const target = await resolvePinValue(node, 'target');
12
+ const yOffset = await resolvePinValue(node, 'add_y', 0);
13
+
14
+ if (target && context.bot?.lookAt) {
15
+ let finalPosition;
16
+ // Если цель - это сущность, у которой есть позиция
17
+ if (target.position) {
18
+ finalPosition = { ...target.position };
19
+ }
20
+ // Если цель - это объект с координатами
21
+ else if (target.x !== undefined && target.y !== undefined && target.z !== undefined) {
22
+ finalPosition = { ...target };
23
+ }
24
+
25
+ if (finalPosition) {
26
+ finalPosition.y += Number(yOffset || 0);
27
+ context.bot.lookAt(finalPosition);
28
+ }
29
+ }
30
+
31
+ await traverse(node, 'exec');
32
+ }
33
+
34
+ module.exports = {
35
+ execute,
36
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @param {object} node - Экземпляр узла из графа.
3
+ * @param {object} context - Контекст выполнения графа.
4
+ * @param {object} helpers - Вспомогательные функции движка.
5
+ * @param {function} helpers.resolvePinValue - Функция для получения значения с пина.
6
+ * @param {function} helpers.traverse - Функция для перехода к следующему узлу.
7
+ */
8
+ async function execute(node, context, helpers) {
9
+ const { resolvePinValue, traverse } = helpers;
10
+
11
+ const varName = await resolvePinValue(node, 'name', '');
12
+ const varValue = await resolvePinValue(node, 'value');
13
+ let shouldPersist = await resolvePinValue(node, 'persist', false);
14
+
15
+ // В графах команд принудительно отключаем сохранение в БД, чтобы избежать случайных записей
16
+ if (context.eventType === 'command') {
17
+ shouldPersist = false;
18
+ }
19
+
20
+ if (varName) {
21
+ context.variables[varName] = varValue;
22
+ if (context.persistenceIntent) {
23
+ context.persistenceIntent.set(varName, shouldPersist);
24
+ }
25
+ }
26
+
27
+ await traverse(node, 'exec');
28
+ }
29
+
30
+ module.exports = {
31
+ execute,
32
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @param {object} node - Экземпляр узла из графа.
3
+ * @param {object} context - Контекст выполнения графа.
4
+ * @param {object} helpers - Вспомогательные функции движка.
5
+ * @param {function} helpers.resolvePinValue - Функция для получения значения с входного пина.
6
+ * @param {function} helpers.traverse - Функция для перехода к следующему узлу.
7
+ * @param {Map} helpers.memo - Карта для мемоизации значений.
8
+ */
9
+ async function execute(node, context, helpers) {
10
+ const { resolvePinValue, traverse, memo } = helpers;
11
+
12
+ const url = await resolvePinValue(node, 'url', '');
13
+ const method = await resolvePinValue(node, 'method', node.data?.method || 'GET');
14
+ const headersStr = await resolvePinValue(node, 'headers', '');
15
+ const body = await resolvePinValue(node, 'body', '');
16
+ const timeout = await resolvePinValue(node, 'timeout', 5000);
17
+
18
+ let headers = {};
19
+ if (headersStr) {
20
+ try {
21
+ headers = JSON.parse(headersStr);
22
+ } catch (e) {
23
+ console.error('[HTTP Request] Ошибка парсинга headers:', e);
24
+ headers = {};
25
+ }
26
+ }
27
+
28
+ let requestBody = null;
29
+ if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
30
+ if (typeof body === 'object') {
31
+ requestBody = JSON.stringify(body);
32
+ } else if (typeof body === 'string') {
33
+ // Пробуем распарсить строку как JSON
34
+ try {
35
+ const parsed = JSON.parse(body);
36
+ requestBody = JSON.stringify(parsed);
37
+ } catch {
38
+ // Если не JSON, отправляем как есть
39
+ requestBody = body;
40
+ }
41
+ } else {
42
+ requestBody = String(body);
43
+ }
44
+
45
+ if (!headers['Content-Type']) {
46
+ headers['Content-Type'] = 'application/json';
47
+ }
48
+ }
49
+
50
+ try {
51
+ const controller = new AbortController();
52
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
53
+
54
+ const response = await fetch(url, {
55
+ method,
56
+ headers,
57
+ body: requestBody,
58
+ signal: controller.signal
59
+ });
60
+
61
+ clearTimeout(timeoutId);
62
+
63
+ const responseText = await response.text();
64
+ let responseBody;
65
+ try {
66
+ responseBody = JSON.parse(responseText);
67
+ } catch {
68
+ responseBody = responseText;
69
+ }
70
+
71
+ const responseHeaders = {};
72
+ response.headers.forEach((value, key) => {
73
+ responseHeaders[key] = value;
74
+ });
75
+
76
+ memo.set(`${node.id}:status`, response.status);
77
+ memo.set(`${node.id}:response`, responseBody);
78
+ memo.set(`${node.id}:response_headers`, responseHeaders);
79
+ memo.set(`${node.id}:success`, response.ok);
80
+ memo.set(`${node.id}:error`, null);
81
+
82
+ await traverse(node, 'exec');
83
+ } catch (error) {
84
+ console.error('[HTTP Request] Ошибка запроса:', error);
85
+
86
+ memo.set(`${node.id}:status`, 0);
87
+ memo.set(`${node.id}:response`, null);
88
+ memo.set(`${node.id}:response_headers`, {});
89
+ memo.set(`${node.id}:success`, false);
90
+ memo.set(`${node.id}:error`, error.message);
91
+
92
+ await traverse(node, 'exec_error');
93
+ }
94
+ }
95
+
96
+ module.exports = {
97
+ execute,
98
+ };