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
@@ -0,0 +1,1093 @@
1
+ const { fork } = require('child_process');
2
+ const path = require('path');
3
+ const prisma = require('../lib/prisma');
4
+ const pidusage = require('pidusage');
5
+ const DependencyService = require('./DependencyService');
6
+ const config = require('../config');
7
+ const fs = require('fs');
8
+ const os = require('os');
9
+ const { v4: uuidv4 } = require('uuid');
10
+ const crypto = require('crypto');
11
+ const { decrypt } = require('./utils/crypto');
12
+ const EventGraphManager = require('./EventGraphManager');
13
+ const nodeRegistry = require('./NodeRegistry');
14
+ const UserService = require('./UserService');
15
+
16
+ const cooldowns = new Map();
17
+ const warningCache = new Map();
18
+ const WARNING_COOLDOWN = 10 * 1000;
19
+
20
+ const STATS_SERVER_URL = 'http://185.65.200.184:3000';
21
+ let instanceId = null;
22
+ const DATA_DIR = path.join(os.homedir(), '.blockmine');
23
+ const INSTANCE_ID_PATH = path.join(DATA_DIR, '.instance_id');
24
+
25
+ function getInstanceId() {
26
+ if (instanceId) return instanceId;
27
+ try {
28
+ if (fs.existsSync(INSTANCE_ID_PATH)) {
29
+ instanceId = fs.readFileSync(INSTANCE_ID_PATH, 'utf-8');
30
+ } else {
31
+ instanceId = uuidv4();
32
+ if (!fs.existsSync(DATA_DIR)) {
33
+ fs.mkdirSync(DATA_DIR, { recursive: true });
34
+ }
35
+ fs.writeFileSync(INSTANCE_ID_PATH, instanceId, 'utf-8');
36
+ }
37
+ } catch (error) {
38
+ console.error('[Telemetry] Ошибка при загрузке/создании Instance ID:', error);
39
+ return null;
40
+ }
41
+ return instanceId;
42
+ }
43
+
44
+ class BotManager {
45
+ constructor() {
46
+ this.bots = new Map();
47
+ this.logCache = new Map();
48
+ this.resourceUsage = new Map();
49
+ this.botConfigs = new Map();
50
+ this.nodeRegistry = nodeRegistry;
51
+ this.pendingPlayerListRequests = new Map();
52
+ this.pendingCommandRequests = new Map();
53
+ this.playerListCache = new Map();
54
+ this.eventGraphManager = null;
55
+ this.uiSubscriptions = new Map();
56
+ this.crashCounters = new Map();
57
+
58
+ getInstanceId();
59
+ setInterval(() => this.updateAllResourceUsage(), 5000);
60
+ setInterval(() => this.syncBotStatuses(), 10000);
61
+ if (config.telemetry?.enabled) {
62
+ setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
63
+ }
64
+ }
65
+
66
+ initialize() {
67
+ if (!this.eventGraphManager) {
68
+ this.eventGraphManager = new EventGraphManager(this);
69
+ }
70
+ }
71
+
72
+ subscribeToPluginUi(botId, pluginName, socket) {
73
+ if (!this.uiSubscriptions.has(botId)) {
74
+ this.uiSubscriptions.set(botId, new Map());
75
+ }
76
+ const botSubscriptions = this.uiSubscriptions.get(botId);
77
+
78
+ if (!botSubscriptions.has(pluginName)) {
79
+ botSubscriptions.set(pluginName, new Set());
80
+ }
81
+ const pluginSubscribers = botSubscriptions.get(pluginName);
82
+
83
+ pluginSubscribers.add(socket);
84
+ console.log(`[UI Sub] Сокет ${socket.id} подписался на ${pluginName} для бота ${botId}. Всего подписчиков: ${pluginSubscribers.size}`);
85
+
86
+ const botProcess = this.bots.get(botId);
87
+ if (botProcess && !botProcess.killed) {
88
+ botProcess.send({ type: 'plugin:ui:start-updates', pluginName });
89
+ }
90
+ }
91
+
92
+ unsubscribeFromPluginUi(botId, pluginName, socket) {
93
+ const botSubscriptions = this.uiSubscriptions.get(botId);
94
+ if (!botSubscriptions) return;
95
+
96
+ const pluginSubscribers = botSubscriptions.get(pluginName);
97
+ if (!pluginSubscribers) return;
98
+
99
+ pluginSubscribers.delete(socket);
100
+ console.log(`[UI Sub] Сокет ${socket.id} отписался от ${pluginName} для бота ${botId}. Осталось: ${pluginSubscribers.size}`);
101
+
102
+ if (pluginSubscribers.size === 0) {
103
+ const botProcess = this.bots.get(botId);
104
+ if (botProcess && !botProcess.killed) {
105
+ botProcess.send({ type: 'plugin:ui:stop-updates', pluginName });
106
+ }
107
+ botSubscriptions.delete(pluginName);
108
+ }
109
+ }
110
+
111
+ handleSocketDisconnect(socket) {
112
+ this.uiSubscriptions.forEach((botSubscriptions, botId) => {
113
+ botSubscriptions.forEach((pluginSubscribers, pluginName) => {
114
+ if (pluginSubscribers.has(socket)) {
115
+ this.unsubscribeFromPluginUi(botId, pluginName, socket);
116
+ }
117
+ });
118
+ });
119
+ }
120
+
121
+ async loadConfigForBot(botId) {
122
+ console.log(`[BotManager] Caching configuration for bot ID ${botId}...`);
123
+ try {
124
+ const [commands, permissions] = await Promise.all([
125
+ prisma.command.findMany({ where: { botId } }),
126
+ prisma.permission.findMany({ where: { botId } }),
127
+ ]);
128
+ const config = {
129
+ commands: new Map(commands.map(cmd => [cmd.name, cmd])),
130
+ permissionsById: new Map(permissions.map(p => [p.id, p])),
131
+ commandAliases: new Map()
132
+ };
133
+ for (const cmd of commands) {
134
+ const aliases = JSON.parse(cmd.aliases || '[]');
135
+ for (const alias of aliases) {
136
+ config.commandAliases.set(alias, cmd.name);
137
+ }
138
+ }
139
+ this.botConfigs.set(botId, config);
140
+ console.log(`[BotManager] Configuration for bot ID ${botId} cached successfully.`);
141
+ return config;
142
+ } catch (error) {
143
+ console.error(`[BotManager] Failed to cache configuration for bot ${botId}:`, error);
144
+ throw new Error(`Failed to load/cache bot configuration for botId ${botId}: ${error.message}`);
145
+ }
146
+ }
147
+
148
+ async _ensureDefaultEventGraphs(botId) {
149
+ return;
150
+ }
151
+
152
+ invalidateConfigCache(botId) {
153
+ if (this.botConfigs.has(botId)) {
154
+ this.botConfigs.delete(botId);
155
+ console.log(`[BotManager] Invalidated config cache for bot ID ${botId}. It will be reloaded on next command.`);
156
+ }
157
+ }
158
+
159
+ reloadBotConfigInRealTime(botId) {
160
+ const { getIO } = require('../real-time/socketHandler');
161
+ this.invalidateConfigCache(botId);
162
+ const child = this.bots.get(botId);
163
+ if (child && !child.killed) {
164
+ child.send({ type: 'config:reload' });
165
+ console.log(`[BotManager] Sent config:reload to bot process ${botId}`);
166
+ getIO().emit('bot:config_reloaded', { botId });
167
+ }
168
+ }
169
+
170
+ triggerHeartbeat() {
171
+ if (!config.telemetry?.enabled) return;
172
+ if (this.heartbeatDebounceTimer) {
173
+ clearTimeout(this.heartbeatDebounceTimer);
174
+ }
175
+ this.heartbeatDebounceTimer = setTimeout(() => {
176
+ this.sendHeartbeat();
177
+ }, 3000);
178
+ }
179
+
180
+ async sendHeartbeat() {
181
+ if (!config.telemetry?.enabled || !instanceId) return;
182
+ try {
183
+ const runningBots = Array.from(this.bots.values())
184
+ .filter(p => p.botConfig)
185
+ .map(p => ({
186
+ username: p.botConfig.username,
187
+ serverHost: p.botConfig.server.host,
188
+ serverPort: p.botConfig.server.port
189
+ }));
190
+
191
+ if (runningBots.length === 0) return;
192
+
193
+ const challengeRes = await fetch(`${STATS_SERVER_URL}/api/challenge?uuid=${instanceId}`);
194
+ if (!challengeRes.ok) throw new Error(`Challenge server error: ${challengeRes.statusText}`);
195
+
196
+ const { challenge, difficulty, prefix } = await challengeRes.json();
197
+ let nonce = 0;
198
+ let hash = '';
199
+ do {
200
+ nonce++;
201
+ hash = crypto.createHash('sha256').update(prefix + challenge + nonce).digest('hex');
202
+ } while (!hash.startsWith('0'.repeat(difficulty)));
203
+
204
+ const packageJson = require('../../../package.json');
205
+ await fetch(`${STATS_SERVER_URL}/api/heartbeat`, {
206
+ method: 'POST',
207
+ headers: { 'Content-Type': 'application/json' },
208
+ body: JSON.stringify({
209
+ instanceUuid: instanceId,
210
+ appVersion: packageJson.version,
211
+ bots: runningBots,
212
+ nonce: nonce
213
+ })
214
+ });
215
+ } catch (error) {
216
+ console.error(`[Telemetry] Не удалось отправить heartbeat: ${error.message}`);
217
+ }
218
+ }
219
+
220
+ async _syncSystemPermissions(botId) {
221
+ const systemPermissions = [
222
+ { name: "admin.*", description: "Все права администратора" },
223
+ { name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
224
+ { name: "user.*", description: "Все права обычного пользователя" },
225
+ { name: "user.say", description: "Доступ к простым командам" },
226
+ { name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
227
+ ];
228
+ const systemGroups = ["User", "Admin"];
229
+ const systemGroupPermissions = {
230
+ "User": ["user.say"],
231
+ "Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
232
+ };
233
+ console.log(`[Permission Sync] Синхронизация системных прав для бота ID ${botId}...`);
234
+ for (const perm of systemPermissions) {
235
+ await prisma.permission.upsert({
236
+ where: { botId_name: { botId, name: perm.name } },
237
+ update: { description: perm.description },
238
+ create: { ...perm, botId, owner: 'system' }
239
+ });
240
+ }
241
+ for (const groupName of systemGroups) {
242
+ await prisma.group.upsert({
243
+ where: { botId_name: { botId, name: groupName } },
244
+ update: {},
245
+ create: { name: groupName, botId, owner: 'system' }
246
+ });
247
+ }
248
+ for (const [groupName, permNames] of Object.entries(systemGroupPermissions)) {
249
+ const group = await prisma.group.findUnique({ where: { botId_name: { botId, name: groupName } } });
250
+ if (group) {
251
+ for (const permName of permNames) {
252
+ const permission = await prisma.permission.findUnique({ where: { botId_name: { botId, name: permName } } });
253
+ if (permission) {
254
+ await prisma.groupPermission.upsert({
255
+ where: { groupId_permissionId: { groupId: group.id, permissionId: permission.id } },
256
+ update: {},
257
+ create: { groupId: group.id, permissionId: permission.id }
258
+ });
259
+ }
260
+ }
261
+ }
262
+ }
263
+ console.log(`[Permission Sync] Синхронизация для бота ID ${botId} завершена.`);
264
+ }
265
+
266
+ async updateAllResourceUsage() {
267
+ const { getIO } = require('../real-time/socketHandler');
268
+ if (this.bots.size === 0) {
269
+ if (this.resourceUsage.size > 0) {
270
+ this.resourceUsage.clear();
271
+ getIO().emit('bots:usage', []);
272
+ }
273
+ return;
274
+ }
275
+ const pids = Array.from(this.bots.values()).map(child => child.pid).filter(Boolean);
276
+ if (pids.length === 0) return;
277
+ try {
278
+ const stats = await pidusage(pids);
279
+ const usageData = [];
280
+ for (const pid in stats) {
281
+ if (!stats[pid]) continue;
282
+ const botId = this.getBotIdByPid(parseInt(pid, 10));
283
+ if (botId) {
284
+ const usage = {
285
+ botId: botId,
286
+ cpu: parseFloat(stats[pid].cpu.toFixed(1)),
287
+ memory: parseFloat((stats[pid].memory / 1024 / 1024).toFixed(1)),
288
+ };
289
+ this.resourceUsage.set(botId, usage);
290
+ usageData.push(usage);
291
+ }
292
+ }
293
+ getIO().emit('bots:usage', usageData);
294
+ } catch (error) {}
295
+ }
296
+
297
+ getBotIdByPid(pid) {
298
+ for (const [botId, child] of this.bots.entries()) {
299
+ if (child.pid === pid) {
300
+ return botId;
301
+ }
302
+ }
303
+ return null;
304
+ }
305
+
306
+ getFullState() {
307
+ const statuses = {};
308
+ for (const [id, child] of this.bots.entries()) {
309
+ statuses[id] = child.killed ? 'stopped' : 'running';
310
+ }
311
+
312
+ const logs = {};
313
+ for (const [botId, logArray] of this.logCache.entries()) {
314
+ logs[botId] = logArray;
315
+ }
316
+
317
+ return {
318
+ statuses,
319
+ logs,
320
+ };
321
+ }
322
+
323
+ emitStatusUpdate(botId, status, message = null) {
324
+ const { getIO } = require('../real-time/socketHandler');
325
+ if (message) this.appendLog(botId, `[SYSTEM] ${message}`);
326
+ getIO().emit('bot:status', { botId, status, message });
327
+ }
328
+
329
+ syncBotStatuses() {
330
+ for (const [botId, child] of this.bots.entries()) {
331
+ const actualStatus = child.killed ? 'stopped' : 'running';
332
+ const { getIO } = require('../real-time/socketHandler');
333
+ getIO().emit('bot:status', { botId, status: actualStatus });
334
+ }
335
+ }
336
+
337
+ appendLog(botId, logContent) {
338
+ const { getIO } = require('../real-time/socketHandler');
339
+ const logEntry = {
340
+ id: Date.now() + Math.random(),
341
+ content: logContent,
342
+ };
343
+ const currentLogs = this.logCache.get(botId) || [];
344
+ const newLogs = [...currentLogs.slice(-199), logEntry];
345
+ this.logCache.set(botId, newLogs);
346
+ getIO().emit('bot:log', { botId, log: logEntry });
347
+ }
348
+
349
+ getBotLogs(botId) {
350
+ return this.logCache.get(botId) || [];
351
+ }
352
+
353
+ async startBot(botConfig) {
354
+ if (this.bots.has(botConfig.id) && !this.bots.get(botConfig.id).killed) {
355
+ this.appendLog(botConfig.id, `[SYSTEM-ERROR] Попытка повторного запуска. Запуск отменен.`);
356
+ return { success: false, message: 'Бот уже запущен или запускается.' };
357
+ }
358
+
359
+ await this._syncSystemPermissions(botConfig.id);
360
+ await this.loadConfigForBot(botConfig.id);
361
+ this.logCache.set(botConfig.id, []);
362
+ this.emitStatusUpdate(botConfig.id, 'starting', '');
363
+
364
+ const allPluginsForBot = await prisma.installedPlugin.findMany({ where: { botId: botConfig.id, isEnabled: true } });
365
+ const { sortedPlugins, hasCriticalIssues, pluginInfo } = DependencyService.resolveDependencies(allPluginsForBot, allPluginsForBot);
366
+
367
+ if (hasCriticalIssues) {
368
+ this.appendLog(botConfig.id, '[DependencyManager] Обнаружены критические проблемы с зависимостями, запуск отменен.');
369
+
370
+ const criticalIssueTypes = new Set(['missing_dependency', 'version_mismatch', 'circular_dependency']);
371
+
372
+ for (const pluginId in pluginInfo) {
373
+ const info = pluginInfo[pluginId];
374
+ if (info.issues.length === 0) continue;
375
+
376
+ const criticalIssues = info.issues.filter(issue => criticalIssueTypes.has(issue.type));
377
+
378
+ if (criticalIssues.length > 0) {
379
+ this.appendLog(botConfig.id, `* Плагин "${info.name}":`);
380
+ for (const issue of criticalIssues) {
381
+ this.appendLog(botConfig.id, ` - ${issue.message}`);
382
+ }
383
+ }
384
+ }
385
+
386
+ this.emitStatusUpdate(botConfig.id, 'stopped', 'Ошибка зависимостей плагинов.');
387
+ return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
388
+ }
389
+
390
+ const decryptedConfig = { ...botConfig };
391
+ if (decryptedConfig.password) decryptedConfig.password = decrypt(decryptedConfig.password);
392
+ if (decryptedConfig.proxyPassword) decryptedConfig.proxyPassword = decrypt(decryptedConfig.proxyPassword);
393
+
394
+ if (decryptedConfig.proxyUsername) decryptedConfig.proxyUsername = decryptedConfig.proxyUsername.trim();
395
+
396
+ const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
397
+ const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
398
+ const child = fork(botProcessPath, [], {
399
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
400
+ env: {
401
+ ...process.env,
402
+ NODE_PATH: path.resolve(__dirname, '../../../node_modules')
403
+ }
404
+ });
405
+
406
+ child.botConfig = botConfig;
407
+
408
+ child.api = {
409
+ sendMessage: (type, message, username) => {
410
+ if (!child.killed) {
411
+ child.send({ type: 'chat', payload: { message, chatType: type, username } });
412
+ }
413
+ },
414
+ sendLog: (message) => {
415
+ this.appendLog(botConfig.id, message);
416
+ }
417
+ };
418
+
419
+ child.on('message', async (message) => {
420
+ const botId = botConfig.id;
421
+ try {
422
+ switch (message.type) {
423
+ case 'event':
424
+ // Отправляем сырые сообщения в WebSocket API
425
+ if (message.eventType === 'raw_message') {
426
+ try {
427
+ const { getIO } = require('../real-time/socketHandler');
428
+ const { broadcastToApiClients } = require('../real-time/botApi');
429
+ broadcastToApiClients(getIO(), botId, 'chat:raw_message', {
430
+ raw_message: message.args.rawText || message.args.raw_message,
431
+ json: message.args.json
432
+ });
433
+ } catch (e) { /* Socket.IO может быть не инициализирован */ }
434
+ }
435
+
436
+ if (this.eventGraphManager) {
437
+ this.eventGraphManager.handleEvent(botId, message.eventType, message.args);
438
+ }
439
+ break;
440
+ case 'plugin:data': {
441
+ const { plugin: pluginName, payload } = message;
442
+ const botSubscriptions = this.uiSubscriptions.get(botId);
443
+ if (!botSubscriptions) break;
444
+
445
+ const pluginSubscribers = botSubscriptions.get(pluginName);
446
+ if (pluginSubscribers && pluginSubscribers.size > 0) {
447
+ pluginSubscribers.forEach(socket => {
448
+ socket.emit('plugin:ui:dataUpdate', payload);
449
+ });
450
+ }
451
+ break;
452
+ }
453
+ case 'send_websocket_message': {
454
+ const { getIO } = require('../real-time/socketHandler');
455
+ const { botId, message: msg } = message.payload;
456
+ getIO().to(`bot_${botId}`).emit('bot:message', { message: msg });
457
+ break;
458
+ }
459
+ case 'plugin:stopped':
460
+ break;
461
+ case 'log':
462
+ this.appendLog(botId, message.content);
463
+ break;
464
+ case 'status':
465
+ this.emitStatusUpdate(botId, message.status);
466
+ break;
467
+ case 'bot_ready':
468
+ this.emitStatusUpdate(botId, 'running', 'Бот успешно подключился к серверу.');
469
+ this.crashCounters.delete(botId);
470
+
471
+ // Broadcast статуса в WebSocket API
472
+ try {
473
+ const { getIO } = require('../real-time/socketHandler');
474
+ const { broadcastBotStatus } = require('../real-time/botApi');
475
+ broadcastBotStatus(getIO(), botId, true);
476
+ } catch (e) { /* Socket.IO может быть не инициализирован */ }
477
+ break;
478
+ case 'validate_and_run_command':
479
+ await this.handleCommandValidation(botConfig, message);
480
+ break;
481
+ case 'register_command':
482
+ await this.handleCommandRegistration(botId, message.commandConfig);
483
+ break;
484
+ case 'register_group':
485
+ await this.handleGroupRegistration(botId, message.groupConfig);
486
+ break;
487
+ case 'register_permissions':
488
+ await this.handlePermissionsRegistration(botId, message);
489
+ break;
490
+ case 'add_permissions_to_group':
491
+ await this.handleAddPermissionsToGroup(botId, message);
492
+ break;
493
+ case 'request_user_action':
494
+ const { requestId, payload } = message;
495
+ const { targetUsername, action, data } = payload;
496
+
497
+ try {
498
+ const user = await UserService.getUser(targetUsername, botConfig.id);
499
+ if (!user) throw new Error(`Пользователь ${targetUsername} не найден.`);
500
+
501
+ let result;
502
+
503
+ switch (action) {
504
+ case 'addGroup':
505
+ result = await user.addGroup(data.group);
506
+ break;
507
+ case 'removeGroup':
508
+ result = await user.removeGroup(data.group);
509
+ break;
510
+ case 'addPermission':
511
+ break;
512
+ case 'removePermission':
513
+ break;
514
+ case 'getGroups':
515
+ result = user.groups ? user.groups.map(g => g.group.name) : [];
516
+ break;
517
+ case 'getPermissions':
518
+ result = Array.from(user.permissionsSet);
519
+ break;
520
+ case 'isBlacklisted':
521
+ result = user.isBlacklisted;
522
+ break;
523
+ case 'setBlacklisted':
524
+ result = await user.setBlacklist(data.value);
525
+ break;
526
+ default:
527
+ throw new Error(`Неизвестное действие: ${action}`);
528
+ }
529
+
530
+ child.send({ type: 'user_action_response', requestId, payload: result });
531
+ } catch (error) {
532
+ console.error(`[BotManager] Ошибка выполнения действия '${action}' для пользователя '${targetUsername}':`, error);
533
+ child.send({ type: 'user_action_response', requestId, error: error.message });
534
+ }
535
+ break;
536
+ case 'playerListUpdate':
537
+ break;
538
+ case 'get_player_list_response': {
539
+ const { requestId, payload } = message;
540
+ const request = this.pendingPlayerListRequests.get(requestId);
541
+ if (request) {
542
+ clearTimeout(request.timeout);
543
+ request.resolve(payload.players);
544
+ this.pendingPlayerListRequests.delete(requestId);
545
+ }
546
+ break;
547
+ }
548
+ case 'execute_command_response': {
549
+ const { requestId, result, error } = message;
550
+ const request = this.pendingCommandRequests.get(requestId);
551
+ if (request) {
552
+ if (error) {
553
+ request.reject(new Error(error));
554
+ } else {
555
+ request.resolve(result);
556
+ }
557
+ this.pendingCommandRequests.delete(requestId);
558
+ }
559
+ break;
560
+ }
561
+ }
562
+ } catch (error) {
563
+ this.appendLog(botId, `[SYSTEM-ERROR] Критическая ошибка в обработчике сообщений от бота: ${error.stack}`);
564
+ console.error(`[BotManager] Критическая ошибка в обработчике сообщений от бота ${botId}:`, error);
565
+ }
566
+ });
567
+
568
+ child.on('error', (err) => this.appendLog(botConfig.id, `[PROCESS FATAL] ${err.stack}`));
569
+ child.stdout.on('data', (data) => console.log(data.toString()));
570
+ child.stderr.on('data', (data) => this.appendLog(botConfig.id, `[STDERR] ${data.toString()}`));
571
+
572
+ child.on('exit', (code, signal) => {
573
+ const botId = botConfig.id;
574
+ this.bots.delete(botId);
575
+ this.resourceUsage.delete(botId);
576
+ this.botConfigs.delete(botId);
577
+
578
+ this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
579
+ this.updateAllResourceUsage();
580
+
581
+ // Broadcast статуса в WebSocket API
582
+ try {
583
+ const { getIO } = require('../real-time/socketHandler');
584
+ const { broadcastBotStatus } = require('../real-time/botApi');
585
+ broadcastBotStatus(getIO(), botId, false);
586
+ } catch (e) { /* Socket.IO может быть не инициализирован */ }
587
+
588
+ if (code === 1) {
589
+ const MAX_RESTART_ATTEMPTS = 5;
590
+ const RESTART_COOLDOWN = 60000;
591
+
592
+ const counter = this.crashCounters.get(botId) || { count: 0, firstCrash: Date.now() };
593
+ const timeSinceFirstCrash = Date.now() - counter.firstCrash;
594
+
595
+ if (timeSinceFirstCrash > RESTART_COOLDOWN) {
596
+ counter.count = 0;
597
+ counter.firstCrash = Date.now();
598
+ }
599
+
600
+ counter.count++;
601
+ this.crashCounters.set(botId, counter);
602
+
603
+ if (counter.count >= MAX_RESTART_ATTEMPTS) {
604
+ console.log(`[BotManager] Бот ${botId} упал ${counter.count} раз подряд. Автоперезапуск остановлен.`);
605
+ this.appendLog(botId, `[SYSTEM] Обнаружено ${counter.count} критических ошибок подряд.`);
606
+ this.appendLog(botId, `[SYSTEM] Исправьте проблему и запустите бота вручную.`);
607
+ this.crashCounters.delete(botId);
608
+ return;
609
+ }
610
+
611
+ console.log(`[BotManager] Обнаружена ошибка с кодом 1 для бота ${botId}. Попытка ${counter.count}/${MAX_RESTART_ATTEMPTS}. Перезапуск через 5 секунд...`);
612
+ this.appendLog(botId, `[SYSTEM] Обнаружена критическая ошибка, перезапуск через 5 секунд... (попытка ${counter.count}/${MAX_RESTART_ATTEMPTS})`);
613
+ setTimeout(() => {
614
+ console.log(`[BotManager] Перезапуск бота ${botId}...`);
615
+ this.startBot(botConfig);
616
+ }, 5000);
617
+ }
618
+ });
619
+
620
+ this.bots.set(botConfig.id, child);
621
+ child.send({ type: 'start', config: fullBotConfig });
622
+
623
+ await this.eventGraphManager.loadGraphsForBot(botConfig.id);
624
+
625
+ this.triggerHeartbeat();
626
+ const { getIO } = require('../real-time/socketHandler');
627
+ getIO().emit('bot:status', { botId: botConfig.id, status: 'starting' });
628
+ return child;
629
+ }
630
+
631
+ async handleCommandValidation(botConfig, message) {
632
+ const { commandName, username, args, typeChat } = message;
633
+ const botId = botConfig.id;
634
+
635
+ try {
636
+ let botConfigCache = this.botConfigs.get(botId);
637
+ if (!botConfigCache) {
638
+ console.log(`[BotManager] No cache for ${botId}, loading...`);
639
+ botConfigCache = await this.loadConfigForBot(botId);
640
+ }
641
+
642
+ const user = await UserService.getUser(username, botId, botConfig);
643
+
644
+ const child = this.bots.get(botId);
645
+ if (!child) return;
646
+
647
+ if (user.isBlacklisted) {
648
+ child.send({
649
+ type: 'handle_blacklist',
650
+ commandName: commandName,
651
+ username,
652
+ typeChat
653
+ });
654
+ return;
655
+ }
656
+
657
+ const mainCommandName = botConfigCache.commandAliases.get(commandName) || commandName;
658
+ const dbCommand = botConfigCache.commands.get(mainCommandName);
659
+
660
+ if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
661
+ return;
662
+ }
663
+
664
+ const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
665
+ if (!allowedTypes.includes(typeChat) && !user.isOwner) {
666
+ if (typeChat === 'global') return;
667
+ child.send({
668
+ type: 'handle_wrong_chat',
669
+ commandName: dbCommand.name,
670
+ username,
671
+ typeChat
672
+ });
673
+ return;
674
+ }
675
+
676
+ const permission = dbCommand.permissionId ? botConfigCache.permissionsById.get(dbCommand.permissionId) : null;
677
+ if (permission && !user.hasPermission(permission.name)) {
678
+ child.send({
679
+ type: 'handle_permission_error',
680
+ commandName: dbCommand.name,
681
+ username,
682
+ typeChat
683
+ });
684
+ return;
685
+ }
686
+
687
+ const domain = (permission?.name || '').split('.')[0] || 'user';
688
+ const bypassCooldownPermission = `${domain}.cooldown.bypass`;
689
+
690
+ if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
691
+ const cooldownKey = `${botId}:${dbCommand.name}:${user.id}`;
692
+ const now = Date.now();
693
+ const lastUsed = cooldowns.get(cooldownKey);
694
+
695
+ if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
696
+ const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
697
+ child.send({
698
+ type: 'handle_cooldown',
699
+ commandName: dbCommand.name,
700
+ username,
701
+ typeChat,
702
+ timeLeft
703
+ });
704
+ return;
705
+ }
706
+ cooldowns.set(cooldownKey, now);
707
+ }
708
+
709
+ if (this.eventGraphManager) {
710
+ this.eventGraphManager.handleEvent(botId, 'command', {
711
+ commandName: dbCommand.name,
712
+ user: { username },
713
+ args,
714
+ typeChat
715
+ });
716
+ }
717
+
718
+ child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
719
+
720
+ } catch (error) {
721
+ console.error(`[BotManager] Command validation error for botId: ${botId}`, {
722
+ command: commandName, user: username, error: error.message, stack: error.stack
723
+ });
724
+ this.sendMessageToBot(botId, `Произошла внутренняя ошибка при выполнении команды.`, 'private', username);
725
+ }
726
+ }
727
+
728
+ async handleCommandRegistration(botId, commandConfig) {
729
+ try {
730
+ let permissionId = null;
731
+ if (commandConfig.permissions) {
732
+ let permission = await prisma.permission.findUnique({
733
+ where: { botId_name: { botId, name: commandConfig.permissions } }
734
+ });
735
+ if (!permission) {
736
+ permission = await prisma.permission.create({
737
+ data: {
738
+ botId,
739
+ name: commandConfig.permissions,
740
+ description: `Автоматически создано для команды ${commandConfig.name}`,
741
+ owner: commandConfig.owner,
742
+ }
743
+ });
744
+ }
745
+ permissionId = permission.id;
746
+ }
747
+ const createData = {
748
+ botId,
749
+ name: commandConfig.name,
750
+ description: commandConfig.description,
751
+ aliases: JSON.stringify(commandConfig.aliases || []),
752
+ owner: commandConfig.owner,
753
+ permissionId: permissionId,
754
+ allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
755
+ cooldown: commandConfig.cooldown || 0,
756
+ };
757
+ const updateData = {
758
+ description: commandConfig.description,
759
+ owner: commandConfig.owner,
760
+ };
761
+ await prisma.command.upsert({
762
+ where: { botId_name: { botId, name: commandConfig.name } },
763
+ update: updateData,
764
+ create: createData,
765
+ });
766
+ this.invalidateConfigCache(botId);
767
+ } catch (error) {
768
+ console.error(`[BotManager] Ошибка при регистрации команды '${commandConfig.name}':`, error);
769
+ }
770
+ }
771
+
772
+ async handleGroupRegistration(botId, groupConfig) {
773
+ try {
774
+ await prisma.group.upsert({
775
+ where: { botId_name: { botId, name: groupConfig.name } },
776
+ update: {
777
+ owner: groupConfig.owner,
778
+ },
779
+ create: {
780
+ botId,
781
+ name: groupConfig.name,
782
+ owner: groupConfig.owner,
783
+ },
784
+ });
785
+ this.invalidateConfigCache(botId);
786
+ } catch (error) {
787
+ console.error(`[BotManager] Ошибка при регистрации группы '${groupConfig.name}':`, error);
788
+ }
789
+ }
790
+
791
+ async handlePermissionsRegistration(botId, message) {
792
+ try {
793
+ const { permissions } = message;
794
+ for (const perm of permissions) {
795
+ if (!perm.name || !perm.owner) {
796
+ console.warn(`[BotManager] Пропущено право без имени или владельца для бота ${botId}:`, perm);
797
+ continue;
798
+ }
799
+ await prisma.permission.upsert({
800
+ where: { botId_name: { botId, name: perm.name } },
801
+ update: { description: perm.description },
802
+ create: {
803
+ botId,
804
+ name: perm.name,
805
+ description: perm.description || '',
806
+ owner: perm.owner,
807
+ },
808
+ });
809
+ }
810
+ this.invalidateConfigCache(botId);
811
+ } catch (error) {
812
+ console.error(`[BotManager] Ошибка при регистрации прав для бота ${botId}:`, error);
813
+ }
814
+ }
815
+
816
+ async handleAddPermissionsToGroup(botId, message) {
817
+ try {
818
+ const { groupName, permissionNames } = message;
819
+
820
+ const group = await prisma.group.findUnique({
821
+ where: { botId_name: { botId, name: groupName } }
822
+ });
823
+
824
+ if (!group) {
825
+ console.warn(`[BotManager] Попытка добавить права в несуществующую группу "${groupName}" для бота ID ${botId}.`);
826
+ return;
827
+ }
828
+
829
+ for (const permName of permissionNames) {
830
+ const permission = await prisma.permission.findUnique({
831
+ where: { botId_name: { botId, name: permName } }
832
+ });
833
+
834
+ if (permission) {
835
+ await prisma.groupPermission.upsert({
836
+ where: { groupId_permissionId: { groupId: group.id, permissionId: permission.id } },
837
+ update: {},
838
+ create: { groupId: group.id, permissionId: permission.id },
839
+ });
840
+ } else {
841
+ console.warn(`[BotManager] Право "${permName}" не найдено для бота ID ${botId} при добавлении в группу "${groupName}".`);
842
+ }
843
+ }
844
+
845
+ this.invalidateConfigCache(botId);
846
+ } catch (error) {
847
+ console.error(`[BotManager] Ошибка при добавлении прав в группу "${message.groupName}" для бота ${botId}:`, error);
848
+ }
849
+ }
850
+
851
+ stopBot(botId) {
852
+ const child = this.bots.get(botId);
853
+ if (child) {
854
+ this.eventGraphManager.unloadGraphsForBot(botId);
855
+
856
+ child.send({ type: 'stop' });
857
+
858
+ setTimeout(() => {
859
+ if (!child.killed) {
860
+ console.log(`[BotManager] Принудительное завершение процесса бота ${botId}`);
861
+ try {
862
+ child.kill('SIGKILL');
863
+ } catch (error) {
864
+ console.error(`[BotManager] Ошибка при принудительном завершении бота ${botId}:`, error);
865
+ }
866
+ }
867
+ }, 5000);
868
+
869
+ this.botConfigs.delete(botId);
870
+ return { success: true };
871
+ }
872
+ return { success: false, message: 'Бот не найден или уже остановлен' };
873
+ }
874
+
875
+ async validateAndExecuteCommandForApi(botId, username, commandName, args) {
876
+ const botConfig = this.bots.get(botId)?.botConfig;
877
+ if (!botConfig) {
878
+ throw new Error('Bot configuration not found.');
879
+ }
880
+
881
+ const typeChat = 'websocket';
882
+
883
+ // --- Используем переданный username для всех проверок ---
884
+ let botConfigCache = this.botConfigs.get(botId);
885
+ if (!botConfigCache) {
886
+ botConfigCache = await this.loadConfigForBot(botId);
887
+ }
888
+
889
+ const user = await UserService.getUser(username, botId, botConfig);
890
+
891
+ if (user.isBlacklisted) {
892
+ throw new Error(`User '${username}' is blacklisted.`);
893
+ }
894
+
895
+ const mainCommandName = botConfigCache.commandAliases.get(commandName) || commandName;
896
+ const dbCommand = botConfigCache.commands.get(mainCommandName);
897
+
898
+ if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
899
+ throw new Error(`Command '${commandName}' not found or is disabled.`);
900
+ }
901
+
902
+ // WebSocket - универсальный транспорт, всегда разрешен
903
+ if (typeChat !== 'websocket') {
904
+ const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
905
+ if (!allowedTypes.includes(typeChat) && !user.isOwner) {
906
+ throw new Error(`Command '${commandName}' cannot be used in this chat type.`);
907
+ }
908
+ }
909
+
910
+ const permission = dbCommand.permissionId ? botConfigCache.permissionsById.get(dbCommand.permissionId) : null;
911
+ if (permission && !user.hasPermission(permission.name)) {
912
+ throw new Error(`User '${username}' has insufficient permissions.`);
913
+ }
914
+
915
+ const domain = (permission?.name || '').split('.')[0] || 'user';
916
+ const bypassCooldownPermission = `${domain}.cooldown.bypass`;
917
+
918
+ if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
919
+ const cooldownKey = `${botId}:${dbCommand.name}:${user.id}`;
920
+ const now = Date.now();
921
+ const lastUsed = cooldowns.get(cooldownKey);
922
+
923
+ if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
924
+ const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
925
+ throw new Error(`Command on cooldown for user '${username}'. Please wait ${timeLeft} seconds.`);
926
+ }
927
+ cooldowns.set(cooldownKey, now);
928
+ }
929
+
930
+ // Если все проверки пройдены, отправляем на выполнение
931
+ return this._executeCommandInProcess(botId, dbCommand.name, args, user, typeChat);
932
+ }
933
+
934
+ async _executeCommandInProcess(botId, commandName, args, user, typeChat) {
935
+ return new Promise(async (resolve, reject) => {
936
+ const child = this.bots.get(botId);
937
+ if (!child || child.killed) {
938
+ return reject(new Error('Bot is not running'));
939
+ }
940
+
941
+ const requestId = uuidv4();
942
+ this.pendingCommandRequests.set(requestId, { resolve, reject });
943
+
944
+ // Передаем только username - User будет восстановлен в child process
945
+ // Это позволяет сохранить все методы класса User
946
+ child.send({
947
+ type: 'execute_command_request',
948
+ requestId,
949
+ payload: {
950
+ commandName,
951
+ args: args || {}, // Аргументы должны быть объектом
952
+ username: user.username, // Только username
953
+ typeChat
954
+ }
955
+ });
956
+
957
+ setTimeout(() => {
958
+ if (this.pendingCommandRequests.has(requestId)) {
959
+ this.pendingCommandRequests.delete(requestId);
960
+ reject(new Error('Command execution timed out.'));
961
+ }
962
+ }, 10000);
963
+ });
964
+ }
965
+
966
+ sendMessageToBot(botId, message, chatType = 'command', username = null) {
967
+ const child = this.bots.get(botId);
968
+ if (child) {
969
+ child.api.sendMessage(chatType, message, username);
970
+ return { success: true };
971
+ }
972
+ return { success: false, message: 'Бот не найден или не запущен' };
973
+ }
974
+
975
+ isBotRunning(botId) {
976
+ const child = this.bots.get(botId);
977
+ return child && !child.killed;
978
+ }
979
+
980
+ invalidateUserCache(botId, username) {
981
+ UserService.clearCache(username, botId);
982
+ const child = this.bots.get(botId);
983
+ if (child) {
984
+ child.send({ type: 'invalidate_user_cache', username });
985
+ }
986
+ return { success: true };
987
+ }
988
+
989
+ invalidateAllUserCache(botId) {
990
+ for (const [cacheKey, user] of UserService.cache.entries()) {
991
+ if (cacheKey.startsWith(`${botId}:`)) {
992
+ UserService.cache.delete(cacheKey);
993
+ }
994
+ }
995
+ console.log(`[BotManager] Кэш пользователей очищен для бота ${botId}`);
996
+
997
+ const child = this.bots.get(botId);
998
+ if (child && !child.killed) {
999
+ child.send({ type: 'invalidate_all_user_cache' });
1000
+ console.log(`[BotManager] Отправлено сообщение об очистке кэша в процесс бота ${botId}`);
1001
+ }
1002
+
1003
+ return { success: true };
1004
+ }
1005
+
1006
+ async getPlayerList(botId) {
1007
+ const PLAYER_LIST_CACHE_TTL = 2000;
1008
+
1009
+ const child = this.bots.get(botId);
1010
+ if (!child || child.killed) {
1011
+ return [];
1012
+ }
1013
+
1014
+ const cachedEntry = this.playerListCache.get(botId);
1015
+ if (cachedEntry && (Date.now() - cachedEntry.timestamp < PLAYER_LIST_CACHE_TTL)) {
1016
+ return cachedEntry.promise;
1017
+ }
1018
+
1019
+ const newPromise = new Promise((resolve) => {
1020
+ const requestId = uuidv4();
1021
+ const timeout = setTimeout(() => {
1022
+ this.pendingPlayerListRequests.delete(requestId);
1023
+ if (this.playerListCache.get(botId)?.promise === newPromise) {
1024
+ this.playerListCache.delete(botId);
1025
+ }
1026
+ resolve([]);
1027
+ }, 5000);
1028
+
1029
+ this.pendingPlayerListRequests.set(requestId, {
1030
+ resolve: (playerList) => {
1031
+ clearTimeout(timeout);
1032
+ this.pendingPlayerListRequests.delete(requestId);
1033
+ this.playerListCache.set(botId, {
1034
+ promise: Promise.resolve(playerList),
1035
+ timestamp: Date.now()
1036
+ });
1037
+ resolve(playerList);
1038
+ },
1039
+ reject: (error) => {
1040
+ clearTimeout(timeout);
1041
+ this.pendingPlayerListRequests.delete(requestId);
1042
+ if (this.playerListCache.get(botId)?.promise === newPromise) {
1043
+ this.playerListCache.delete(botId);
1044
+ }
1045
+ resolve([]);
1046
+ },
1047
+ });
1048
+
1049
+ child.send({ type: 'system:get_player_list', requestId });
1050
+ });
1051
+
1052
+ this.playerListCache.set(botId, {
1053
+ promise: newPromise,
1054
+ timestamp: Date.now()
1055
+ });
1056
+
1057
+ return newPromise;
1058
+ }
1059
+
1060
+ setEventGraphManager(manager) {
1061
+ this.eventGraphManager = manager;
1062
+ }
1063
+
1064
+ lookAt(botId, position) {
1065
+ const botProcess = this.bots.get(botId);
1066
+ if (botProcess && !botProcess.killed) {
1067
+ botProcess.send({ type: 'action', name: 'lookAt', payload: { position } });
1068
+ } else {
1069
+ console.error(`[BotManager] Не удалось найти запущенный процесс для бота ${botId}, чтобы выполнить lookAt.`);
1070
+ }
1071
+ }
1072
+
1073
+ async reloadPlugins(botId) {
1074
+ const child = this.bots.get(botId);
1075
+ if (child && !child.killed) {
1076
+ child.send({ type: 'plugins:reload' });
1077
+ console.log(`[BotManager] Sent plugins:reload to bot process ${botId}`);
1078
+ const { getIO } = require('../real-time/socketHandler');
1079
+ getIO().emit('bot:plugins_reloaded', { botId });
1080
+ return { success: true, message: 'Команда на перезагрузку плагинов отправлена.' };
1081
+ }
1082
+ return { success: false, message: 'Бот не запущен.' };
1083
+ }
1084
+
1085
+ sendServerCommandToBot(botId, command) {
1086
+ const child = this.bots.get(botId);
1087
+ if (child) {
1088
+ child.send({ type: 'server_command', payload: { command } });
1089
+ }
1090
+ }
1091
+ }
1092
+
1093
+ module.exports = new BotManager();