blockmine 1.19.1 → 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 (72) hide show
  1. package/.claude/settings.local.json +23 -0
  2. package/CHANGELOG.md +23 -0
  3. package/backend/src/core/BotManager.js +34 -4
  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 +37 -629
  8. package/backend/src/core/NodeRegistry.js +146 -2
  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/nul +0 -0
  67. package/package.json +1 -1
  68. package/.kiro/steering/product.md +0 -27
  69. package/.kiro/steering/structure.md +0 -89
  70. package/.kiro/steering/tech.md +0 -94
  71. package/frontend/dist/assets/index-BFd7YoAj.css +0 -1
  72. package/frontend/dist/assets/index-DxdxTe6I.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 }
@@ -299,8 +324,10 @@ class NodeRegistry {
299
324
  type: 'action:send_message',
300
325
  label: '🗣️ Отправить сообщение',
301
326
  category: 'Действия',
302
- description: 'Отправляет сообщение в чат',
327
+ description: 'Отправляет сообщение в чат. Поддерживает переменные в формате {varName}',
303
328
  graphType: all,
329
+ dynamicPins: true,
330
+ executor: require('./nodes/action_send_message').execute,
304
331
  pins: {
305
332
  inputs: [
306
333
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -320,6 +347,7 @@ class NodeRegistry {
320
347
  category: 'Действия',
321
348
  description: 'Отправляет сообщение в консоль на странице бота.',
322
349
  graphType: all,
350
+ executor: require('./nodes/action_send_log').execute,
323
351
  pins: {
324
352
  inputs: [
325
353
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -337,6 +365,7 @@ class NodeRegistry {
337
365
  category: 'Действия',
338
366
  description: 'Поворачивает голову бота в сторону координат или сущности.',
339
367
  graphType: all,
368
+ executor: require('./nodes/action_bot_look_at').execute,
340
369
  pins: {
341
370
  inputs: [
342
371
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -355,6 +384,7 @@ class NodeRegistry {
355
384
  category: 'Действия',
356
385
  description: 'Сохраняет значение в переменную графа.',
357
386
  graphType: all,
387
+ executor: require('./nodes/action_bot_set_variable').execute,
358
388
  pins: {
359
389
  inputs: [
360
390
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -368,12 +398,41 @@ class NodeRegistry {
368
398
  }
369
399
  });
370
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
+
371
429
  this.registerNodeType({
372
430
  type: 'data:get_argument',
373
431
  label: '📥 Получить аргумент',
374
432
  category: 'Данные',
375
433
  description: 'Получает значение аргумента команды по его имени.',
376
434
  graphType: command,
435
+ evaluator: require('./nodes/data_get_argument').evaluate,
377
436
  data: {
378
437
  argumentName: {
379
438
  type: 'argument',
@@ -395,6 +454,7 @@ class NodeRegistry {
395
454
  category: 'Данные',
396
455
  description: 'Получает значение переменной графа.',
397
456
  graphType: all,
457
+ evaluator: require('./nodes/data_get_variable').evaluate,
398
458
  pins: {
399
459
  inputs: [],
400
460
  outputs: [
@@ -409,6 +469,7 @@ class NodeRegistry {
409
469
  category: 'Данные',
410
470
  description: 'Получает определенное поле из объекта сущности (например, "position.x", "username").',
411
471
  graphType: all,
472
+ evaluator: require('./nodes/data_get_entity_field').evaluate,
412
473
  pins: {
413
474
  inputs: [
414
475
  { id: 'entity', name: 'Сущность', type: 'Object', required: true },
@@ -426,8 +487,10 @@ class NodeRegistry {
426
487
  type: 'data:string_literal',
427
488
  label: '📜 Строка',
428
489
  category: 'Данные',
429
- description: 'Простое текстовое значение.',
490
+ description: 'Текстовое значение с поддержкой переменных. Используйте {имя} для вставки значений.',
430
491
  graphType: all,
492
+ dynamicPins: true,
493
+ evaluator: require('./nodes/data_string_literal').evaluate,
431
494
  pins: {
432
495
  inputs: [],
433
496
  outputs: [
@@ -442,6 +505,7 @@ class NodeRegistry {
442
505
  category: 'Данные',
443
506
  description: 'Простое числовое значение.',
444
507
  graphType: all,
508
+ evaluator: require('./nodes/data_number_literal').evaluate,
445
509
  pins: {
446
510
  inputs: [
447
511
  { id: 'value', name: 'Значение', type: 'Number', required: true }
@@ -458,6 +522,7 @@ class NodeRegistry {
458
522
  category: 'Данные',
459
523
  description: 'Значение Истина/Ложь.',
460
524
  graphType: all,
525
+ evaluator: require('./nodes/data_boolean_literal').evaluate,
461
526
  pins: {
462
527
  inputs: [
463
528
  { id: 'value', name: 'Значение', type: 'Boolean', required: true }
@@ -475,6 +540,7 @@ class NodeRegistry {
475
540
  description: 'Создает массив из элементов.',
476
541
  graphType: all,
477
542
  dynamicPins: true,
543
+ evaluator: require('./nodes/data_array_literal').evaluate,
478
544
  pins: {
479
545
  inputs: [],
480
546
  outputs: [
@@ -490,6 +556,7 @@ class NodeRegistry {
490
556
  description: 'Создает JSON-объект из пар ключ-значение.',
491
557
  graphType: all,
492
558
  dynamicPins: true,
559
+ evaluator: require('./nodes/data_make_object').evaluate,
493
560
  pins: {
494
561
  inputs: [],
495
562
  outputs: [
@@ -504,6 +571,7 @@ class NodeRegistry {
504
571
  category: 'Данные',
505
572
  description: 'Приводит входящее значение к указанному целевому типу.',
506
573
  graphType: all,
574
+ evaluator: require('./nodes/data_cast').evaluate,
507
575
  pins: {
508
576
  inputs: [
509
577
  { id: 'value', name: 'Значение', type: 'Wildcard', required: true }
@@ -520,6 +588,7 @@ class NodeRegistry {
520
588
  category: 'Массив',
521
589
  graphType: 'all',
522
590
  description: 'Возвращает количество элементов в массиве или длину строки.',
591
+ evaluator: require('./nodes/data_length').evaluate,
523
592
  pins: {
524
593
  inputs: [
525
594
  { id: 'data', name: 'Массив или Строка', type: 'Any', required: true }
@@ -536,6 +605,8 @@ class NodeRegistry {
536
605
  category: 'Строки',
537
606
  description: 'Проверяет, содержит ли одна строка другую.',
538
607
  graphType: all,
608
+ executor: require('./nodes/string_contains').execute,
609
+ evaluator: require('./nodes/string_contains').evaluate,
539
610
  pins: {
540
611
  inputs: [
541
612
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -550,12 +621,35 @@ class NodeRegistry {
550
621
  }
551
622
  });
552
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
+
553
645
  this.registerNodeType({
554
646
  type: 'string:equals',
555
647
  label: 'Строка: Равно',
556
648
  category: 'Строки',
557
649
  description: 'Проверяет, равны ли строки (с учетом/без учета регистра).',
558
650
  graphType: all,
651
+ executor: require('./nodes/string_equals').execute,
652
+ evaluator: require('./nodes/string_equals').evaluate,
559
653
  pins: {
560
654
  inputs: [
561
655
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -576,6 +670,8 @@ class NodeRegistry {
576
670
  category: 'Строки',
577
671
  description: 'Проверяет, начинается ли строка с указанной подстроки.',
578
672
  graphType: all,
673
+ executor: require('./nodes/string_starts_with').execute,
674
+ evaluator: require('./nodes/string_starts_with').evaluate,
579
675
  pins: {
580
676
  inputs: [
581
677
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -596,6 +692,8 @@ class NodeRegistry {
596
692
  category: 'Строки',
597
693
  description: 'Проверяет, заканчивается ли строка указанной подстрокой.',
598
694
  graphType: all,
695
+ executor: require('./nodes/string_ends_with').execute,
696
+ evaluator: require('./nodes/string_ends_with').evaluate,
599
697
  pins: {
600
698
  inputs: [
601
699
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -616,6 +714,8 @@ class NodeRegistry {
616
714
  category: 'Строки',
617
715
  description: 'Возвращает количество символов в строке.',
618
716
  graphType: all,
717
+ executor: require('./nodes/string_length').execute,
718
+ evaluator: require('./nodes/string_length').evaluate,
619
719
  pins: {
620
720
  inputs: [
621
721
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -634,6 +734,8 @@ class NodeRegistry {
634
734
  category: 'Строки',
635
735
  description: 'Разделяет строку на массив подстрок по разделителю.',
636
736
  graphType: all,
737
+ executor: require('./nodes/string_split').execute,
738
+ evaluator: require('./nodes/string_split').evaluate,
637
739
  pins: {
638
740
  inputs: [
639
741
  { id: 'exec', name: 'Exec', type: 'Exec', required: true },
@@ -654,6 +756,7 @@ class NodeRegistry {
654
756
  description: 'Объединяет две или более строки в одну.',
655
757
  graphType: all,
656
758
  dynamicPins: true,
759
+ evaluator: require('./nodes/string_concat').evaluate,
657
760
  pins: {
658
761
  inputs: [],
659
762
  outputs: [
@@ -668,6 +771,7 @@ class NodeRegistry {
668
771
  category: 'Математика',
669
772
  description: 'Выполняет математическую операцию над двумя числами.',
670
773
  graphType: all,
774
+ evaluator: require('./nodes/math_operation').evaluate,
671
775
  pins: {
672
776
  inputs: [
673
777
  { id: 'a', name: 'A', type: 'Number', required: true },
@@ -686,6 +790,7 @@ class NodeRegistry {
686
790
  description: 'Выполняет логическую операцию. Для НЕ (NOT) используется только вход А.',
687
791
  graphType: all,
688
792
  dynamicPins: true,
793
+ evaluator: require('./nodes/logic_operation').evaluate,
689
794
  pins: {
690
795
  inputs: [
691
796
  { id: 'a', name: 'A', type: 'Boolean', required: true },
@@ -703,6 +808,7 @@ class NodeRegistry {
703
808
  category: 'Отладка',
704
809
  description: 'Выводит значение в консоль терминала, где запущен бот.',
705
810
  graphType: all,
811
+ executor: require('./nodes/debug_log').execute,
706
812
  pins: {
707
813
  inputs: [
708
814
  { id: 'exec', name: 'Exec', type: 'Exec' },
@@ -720,6 +826,7 @@ class NodeRegistry {
720
826
  category: 'Математика',
721
827
  graphType: 'all',
722
828
  description: 'Генерирует случайное число в заданном диапазоне.',
829
+ evaluator: require('./nodes/math_random_number').evaluate,
723
830
  pins: {
724
831
  inputs: [
725
832
  { id: 'min', name: 'Мин', type: 'Number' },
@@ -735,6 +842,7 @@ class NodeRegistry {
735
842
  category: 'Массив',
736
843
  graphType: 'all',
737
844
  description: 'Возвращает случайный элемент из массива и его индекс.',
845
+ evaluator: require('./nodes/array_get_random_element').evaluate,
738
846
  pins: {
739
847
  inputs: [
740
848
  { id: 'array', name: 'Массив', type: 'Array', required: true }
@@ -752,6 +860,7 @@ class NodeRegistry {
752
860
  category: 'Массив',
753
861
  description: 'Проверяет, содержит ли массив указанный элемент и возвращает его индекс.',
754
862
  graphType: all,
863
+ evaluator: require('./nodes/array_contains').evaluate,
755
864
  pins: {
756
865
  inputs: [
757
866
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -770,6 +879,7 @@ class NodeRegistry {
770
879
  category: 'Массив',
771
880
  description: 'Получает элемент массива по его индексу.',
772
881
  graphType: all,
882
+ evaluator: require('./nodes/array_get_by_index').evaluate,
773
883
  pins: {
774
884
  inputs: [
775
885
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -787,6 +897,7 @@ class NodeRegistry {
787
897
  category: 'Массив',
788
898
  description: 'Добавляет элемент в конец массива.',
789
899
  graphType: all,
900
+ evaluator: require('./nodes/array_add_element').evaluate,
790
901
  pins: {
791
902
  inputs: [
792
903
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -804,6 +915,7 @@ class NodeRegistry {
804
915
  category: 'Массив',
805
916
  description: 'Удаляет элемент из массива по его индексу.',
806
917
  graphType: all,
918
+ evaluator: require('./nodes/array_remove_by_index').evaluate,
807
919
  pins: {
808
920
  inputs: [
809
921
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -821,6 +933,7 @@ class NodeRegistry {
821
933
  category: 'Массив',
822
934
  description: 'Находит индекс элемента в массиве (или -1 если не найден).',
823
935
  graphType: all,
936
+ evaluator: require('./nodes/array_find_index').evaluate,
824
937
  pins: {
825
938
  inputs: [
826
939
  { id: 'array', name: 'Массив', type: 'Array', required: true },
@@ -839,6 +952,7 @@ class NodeRegistry {
839
952
  description: 'Создает объект из пар ключ-значение.',
840
953
  graphType: all,
841
954
  dynamicPins: true,
955
+ evaluator: require('./nodes/object_create').evaluate,
842
956
  pins: {
843
957
  inputs: [],
844
958
  outputs: [
@@ -853,6 +967,7 @@ class NodeRegistry {
853
967
  category: 'Объект',
854
968
  description: 'Получает значение по ключу из объекта.',
855
969
  graphType: all,
970
+ evaluator: require('./nodes/object_get').evaluate,
856
971
  pins: {
857
972
  inputs: [
858
973
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -870,6 +985,7 @@ class NodeRegistry {
870
985
  category: 'Объект',
871
986
  description: 'Добавляет или изменяет значение по ключу в объекте.',
872
987
  graphType: all,
988
+ evaluator: require('./nodes/object_set').evaluate,
873
989
  pins: {
874
990
  inputs: [
875
991
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -888,6 +1004,7 @@ class NodeRegistry {
888
1004
  category: 'Объект',
889
1005
  description: 'Удаляет ключ из объекта.',
890
1006
  graphType: all,
1007
+ evaluator: require('./nodes/object_delete').evaluate,
891
1008
  pins: {
892
1009
  inputs: [
893
1010
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -905,6 +1022,7 @@ class NodeRegistry {
905
1022
  category: 'Объект',
906
1023
  description: 'Проверяет наличие ключа в объекте и возвращает значение.',
907
1024
  graphType: all,
1025
+ evaluator: require('./nodes/object_has_key').evaluate,
908
1026
  pins: {
909
1027
  inputs: [
910
1028
  { id: 'object', name: 'Объект', type: 'Object', required: true },
@@ -923,6 +1041,7 @@ class NodeRegistry {
923
1041
  category: 'Данные',
924
1042
  graphType: 'all',
925
1043
  description: 'Возвращает массив с именами всех игроков на сервере.',
1044
+ evaluator: require('./nodes/data_get_server_players').evaluate,
926
1045
  pins: {
927
1046
  inputs: [],
928
1047
  outputs: [
@@ -937,6 +1056,7 @@ class NodeRegistry {
937
1056
  category: 'Логика',
938
1057
  description: 'Сравнивает два значения.',
939
1058
  graphType: all,
1059
+ evaluator: require('./nodes/logic_compare').evaluate,
940
1060
  pins: {
941
1061
  inputs: [
942
1062
  { id: 'a', name: 'A', type: 'Wildcard' },
@@ -954,6 +1074,7 @@ class NodeRegistry {
954
1074
  category: 'Бот',
955
1075
  description: 'Возвращает текущую позицию бота в мире.',
956
1076
  graphType: all,
1077
+ evaluator: require('./nodes/bot_get_position').evaluate,
957
1078
  pins: {
958
1079
  inputs: [],
959
1080
  outputs: [
@@ -969,6 +1090,7 @@ class NodeRegistry {
969
1090
  category: 'Пользователи',
970
1091
  description: 'Проверяет, находится ли пользователь в черном списке.',
971
1092
  graphType: all,
1093
+ evaluator: require('./nodes/user_check_blacklist').evaluate,
972
1094
  pins: {
973
1095
  inputs: [
974
1096
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -985,6 +1107,7 @@ class NodeRegistry {
985
1107
  category: 'Пользователи',
986
1108
  description: 'Добавляет или убирает пользователя из черного списка.',
987
1109
  graphType: all,
1110
+ executor: require('./nodes/user_set_blacklist').execute,
988
1111
  pins: {
989
1112
  inputs: [
990
1113
  { id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
@@ -1005,6 +1128,7 @@ class NodeRegistry {
1005
1128
  category: 'Пользователь',
1006
1129
  description: 'Возвращает массив названий групп, в которых состоит пользователь.',
1007
1130
  graphType: all,
1131
+ evaluator: require('./nodes/user_get_groups').evaluate,
1008
1132
  pins: {
1009
1133
  inputs: [
1010
1134
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1021,6 +1145,7 @@ class NodeRegistry {
1021
1145
  category: 'Пользователь',
1022
1146
  description: 'Возвращает массив прав пользователя.',
1023
1147
  graphType: all,
1148
+ evaluator: require('./nodes/user_get_permissions').evaluate,
1024
1149
  pins: {
1025
1150
  inputs: [
1026
1151
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1037,6 +1162,7 @@ class NodeRegistry {
1037
1162
  category: 'Данные',
1038
1163
  description: 'Получает различные данные из объекта пользователя.',
1039
1164
  graphType: all,
1165
+ evaluator: require('./nodes/data_get_user_field').evaluate,
1040
1166
  pins: {
1041
1167
  inputs: [
1042
1168
  { id: 'user', name: 'Пользователь', type: 'User', required: true }
@@ -1064,6 +1190,23 @@ class NodeRegistry {
1064
1190
  }
1065
1191
  });
1066
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
+
1067
1210
  this.registerNodeType({
1068
1211
  type: 'flow:switch',
1069
1212
  label: '🔄 Switch (свитч)',
@@ -1071,6 +1214,7 @@ class NodeRegistry {
1071
1214
  description: 'Выполняет разные действия в зависимости от значения. Автоматически определяет тип сравнения.',
1072
1215
  graphType: all,
1073
1216
  dynamicPins: true,
1217
+ executor: require('./nodes/flow_switch').execute,
1074
1218
  pins: {
1075
1219
  inputs: [
1076
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
+ };