blockmine 1.20.0 → 1.22.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 (434) hide show
  1. package/.claude/agents/README.md +469 -0
  2. package/.claude/agents/auth-route-debugger.md +118 -0
  3. package/.claude/agents/auth-route-tester.md +93 -0
  4. package/.claude/agents/auto-error-resolver.md +97 -0
  5. package/.claude/agents/build-optimizer.md +236 -0
  6. package/.claude/agents/code-architecture-reviewer.md +83 -0
  7. package/.claude/agents/code-refactor-master.md +94 -0
  8. package/.claude/agents/cost-optimizer.md +134 -0
  9. package/.claude/agents/deployment-orchestrator.md +113 -0
  10. package/.claude/agents/documentation-architect.md +82 -0
  11. package/.claude/agents/frontend-error-fixer.md +77 -0
  12. package/.claude/agents/iac-code-generator.md +71 -0
  13. package/.claude/agents/incident-responder.md +346 -0
  14. package/.claude/agents/infrastructure-architect.md +31 -0
  15. package/.claude/agents/kubernetes-specialist.md +56 -0
  16. package/.claude/agents/migration-planner.md +181 -0
  17. package/.claude/agents/network-architect.md +196 -0
  18. package/.claude/agents/plan-reviewer.md +52 -0
  19. package/.claude/agents/refactor-planner.md +63 -0
  20. package/.claude/agents/security-scanner.md +102 -0
  21. package/.claude/agents/web-research-specialist.md +78 -0
  22. package/.claude/commands/cost-analysis.md +315 -0
  23. package/.claude/commands/dev-docs-update.md +55 -0
  24. package/.claude/commands/dev-docs.md +51 -0
  25. package/.claude/commands/incident-debug.md +247 -0
  26. package/.claude/commands/infra-plan.md +81 -0
  27. package/.claude/commands/migration-plan.md +478 -0
  28. package/.claude/commands/route-research-for-testing.md +37 -0
  29. package/.claude/commands/security-review.md +66 -0
  30. package/.claude/hooks/CONFIG.md +448 -0
  31. package/.claude/hooks/README.md +163 -0
  32. package/.claude/hooks/SKILL_ACTIVATION_COMPLETE.md +226 -0
  33. package/.claude/hooks/WINDOWS_HOOKS_README.md +151 -0
  34. package/.claude/hooks/add-skill-activation-banners.ts +132 -0
  35. package/.claude/hooks/comprehensive-skill-test.ts +1315 -0
  36. package/.claude/hooks/error-handling-reminder.sh +12 -0
  37. package/.claude/hooks/error-handling-reminder.ts +222 -0
  38. package/.claude/hooks/k8s-manifest-validator.sh +56 -0
  39. package/.claude/hooks/package-lock.json +556 -0
  40. package/.claude/hooks/package.json +16 -0
  41. package/.claude/hooks/post-tool-use-tracker.ps1 +174 -0
  42. package/.claude/hooks/post-tool-use-tracker.sh +183 -0
  43. package/.claude/hooks/security-policy-check.sh +247 -0
  44. package/.claude/hooks/skill-activation-prompt.ps1 +10 -0
  45. package/.claude/hooks/skill-activation-prompt.sh +10 -0
  46. package/.claude/hooks/skill-activation-prompt.ts +141 -0
  47. package/.claude/hooks/stop-build-check-enhanced.sh +130 -0
  48. package/.claude/hooks/terraform-validator.sh +53 -0
  49. package/.claude/hooks/test-input.json +7 -0
  50. package/.claude/hooks/test-skill-activation.ts +427 -0
  51. package/.claude/hooks/trigger-build-resolver.sh +79 -0
  52. package/.claude/hooks/tsc-check.sh +173 -0
  53. package/.claude/hooks/tsconfig.json +19 -0
  54. package/.claude/settings.json +55 -0
  55. package/.claude/settings.local.json +28 -3
  56. package/.claude/skills/README.md +507 -0
  57. package/.claude/skills/api-engineering/SKILL.md +63 -0
  58. package/.claude/skills/api-engineering/resources/api-versioning.md +88 -0
  59. package/.claude/skills/api-engineering/resources/graphql-patterns.md +106 -0
  60. package/.claude/skills/api-engineering/resources/rate-limiting.md +118 -0
  61. package/.claude/skills/api-engineering/resources/rest-api-design.md +105 -0
  62. package/.claude/skills/backend-dev-guidelines/SKILL.md +306 -0
  63. package/.claude/skills/backend-dev-guidelines/resources/architecture-overview.md +451 -0
  64. package/.claude/skills/backend-dev-guidelines/resources/async-and-errors.md +307 -0
  65. package/.claude/skills/backend-dev-guidelines/resources/complete-examples.md +638 -0
  66. package/.claude/skills/backend-dev-guidelines/resources/configuration.md +275 -0
  67. package/.claude/skills/backend-dev-guidelines/resources/database-patterns.md +224 -0
  68. package/.claude/skills/backend-dev-guidelines/resources/middleware-guide.md +213 -0
  69. package/.claude/skills/backend-dev-guidelines/resources/routing-and-controllers.md +756 -0
  70. package/.claude/skills/backend-dev-guidelines/resources/sentry-and-monitoring.md +336 -0
  71. package/.claude/skills/backend-dev-guidelines/resources/services-and-repositories.md +789 -0
  72. package/.claude/skills/backend-dev-guidelines/resources/testing-guide.md +235 -0
  73. package/.claude/skills/backend-dev-guidelines/resources/validation-patterns.md +754 -0
  74. package/.claude/skills/budget-and-cost-management/SKILL.md +850 -0
  75. package/.claude/skills/build-engineering/SKILL.md +431 -0
  76. package/.claude/skills/build-engineering/resources/artifact-repositories.md +72 -0
  77. package/.claude/skills/build-engineering/resources/build-caching.md +96 -0
  78. package/.claude/skills/build-engineering/resources/build-pipelines.md +105 -0
  79. package/.claude/skills/build-engineering/resources/build-security.md +95 -0
  80. package/.claude/skills/build-engineering/resources/build-systems.md +389 -0
  81. package/.claude/skills/build-engineering/resources/compilation-optimization.md +201 -0
  82. package/.claude/skills/build-engineering/resources/dependency-management.md +73 -0
  83. package/.claude/skills/build-engineering/resources/monorepo-builds.md +110 -0
  84. package/.claude/skills/build-engineering/resources/performance-optimization.md +113 -0
  85. package/.claude/skills/build-engineering/resources/reproducible-builds.md +82 -0
  86. package/.claude/skills/cloud-engineering/SKILL.md +675 -0
  87. package/.claude/skills/cloud-engineering/resources/aws-patterns.md +742 -0
  88. package/.claude/skills/cloud-engineering/resources/azure-patterns.md +714 -0
  89. package/.claude/skills/cloud-engineering/resources/cleared-cloud-environments.md +987 -0
  90. package/.claude/skills/cloud-engineering/resources/cloud-cost-optimization.md +757 -0
  91. package/.claude/skills/cloud-engineering/resources/cloud-networking.md +1058 -0
  92. package/.claude/skills/cloud-engineering/resources/cloud-security-tools.md +1530 -0
  93. package/.claude/skills/cloud-engineering/resources/cloud-security.md +990 -0
  94. package/.claude/skills/cloud-engineering/resources/gcp-patterns.md +758 -0
  95. package/.claude/skills/cloud-engineering/resources/migration-strategies.md +820 -0
  96. package/.claude/skills/cloud-engineering/resources/multi-cloud-strategies.md +670 -0
  97. package/.claude/skills/cloud-engineering/resources/oci-patterns.md +1198 -0
  98. package/.claude/skills/cloud-engineering/resources/serverless-patterns.md +795 -0
  99. package/.claude/skills/cloud-engineering/resources/well-architected-frameworks.md +966 -0
  100. package/.claude/skills/cybersecurity/SKILL.md +409 -0
  101. package/.claude/skills/cybersecurity/resources/security-architecture.md +266 -0
  102. package/.claude/skills/database-engineering/SKILL.md +61 -0
  103. package/.claude/skills/database-engineering/resources/backup-and-recovery.md +72 -0
  104. package/.claude/skills/database-engineering/resources/database-replication.md +63 -0
  105. package/.claude/skills/database-engineering/resources/postgresql-fundamentals.md +70 -0
  106. package/.claude/skills/database-engineering/resources/query-optimization.md +68 -0
  107. package/.claude/skills/devsecops/SKILL.md +374 -0
  108. package/.claude/skills/devsecops/resources/ci-cd-security.md +204 -0
  109. package/.claude/skills/devsecops/resources/compliance-automation.md +530 -0
  110. package/.claude/skills/devsecops/resources/compliance-frameworks.md +2322 -0
  111. package/.claude/skills/devsecops/resources/container-security.md +915 -0
  112. package/.claude/skills/devsecops/resources/cspm-integration.md +1440 -0
  113. package/.claude/skills/devsecops/resources/policy-enforcement.md +619 -0
  114. package/.claude/skills/devsecops/resources/secrets-management.md +755 -0
  115. package/.claude/skills/devsecops/resources/security-monitoring.md +146 -0
  116. package/.claude/skills/devsecops/resources/security-scanning.md +887 -0
  117. package/.claude/skills/devsecops/resources/security-testing.md +203 -0
  118. package/.claude/skills/devsecops/resources/supply-chain-security.md +518 -0
  119. package/.claude/skills/devsecops/resources/vulnerability-management.md +481 -0
  120. package/.claude/skills/devsecops/resources/zero-trust-architecture.md +177 -0
  121. package/.claude/skills/documentation-as-code/SKILL.md +323 -0
  122. package/.claude/skills/documentation-as-code/resources/api-documentation.md +90 -0
  123. package/.claude/skills/documentation-as-code/resources/changelog-management.md +79 -0
  124. package/.claude/skills/documentation-as-code/resources/diagram-generation.md +44 -0
  125. package/.claude/skills/documentation-as-code/resources/docs-as-code-workflow.md +99 -0
  126. package/.claude/skills/documentation-as-code/resources/documentation-automation.md +68 -0
  127. package/.claude/skills/documentation-as-code/resources/documentation-sites.md +79 -0
  128. package/.claude/skills/documentation-as-code/resources/markdown-best-practices.md +162 -0
  129. package/.claude/skills/documentation-as-code/resources/openapi-specification.md +77 -0
  130. package/.claude/skills/documentation-as-code/resources/readme-engineering.md +60 -0
  131. package/.claude/skills/documentation-as-code/resources/technical-writing-guide.md +202 -0
  132. package/.claude/skills/engineering-management/SKILL.md +356 -0
  133. package/.claude/skills/engineering-management/resources/career-ladders.md +609 -0
  134. package/.claude/skills/engineering-management/resources/hiring-and-assessment.md +555 -0
  135. package/.claude/skills/engineering-management/resources/one-on-one-guides.md +609 -0
  136. package/.claude/skills/engineering-management/resources/resource-planning.md +557 -0
  137. package/.claude/skills/engineering-management/resources/team-organization-patterns.md +491 -0
  138. package/.claude/skills/engineering-management/resources/technical-interviews.md +474 -0
  139. package/.claude/skills/engineering-operations-management/SKILL.md +817 -0
  140. package/.claude/skills/error-tracking/SKILL.md +379 -0
  141. package/.claude/skills/frontend-dev-guidelines/SKILL.md +403 -0
  142. package/.claude/skills/frontend-dev-guidelines/resources/common-patterns.md +331 -0
  143. package/.claude/skills/frontend-dev-guidelines/resources/complete-examples.md +872 -0
  144. package/.claude/skills/frontend-dev-guidelines/resources/component-patterns.md +502 -0
  145. package/.claude/skills/frontend-dev-guidelines/resources/data-fetching.md +767 -0
  146. package/.claude/skills/frontend-dev-guidelines/resources/file-organization.md +502 -0
  147. package/.claude/skills/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
  148. package/.claude/skills/frontend-dev-guidelines/resources/performance.md +406 -0
  149. package/.claude/skills/frontend-dev-guidelines/resources/routing-guide.md +364 -0
  150. package/.claude/skills/frontend-dev-guidelines/resources/styling-guide.md +428 -0
  151. package/.claude/skills/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
  152. package/.claude/skills/general-it-engineering/SKILL.md +393 -0
  153. package/.claude/skills/general-it-engineering/resources/asset-management.md +712 -0
  154. package/.claude/skills/general-it-engineering/resources/automation-orchestration.md +817 -0
  155. package/.claude/skills/general-it-engineering/resources/business-continuity.md +786 -0
  156. package/.claude/skills/general-it-engineering/resources/change-management.md +715 -0
  157. package/.claude/skills/general-it-engineering/resources/enterprise-monitoring.md +729 -0
  158. package/.claude/skills/general-it-engineering/resources/help-desk-operations.md +738 -0
  159. package/.claude/skills/general-it-engineering/resources/incident-service-management.md +834 -0
  160. package/.claude/skills/general-it-engineering/resources/it-governance.md +753 -0
  161. package/.claude/skills/general-it-engineering/resources/itil-framework.md +503 -0
  162. package/.claude/skills/general-it-engineering/resources/service-management.md +669 -0
  163. package/.claude/skills/infrastructure-architecture/SKILL.md +328 -0
  164. package/.claude/skills/infrastructure-architecture/resources/architecture-decision-records.md +505 -0
  165. package/.claude/skills/infrastructure-architecture/resources/architecture-patterns.md +528 -0
  166. package/.claude/skills/infrastructure-architecture/resources/capacity-planning.md +453 -0
  167. package/.claude/skills/infrastructure-architecture/resources/cleared-environment-architecture.md +773 -0
  168. package/.claude/skills/infrastructure-architecture/resources/cost-architecture.md +499 -0
  169. package/.claude/skills/infrastructure-architecture/resources/data-architecture.md +501 -0
  170. package/.claude/skills/infrastructure-architecture/resources/disaster-recovery.md +535 -0
  171. package/.claude/skills/infrastructure-architecture/resources/migration-architecture.md +512 -0
  172. package/.claude/skills/infrastructure-architecture/resources/multi-region-design.md +608 -0
  173. package/.claude/skills/infrastructure-architecture/resources/reference-architectures.md +562 -0
  174. package/.claude/skills/infrastructure-architecture/resources/security-architecture.md +538 -0
  175. package/.claude/skills/infrastructure-architecture/resources/system-design-principles.md +489 -0
  176. package/.claude/skills/infrastructure-architecture/resources/workload-classification.md +1000 -0
  177. package/.claude/skills/infrastructure-strategy/SKILL.md +924 -0
  178. package/.claude/skills/network-engineering/SKILL.md +385 -0
  179. package/.claude/skills/network-engineering/resources/dns-management.md +738 -0
  180. package/.claude/skills/network-engineering/resources/load-balancing.md +820 -0
  181. package/.claude/skills/network-engineering/resources/network-architecture.md +546 -0
  182. package/.claude/skills/network-engineering/resources/network-security.md +921 -0
  183. package/.claude/skills/network-engineering/resources/network-troubleshooting.md +749 -0
  184. package/.claude/skills/network-engineering/resources/routing-switching.md +373 -0
  185. package/.claude/skills/network-engineering/resources/sdn-networking.md +695 -0
  186. package/.claude/skills/network-engineering/resources/service-mesh-networking.md +777 -0
  187. package/.claude/skills/network-engineering/resources/tcp-ip-protocols.md +444 -0
  188. package/.claude/skills/network-engineering/resources/vpn-connectivity.md +672 -0
  189. package/.claude/skills/observability-engineering/SKILL.md +101 -0
  190. package/.claude/skills/observability-engineering/resources/apm-tools.md +97 -0
  191. package/.claude/skills/observability-engineering/resources/correlation-strategies.md +87 -0
  192. package/.claude/skills/observability-engineering/resources/distributed-tracing.md +98 -0
  193. package/.claude/skills/observability-engineering/resources/logs-aggregation.md +118 -0
  194. package/.claude/skills/observability-engineering/resources/observability-cost-optimization.md +141 -0
  195. package/.claude/skills/observability-engineering/resources/opentelemetry.md +110 -0
  196. package/.claude/skills/platform-engineering/SKILL.md +555 -0
  197. package/.claude/skills/platform-engineering/resources/architecture-overview.md +600 -0
  198. package/.claude/skills/platform-engineering/resources/container-orchestration.md +916 -0
  199. package/.claude/skills/platform-engineering/resources/cost-optimization.md +634 -0
  200. package/.claude/skills/platform-engineering/resources/developer-platforms.md +670 -0
  201. package/.claude/skills/platform-engineering/resources/gitops-automation.md +650 -0
  202. package/.claude/skills/platform-engineering/resources/infrastructure-as-code.md +778 -0
  203. package/.claude/skills/platform-engineering/resources/infrastructure-standards.md +708 -0
  204. package/.claude/skills/platform-engineering/resources/multi-tenancy.md +602 -0
  205. package/.claude/skills/platform-engineering/resources/platform-security.md +711 -0
  206. package/.claude/skills/platform-engineering/resources/resource-management.md +592 -0
  207. package/.claude/skills/platform-engineering/resources/service-mesh.md +628 -0
  208. package/.claude/skills/release-engineering/SKILL.md +393 -0
  209. package/.claude/skills/release-engineering/resources/artifact-management.md +108 -0
  210. package/.claude/skills/release-engineering/resources/build-optimization.md +84 -0
  211. package/.claude/skills/release-engineering/resources/ci-cd-pipelines.md +411 -0
  212. package/.claude/skills/release-engineering/resources/deployment-strategies.md +197 -0
  213. package/.claude/skills/release-engineering/resources/pipeline-security.md +62 -0
  214. package/.claude/skills/release-engineering/resources/progressive-delivery.md +83 -0
  215. package/.claude/skills/release-engineering/resources/release-automation.md +68 -0
  216. package/.claude/skills/release-engineering/resources/release-orchestration.md +77 -0
  217. package/.claude/skills/release-engineering/resources/rollback-strategies.md +66 -0
  218. package/.claude/skills/release-engineering/resources/versioning-strategies.md +59 -0
  219. package/.claude/skills/route-tester/SKILL.md +392 -0
  220. package/.claude/skills/skill-developer/ADVANCED.md +197 -0
  221. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +306 -0
  222. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +152 -0
  223. package/.claude/skills/skill-developer/SKILL.md +430 -0
  224. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +315 -0
  225. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +305 -0
  226. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +514 -0
  227. package/.claude/skills/skill-rules.json +2940 -0
  228. package/.claude/skills/sre/SKILL.md +464 -0
  229. package/.claude/skills/sre/resources/alerting-best-practices.md +282 -0
  230. package/.claude/skills/sre/resources/capacity-planning.md +226 -0
  231. package/.claude/skills/sre/resources/chaos-engineering.md +193 -0
  232. package/.claude/skills/sre/resources/disaster-recovery.md +232 -0
  233. package/.claude/skills/sre/resources/incident-management.md +436 -0
  234. package/.claude/skills/sre/resources/observability-stack.md +240 -0
  235. package/.claude/skills/sre/resources/on-call-runbooks.md +167 -0
  236. package/.claude/skills/sre/resources/performance-optimization.md +108 -0
  237. package/.claude/skills/sre/resources/reliability-patterns.md +183 -0
  238. package/.claude/skills/sre/resources/slo-sli-sla.md +464 -0
  239. package/.claude/skills/sre/resources/toil-reduction.md +145 -0
  240. package/.claude/skills/systems-engineering/SKILL.md +648 -0
  241. package/.claude/skills/systems-engineering/resources/automation-patterns.md +771 -0
  242. package/.claude/skills/systems-engineering/resources/configuration-management.md +998 -0
  243. package/.claude/skills/systems-engineering/resources/linux-administration.md +672 -0
  244. package/.claude/skills/systems-engineering/resources/networking-fundamentals.md +982 -0
  245. package/.claude/skills/systems-engineering/resources/performance-tuning.md +871 -0
  246. package/.claude/skills/systems-engineering/resources/powershell-scripting.md +482 -0
  247. package/.claude/skills/systems-engineering/resources/security-hardening.md +739 -0
  248. package/.claude/skills/systems-engineering/resources/shell-scripting.md +915 -0
  249. package/.claude/skills/systems-engineering/resources/storage-management.md +628 -0
  250. package/.claude/skills/systems-engineering/resources/system-monitoring.md +787 -0
  251. package/.claude/skills/systems-engineering/resources/troubleshooting-guide.md +753 -0
  252. package/.claude/skills/systems-engineering/resources/windows-administration.md +738 -0
  253. package/.claude/skills/technical-leadership/SKILL.md +728 -0
  254. package/CHANGELOG.md +90 -39
  255. package/README.md +94 -0
  256. package/backend/docs/SECRETS_DOCUMENTATION.md +327 -0
  257. package/backend/jest.config.js +59 -0
  258. package/backend/package-lock.json +6129 -0
  259. package/backend/package.json +16 -4
  260. package/backend/prisma/migrations/20251026104609_add_websocket_api/migration.sql +33 -0
  261. package/backend/prisma/schema.prisma +33 -0
  262. package/backend/src/__tests__/core/DependencyService.test.js +336 -0
  263. package/backend/src/__tests__/core/UserService.test.js +875 -0
  264. package/backend/src/__tests__/repositories/BaseRepository.test.js +146 -0
  265. package/backend/src/__tests__/repositories/BotRepository.test.js +118 -0
  266. package/backend/src/__tests__/repositories/CommandRepository.test.js +132 -0
  267. package/backend/src/__tests__/repositories/EventGraphRepository.test.js +93 -0
  268. package/backend/src/__tests__/repositories/GroupRepository.test.js +155 -0
  269. package/backend/src/__tests__/repositories/PermissionRepository.test.js +130 -0
  270. package/backend/src/__tests__/repositories/PluginRepository.test.js +107 -0
  271. package/backend/src/__tests__/repositories/ServerRepository.test.js +80 -0
  272. package/backend/src/__tests__/repositories/UserRepository.test.js +128 -0
  273. package/backend/src/__tests__/secretsFilter.test.js +425 -0
  274. package/backend/src/__tests__/services/BotLifecycleService.test.js +411 -0
  275. package/backend/src/__tests__/services/BotProcessManager.test.js +285 -0
  276. package/backend/src/__tests__/services/CacheManager.test.js +125 -0
  277. package/backend/src/__tests__/services/CommandExecutionService.test.js +460 -0
  278. package/backend/src/__tests__/services/ResourceMonitorService.test.js +207 -0
  279. package/backend/src/__tests__/services/TelemetryService.test.js +291 -0
  280. package/backend/src/__tests__/setup.js +25 -0
  281. package/backend/src/api/routes/apiKeys.js +181 -0
  282. package/backend/src/api/routes/bots.js +49 -7
  283. package/backend/src/api/routes/plugins.js +2 -1
  284. package/backend/src/api/routes/system.js +174 -0
  285. package/backend/src/container.js +82 -0
  286. package/backend/src/core/BotManager.js +142 -871
  287. package/backend/src/core/BotManager.old.js +1093 -0
  288. package/backend/src/core/BotProcess.js +1092 -850
  289. package/backend/src/core/BreakLoopSignal.js +8 -0
  290. package/backend/src/core/EventGraphManager.js +280 -193
  291. package/backend/src/core/GraphExecutionEngine.js +321 -928
  292. package/backend/src/core/MessageQueue.js +27 -6
  293. package/backend/src/core/NodeRegistry.js +37 -991
  294. package/backend/src/core/PluginManager.js +62 -12
  295. package/backend/src/core/PrismaService.js +32 -0
  296. package/backend/src/core/UserService.js +3 -3
  297. package/backend/src/core/__tests__/PrismaService.test.js +24 -0
  298. package/backend/src/core/commands/README.md +305 -0
  299. package/backend/src/core/commands/dev.js +13 -7
  300. package/backend/src/core/commands/ping.js +10 -4
  301. package/backend/src/core/commands/whois.js +63 -0
  302. package/backend/src/core/config/validation.js +27 -0
  303. package/backend/src/core/constants/graphTypes.js +21 -0
  304. package/backend/src/core/node-registries/actions.js +132 -0
  305. package/backend/src/core/node-registries/arrays.js +137 -0
  306. package/backend/src/core/node-registries/bot.js +23 -0
  307. package/backend/src/core/node-registries/data.js +290 -0
  308. package/backend/src/core/node-registries/debug.js +26 -0
  309. package/backend/src/core/node-registries/events.js +187 -0
  310. package/backend/src/core/node-registries/flow.js +139 -0
  311. package/backend/src/core/node-registries/logic.js +45 -0
  312. package/backend/src/core/node-registries/math.js +42 -0
  313. package/backend/src/core/node-registries/objects.js +98 -0
  314. package/backend/src/core/node-registries/strings.js +153 -0
  315. package/backend/src/core/node-registries/time.js +113 -0
  316. package/backend/src/core/node-registries/users.js +79 -0
  317. package/backend/src/core/nodes/actions/bot_look_at.js +36 -0
  318. package/backend/src/core/nodes/actions/bot_set_variable.js +32 -0
  319. package/backend/src/core/nodes/actions/http_request.js +98 -0
  320. package/backend/src/core/nodes/actions/send_log.js +28 -0
  321. package/backend/src/core/nodes/actions/send_message.js +32 -0
  322. package/backend/src/core/nodes/actions/send_websocket_response.js +33 -0
  323. package/backend/src/core/nodes/arrays/add_element.js +23 -0
  324. package/backend/src/core/nodes/arrays/contains.js +40 -0
  325. package/backend/src/core/nodes/arrays/find_index.js +23 -0
  326. package/backend/src/core/nodes/arrays/get_by_index.js +23 -0
  327. package/backend/src/core/nodes/arrays/get_next.js +35 -0
  328. package/backend/src/core/nodes/arrays/get_random_element.js +32 -0
  329. package/backend/src/core/nodes/arrays/remove_by_index.js +30 -0
  330. package/backend/src/core/nodes/bot/get_position.js +20 -0
  331. package/backend/src/core/nodes/data/array_literal.js +31 -0
  332. package/backend/src/core/nodes/data/boolean_literal.js +21 -0
  333. package/backend/src/core/nodes/data/cast.js +42 -0
  334. package/backend/src/core/nodes/data/datetime_literal.js +27 -0
  335. package/backend/src/core/nodes/data/entity_info.js +69 -0
  336. package/backend/src/core/nodes/data/get_argument.js +23 -0
  337. package/backend/src/core/nodes/data/get_bot_look.js +14 -0
  338. package/backend/src/core/nodes/data/get_entity_field.js +18 -0
  339. package/backend/src/core/nodes/data/get_nearby_entities.js +32 -0
  340. package/backend/src/core/nodes/data/get_nearby_players.js +64 -0
  341. package/backend/src/core/nodes/data/get_server_players.js +18 -0
  342. package/backend/src/core/nodes/data/get_user_field.js +40 -0
  343. package/backend/src/core/nodes/data/get_variable.js +23 -0
  344. package/backend/src/core/nodes/data/length.js +25 -0
  345. package/backend/src/core/nodes/data/make_object.js +31 -0
  346. package/backend/src/core/nodes/data/number_literal.js +21 -0
  347. package/backend/src/core/nodes/data/string_literal.js +34 -0
  348. package/backend/src/core/nodes/data/type_check.js +53 -0
  349. package/backend/src/core/nodes/debug/log.js +16 -0
  350. package/backend/src/core/nodes/flow/branch.js +15 -0
  351. package/backend/src/core/nodes/flow/break.js +14 -0
  352. package/backend/src/core/nodes/flow/delay.js +43 -0
  353. package/backend/src/core/nodes/flow/for_each.js +39 -0
  354. package/backend/src/core/nodes/flow/sequence.js +16 -0
  355. package/backend/src/core/nodes/flow/switch.js +47 -0
  356. package/backend/src/core/nodes/flow/while.js +64 -0
  357. package/backend/src/core/nodes/logic/__tests__/compare.test.js +83 -0
  358. package/backend/src/core/nodes/logic/compare.js +33 -0
  359. package/backend/src/core/nodes/logic/operation.js +35 -0
  360. package/backend/src/core/nodes/math/__tests__/operation.test.js +65 -0
  361. package/backend/src/core/nodes/math/operation.js +31 -0
  362. package/backend/src/core/nodes/math/random_number.js +43 -0
  363. package/backend/src/core/nodes/objects/create.js +40 -0
  364. package/backend/src/core/nodes/objects/delete.js +26 -0
  365. package/backend/src/core/nodes/objects/get.js +23 -0
  366. package/backend/src/core/nodes/objects/has_key.js +30 -0
  367. package/backend/src/core/nodes/objects/set.js +27 -0
  368. package/backend/src/core/nodes/strings/__tests__/concat.test.js +89 -0
  369. package/backend/src/core/nodes/strings/concat.js +27 -0
  370. package/backend/src/core/nodes/strings/contains.js +41 -0
  371. package/backend/src/core/nodes/strings/ends_with.js +43 -0
  372. package/backend/src/core/nodes/strings/equals.js +36 -0
  373. package/backend/src/core/nodes/strings/length.js +36 -0
  374. package/backend/src/core/nodes/strings/matches.js +39 -0
  375. package/backend/src/core/nodes/strings/split.js +37 -0
  376. package/backend/src/core/nodes/strings/starts_with.js +43 -0
  377. package/backend/src/core/nodes/time/__tests__/now.test.js +24 -0
  378. package/backend/src/core/nodes/time/add.js +33 -0
  379. package/backend/src/core/nodes/time/compare.js +35 -0
  380. package/backend/src/core/nodes/time/diff.js +29 -0
  381. package/backend/src/core/nodes/time/format.js +32 -0
  382. package/backend/src/core/nodes/time/now.js +18 -0
  383. package/backend/src/core/nodes/users/check_blacklist.js +37 -0
  384. package/backend/src/core/nodes/users/get_groups.js +36 -0
  385. package/backend/src/core/nodes/users/get_permissions.js +36 -0
  386. package/backend/src/core/nodes/users/set_blacklist.js +37 -0
  387. package/backend/src/core/services/BotLifecycleService.js +596 -0
  388. package/backend/src/core/services/BotProcessManager.js +163 -0
  389. package/backend/src/core/services/CacheManager.js +111 -0
  390. package/backend/src/core/services/CommandExecutionService.js +351 -0
  391. package/backend/src/core/services/ResourceMonitorService.js +90 -0
  392. package/backend/src/core/services/TelemetryService.js +124 -0
  393. package/backend/src/core/services/ValidationService.js +132 -0
  394. package/backend/src/core/services/__tests__/ValidationService.test.js +148 -0
  395. package/backend/src/core/services.js +20 -5
  396. package/backend/src/core/system/CommandContext.js +84 -0
  397. package/backend/src/core/system/Transport.js +78 -0
  398. package/backend/src/core/utils/__tests__/jsonParser.test.js +44 -0
  399. package/backend/src/core/utils/jsonParser.js +18 -0
  400. package/backend/src/core/utils/secretsFilter.js +262 -0
  401. package/backend/src/core/utils/variableParser.js +89 -0
  402. package/backend/src/core/validation/__tests__/nodeSchemas.test.js +175 -0
  403. package/backend/src/core/validation/nodeSchemas.js +112 -0
  404. package/backend/src/lib/prisma.js +2 -4
  405. package/backend/src/real-time/botApi/handlers/commandHandlers.js +28 -0
  406. package/backend/src/real-time/botApi/handlers/graphHandlers.js +99 -0
  407. package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +147 -0
  408. package/backend/src/real-time/botApi/handlers/index.js +43 -0
  409. package/backend/src/real-time/botApi/handlers/messageHandlers.js +66 -0
  410. package/backend/src/real-time/botApi/handlers/statusHandlers.js +17 -0
  411. package/backend/src/real-time/botApi/handlers/userHandlers.js +141 -0
  412. package/backend/src/real-time/botApi/index.js +40 -0
  413. package/backend/src/real-time/botApi/middleware.js +79 -0
  414. package/backend/src/real-time/botApi/utils.js +54 -0
  415. package/backend/src/real-time/socketHandler.js +6 -2
  416. package/backend/src/repositories/BaseRepository.js +43 -0
  417. package/backend/src/repositories/BotRepository.js +42 -0
  418. package/backend/src/repositories/CommandRepository.js +53 -0
  419. package/backend/src/repositories/EventGraphRepository.js +40 -0
  420. package/backend/src/repositories/GroupRepository.js +69 -0
  421. package/backend/src/repositories/PermissionRepository.js +48 -0
  422. package/backend/src/repositories/PluginRepository.js +42 -0
  423. package/backend/src/repositories/ServerRepository.js +27 -0
  424. package/backend/src/repositories/UserRepository.js +48 -0
  425. package/backend/src/server.js +3 -0
  426. package/backend/src/test-refactor.js +85 -0
  427. package/frontend/dist/assets/index-CfTo92bP.css +1 -0
  428. package/frontend/dist/assets/index-CiFD5X9Z.js +8344 -0
  429. package/frontend/dist/index.html +2 -2
  430. package/frontend/package.json +1 -5
  431. package/package.json +2 -1
  432. package/frontend/dist/assets/index-BFd7YoAj.css +0 -1
  433. package/frontend/dist/assets/index-CMMutadc.js +0 -8352
  434. package/nul +0 -0
@@ -1,850 +1,1092 @@
1
- const mineflayer = require('mineflayer');
2
- const { SocksClient } = require('socks');
3
- const EventEmitter = require('events');
4
- const { v4: uuidv4 } = require('uuid');
5
- const { Vec3 } = require('vec3');
6
- const { PrismaClient } = require('@prisma/client');
7
- const { loadCommands } = require('./system/CommandRegistry');
8
- const { initializePlugins } = require('./PluginLoader');
9
- const MessageQueue = require('./MessageQueue');
10
- const Command = require('./system/Command');
11
- const { parseArguments } = require('./system/parseArguments');
12
- const GraphExecutionEngine = require('./GraphExecutionEngine');
13
- const NodeRegistry = require('./NodeRegistry');
14
-
15
- const UserService = require('./UserService');
16
- const PermissionManager = require('./ipc/PermissionManager.stub.js');
17
-
18
- let bot = null;
19
- const prisma = new PrismaClient();
20
- const pluginUiState = new Map();
21
- const pendingRequests = new Map();
22
- const entityMoveThrottles = new Map();
23
- let connectionTimeout = null;
24
-
25
- const originalJSONParse = JSON.parse
26
- JSON.parse = function(text, reviver) {
27
- if (typeof text !== 'string') return originalJSONParse(text, reviver)
28
- try {
29
- return originalJSONParse(text, reviver)
30
- } catch (e) {
31
- const fixed = text.replace(/([{,])\s*([a-zA-Z0-9_]+)\s*:/g, '$1"$2":')
32
- return originalJSONParse(fixed, reviver)
33
- }
34
- }
35
-
36
- function sendLog(content) {
37
- if (process.send) {
38
- process.send({ type: 'log', content });
39
- } else {
40
- console.log(`[ChildProcess Log] ${content}`);
41
- }
42
- }
43
-
44
-
45
- function sendEvent(eventName, eventArgs) {
46
- if (process.send) {
47
- process.send({ type: 'event', eventType: eventName, args: eventArgs });
48
- }
49
- }
50
-
51
- async function fetchNewConfig(botId, prisma) {
52
- try {
53
- const botData = await prisma.bot.findUnique({
54
- where: { id: botId },
55
- include: {
56
- server: true,
57
- installedPlugins: {
58
- where: { isEnabled: true }
59
- },
60
- }
61
- });
62
-
63
- if (!botData) return null;
64
-
65
- const commands = await prisma.command.findMany({ where: { botId } });
66
-
67
- return { ...botData, commands };
68
- } catch (error) {
69
- sendLog(`[fetchNewConfig] Error: ${error.message}`);
70
- return null;
71
- }
72
- }
73
-
74
- function handleIncomingCommand(type, username, message) {
75
- if (!message.startsWith(bot.config.prefix || '@')) return;
76
-
77
- const rawMessage = message.slice((bot.config.prefix || '@').length).trim();
78
- const commandParts = rawMessage.split(/ +/);
79
- const commandName = commandParts.shift().toLowerCase();
80
- const restOfMessage = commandParts.join(' ');
81
-
82
- const commandInstance = bot.commands.get(commandName) ||
83
- Array.from(bot.commands.values()).find(cmd => cmd.aliases.includes(commandName));
84
-
85
- if (!commandInstance) return;
86
-
87
- try {
88
- const processedArgs = {};
89
- const parsedArgs = parseArguments(restOfMessage);
90
- let currentArgIndex = 0;
91
-
92
- for (const argDef of commandInstance.isVisual ? JSON.parse(dbCommand.argumentsJson || '[]') : commandInstance.args) {
93
- if (argDef.type === 'greedy_string') {
94
- if (currentArgIndex < parsedArgs.length) {
95
- processedArgs[argDef.name] = parsedArgs.slice(currentArgIndex).join(' ');
96
- currentArgIndex = parsedArgs.length;
97
- }
98
- } else if (currentArgIndex < parsedArgs.length) {
99
- let value = parsedArgs[currentArgIndex];
100
- if (argDef.type === 'number') {
101
- const numValue = parseFloat(value);
102
- if (isNaN(numValue)) {
103
- bot.api.sendMessage(type, `Ошибка: Аргумент \"${argDef.description}\" должен быть числом.`, username);
104
- return;
105
- }
106
- value = numValue;
107
- }
108
- processedArgs[argDef.name] = value;
109
- currentArgIndex++;
110
- }
111
-
112
- if (processedArgs[argDef.name] === undefined) {
113
- if (argDef.required) {
114
- const usage = commandInstance.args.map(arg => {
115
- return arg.required ? `<${arg.description || arg.name}>` : `[${arg.description || arg.name}]`;
116
- }).join(' ');
117
-
118
- bot.api.sendMessage(type, `Ошибка: Необходимо указать: ${argDef.description || argDef.name}`, username);
119
- bot.api.sendMessage(type, `Использование: ${bot.config.prefix}${commandInstance.name} ${usage}`, username);
120
- return;
121
- }
122
- if (argDef.default !== undefined) {
123
- processedArgs[argDef.name] = argDef.default;
124
- }
125
- }
126
- }
127
-
128
- if (process.send) {
129
- process.send({
130
- type: 'validate_and_run_command',
131
- commandName: commandInstance.name,
132
- username,
133
- args: processedArgs,
134
- typeChat: type
135
- });
136
- }
137
- } catch (e) {
138
- sendLog(`[BotProcess] Ошибка парсинга аргументов: ${e.message}`);
139
- }
140
- }
141
-
142
- process.on('message', async (message) => {
143
- if (message.type === 'plugin:ui:start-updates') {
144
- const { pluginName } = message;
145
- const state = pluginUiState.get(pluginName);
146
- if (state && process.send) {
147
- process.send({
148
- type: 'plugin:data',
149
- plugin: pluginName,
150
- payload: state
151
- });
152
- }
153
- } else if (message.type === 'user_action_response') {
154
- if (pendingRequests.has(message.requestId)) {
155
- const { resolve, reject } = pendingRequests.get(message.requestId);
156
- if (message.error) {
157
- reject(new Error(message.error));
158
- } else {
159
- resolve(message.payload);
160
- }
161
- pendingRequests.delete(message.requestId);
162
- }
163
- } else if (message.type === 'system:get_player_list') {
164
- const playerList = bot ? Object.keys(bot.players) : [];
165
- if (process.send) {
166
- process.send({
167
- type: 'get_player_list_response',
168
- requestId: message.requestId,
169
- payload: { players: playerList }
170
- });
171
- }
172
- } else if (message.type === 'start') {
173
- const config = message.config;
174
- sendLog(`[System] Получена команда на запуск бота ${config.username}...`);
175
- try {
176
- const botOptions = {
177
- host: config.server.host,
178
- port: config.server.port,
179
- username: config.username,
180
- password: config.password,
181
- version: config.server.version,
182
- auth: 'offline',
183
- hideErrors: false,
184
- chat: 'enabled',
185
- };
186
-
187
- if (config.proxyHost && config.proxyPort) {
188
- sendLog(`[System] Используется прокси: ${config.proxyHost}:${config.proxyPort}`);
189
-
190
- const cleanProxyUsername = config.proxyUsername ? config.proxyUsername.trim() : null;
191
- const cleanProxyPassword = config.proxyPassword || null;
192
-
193
- botOptions.connect = (client) => {
194
- SocksClient.createConnection({
195
- proxy: {
196
- host: config.proxyHost,
197
- port: config.proxyPort,
198
- type: 5,
199
- userId: cleanProxyUsername,
200
- password: cleanProxyPassword
201
- },
202
- command: 'connect',
203
- destination: {
204
- host: config.server.host,
205
- port: config.server.port
206
- }
207
- }).then(info => {
208
- client.setSocket(info.socket);
209
- client.emit('connect');
210
- }).catch(err => {
211
- sendLog(`[Proxy Error] ${err.message}`);
212
- sendLog(`[Debug] Full proxy error: ${JSON.stringify(err)}`);
213
- client.emit('error', err);
214
- });
215
- }
216
- } else {
217
- sendLog(`[System] Прокси не настроен, используется прямое подключение.`);
218
- }
219
-
220
- bot = mineflayer.createBot(botOptions);
221
-
222
- connectionTimeout = setTimeout(() => {
223
- if (bot && !bot.player) {
224
- sendLog('[System] Таймаут подключения к серверу (30 секунд). Завершение работы...');
225
- process.exit(1);
226
- }
227
- }, 30000);
228
-
229
- bot.pluginUiState = pluginUiState;
230
-
231
- let isReady = false;
232
-
233
- bot.events = new EventEmitter();
234
- bot.events.setMaxListeners(30);
235
- bot.config = config;
236
- bot.sendLog = sendLog;
237
- bot.messageQueue = new MessageQueue(bot);
238
-
239
- const installedPluginNames = config.plugins.map(p => p.name);
240
- bot.api = {
241
- Command: Command,
242
- events: bot.events,
243
- sendMessage: (type, message, username) => bot.messageQueue.enqueue(type, message, username),
244
- sendMessageAndWaitForReply: (command, patterns, timeout) => bot.messageQueue.enqueueAndWait(command, patterns, timeout),
245
- getUser: async (username) => {
246
- return await UserService.getUser(username, bot.config.id, bot.config);
247
- },
248
- registerPermissions: (permissions) => PermissionManager.registerPermissions(bot.config.id, permissions),
249
- registerGroup: (groupConfig) => PermissionManager.registerGroup(bot.config.id, groupConfig),
250
- addPermissionsToGroup: (groupName, permissionNames) => PermissionManager.addPermissionsToGroup(bot.config.id, groupName, permissionNames),
251
- installedPlugins: installedPluginNames,
252
- registerCommand: async (command) => {
253
- try {
254
- let permissionId = null;
255
- if (command.permissions) {
256
- const permission = await prisma.permission.findUnique({
257
- where: {
258
- botId_name: {
259
- botId: bot.config.id,
260
- name: command.permissions,
261
- },
262
- },
263
- });
264
-
265
- if (permission) {
266
- permissionId = permission.id;
267
- } else {
268
- sendLog(`[API] Внимание: право \"${command.permissions}\" не найдено для команды \"${command.name}\". Команда будет создана без привязанного права.`);
269
- }
270
- }
271
-
272
- let pluginOwnerId = null;
273
- if (command.owner && command.owner.startsWith('plugin:')) {
274
- const pluginName = command.owner.replace('plugin:', '');
275
- const plugin = await prisma.installedPlugin.findFirst({
276
- where: {
277
- botId: bot.config.id,
278
- name: pluginName
279
- }
280
- });
281
- if (plugin) {
282
- pluginOwnerId = plugin.id;
283
- }
284
- }
285
-
286
- const commandData = {
287
- botId: bot.config.id,
288
- name: command.name,
289
- description: command.description || '',
290
- owner: command.owner || 'unknown',
291
- permissionId: permissionId,
292
- cooldown: command.cooldown || 0,
293
- isEnabled: command.isActive !== undefined ? command.isActive : true,
294
- aliases: JSON.stringify(command.aliases || []),
295
- allowedChatTypes: JSON.stringify(command.allowedChatTypes || ['chat', 'private']),
296
- argumentsJson: JSON.stringify(command.args || []),
297
- pluginOwnerId: pluginOwnerId,
298
- };
299
-
300
- await prisma.command.upsert({
301
- where: {
302
- botId_name: {
303
- botId: commandData.botId,
304
- name: commandData.name,
305
- }
306
- },
307
- update: {
308
- description: commandData.description,
309
- aliases: commandData.aliases,
310
- allowedChatTypes: commandData.allowedChatTypes,
311
- cooldown: commandData.cooldown,
312
- isEnabled: commandData.isEnabled,
313
- argumentsJson: commandData.argumentsJson,
314
- permissionId: commandData.permissionId,
315
- },
316
- create: commandData,
317
- });
318
-
319
- if (process.send) {
320
- process.send({
321
- type: 'register_command',
322
- commandConfig: {
323
- name: command.name,
324
- description: command.description,
325
- aliases: command.aliases,
326
- owner: command.owner,
327
- permissions: command.permissions,
328
- cooldown: command.cooldown,
329
- allowedChatTypes: command.allowedChatTypes,
330
- }
331
- });
332
- }
333
- sendLog(`[API] Команда \"${command.name}\" от плагина \"${command.owner}\" зарегистрирована в процессе.`);
334
-
335
- if (!bot.commands) bot.commands = new Map();
336
- bot.commands.set(command.name, command);
337
- if (Array.isArray(command.aliases)) {
338
- for (const alias of command.aliases) {
339
- bot.commands.set(alias, command);
340
- }
341
- }
342
- } catch (error) {
343
- sendLog(`[API] Ошибка при регистрации команды: ${error.message}`);
344
- }
345
- },
346
- performUserAction: (username, action, data = {}) => {
347
- return new Promise((resolve, reject) => {
348
- const requestId = uuidv4();
349
- pendingRequests.set(requestId, { resolve, reject });
350
-
351
- if (process.send) {
352
- process.send({
353
- type: 'request_user_action',
354
- requestId,
355
- payload: {
356
- targetUsername: username,
357
- action,
358
- data
359
- }
360
- });
361
- } else {
362
- reject(new Error('IPC channel is not available.'));
363
- }
364
-
365
- setTimeout(() => {
366
- if (pendingRequests.has(requestId)) {
367
- reject(new Error('Request to main process timed out.'));
368
- pendingRequests.delete(requestId);
369
- }
370
- }, 10000);
371
- });
372
- },
373
- registerEventGraph: async (graphData) => {
374
- try {
375
- let pluginOwnerId = null;
376
- if (graphData.owner && graphData.owner.startsWith('plugin:')) {
377
- const pluginName = graphData.owner.replace('plugin:', '');
378
- const plugin = await prisma.installedPlugin.findFirst({
379
- where: {
380
- botId: bot.config.id,
381
- name: pluginName
382
- }
383
- });
384
- if (plugin) {
385
- pluginOwnerId = plugin.id;
386
- }
387
- }
388
-
389
- const graphDataToSave = {
390
- botId: bot.config.id,
391
- name: graphData.name,
392
- isEnabled: graphData.isEnabled !== undefined ? graphData.isEnabled : true,
393
- graphJson: graphData.graphJson || JSON.stringify({ nodes: [], connections: [] }),
394
- variables: JSON.stringify(graphData.variables || []),
395
- pluginOwnerId: pluginOwnerId,
396
- };
397
-
398
- const eventGraph = await prisma.eventGraph.upsert({
399
- where: {
400
- botId_name: {
401
- botId: bot.config.id,
402
- name: graphData.name
403
- }
404
- },
405
- update: {
406
- isEnabled: graphDataToSave.isEnabled,
407
- graphJson: graphDataToSave.graphJson,
408
- variables: graphDataToSave.variables,
409
- pluginOwnerId: graphDataToSave.pluginOwnerId,
410
- },
411
- create: graphDataToSave,
412
- });
413
-
414
- if (graphData.triggers && Array.isArray(graphData.triggers)) {
415
- await prisma.eventTrigger.deleteMany({
416
- where: { graphId: eventGraph.id }
417
- });
418
-
419
- if (graphData.triggers.length > 0) {
420
- await prisma.eventTrigger.createMany({
421
- data: graphData.triggers.map(eventType => ({
422
- graphId: eventGraph.id,
423
- eventType
424
- }))
425
- });
426
- }
427
- }
428
-
429
- sendLog(`[API] Граф события "${graphData.name}" от плагина "${graphData.owner}" зарегистрирован.`);
430
- return eventGraph;
431
- } catch (error) {
432
- sendLog(`[API] Ошибка при регистрации графа события: ${error.message}`);
433
- throw error;
434
- }
435
- },
436
- executeCommand: (command) => {
437
- sendLog(`[Graph] Выполнение серверной команды: ${command}`);
438
- bot.chat(command);
439
- },
440
- lookAt: (position) => {
441
- if (bot && position) {
442
- bot.lookAt(position);
443
- }
444
- },
445
- sendUiUpdate: (pluginName, stateUpdate) => {
446
- const currentState = pluginUiState.get(pluginName) || {};
447
- const newState = { ...currentState, ...stateUpdate };
448
- pluginUiState.set(pluginName, newState);
449
-
450
-
451
- if (process.send) {
452
- process.send({
453
- type: 'plugin:data',
454
- plugin: pluginName,
455
- payload: newState
456
- });
457
- }
458
- }
459
- };
460
-
461
- const processApi = {
462
- appendLog: (botId, message) => {
463
- if (process.send) {
464
- process.send({ type: 'log', content: message });
465
- }
466
- }
467
- };
468
-
469
- bot.graphExecutionEngine = new GraphExecutionEngine(NodeRegistry, processApi);
470
-
471
- bot.commands = await loadCommands();
472
-
473
- const dbCommands = await prisma.command.findMany({ where: { botId: config.id } });
474
-
475
- for (const dbCommand of dbCommands) {
476
- if (!dbCommand.isEnabled) {
477
- if (bot.commands.has(dbCommand.name)) {
478
- bot.commands.delete(dbCommand.name);
479
- }
480
- continue;
481
- }
482
-
483
- const existingCommand = bot.commands.get(dbCommand.name);
484
-
485
- if (existingCommand) {
486
- existingCommand.description = dbCommand.description;
487
- existingCommand.cooldown = dbCommand.cooldown;
488
- existingCommand.aliases = JSON.parse(dbCommand.aliases || '[]');
489
- existingCommand.permissionId = dbCommand.permissionId;
490
- existingCommand.allowedChatTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
491
- } else if (dbCommand.isVisual) {
492
- const visualCommand = new Command({
493
- name: dbCommand.name,
494
- description: dbCommand.description,
495
- aliases: JSON.parse(dbCommand.aliases || '[]'),
496
- cooldown: dbCommand.cooldown,
497
- allowedChatTypes: JSON.parse(dbCommand.allowedChatTypes || '[]'),
498
- args: JSON.parse(dbCommand.argumentsJson || '[]'),
499
- owner: 'visual_editor',
500
- });
501
- visualCommand.permissionId = dbCommand.permissionId;
502
- visualCommand.graphJson = dbCommand.graphJson;
503
- visualCommand.owner = 'visual_editor';
504
- visualCommand.handler = (botInstance, typeChat, user, args) => {
505
- const playerList = bot ? Object.keys(bot.players) : [];
506
- const botState = bot ? { yaw: bot.entity.yaw, pitch: bot.entity.pitch } : {};
507
- const context = { bot: botInstance.api, user, args, typeChat, players: playerList, botState };
508
- return bot.graphExecutionEngine.execute(visualCommand.graphJson, context);
509
- };
510
- bot.commands.set(visualCommand.name, visualCommand);
511
- }
512
- }
513
-
514
- if (process.send) {
515
- for (const cmd of bot.commands.values()) {
516
- process.send({
517
- type: 'register_command',
518
- commandConfig: {
519
- name: cmd.name,
520
- description: cmd.description,
521
- aliases: cmd.aliases,
522
- owner: cmd.owner,
523
- permissions: cmd.permissions,
524
- cooldown: cmd.cooldown,
525
- allowedChatTypes: cmd.allowedChatTypes,
526
- }
527
- });
528
- }
529
- }
530
-
531
- await initializePlugins(bot, config.plugins, prisma);
532
- sendLog('[System] Все системы инициализированы.');
533
-
534
- let messageHandledByCustomParser = false;
535
-
536
- bot.on('message', (jsonMsg) => {
537
- const ansiMessage = jsonMsg.toAnsi();
538
- if (ansiMessage.trim()) {
539
- sendLog(ansiMessage);
540
- }
541
-
542
- messageHandledByCustomParser = false;
543
- const rawMessageText = jsonMsg.toString();
544
- bot.events.emit('core:raw_message', rawMessageText, jsonMsg);
545
-
546
- sendEvent('raw_message', {
547
- rawText: rawMessageText
548
- });
549
- });
550
-
551
- bot.events.on('chat:message', (data) => {
552
- messageHandledByCustomParser = true;
553
- sendEvent('chat', {
554
- username: data.username,
555
- message: data.message,
556
- chatType: data.type,
557
- raw: data.raw,
558
- });
559
- handleIncomingCommand(data.type, data.username, data.message);
560
- });
561
-
562
- bot.on('chat', (username, message) => {
563
- if (messageHandledByCustomParser) return;
564
- handleIncomingCommand('chat', username, message);
565
- });
566
-
567
- bot.on('whisper', (username, message) => {
568
- if (messageHandledByCustomParser) return;
569
- handleIncomingCommand('whisper', username, message);
570
- });
571
-
572
- bot.on('userAction', async ({ action, target, ...data }) => {
573
- if (!target) return;
574
-
575
- try {
576
- switch (action) {
577
- case 'addGroup':
578
- if (data.group) {
579
- await bot.api.performUserAction(target, 'addGroup', { group: data.group });
580
- }
581
- break;
582
- case 'removeGroup':
583
- if (data.group) {
584
- await bot.api.performUserAction(target, 'removeGroup', { group: data.group });
585
- }
586
- break;
587
- }
588
- } catch (error) {
589
- sendLog(`Ошибка при обработке userAction: ${error.message}`);
590
- }
591
- });
592
-
593
- bot.on('login', () => {
594
- if (connectionTimeout) {
595
- clearTimeout(connectionTimeout);
596
- connectionTimeout = null;
597
- }
598
- sendLog('[Event: login] Успешно залогинился!');
599
- if (process.send) {
600
- process.send({ type: 'bot_ready' });
601
- process.send({ type: 'status', status: 'running' });
602
- }
603
- });
604
-
605
- bot.on('death', () => {
606
- sendEvent('botDied', { user: { username: bot.username } });
607
- });
608
-
609
- bot.on('kicked', (reason) => {
610
- let reasonText;
611
- try { reasonText = JSON.parse(reason).text || reason; } catch (e) { reasonText = reason; }
612
- sendLog(`[Event: kicked] Меня кикнули. Причина: ${reasonText}.`);
613
- process.exit(0);
614
- });
615
-
616
- bot.on('error', (err) => {
617
- if (connectionTimeout) {
618
- clearTimeout(connectionTimeout);
619
- connectionTimeout = null;
620
- }
621
- sendLog(`[Event: error] Произошла ошибка: ${err.stack || err.message}`);
622
- });
623
-
624
- bot.on('end', (reason) => {
625
- if (connectionTimeout) {
626
- clearTimeout(connectionTimeout);
627
- connectionTimeout = null;
628
- }
629
- const restartableReasons = ['socketClosed', 'keepAliveError'];
630
- const exitCode = restartableReasons.includes(reason) ? 1 : 0;
631
-
632
- sendLog(`[Event: end] Отключен от сервера. Причина: ${reason}`);
633
- process.exit(exitCode);
634
- });
635
-
636
- bot.on('playerJoined', (player) => {
637
- if (!isReady) return;
638
- sendEvent('playerJoined', { user: { username: player.username, uuid: player.uuid } });
639
- });
640
-
641
- bot.on('playerLeft', (player) => {
642
- if (!isReady) return;
643
- sendEvent('playerLeft', { user: { username: player.username, uuid: player.uuid } });
644
- });
645
-
646
- bot.on('entitySpawn', (entity) => {
647
- if (!isReady) return;
648
- const serialized = serializeEntity(entity);
649
- sendEvent('entitySpawn', { entity: serialized });
650
- });
651
-
652
- bot.on('entityMoved', (entity) => {
653
- if (!isReady) return;
654
- const now = Date.now();
655
- const lastSent = entityMoveThrottles.get(entity.id);
656
- if (!lastSent || now - lastSent > 500) {
657
- entityMoveThrottles.set(entity.id, now);
658
- sendEvent('entityMoved', { entity: serializeEntity(entity) });
659
- }
660
- });
661
-
662
- bot.on('entityGone', (entity) => {
663
- if (!isReady) return;
664
- sendEvent('entityGone', { entity: serializeEntity(entity) });
665
- entityMoveThrottles.delete(entity.id);
666
- });
667
-
668
- bot.on('spawn', () => {
669
- sendLog('[Event: spawn] Бот заспавнился в мире.');
670
- setTimeout(() => {
671
- isReady = true;
672
- sendLog('[BotProcess] Бот готов к приему событий.');
673
- }, 3000);
674
- });
675
- } catch (err) {
676
- sendLog(`[CRITICAL] Критическая ошибка при создании бота: ${err.stack}`);
677
- process.exit(1);
678
- }
679
- } else if (message.type === 'config:reload') {
680
- sendLog('[System] Received config:reload command. Reloading configuration...');
681
- try {
682
- const newConfig = await fetchNewConfig(bot.config.id, prisma);
683
- if (newConfig) {
684
- bot.config = { ...bot.config, ...newConfig };
685
- const newCommands = await loadCommands();
686
- const newPlugins = bot.config.plugins;
687
- bot.commands = newCommands;
688
- await initializePlugins(bot, newPlugins, prisma);
689
- sendLog('[System] Bot configuration and plugins reloaded successfully.');
690
- } else {
691
- sendLog('[System] Failed to fetch new configuration.');
692
- }
693
- } catch (error) {
694
- sendLog(`[System] Error reloading configuration: ${error.message}`);
695
- }
696
- } else if (message.type === 'stop') {
697
- if (connectionTimeout) {
698
- clearTimeout(connectionTimeout);
699
- connectionTimeout = null;
700
- }
701
- if (bot) bot.quit();
702
- else process.exit(0);
703
- } else if (message.type === 'chat') {
704
- if (bot && bot.entity) {
705
- const { message: msg, chatType, username } = message.payload;
706
- bot.messageQueue.enqueue(chatType, msg, username);
707
- }
708
- } else if (message.type === 'execute_handler') {
709
- const { commandName, username, args, typeChat } = message;
710
- const commandInstance = bot.commands.get(commandName);
711
- if (commandInstance) {
712
- const fakeUser = { username };
713
- commandInstance.handler(bot, typeChat, fakeUser, args).catch(e => {
714
- sendLog(`[Handler Error] Ошибка в handler-е команды ${commandName}: ${e.message}`);
715
- });
716
- }
717
- } else if (message.type === 'invalidate_user_cache') {
718
- if (message.username && bot && bot.config) {
719
- UserService.clearCache(message.username, bot.config.id);
720
- }
721
- } else if (message.type === 'invalidate_all_user_cache') {
722
- if (bot && bot.config) {
723
- for (const [cacheKey, user] of UserService.cache.entries()) {
724
- if (cacheKey.startsWith(`${bot.config.id}:`)) {
725
- UserService.cache.delete(cacheKey);
726
- }
727
- }
728
- sendLog(`[BotProcess] Кэш пользователей очищен для бота ${bot.config.id}`);
729
- }
730
- } else if (message.type === 'handle_permission_error') {
731
- const { commandName, username, typeChat } = message;
732
- const commandInstance = bot.commands.get(commandName);
733
- if (commandInstance) {
734
- if (commandInstance.onInsufficientPermissions !== Command.prototype.onInsufficientPermissions) {
735
- commandInstance.onInsufficientPermissions(bot, typeChat, { username });
736
- } else {
737
- bot.api.sendMessage(typeChat, `У вас нет прав для выполнения команды ${commandName}.`, username);
738
- }
739
- }
740
- } else if (message.type === 'handle_wrong_chat') {
741
- const { commandName, username, typeChat } = message;
742
- const commandInstance = bot.commands.get(commandName);
743
- if (commandInstance) {
744
- if (commandInstance.onWrongChatType !== Command.prototype.onWrongChatType) {
745
- commandInstance.onWrongChatType(bot, typeChat, { username });
746
- } else {
747
- bot.api.sendMessage('private', `Команду ${commandName} нельзя использовать в этом типе чата - ${typeChat}.`, username);
748
- }
749
- }
750
- } else if (message.type === 'handle_cooldown') {
751
- const { commandName, username, typeChat, timeLeft } = message;
752
- const commandInstance = bot.commands.get(commandName);
753
- if (commandInstance) {
754
- if (commandInstance.onCooldown !== Command.prototype.onCooldown) {
755
- commandInstance.onCooldown(bot, typeChat, { username }, timeLeft);
756
- } else {
757
- bot.api.sendMessage(typeChat, `Команду ${commandName} можно будет использовать через ${timeLeft} сек.`, username);
758
- }
759
- }
760
- } else if (message.type === 'handle_blacklist') {
761
- const { commandName, username, typeChat } = message;
762
- const commandInstance = bot.commands.get(commandName);
763
- if (commandInstance) {
764
- if (commandInstance.onBlacklisted !== Command.prototype.onBlacklisted) {
765
- commandInstance.onBlacklisted(bot, typeChat, { username });
766
- }
767
- }
768
- } else if (message.type === 'action') {
769
- if (message.name === 'lookAt' && bot && message.payload.position) {
770
- const { x, y, z } = message.payload.position;
771
- if (typeof x === 'number' && typeof y === 'number' && typeof z === 'number') {
772
- bot.lookAt(new Vec3(x, y, z));
773
- } else {
774
- sendLog(`[BotProcess] Ошибка lookAt: получены невалидные координаты: ${JSON.stringify(message.payload.position)}`);
775
- }
776
- }
777
- } else if (message.type === 'plugins:reload') {
778
- sendLog('[System] Получена команда на перезагрузку плагинов...');
779
- const newConfig = await fetchNewConfig(bot.config.id, prisma);
780
- if (newConfig) {
781
- bot.config.plugins = newConfig.installedPlugins;
782
- bot.commands.clear();
783
- await loadCommands(bot, newConfig.commands);
784
- await initializePlugins(bot, newConfig.installedPlugins, prisma);
785
- sendLog('[System] Плагины успешно перезагружены.');
786
- } else {
787
- sendLog('[System] Не удалось получить новую конфигурацию для перезагрузки плагинов.');
788
- }
789
- } else if (message.type === 'server_command') {
790
- if (bot && message.payload && message.payload.command) {
791
- bot.chat(message.payload.command);
792
- }
793
- }
794
- });
795
-
796
- process.on('unhandledRejection', (reason, promise) => {
797
- const errorMsg = `[FATAL] Необработанная ошибка процесса: ${reason?.stack || reason}`;
798
- sendLog(errorMsg);
799
- setTimeout(() => process.exit(1), 100);
800
- });
801
-
802
- process.on('uncaughtException', (error) => {
803
- const errorMsg = `[FATAL] Необработанное исключение: ${error.stack || error.message}`;
804
- sendLog(errorMsg);
805
- setTimeout(() => process.exit(1), 100);
806
- });
807
-
808
- process.on('SIGTERM', () => {
809
- sendLog('[System] Получен сигнал SIGTERM. Завершение работы...');
810
- if (bot) {
811
- try {
812
- bot.quit();
813
- } catch (error) {
814
- sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
815
- }
816
- }
817
- setTimeout(() => process.exit(0), 100);
818
- });
819
-
820
- process.on('SIGINT', () => {
821
- sendLog('[System] Получен сигнал SIGINT. Завершение работы...');
822
- if (bot) {
823
- try {
824
- bot.quit();
825
- } catch (error) {
826
- sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
827
- }
828
- }
829
- setTimeout(() => process.exit(0), 100);
830
- });
831
-
832
-
833
- function serializeEntity(entity) {
834
- if (!entity) return null;
835
- return {
836
- id: entity.id,
837
- type: entity.type,
838
- username: entity.username,
839
- displayName: entity.displayName,
840
- position: entity.position,
841
- yaw: entity.yaw,
842
- pitch: entity.pitch,
843
- onGround: entity.onGround,
844
- isValid: entity.isValid,
845
- heldItem: entity.heldItem,
846
- equipment: entity.equipment,
847
- metadata: entity.metadata
848
- };
849
- }
850
-
1
+ const mineflayer = require('mineflayer');
2
+ const { SocksClient } = require('socks');
3
+ const EventEmitter = require('events');
4
+ const { v4: uuidv4 } = require('uuid');
5
+ const { Vec3 } = require('vec3');
6
+ const { PrismaClient } = require('@prisma/client');
7
+ const { loadCommands } = require('./system/CommandRegistry');
8
+ const { initializePlugins } = require('./PluginLoader');
9
+ const MessageQueue = require('./MessageQueue');
10
+ const Command = require('./system/Command');
11
+ const { parseArguments } = require('./system/parseArguments');
12
+ const GraphExecutionEngine = require('./GraphExecutionEngine');
13
+ const NodeRegistry = require('./NodeRegistry');
14
+
15
+ const UserService = require('./UserService');
16
+ const PermissionManager = require('./ipc/PermissionManager.stub.js');
17
+ const Transport = require('./system/Transport');
18
+ const CommandContext = require('./system/CommandContext');
19
+
20
+ let bot = null;
21
+ const prisma = new PrismaClient();
22
+ const pluginUiState = new Map();
23
+ const pendingRequests = new Map();
24
+ const entityMoveThrottles = new Map();
25
+ let connectionTimeout = null;
26
+
27
+ const originalJSONParse = JSON.parse
28
+ JSON.parse = function(text, reviver) {
29
+ if (typeof text !== 'string') return originalJSONParse(text, reviver)
30
+ try {
31
+ return originalJSONParse(text, reviver)
32
+ } catch (e) {
33
+ const fixed = text.replace(/([{,])\s*([a-zA-Z0-9_]+)\s*:/g, '$1"$2":')
34
+ return originalJSONParse(fixed, reviver)
35
+ }
36
+ }
37
+
38
+ function sendLog(content) {
39
+ if (process.send) {
40
+ process.send({ type: 'log', content });
41
+ } else {
42
+ console.log(`[ChildProcess Log] ${content}`);
43
+ }
44
+ }
45
+
46
+
47
+ function sendEvent(eventName, eventArgs) {
48
+ if (process.send) {
49
+ // Добавляем информацию о боте (позицию) во все события
50
+ const enrichedArgs = {
51
+ ...eventArgs,
52
+ botEntity: bot && bot.entity ? {
53
+ position: bot.entity.position,
54
+ yaw: bot.entity.yaw,
55
+ pitch: bot.entity.pitch
56
+ } : null
57
+ };
58
+ process.send({ type: 'event', eventType: eventName, args: enrichedArgs });
59
+ }
60
+ }
61
+
62
+ async function fetchNewConfig(botId, prisma) {
63
+ try {
64
+ const botData = await prisma.bot.findUnique({
65
+ where: { id: botId },
66
+ include: {
67
+ server: true,
68
+ installedPlugins: {
69
+ where: { isEnabled: true }
70
+ },
71
+ }
72
+ });
73
+
74
+ if (!botData) return null;
75
+
76
+ const commands = await prisma.command.findMany({ where: { botId } });
77
+
78
+ return { ...botData, commands };
79
+ } catch (error) {
80
+ sendLog(`[fetchNewConfig] Error: ${error.message}`);
81
+ return null;
82
+ }
83
+ }
84
+
85
+ function handleIncomingCommand(type, username, message) {
86
+ if (!message.startsWith(bot.config.prefix || '@')) return;
87
+
88
+ const rawMessage = message.slice((bot.config.prefix || '@').length).trim();
89
+ const commandParts = rawMessage.split(/ +/);
90
+ const commandName = commandParts.shift().toLowerCase();
91
+ const restOfMessage = commandParts.join(' ');
92
+
93
+ const commandInstance = bot.commands.get(commandName) ||
94
+ Array.from(bot.commands.values()).find(cmd => cmd.aliases.includes(commandName));
95
+
96
+ if (!commandInstance) return;
97
+
98
+ try {
99
+ const processedArgs = {};
100
+ const parsedArgs = parseArguments(restOfMessage);
101
+ let currentArgIndex = 0;
102
+
103
+ const argsDef = commandInstance.isVisual && commandInstance.args ? commandInstance.args : (commandInstance.args || []);
104
+ for (const argDef of argsDef) {
105
+ if (argDef.type === 'greedy_string') {
106
+ if (currentArgIndex < parsedArgs.length) {
107
+ processedArgs[argDef.name] = parsedArgs.slice(currentArgIndex).join(' ');
108
+ currentArgIndex = parsedArgs.length;
109
+ }
110
+ } else if (currentArgIndex < parsedArgs.length) {
111
+ let value = parsedArgs[currentArgIndex];
112
+ if (argDef.type === 'number') {
113
+ const numValue = parseFloat(value);
114
+ if (isNaN(numValue)) {
115
+ bot.api.sendMessage(type, `Ошибка: Аргумент \"${argDef.description}\" должен быть числом.`, username);
116
+ return;
117
+ }
118
+ value = numValue;
119
+ }
120
+ processedArgs[argDef.name] = value;
121
+ currentArgIndex++;
122
+ }
123
+
124
+ if (processedArgs[argDef.name] === undefined) {
125
+ if (argDef.required) {
126
+ const usage = commandInstance.args.map(arg => {
127
+ return arg.required ? `<${arg.description || arg.name}>` : `[${arg.description || arg.name}]`;
128
+ }).join(' ');
129
+
130
+ bot.api.sendMessage(type, `Ошибка: Необходимо указать: ${argDef.description || argDef.name}`, username);
131
+ bot.api.sendMessage(type, `Использование: ${bot.config.prefix}${commandInstance.name} ${usage}`, username);
132
+ return;
133
+ }
134
+ if (argDef.default !== undefined) {
135
+ processedArgs[argDef.name] = argDef.default;
136
+ }
137
+ }
138
+ }
139
+
140
+ if (process.send) {
141
+ process.send({
142
+ type: 'validate_and_run_command',
143
+ commandName: commandInstance.name,
144
+ username,
145
+ args: processedArgs,
146
+ typeChat: type
147
+ });
148
+ }
149
+ } catch (e) {
150
+ sendLog(`[BotProcess] Ошибка парсинга аргументов: ${e.message}`);
151
+ }
152
+ }
153
+
154
+ process.on('message', async (message) => {
155
+ if (message.type === 'plugin:ui:start-updates') {
156
+ const { pluginName } = message;
157
+ const state = pluginUiState.get(pluginName);
158
+ if (state && process.send) {
159
+ process.send({
160
+ type: 'plugin:data',
161
+ plugin: pluginName,
162
+ payload: state
163
+ });
164
+ }
165
+ } else if (message.type === 'user_action_response') {
166
+ if (pendingRequests.has(message.requestId)) {
167
+ const { resolve, reject } = pendingRequests.get(message.requestId);
168
+ if (message.error) {
169
+ reject(new Error(message.error));
170
+ } else {
171
+ resolve(message.payload);
172
+ }
173
+ pendingRequests.delete(message.requestId);
174
+ }
175
+ } else if (message.type === 'system:get_player_list') {
176
+ const playerList = bot ? Object.keys(bot.players) : [];
177
+ if (process.send) {
178
+ process.send({
179
+ type: 'get_player_list_response',
180
+ requestId: message.requestId,
181
+ payload: { players: playerList }
182
+ });
183
+ }
184
+ } else if (message.type === 'system:get_nearby_entities') {
185
+ const entities = [];
186
+ if (bot && bot.entities) {
187
+ const centerPos = message.payload?.position || bot.entity?.position;
188
+ const radius = message.payload?.radius || 32;
189
+
190
+ if (centerPos) {
191
+ // Перебираем все сущности
192
+ for (const entity of Object.values(bot.entities)) {
193
+ if (entity && entity.position && entity.isValid) {
194
+ // Вычисляем расстояние
195
+ const dx = entity.position.x - centerPos.x;
196
+ const dy = entity.position.y - centerPos.y;
197
+ const dz = entity.position.z - centerPos.z;
198
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
199
+
200
+ // Если существо в радиусе, добавляем в список
201
+ if (distance <= radius) {
202
+ entities.push(serializeEntity(entity));
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ if (process.send) {
210
+ process.send({
211
+ type: 'get_nearby_entities_response',
212
+ requestId: message.requestId,
213
+ payload: { entities }
214
+ });
215
+ }
216
+ } else if (message.type === 'start') {
217
+ const config = message.config;
218
+ sendLog(`[System] Получена команда на запуск бота ${config.username}...`);
219
+ try {
220
+ const botOptions = {
221
+ host: config.server.host,
222
+ port: config.server.port,
223
+ username: config.username,
224
+ password: config.password,
225
+ version: config.server.version,
226
+ auth: 'offline',
227
+ hideErrors: false,
228
+ chat: 'enabled',
229
+ };
230
+
231
+ if (config.proxyHost && config.proxyPort) {
232
+ sendLog(`[System] Используется прокси: ${config.proxyHost}:${config.proxyPort}`);
233
+
234
+ const cleanProxyUsername = config.proxyUsername ? config.proxyUsername.trim() : null;
235
+ const cleanProxyPassword = config.proxyPassword || null;
236
+
237
+ botOptions.connect = (client) => {
238
+ SocksClient.createConnection({
239
+ proxy: {
240
+ host: config.proxyHost,
241
+ port: config.proxyPort,
242
+ type: 5,
243
+ userId: cleanProxyUsername,
244
+ password: cleanProxyPassword
245
+ },
246
+ command: 'connect',
247
+ destination: {
248
+ host: config.server.host,
249
+ port: config.server.port
250
+ }
251
+ }).then(info => {
252
+ client.setSocket(info.socket);
253
+ client.emit('connect');
254
+ }).catch(err => {
255
+ sendLog(`[Proxy Error] SOCKS connection failed: ${err.message}. Bot will attempt to restart.`);
256
+ client.emit('error', err);
257
+ process.exit(1);
258
+ });
259
+ }
260
+ } else {
261
+ sendLog(`[System] Прокси не настроен, используется прямое подключение.`);
262
+ }
263
+
264
+ bot = mineflayer.createBot(botOptions);
265
+
266
+ connectionTimeout = setTimeout(() => {
267
+ if (bot && !bot.player) {
268
+ sendLog('[System] Таймаут подключения к серверу (30 секунд). Завершение работы...');
269
+ process.exit(1);
270
+ }
271
+ }, 30000);
272
+
273
+ bot.pluginUiState = pluginUiState;
274
+
275
+ let isReady = false;
276
+
277
+ bot.events = new EventEmitter();
278
+ bot.events.setMaxListeners(30);
279
+ bot.config = config;
280
+ bot.sendLog = sendLog;
281
+ bot.messageQueue = new MessageQueue(bot);
282
+
283
+ const installedPluginNames = config.plugins.map(p => p.name);
284
+ bot.api = {
285
+ Command: Command,
286
+ events: bot.events,
287
+ sendMessage: (type, message, username) => {
288
+ if (type === 'websocket') {
289
+ if (process.send) {
290
+ process.send({
291
+ type: 'send_websocket_message',
292
+ payload: {
293
+ botId: bot.config.id,
294
+ message: message,
295
+ }
296
+ });
297
+ }
298
+ } else {
299
+ bot.messageQueue.enqueue(type, message, username);
300
+ }
301
+ },
302
+ sendMessageAndWaitForReply: (command, patterns, timeout) => bot.messageQueue.enqueueAndWait(command, patterns, timeout),
303
+ getUser: async (username) => {
304
+ return await UserService.getUser(username, bot.config.id, bot.config);
305
+ },
306
+ registerPermissions: (permissions) => PermissionManager.registerPermissions(bot.config.id, permissions),
307
+ registerGroup: (groupConfig) => PermissionManager.registerGroup(bot.config.id, groupConfig),
308
+ addPermissionsToGroup: (groupName, permissionNames) => PermissionManager.addPermissionsToGroup(bot.config.id, groupName, permissionNames),
309
+ installedPlugins: installedPluginNames,
310
+ registerCommand: async (command) => {
311
+ try {
312
+ let permissionId = null;
313
+ if (command.permissions) {
314
+ let permission = await prisma.permission.findUnique({
315
+ where: {
316
+ botId_name: {
317
+ botId: bot.config.id,
318
+ name: command.permissions,
319
+ },
320
+ },
321
+ });
322
+
323
+ if (!permission) {
324
+ // Автоматически создаем право, если оно не найдено
325
+ permission = await prisma.permission.create({
326
+ data: {
327
+ botId: bot.config.id,
328
+ name: command.permissions,
329
+ description: `Автоматически создано для команды ${command.name}`,
330
+ owner: command.owner || 'system',
331
+ },
332
+ });
333
+ sendLog(`[API] Право \"${command.permissions}\" автоматически создано для команды \"${command.name}\".`);
334
+ }
335
+ permissionId = permission.id;
336
+ }
337
+
338
+ let pluginOwnerId = null;
339
+ if (command.owner && command.owner.startsWith('plugin:')) {
340
+ const pluginName = command.owner.replace('plugin:', '');
341
+ const plugin = await prisma.installedPlugin.findFirst({
342
+ where: {
343
+ botId: bot.config.id,
344
+ name: pluginName
345
+ }
346
+ });
347
+ if (plugin) {
348
+ pluginOwnerId = plugin.id;
349
+ }
350
+ }
351
+
352
+ const commandData = {
353
+ botId: bot.config.id,
354
+ name: command.name,
355
+ description: command.description || '',
356
+ owner: command.owner || 'unknown',
357
+ permissionId: permissionId,
358
+ cooldown: command.cooldown || 0,
359
+ isEnabled: command.isActive !== undefined ? command.isActive : true,
360
+ aliases: JSON.stringify(command.aliases || []),
361
+ allowedChatTypes: JSON.stringify(command.allowedChatTypes || ['chat', 'private']),
362
+ argumentsJson: JSON.stringify(command.args || []),
363
+ pluginOwnerId: pluginOwnerId,
364
+ };
365
+
366
+ await prisma.command.upsert({
367
+ where: {
368
+ botId_name: {
369
+ botId: commandData.botId,
370
+ name: commandData.name,
371
+ }
372
+ },
373
+ update: {
374
+ description: commandData.description,
375
+ aliases: commandData.aliases,
376
+ allowedChatTypes: commandData.allowedChatTypes,
377
+ cooldown: commandData.cooldown,
378
+ isEnabled: commandData.isEnabled,
379
+ argumentsJson: commandData.argumentsJson,
380
+ permissionId: commandData.permissionId,
381
+ },
382
+ create: commandData,
383
+ });
384
+
385
+ if (process.send) {
386
+ process.send({
387
+ type: 'register_command',
388
+ commandConfig: {
389
+ name: command.name,
390
+ description: command.description,
391
+ aliases: command.aliases,
392
+ owner: command.owner,
393
+ permissions: command.permissions,
394
+ cooldown: command.cooldown,
395
+ allowedChatTypes: command.allowedChatTypes,
396
+ }
397
+ });
398
+ }
399
+ sendLog(`[API] Команда \"${command.name}\" от плагина \"${command.owner}\" зарегистрирована в процессе.`);
400
+
401
+ if (!bot.commands) bot.commands = new Map();
402
+ bot.commands.set(command.name, command);
403
+ if (Array.isArray(command.aliases)) {
404
+ for (const alias of command.aliases) {
405
+ bot.commands.set(alias, command);
406
+ }
407
+ }
408
+ } catch (error) {
409
+ sendLog(`[API] Ошибка при регистрации команды: ${error.message}`);
410
+ }
411
+ },
412
+ performUserAction: (username, action, data = {}) => {
413
+ return new Promise((resolve, reject) => {
414
+ const requestId = uuidv4();
415
+ pendingRequests.set(requestId, { resolve, reject });
416
+
417
+ if (process.send) {
418
+ process.send({
419
+ type: 'request_user_action',
420
+ requestId,
421
+ payload: {
422
+ targetUsername: username,
423
+ action,
424
+ data
425
+ }
426
+ });
427
+ } else {
428
+ reject(new Error('IPC channel is not available.'));
429
+ }
430
+
431
+ setTimeout(() => {
432
+ if (pendingRequests.has(requestId)) {
433
+ reject(new Error('Request to main process timed out.'));
434
+ pendingRequests.delete(requestId);
435
+ }
436
+ }, 10000);
437
+ });
438
+ },
439
+ registerEventGraph: async (graphData) => {
440
+ try {
441
+ let pluginOwnerId = null;
442
+ if (graphData.owner && graphData.owner.startsWith('plugin:')) {
443
+ const pluginName = graphData.owner.replace('plugin:', '');
444
+ const plugin = await prisma.installedPlugin.findFirst({
445
+ where: {
446
+ botId: bot.config.id,
447
+ name: pluginName
448
+ }
449
+ });
450
+ if (plugin) {
451
+ pluginOwnerId = plugin.id;
452
+ }
453
+ }
454
+
455
+ const graphDataToSave = {
456
+ botId: bot.config.id,
457
+ name: graphData.name,
458
+ isEnabled: graphData.isEnabled !== undefined ? graphData.isEnabled : true,
459
+ graphJson: graphData.graphJson || JSON.stringify({ nodes: [], connections: [] }),
460
+ variables: JSON.stringify(graphData.variables || []),
461
+ pluginOwnerId: pluginOwnerId,
462
+ };
463
+
464
+ const eventGraph = await prisma.eventGraph.upsert({
465
+ where: {
466
+ botId_name: {
467
+ botId: bot.config.id,
468
+ name: graphData.name
469
+ }
470
+ },
471
+ update: {
472
+ isEnabled: graphDataToSave.isEnabled,
473
+ graphJson: graphDataToSave.graphJson,
474
+ variables: graphDataToSave.variables,
475
+ pluginOwnerId: graphDataToSave.pluginOwnerId,
476
+ },
477
+ create: graphDataToSave,
478
+ });
479
+
480
+ if (graphData.triggers && Array.isArray(graphData.triggers)) {
481
+ await prisma.eventTrigger.deleteMany({
482
+ where: { graphId: eventGraph.id }
483
+ });
484
+
485
+ if (graphData.triggers.length > 0) {
486
+ await prisma.eventTrigger.createMany({
487
+ data: graphData.triggers.map(eventType => ({
488
+ graphId: eventGraph.id,
489
+ eventType
490
+ }))
491
+ });
492
+ }
493
+ }
494
+
495
+ sendLog(`[API] Граф события "${graphData.name}" от плагина "${graphData.owner}" зарегистрирован.`);
496
+ return eventGraph;
497
+ } catch (error) {
498
+ sendLog(`[API] Ошибка при регистрации графа события: ${error.message}`);
499
+ throw error;
500
+ }
501
+ },
502
+ executeCommand: (command) => {
503
+ sendLog(`[Graph] Выполнение серверной команды: ${command}`);
504
+ bot.chat(command);
505
+ },
506
+ lookAt: (position) => {
507
+ if (bot && position) {
508
+ bot.lookAt(position);
509
+ }
510
+ },
511
+ getNearbyEntities: (position = null, radius = 32) => {
512
+ const entities = [];
513
+ if (bot && bot.entities) {
514
+ const centerPos = position || bot.entity?.position;
515
+
516
+ if (centerPos) {
517
+ for (const entity of Object.values(bot.entities)) {
518
+ if (entity && entity.position && entity.isValid) {
519
+ const dx = entity.position.x - centerPos.x;
520
+ const dy = entity.position.y - centerPos.y;
521
+ const dz = entity.position.z - centerPos.z;
522
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
523
+
524
+ if (distance <= radius) {
525
+ entities.push(serializeEntity(entity));
526
+ }
527
+ }
528
+ }
529
+ }
530
+ }
531
+ return entities;
532
+ },
533
+ sendLog: (message) => {
534
+ sendLog(message);
535
+ },
536
+ sendUiUpdate: (pluginName, stateUpdate) => {
537
+ const currentState = pluginUiState.get(pluginName) || {};
538
+ const newState = { ...currentState, ...stateUpdate };
539
+ pluginUiState.set(pluginName, newState);
540
+
541
+
542
+ if (process.send) {
543
+ process.send({
544
+ type: 'plugin:data',
545
+ plugin: pluginName,
546
+ payload: newState
547
+ });
548
+ }
549
+ }
550
+ };
551
+
552
+ // Упрощенный alias для отправки сообщений (используется в командах и нодах)
553
+ bot.sendMessage = (type, message, username) => {
554
+ bot.api.sendMessage(type, message, username);
555
+ };
556
+
557
+ // Добавляем bot.sendLog для команд
558
+ bot.sendLog = (message) => sendLog(message);
559
+
560
+ const processApi = {
561
+ appendLog: (botId, message) => {
562
+ if (process.send) {
563
+ process.send({ type: 'log', content: message });
564
+ }
565
+ }
566
+ };
567
+
568
+ bot.graphExecutionEngine = new GraphExecutionEngine(NodeRegistry, processApi);
569
+
570
+ bot.commands = await loadCommands();
571
+
572
+ const dbCommands = await prisma.command.findMany({ where: { botId: config.id } });
573
+
574
+ for (const dbCommand of dbCommands) {
575
+ const existingCommand = bot.commands.get(dbCommand.name);
576
+
577
+ // Не удаляем выключенные команды, а помечаем их
578
+ // Владельцы смогут использовать выключенные команды через проверку в CommandExecutionService
579
+
580
+ if (existingCommand) {
581
+ existingCommand.isEnabled = dbCommand.isEnabled;
582
+ existingCommand.description = dbCommand.description;
583
+ existingCommand.cooldown = dbCommand.cooldown;
584
+ existingCommand.aliases = JSON.parse(dbCommand.aliases || '[]');
585
+ existingCommand.permissionId = dbCommand.permissionId;
586
+ existingCommand.allowedChatTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
587
+
588
+ // Добавляем алиасы в bot.commands для быстрого доступа
589
+ const aliases = JSON.parse(dbCommand.aliases || '[]');
590
+ for (const alias of aliases) {
591
+ bot.commands.set(alias, existingCommand);
592
+ }
593
+ } else if (dbCommand.isVisual) {
594
+ const visualCommand = new Command({
595
+ name: dbCommand.name,
596
+ description: dbCommand.description,
597
+ aliases: JSON.parse(dbCommand.aliases || '[]'),
598
+ cooldown: dbCommand.cooldown,
599
+ allowedChatTypes: JSON.parse(dbCommand.allowedChatTypes || '[]'),
600
+ args: JSON.parse(dbCommand.argumentsJson || '[]'),
601
+ owner: 'visual_editor',
602
+ });
603
+ visualCommand.permissionId = dbCommand.permissionId;
604
+ visualCommand.graphJson = dbCommand.graphJson;
605
+ visualCommand.owner = 'visual_editor';
606
+ visualCommand.handler = (botInstance, typeChat, user, args) => {
607
+ const playerList = bot ? Object.keys(bot.players) : [];
608
+ const botState = bot ? { yaw: bot.entity.yaw, pitch: bot.entity.pitch } : {};
609
+ const botEntity = bot && bot.entity ? {
610
+ position: bot.entity.position,
611
+ yaw: bot.entity.yaw,
612
+ pitch: bot.entity.pitch
613
+ } : null;
614
+ const context = {
615
+ bot: botInstance.api,
616
+ user,
617
+ args,
618
+ typeChat,
619
+ players: playerList,
620
+ botState,
621
+ botEntity
622
+ };
623
+ return bot.graphExecutionEngine.execute(visualCommand.graphJson, context);
624
+ };
625
+ bot.commands.set(visualCommand.name, visualCommand);
626
+
627
+ // Добавляем алиасы визуальных команд
628
+ const visualAliases = JSON.parse(dbCommand.aliases || '[]');
629
+ for (const alias of visualAliases) {
630
+ bot.commands.set(alias, visualCommand);
631
+ }
632
+ }
633
+ }
634
+
635
+ // Добавляем алиасы для всех загруженных команд (системных и плагинов)
636
+ for (const cmd of bot.commands.values()) {
637
+ if (cmd.aliases && Array.isArray(cmd.aliases)) {
638
+ for (const alias of cmd.aliases) {
639
+ if (!bot.commands.has(alias)) {
640
+ bot.commands.set(alias, cmd);
641
+ }
642
+ }
643
+ }
644
+ }
645
+
646
+ if (process.send) {
647
+ for (const cmd of bot.commands.values()) {
648
+ process.send({
649
+ type: 'register_command',
650
+ commandConfig: {
651
+ name: cmd.name,
652
+ description: cmd.description,
653
+ aliases: cmd.aliases,
654
+ owner: cmd.owner,
655
+ permissions: cmd.permissions,
656
+ cooldown: cmd.cooldown,
657
+ allowedChatTypes: cmd.allowedChatTypes,
658
+ }
659
+ });
660
+ }
661
+ }
662
+
663
+ await initializePlugins(bot, config.plugins, prisma);
664
+ sendLog('[System] Все системы инициализированы.');
665
+
666
+ let messageHandledByCustomParser = false;
667
+
668
+ bot.on('message', (jsonMsg) => {
669
+ const logContent = jsonMsg.toAnsi();
670
+
671
+ if (logContent.trim()) {
672
+ sendLog(logContent);
673
+ }
674
+
675
+ messageHandledByCustomParser = false;
676
+ const rawMessageText = jsonMsg.toString();
677
+ bot.events.emit('core:raw_message', rawMessageText, jsonMsg);
678
+
679
+ sendEvent('raw_message', {
680
+ rawText: rawMessageText,
681
+ json: jsonMsg
682
+ });
683
+ });
684
+
685
+ bot.events.on('chat:message', (data) => {
686
+ messageHandledByCustomParser = true;
687
+ sendEvent('chat', {
688
+ username: data.username,
689
+ message: data.message,
690
+ chatType: data.type,
691
+ raw: data.raw,
692
+ });
693
+ handleIncomingCommand(data.type, data.username, data.message);
694
+ });
695
+
696
+ bot.on('chat', (username, message) => {
697
+ if (messageHandledByCustomParser) return;
698
+ handleIncomingCommand('chat', username, message);
699
+ });
700
+
701
+ bot.on('whisper', (username, message) => {
702
+ if (messageHandledByCustomParser) return;
703
+ handleIncomingCommand('whisper', username, message);
704
+ });
705
+
706
+ bot.on('userAction', async ({ action, target, ...data }) => {
707
+ if (!target) return;
708
+
709
+ try {
710
+ switch (action) {
711
+ case 'addGroup':
712
+ if (data.group) {
713
+ await bot.api.performUserAction(target, 'addGroup', { group: data.group });
714
+ }
715
+ break;
716
+ case 'removeGroup':
717
+ if (data.group) {
718
+ await bot.api.performUserAction(target, 'removeGroup', { group: data.group });
719
+ }
720
+ break;
721
+ }
722
+ } catch (error) {
723
+ sendLog(`Ошибка при обработке userAction: ${error.message}`);
724
+ }
725
+ });
726
+
727
+ bot.on('login', () => {
728
+ if (connectionTimeout) {
729
+ clearTimeout(connectionTimeout);
730
+ connectionTimeout = null;
731
+ }
732
+ sendLog('[Event: login] Успешно залогинился!');
733
+ if (process.send) {
734
+ process.send({ type: 'bot_ready' });
735
+ process.send({ type: 'status', status: 'running' });
736
+ }
737
+ });
738
+
739
+ bot.on('death', () => {
740
+ sendEvent('botDied', { user: { username: bot.username } });
741
+ });
742
+
743
+ bot.on('health', () => {
744
+ sendEvent('health', {
745
+ health: bot.health,
746
+ food: bot.food,
747
+ saturation: bot.foodSaturation
748
+ });
749
+ });
750
+
751
+ bot.on('kicked', (reason) => {
752
+ let reasonText;
753
+ try { reasonText = JSON.parse(reason).text || reason; } catch (e) { reasonText = reason; }
754
+ sendLog(`[Event: kicked] Меня кикнули. Причина: ${reasonText}.`);
755
+ process.exit(0);
756
+ });
757
+
758
+ bot.on('error', (err) => {
759
+ if (connectionTimeout) {
760
+ clearTimeout(connectionTimeout);
761
+ connectionTimeout = null;
762
+ }
763
+ sendLog(`[Event: error] Произошла ошибка: ${err.stack || err.message}`);
764
+ });
765
+
766
+ bot.on('end', (reason) => {
767
+ if (connectionTimeout) {
768
+ clearTimeout(connectionTimeout);
769
+ connectionTimeout = null;
770
+ }
771
+ const restartableReasons = ['socketClosed', 'keepAliveError'];
772
+ const exitCode = restartableReasons.includes(reason) ? 1 : 0;
773
+
774
+ sendLog(`[Event: end] Отключен от сервера. Причина: ${reason}`);
775
+ process.exit(exitCode);
776
+ });
777
+
778
+ bot.on('playerJoined', (player) => {
779
+ if (!isReady) return;
780
+ sendEvent('playerJoined', { user: { username: player.username, uuid: player.uuid } });
781
+ });
782
+
783
+ bot.on('playerLeft', (player) => {
784
+ if (!isReady) return;
785
+ sendEvent('playerLeft', { user: { username: player.username, uuid: player.uuid } });
786
+ });
787
+
788
+ bot.on('entitySpawn', (entity) => {
789
+ if (!isReady) return;
790
+ const serialized = serializeEntity(entity);
791
+ sendEvent('entitySpawn', { entity: serialized });
792
+ });
793
+
794
+ bot.on('entityMoved', (entity) => {
795
+ if (!isReady) return;
796
+ const now = Date.now();
797
+ const lastSent = entityMoveThrottles.get(entity.id);
798
+ if (!lastSent || now - lastSent > 500) {
799
+ entityMoveThrottles.set(entity.id, now);
800
+ sendEvent('entityMoved', { entity: serializeEntity(entity) });
801
+ }
802
+ });
803
+
804
+ bot.on('entityGone', (entity) => {
805
+ if (!isReady) return;
806
+ sendEvent('entityGone', { entity: serializeEntity(entity) });
807
+ entityMoveThrottles.delete(entity.id);
808
+ });
809
+
810
+ bot.on('spawn', () => {
811
+ try {
812
+ if (bot._client && bot._client.options) {
813
+ bot._client.options.chat = 'enabled';
814
+ }
815
+ if (bot.chatEnabled !== undefined) {
816
+ bot.chatEnabled = true;
817
+ }
818
+ } catch (err) {
819
+ }
820
+ setTimeout(() => {
821
+ isReady = true;
822
+ }, 3000);
823
+ });
824
+ } catch (err) {
825
+ sendLog(`[CRITICAL] Критическая ошибка при создании бота: ${err.stack}`);
826
+ process.exit(1);
827
+ }
828
+ } else if (message.type === 'config:reload') {
829
+ sendLog('[System] Received config:reload command. Reloading configuration...');
830
+ try {
831
+ const newConfig = await fetchNewConfig(bot.config.id, prisma);
832
+ if (newConfig) {
833
+ bot.config = { ...bot.config, ...newConfig };
834
+ const newCommands = await loadCommands();
835
+ const newPlugins = bot.config.plugins;
836
+ bot.commands = newCommands;
837
+ await initializePlugins(bot, newPlugins, prisma);
838
+ sendLog('[System] Bot configuration and plugins reloaded successfully.');
839
+ } else {
840
+ sendLog('[System] Failed to fetch new configuration.');
841
+ }
842
+ } catch (error) {
843
+ sendLog(`[System] Error reloading configuration: ${error.message}`);
844
+ }
845
+ } else if (message.type === 'stop') {
846
+ if (connectionTimeout) {
847
+ clearTimeout(connectionTimeout);
848
+ connectionTimeout = null;
849
+ }
850
+ if (bot) bot.quit();
851
+ else process.exit(0);
852
+ } else if (message.type === 'chat') {
853
+ if (bot && bot.entity) {
854
+ const { message: msg, chatType, username } = message.payload;
855
+ bot.messageQueue.enqueue(chatType, msg, username);
856
+ }
857
+ } else if (message.type === 'execute_handler') {
858
+ const { commandName, username, args, typeChat } = message;
859
+ const commandInstance = bot.commands.get(commandName);
860
+ if (commandInstance) {
861
+ (async () => {
862
+ try {
863
+ // Получаем полный объект User из базы данных
864
+ const user = await UserService.getUser(username, bot.config.id, bot.config);
865
+
866
+ // Проверяем сигнатуру handler - старая (4 аргумента) или новая (1 аргумент context)
867
+ const handlerParamCount = commandInstance.handler.length;
868
+
869
+ if (handlerParamCount === 1) {
870
+ // Новая сигнатура: handler(context)
871
+ const transport = new Transport(typeChat, bot);
872
+ const context = new CommandContext(bot, user, args, transport);
873
+ await commandInstance.handler(context);
874
+ } else {
875
+ // Старая сигнатура: handler(bot, typeChat, user, args)
876
+ await commandInstance.handler(bot, typeChat, user, args);
877
+ }
878
+ } catch (e) {
879
+ sendLog(`[Handler Error] Ошибка в handler-е команды ${commandName}: ${e.message}`);
880
+ sendLog(`[Handler Error] Stack trace: ${e.stack}`);
881
+ }
882
+ })();
883
+ }
884
+ } else if (message.type === 'execute_command_request') {
885
+ const { requestId, payload } = message;
886
+ const { commandName, args, username, typeChat } = payload;
887
+
888
+ (async () => {
889
+ try {
890
+ const commandInstance = bot.commands.get(commandName);
891
+ if (!commandInstance) {
892
+ throw new Error(`Command '${commandName}' not found.`);
893
+ }
894
+
895
+ // Восстанавливаем полный User объект из username
896
+ const user = await UserService.getUser(username, bot.config.id, bot.config);
897
+
898
+ let result;
899
+
900
+ // Проверяем сигнатуру handler - старая (4 аргумента) или новая (1 аргумент context)
901
+ const handlerParamCount = commandInstance.handler.length;
902
+
903
+ if (handlerParamCount === 1) {
904
+ // Новая сигнатура: handler(context)
905
+ const transport = new Transport(typeChat, bot);
906
+ const context = new CommandContext(bot, user, args, transport);
907
+
908
+ if (typeChat === 'websocket') {
909
+ result = await commandInstance.handler(context);
910
+ if (process.send) {
911
+ process.send({ type: 'execute_command_response', requestId, result });
912
+ }
913
+ } else {
914
+ commandInstance.handler(context).catch(e => {
915
+ sendLog(`[Handler Error] Ошибка в handler-е команды ${commandName}: ${e.message}`);
916
+ });
917
+ }
918
+ } else {
919
+ // Старая сигнатура: handler(bot, typeChat, user, args)
920
+ if (typeChat === 'websocket') {
921
+ // Для websocket перехватываем bot.sendMessage
922
+ const originalSendMessage = bot.sendMessage;
923
+ let resultFromSendMessage = null;
924
+ let sendMessageCalled = false;
925
+
926
+ bot.sendMessage = (type, message, username) => {
927
+ if (type === 'websocket') {
928
+ resultFromSendMessage = message;
929
+ sendMessageCalled = true;
930
+ } else {
931
+ originalSendMessage.call(bot, type, message, username);
932
+ }
933
+ };
934
+
935
+ try {
936
+ const returnValue = await commandInstance.handler(bot, typeChat, user, args);
937
+ result = sendMessageCalled ? resultFromSendMessage : returnValue;
938
+
939
+ if (process.send) {
940
+ process.send({ type: 'execute_command_response', requestId, result });
941
+ }
942
+ } finally {
943
+ bot.sendMessage = originalSendMessage;
944
+ }
945
+ } else {
946
+ // Для игровых команд просто выполняем
947
+ commandInstance.handler(bot, typeChat, user, args).catch(e => {
948
+ sendLog(`[Handler Error] Ошибка в handler-е команды ${commandName}: ${e.message}`);
949
+ });
950
+ }
951
+ }
952
+
953
+ } catch (error) {
954
+ if (process.send) {
955
+ process.send({ type: 'execute_command_response', requestId, error: error.message });
956
+ }
957
+ }
958
+ })();
959
+ } else if (message.type === 'invalidate_user_cache') {
960
+ if (message.username && bot && bot.config) {
961
+ UserService.clearCache(message.username, bot.config.id);
962
+ }
963
+ } else if (message.type === 'invalidate_all_user_cache') {
964
+ if (bot && bot.config) {
965
+ for (const [cacheKey, user] of UserService.cache.entries()) {
966
+ if (cacheKey.startsWith(`${bot.config.id}:`)) {
967
+ UserService.cache.delete(cacheKey);
968
+ }
969
+ }
970
+ sendLog(`[BotProcess] Кэш пользователей очищен для бота ${bot.config.id}`);
971
+ }
972
+ } else if (message.type === 'handle_permission_error') {
973
+ const { commandName, username, typeChat } = message;
974
+ const commandInstance = bot.commands.get(commandName);
975
+ if (commandInstance) {
976
+ if (commandInstance.onInsufficientPermissions !== Command.prototype.onInsufficientPermissions) {
977
+ commandInstance.onInsufficientPermissions(bot, typeChat, { username });
978
+ } else {
979
+ bot.api.sendMessage(typeChat, `У вас нет прав для выполнения команды ${commandName}.`, username);
980
+ }
981
+ }
982
+ } else if (message.type === 'handle_wrong_chat') {
983
+ const { commandName, username, typeChat } = message;
984
+ const commandInstance = bot.commands.get(commandName);
985
+ if (commandInstance) {
986
+ if (commandInstance.onWrongChatType !== Command.prototype.onWrongChatType) {
987
+ commandInstance.onWrongChatType(bot, typeChat, { username });
988
+ } else {
989
+ bot.api.sendMessage('private', `Команду ${commandName} нельзя использовать в этом типе чата - ${typeChat}.`, username);
990
+ }
991
+ }
992
+ } else if (message.type === 'handle_cooldown') {
993
+ const { commandName, username, typeChat, timeLeft } = message;
994
+ const commandInstance = bot.commands.get(commandName);
995
+ if (commandInstance) {
996
+ if (commandInstance.onCooldown !== Command.prototype.onCooldown) {
997
+ commandInstance.onCooldown(bot, typeChat, { username }, timeLeft);
998
+ } else {
999
+ bot.api.sendMessage(typeChat, `Команду ${commandName} можно будет использовать через ${timeLeft} сек.`, username);
1000
+ }
1001
+ }
1002
+ } else if (message.type === 'handle_blacklist') {
1003
+ const { commandName, username, typeChat } = message;
1004
+ const commandInstance = bot.commands.get(commandName);
1005
+ if (commandInstance) {
1006
+ if (commandInstance.onBlacklisted !== Command.prototype.onBlacklisted) {
1007
+ commandInstance.onBlacklisted(bot, typeChat, { username });
1008
+ }
1009
+ }
1010
+ } else if (message.type === 'action') {
1011
+ if (message.name === 'lookAt' && bot && message.payload.position) {
1012
+ const { x, y, z } = message.payload.position;
1013
+ if (typeof x === 'number' && typeof y === 'number' && typeof z === 'number') {
1014
+ bot.lookAt(new Vec3(x, y, z));
1015
+ } else {
1016
+ sendLog(`[BotProcess] Ошибка lookAt: получены невалидные координаты: ${JSON.stringify(message.payload.position)}`);
1017
+ }
1018
+ }
1019
+ } else if (message.type === 'plugins:reload') {
1020
+ sendLog('[System] Получена команда на перезагрузку плагинов...');
1021
+ const newConfig = await fetchNewConfig(bot.config.id, prisma);
1022
+ if (newConfig) {
1023
+ bot.config.plugins = newConfig.installedPlugins;
1024
+ bot.commands.clear();
1025
+ await loadCommands(bot, newConfig.commands);
1026
+ await initializePlugins(bot, newConfig.installedPlugins, prisma);
1027
+ sendLog('[System] Плагины успешно перезагружены.');
1028
+ } else {
1029
+ sendLog('[System] Не удалось получить новую конфигурацию для перезагрузки плагинов.');
1030
+ }
1031
+ } else if (message.type === 'server_command') {
1032
+ if (bot && message.payload && message.payload.command) {
1033
+ bot.chat(message.payload.command);
1034
+ }
1035
+ }
1036
+ });
1037
+
1038
+ process.on('unhandledRejection', (reason, promise) => {
1039
+ const errorMsg = `[FATAL] Необработанная ошибка процесса: ${reason?.stack || reason}`;
1040
+ sendLog(errorMsg);
1041
+ setTimeout(() => process.exit(1), 100);
1042
+ });
1043
+
1044
+ process.on('uncaughtException', (error) => {
1045
+ const errorMsg = `[FATAL] Необработанное исключение: ${error.stack || error.message}`;
1046
+ sendLog(errorMsg);
1047
+ setTimeout(() => process.exit(1), 100);
1048
+ });
1049
+
1050
+ process.on('SIGTERM', () => {
1051
+ sendLog('[System] Получен сигнал SIGTERM. Завершение работы...');
1052
+ if (bot) {
1053
+ try {
1054
+ bot.quit();
1055
+ } catch (error) {
1056
+ sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
1057
+ }
1058
+ }
1059
+ setTimeout(() => process.exit(0), 100);
1060
+ });
1061
+
1062
+ process.on('SIGINT', () => {
1063
+ sendLog('[System] Получен сигнал SIGINT. Завершение работы...');
1064
+ if (bot) {
1065
+ try {
1066
+ bot.quit();
1067
+ } catch (error) {
1068
+ sendLog(`[System] Ошибка при корректном завершении бота: ${error.message}`);
1069
+ }
1070
+ }
1071
+ setTimeout(() => process.exit(0), 100);
1072
+ });
1073
+
1074
+
1075
+ function serializeEntity(entity) {
1076
+ if (!entity) return null;
1077
+ return {
1078
+ id: entity.id,
1079
+ type: entity.type,
1080
+ username: entity.username,
1081
+ displayName: entity.displayName,
1082
+ position: entity.position,
1083
+ yaw: entity.yaw,
1084
+ pitch: entity.pitch,
1085
+ onGround: entity.onGround,
1086
+ isValid: entity.isValid,
1087
+ heldItem: entity.heldItem,
1088
+ equipment: entity.equipment,
1089
+ metadata: entity.metadata
1090
+ };
1091
+ }
1092
+