blockmine 1.22.0 → 1.23.1
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.
- package/.claude/agents/code-architect.md +34 -0
- package/.claude/agents/code-explorer.md +51 -0
- package/.claude/agents/code-reviewer.md +46 -0
- package/.claude/commands/feature-dev.md +125 -0
- package/.claude/settings.json +5 -1
- package/.claude/settings.local.json +12 -1
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/CHANGELOG.md +32 -1
- package/README.md +302 -152
- package/backend/package-lock.json +681 -9
- package/backend/package.json +8 -0
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
- package/backend/prisma/schema.prisma +70 -1
- package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
- package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
- package/backend/src/api/middleware/auth.js +27 -0
- package/backend/src/api/middleware/botAccess.js +7 -3
- package/backend/src/api/middleware/panelApiAuth.js +135 -0
- package/backend/src/api/routes/aiAssistant.js +995 -0
- package/backend/src/api/routes/auth.js +90 -54
- package/backend/src/api/routes/botCommands.js +107 -0
- package/backend/src/api/routes/botGroups.js +165 -0
- package/backend/src/api/routes/botHistory.js +108 -0
- package/backend/src/api/routes/botPermissions.js +99 -0
- package/backend/src/api/routes/botStatus.js +36 -0
- package/backend/src/api/routes/botUsers.js +162 -0
- package/backend/src/api/routes/bots.js +108 -59
- package/backend/src/api/routes/eventGraphs.js +4 -1
- package/backend/src/api/routes/logs.js +13 -3
- package/backend/src/api/routes/panel.js +3 -3
- package/backend/src/api/routes/panelApiKeys.js +179 -0
- package/backend/src/api/routes/pluginIde.js +1715 -135
- package/backend/src/api/routes/plugins.js +170 -13
- package/backend/src/api/routes/proxies.js +130 -0
- package/backend/src/api/routes/search.js +4 -0
- package/backend/src/api/routes/servers.js +20 -3
- package/backend/src/api/routes/settings.js +5 -0
- package/backend/src/api/routes/system.js +3 -3
- package/backend/src/api/routes/traces.js +131 -0
- package/backend/src/config/debug.config.js +36 -0
- package/backend/src/core/BotHistoryStore.js +180 -0
- package/backend/src/core/BotManager.js +14 -4
- package/backend/src/core/BotProcess.js +1517 -1092
- package/backend/src/core/EventGraphManager.js +194 -280
- package/backend/src/core/GraphExecutionEngine.js +1004 -321
- package/backend/src/core/MessageQueue.js +12 -6
- package/backend/src/core/PluginLoader.js +99 -5
- package/backend/src/core/PluginManager.js +74 -13
- package/backend/src/core/TaskScheduler.js +1 -1
- package/backend/src/core/commands/whois.js +1 -1
- package/backend/src/core/node-registries/actions.js +72 -2
- package/backend/src/core/node-registries/arrays.js +18 -0
- package/backend/src/core/node-registries/data.js +1 -1
- package/backend/src/core/node-registries/events.js +14 -0
- package/backend/src/core/node-registries/logic.js +17 -0
- package/backend/src/core/node-registries/strings.js +34 -0
- package/backend/src/core/node-registries/type.js +25 -0
- package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
- package/backend/src/core/nodes/actions/create_command.js +189 -0
- package/backend/src/core/nodes/actions/delete_command.js +92 -0
- package/backend/src/core/nodes/actions/http_request.js +23 -4
- package/backend/src/core/nodes/actions/send_message.js +2 -12
- package/backend/src/core/nodes/actions/update_command.js +133 -0
- package/backend/src/core/nodes/arrays/join.js +28 -0
- package/backend/src/core/nodes/data/cast.js +2 -1
- package/backend/src/core/nodes/data/string_literal.js +2 -13
- package/backend/src/core/nodes/logic/not.js +22 -0
- package/backend/src/core/nodes/strings/starts_with.js +1 -1
- package/backend/src/core/nodes/strings/to_lower.js +22 -0
- package/backend/src/core/nodes/strings/to_upper.js +22 -0
- package/backend/src/core/nodes/type/to_string.js +32 -0
- package/backend/src/core/services/BotLifecycleService.js +835 -596
- package/backend/src/core/services/CommandExecutionService.js +430 -351
- package/backend/src/core/services/DebugSessionManager.js +347 -0
- package/backend/src/core/services/GraphCollaborationManager.js +501 -0
- package/backend/src/core/services/MinecraftBotManager.js +259 -0
- package/backend/src/core/services/MinecraftViewerService.js +216 -0
- package/backend/src/core/services/TraceCollectorService.js +545 -0
- package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
- package/backend/src/core/system/Transport.js +0 -4
- package/backend/src/core/validation/nodeSchemas.js +6 -6
- package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
- package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
- package/backend/src/real-time/botApi/utils.js +11 -0
- package/backend/src/real-time/panelNamespace.js +387 -0
- package/backend/src/real-time/presence.js +7 -2
- package/backend/src/real-time/socketHandler.js +395 -4
- package/backend/src/server.js +18 -0
- package/frontend/dist/assets/index-DqzDkFsP.js +11210 -0
- package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package-lock.json +9437 -0
- package/frontend/package.json +8 -0
- package/package.json +2 -2
- package/screen/console.png +0 -0
- package/screen/dashboard.png +0 -0
- package/screen/graph_collabe.png +0 -0
- package/screen/graph_live_debug.png +0 -0
- package/screen/management_command.png +0 -0
- package/screen/node_debug_trace.png +0 -0
- package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
- package/screen/websocket.png +0 -0
- package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
- package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
- package/frontend/dist/assets/index-CfTo92bP.css +0 -1
- package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
|
@@ -1,596 +1,835 @@
|
|
|
1
|
-
const DependencyService = require('../DependencyService');
|
|
2
|
-
const { decrypt } = require('../utils/crypto');
|
|
3
|
-
const UserService = require('../UserService');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await this.
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (decryptedConfig.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
case '
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
this.
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
const {
|
|
313
|
-
const { broadcastBotStatus } = require('../../real-time/botApi');
|
|
314
|
-
broadcastBotStatus(
|
|
315
|
-
} catch (e) { /* Socket.IO может быть не инициализирован */ }
|
|
316
|
-
|
|
317
|
-
//
|
|
318
|
-
if (
|
|
319
|
-
this.
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
{
|
|
409
|
-
{
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
this.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (this.processManager.sendMessage(botId, { type: '
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
},
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
1
|
+
const DependencyService = require('../DependencyService');
|
|
2
|
+
const { decrypt } = require('../utils/crypto');
|
|
3
|
+
const UserService = require('../UserService');
|
|
4
|
+
const PermissionManager = require('../PermissionManager');
|
|
5
|
+
|
|
6
|
+
class BotLifecycleService {
|
|
7
|
+
constructor({
|
|
8
|
+
botRepository,
|
|
9
|
+
pluginRepository,
|
|
10
|
+
commandRepository,
|
|
11
|
+
permissionRepository,
|
|
12
|
+
botProcessManager,
|
|
13
|
+
cacheManager,
|
|
14
|
+
resourceMonitorService,
|
|
15
|
+
telemetryService,
|
|
16
|
+
eventGraphManager,
|
|
17
|
+
commandExecutionService,
|
|
18
|
+
logger
|
|
19
|
+
}) {
|
|
20
|
+
this.botRepository = botRepository;
|
|
21
|
+
this.pluginRepository = pluginRepository;
|
|
22
|
+
this.commandRepository = commandRepository;
|
|
23
|
+
this.permissionRepository = permissionRepository;
|
|
24
|
+
this.processManager = botProcessManager;
|
|
25
|
+
this.cache = cacheManager;
|
|
26
|
+
this.resourceMonitor = resourceMonitorService;
|
|
27
|
+
this.telemetry = telemetryService;
|
|
28
|
+
this.eventGraphManager = eventGraphManager;
|
|
29
|
+
this.commandExecutionService = commandExecutionService;
|
|
30
|
+
this.logger = logger;
|
|
31
|
+
|
|
32
|
+
this.logCache = new Map();
|
|
33
|
+
this.crashCounters = new Map();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async startBot(botConfig) {
|
|
37
|
+
const botId = botConfig.id;
|
|
38
|
+
|
|
39
|
+
if (this.processManager.isRunning(botId)) {
|
|
40
|
+
this.appendLog(botId, `[SYSTEM-ERROR] Попытка повторного запуска. Запуск отменен.`);
|
|
41
|
+
return { success: false, message: 'Бот уже запущен или запускается.' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await this._syncSystemPermissions(botId);
|
|
45
|
+
await this.loadConfigForBot(botId);
|
|
46
|
+
this.logCache.set(botId, []);
|
|
47
|
+
this.emitStatusUpdate(botId, 'starting', '');
|
|
48
|
+
|
|
49
|
+
const allPluginsForBot = await this.pluginRepository.findEnabledByBotId(botId);
|
|
50
|
+
const { sortedPlugins, hasCriticalIssues, pluginInfo } = DependencyService.resolveDependencies(allPluginsForBot, allPluginsForBot);
|
|
51
|
+
|
|
52
|
+
if (hasCriticalIssues) {
|
|
53
|
+
this.appendLog(botId, '[DependencyManager] Обнаружены критические проблемы с зависимостями, запуск отменен.');
|
|
54
|
+
|
|
55
|
+
const criticalIssueTypes = new Set(['missing_dependency', 'version_mismatch', 'circular_dependency']);
|
|
56
|
+
|
|
57
|
+
for (const pluginId in pluginInfo) {
|
|
58
|
+
const info = pluginInfo[pluginId];
|
|
59
|
+
if (info.issues.length === 0) continue;
|
|
60
|
+
|
|
61
|
+
const criticalIssues = info.issues.filter(issue => criticalIssueTypes.has(issue.type));
|
|
62
|
+
|
|
63
|
+
if (criticalIssues.length > 0) {
|
|
64
|
+
this.appendLog(botId, `* Плагин "${info.name}":`);
|
|
65
|
+
for (const issue of criticalIssues) {
|
|
66
|
+
this.appendLog(botId, ` - ${issue.message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.emitStatusUpdate(botId, 'stopped', 'Ошибка зависимостей плагинов.');
|
|
72
|
+
return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const decryptedConfig = { ...botConfig };
|
|
76
|
+
|
|
77
|
+
if (decryptedConfig.proxy) {
|
|
78
|
+
decryptedConfig.proxyHost = decryptedConfig.proxy.host;
|
|
79
|
+
decryptedConfig.proxyPort = decryptedConfig.proxy.port;
|
|
80
|
+
decryptedConfig.proxyUsername = decryptedConfig.proxy.username;
|
|
81
|
+
decryptedConfig.proxyPassword = decryptedConfig.proxy.password;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (decryptedConfig.password) decryptedConfig.password = decrypt(decryptedConfig.password);
|
|
85
|
+
if (decryptedConfig.proxyPassword) decryptedConfig.proxyPassword = decrypt(decryptedConfig.proxyPassword);
|
|
86
|
+
if (decryptedConfig.proxyUsername) decryptedConfig.proxyUsername = decryptedConfig.proxyUsername.trim();
|
|
87
|
+
|
|
88
|
+
const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
|
|
89
|
+
|
|
90
|
+
const child = await this.processManager.spawn(botConfig, fullBotConfig);
|
|
91
|
+
|
|
92
|
+
child.api = {
|
|
93
|
+
sendMessage: (type, message, username) => {
|
|
94
|
+
if (!child.killed) {
|
|
95
|
+
child.send({ type: 'chat', payload: { message, chatType: type, username } });
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
sendLog: (message) => {
|
|
99
|
+
this.appendLog(botId, message);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Регистрируем обработчики сообщений от child process
|
|
104
|
+
this._setupChildProcessHandlers(child, botConfig);
|
|
105
|
+
|
|
106
|
+
child.send({ type: 'start', config: fullBotConfig });
|
|
107
|
+
|
|
108
|
+
await this.eventGraphManager.loadGraphsForBot(botId);
|
|
109
|
+
|
|
110
|
+
this.telemetry.triggerHeartbeat();
|
|
111
|
+
this.emitStatusUpdate(botId, 'starting');
|
|
112
|
+
|
|
113
|
+
return child;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async stopBot(botId) {
|
|
117
|
+
const child = this.processManager.getProcess(botId);
|
|
118
|
+
if (child) {
|
|
119
|
+
this.eventGraphManager.unloadGraphsForBot(botId);
|
|
120
|
+
|
|
121
|
+
// Очищаем traces для этого бота
|
|
122
|
+
const { getTraceCollector } = require('./TraceCollectorService');
|
|
123
|
+
const traceCollector = getTraceCollector();
|
|
124
|
+
traceCollector.clearForBot(botId);
|
|
125
|
+
|
|
126
|
+
child.send({ type: 'stop' });
|
|
127
|
+
|
|
128
|
+
// Принудительное завершение через 5 секунд
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
if (!child.killed) {
|
|
131
|
+
this.logger.warn({ botId }, 'Принудительное завершение процесса');
|
|
132
|
+
try {
|
|
133
|
+
child.kill('SIGKILL');
|
|
134
|
+
} catch (error) {
|
|
135
|
+
this.logger.error({ botId, error }, 'Ошибка принудительного завершения');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}, 5000);
|
|
139
|
+
|
|
140
|
+
this.cache.clearBotCache(botId);
|
|
141
|
+
return { success: true };
|
|
142
|
+
}
|
|
143
|
+
return { success: false, message: 'Бот не найден или уже остановлен' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
getChildProcess(botId) {
|
|
147
|
+
return this.processManager.getProcess(botId);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async restartBot(botId) {
|
|
151
|
+
const botConfig = this.processManager.getProcess(botId)?.botConfig;
|
|
152
|
+
if (!botConfig) {
|
|
153
|
+
throw new Error('Bot configuration not found');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await this.stopBot(botId);
|
|
157
|
+
|
|
158
|
+
// Ждём завершения процесса
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
160
|
+
|
|
161
|
+
return this.startBot(botConfig);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
_setupChildProcessHandlers(child, botConfig) {
|
|
165
|
+
const botId = botConfig.id;
|
|
166
|
+
|
|
167
|
+
child.on('message', async (message) => {
|
|
168
|
+
try {
|
|
169
|
+
switch (message.type) {
|
|
170
|
+
case 'event':
|
|
171
|
+
await this._handleEventMessage(botId, message);
|
|
172
|
+
break;
|
|
173
|
+
case 'plugin:data':
|
|
174
|
+
this._handlePluginDataMessage(botId, message);
|
|
175
|
+
break;
|
|
176
|
+
case 'send_websocket_message':
|
|
177
|
+
this._handleWebSocketMessage(message);
|
|
178
|
+
break;
|
|
179
|
+
case 'log':
|
|
180
|
+
this.appendLog(botId, message.content);
|
|
181
|
+
break;
|
|
182
|
+
case 'plugin-log':
|
|
183
|
+
this._handlePluginLog(message.log);
|
|
184
|
+
break;
|
|
185
|
+
case 'status':
|
|
186
|
+
this.emitStatusUpdate(botId, message.status);
|
|
187
|
+
break;
|
|
188
|
+
case 'bot_ready':
|
|
189
|
+
this._handleBotReady(botId);
|
|
190
|
+
break;
|
|
191
|
+
case 'validate_and_run_command':
|
|
192
|
+
if (this.commandExecutionService) {
|
|
193
|
+
const botConfig = child.botConfig;
|
|
194
|
+
if (botConfig) {
|
|
195
|
+
await this.commandExecutionService.handleCommandValidation(botConfig, message);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
case 'request_user_action':
|
|
200
|
+
await this._handleUserAction(botId, child, message);
|
|
201
|
+
break;
|
|
202
|
+
case 'get_player_list_response':
|
|
203
|
+
this.processManager.resolvePlayerListRequest(message.requestId, message.payload.players);
|
|
204
|
+
break;
|
|
205
|
+
case 'get_nearby_entities_response':
|
|
206
|
+
this.processManager.resolveNearbyEntitiesRequest(message.requestId, message.payload.entities);
|
|
207
|
+
break;
|
|
208
|
+
case 'execute_command_response':
|
|
209
|
+
this.processManager.resolveCommandRequest(message.requestId, message.result, message.error);
|
|
210
|
+
break;
|
|
211
|
+
case 'register_command':
|
|
212
|
+
await this._handleCommandRegistration(botId, message.commandConfig);
|
|
213
|
+
break;
|
|
214
|
+
case 'register_permissions':
|
|
215
|
+
await this._handlePermissionsRegistration(botId, message);
|
|
216
|
+
break;
|
|
217
|
+
case 'register_group':
|
|
218
|
+
await this._handleGroupRegistration(botId, message);
|
|
219
|
+
break;
|
|
220
|
+
case 'add_permissions_to_group':
|
|
221
|
+
await this._handleAddPermissionsToGroup(botId, message);
|
|
222
|
+
break;
|
|
223
|
+
case 'trace:completed':
|
|
224
|
+
await this._handleTraceCompleted(botId, message.trace);
|
|
225
|
+
break;
|
|
226
|
+
case 'debug:check_breakpoint':
|
|
227
|
+
await this._handleDebugBreakpointCheck(botId, child, message);
|
|
228
|
+
break;
|
|
229
|
+
case 'debug:check_step_mode':
|
|
230
|
+
await this._handleDebugStepModeCheck(botId, child, message);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
this.appendLog(botId, `[SYSTEM-ERROR] Критическая ошибка в обработчике: ${error.stack}`);
|
|
235
|
+
this.logger.error({ botId, error }, 'Критическая ошибка в обработчике сообщений');
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
child.on('error', (err) => this.appendLog(botId, `[PROCESS FATAL] ${err.stack}`));
|
|
240
|
+
child.stdout.on('data', (data) => console.log(data.toString()));
|
|
241
|
+
child.stderr.on('data', (data) => this.appendLog(botId, `[STDERR] ${data.toString()}`));
|
|
242
|
+
|
|
243
|
+
child.on('exit', (code, signal) => {
|
|
244
|
+
this._handleProcessExit(botId, botConfig, code, signal);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async _handleEventMessage(botId, message) {
|
|
249
|
+
if (message.eventType === 'raw_message') {
|
|
250
|
+
try {
|
|
251
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
252
|
+
const { broadcastToApiClients } = require('../../real-time/botApi');
|
|
253
|
+
broadcastToApiClients(getIOSafe(), botId, 'chat:raw_message', {
|
|
254
|
+
raw_message: message.args.rawText || message.args.raw_message,
|
|
255
|
+
json: message.args.json
|
|
256
|
+
});
|
|
257
|
+
} catch (e) { /* Socket.IO может быть не инициализирован */ }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const { broadcastToPanelNamespace } = require('../../real-time/panelNamespace');
|
|
262
|
+
broadcastToPanelNamespace(botId, 'bot:event', {
|
|
263
|
+
botId,
|
|
264
|
+
eventType: message.eventType,
|
|
265
|
+
data: message.args || {},
|
|
266
|
+
timestamp: new Date().toISOString()
|
|
267
|
+
});
|
|
268
|
+
} catch (e) { /* Socket.IO может быть не инициализирован */ }
|
|
269
|
+
|
|
270
|
+
if (this.eventGraphManager) {
|
|
271
|
+
this.eventGraphManager.handleEvent(botId, message.eventType, message.args);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_handlePluginDataMessage(botId, message) {
|
|
276
|
+
const { plugin: pluginName, payload } = message;
|
|
277
|
+
const pluginSubscribers = this.processManager.getPluginSubscribers(botId, pluginName);
|
|
278
|
+
|
|
279
|
+
if (pluginSubscribers && pluginSubscribers.size > 0) {
|
|
280
|
+
pluginSubscribers.forEach(socket => {
|
|
281
|
+
socket.emit('plugin:ui:dataUpdate', payload);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
_handlePluginLog(logData) {
|
|
287
|
+
const { getIOSafe, addPluginLogToBuffer } = require('../../real-time/socketHandler');
|
|
288
|
+
const { botId, pluginName } = logData;
|
|
289
|
+
|
|
290
|
+
// Добавляем лог в буфер
|
|
291
|
+
addPluginLogToBuffer(botId, pluginName, logData);
|
|
292
|
+
|
|
293
|
+
// Отправляем через Socket.IO в комнату плагина
|
|
294
|
+
const io = getIOSafe();
|
|
295
|
+
if (io) {
|
|
296
|
+
const room = `plugin-logs:${botId}:${pluginName}`;
|
|
297
|
+
io.to(room).emit('plugin-log', logData);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_handleWebSocketMessage(message) {
|
|
302
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
303
|
+
const { botId, message: msg } = message.payload;
|
|
304
|
+
getIOSafe().to(`bot_${botId}`).emit('bot:message', { message: msg });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
_handleBotReady(botId) {
|
|
308
|
+
this.emitStatusUpdate(botId, 'running', 'Бот успешно подключился к серверу.');
|
|
309
|
+
this.crashCounters.delete(botId);
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
313
|
+
const { broadcastBotStatus } = require('../../real-time/botApi');
|
|
314
|
+
broadcastBotStatus(getIOSafe(), botId, true);
|
|
315
|
+
} catch (e) { /* Socket.IO может быть не инициализирован */ }
|
|
316
|
+
|
|
317
|
+
// Триггерим событие запуска бота
|
|
318
|
+
if (this.eventGraphManager) {
|
|
319
|
+
this.eventGraphManager.handleEvent(botId, 'botStartup', {});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async _handleCommandRegistration(botId, commandConfig) {
|
|
324
|
+
if (this.commandExecutionService) {
|
|
325
|
+
await this.commandExecutionService.handleCommandRegistration(botId, commandConfig);
|
|
326
|
+
// this.logger.debug({ botId, commandName: commandConfig.name }, 'Команда зарегистрирована');
|
|
327
|
+
} else {
|
|
328
|
+
this.logger.warn({ botId }, 'CommandExecutionService не доступен для регистрации команды');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async _handlePermissionsRegistration(botId, message) {
|
|
333
|
+
try {
|
|
334
|
+
await PermissionManager.registerPermissions(botId, message.permissions);
|
|
335
|
+
this.logger.debug({ botId, count: message.permissions.length }, 'Права зарегистрированы');
|
|
336
|
+
} catch (error) {
|
|
337
|
+
this.logger.error({ botId, error }, 'Ошибка регистрации прав');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async _handleGroupRegistration(botId, message) {
|
|
342
|
+
try {
|
|
343
|
+
await PermissionManager.registerGroup(botId, message.groupConfig);
|
|
344
|
+
this.logger.debug({ botId, groupName: message.groupConfig.name }, 'Группа зарегистрирована');
|
|
345
|
+
} catch (error) {
|
|
346
|
+
this.logger.error({ botId, error }, 'Ошибка регистрации группы');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async _handleAddPermissionsToGroup(botId, message) {
|
|
351
|
+
try {
|
|
352
|
+
await PermissionManager.addPermissionsToGroup(botId, message.groupName, message.permissionNames);
|
|
353
|
+
this.logger.debug({ botId, groupName: message.groupName, count: message.permissionNames.length }, 'Права добавлены в группу');
|
|
354
|
+
} catch (error) {
|
|
355
|
+
this.logger.error({ botId, error }, 'Ошибка добавления прав в группу');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async _handleUserAction(botId, child, message) {
|
|
360
|
+
const { requestId, payload } = message;
|
|
361
|
+
const { targetUsername, action, data } = payload;
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const botConfig = child.botConfig;
|
|
365
|
+
const user = await UserService.getUser(targetUsername, botId, botConfig);
|
|
366
|
+
if (!user) throw new Error(`Пользователь ${targetUsername} не найден.`);
|
|
367
|
+
|
|
368
|
+
let result;
|
|
369
|
+
|
|
370
|
+
switch (action) {
|
|
371
|
+
case 'addGroup':
|
|
372
|
+
result = await user.addGroup(data.group);
|
|
373
|
+
break;
|
|
374
|
+
case 'removeGroup':
|
|
375
|
+
result = await user.removeGroup(data.group);
|
|
376
|
+
break;
|
|
377
|
+
case 'getGroups':
|
|
378
|
+
result = user.groups ? user.groups.map(g => g.group.name) : [];
|
|
379
|
+
break;
|
|
380
|
+
case 'getPermissions':
|
|
381
|
+
result = Array.from(user.permissionsSet);
|
|
382
|
+
break;
|
|
383
|
+
case 'isBlacklisted':
|
|
384
|
+
result = user.isBlacklisted;
|
|
385
|
+
break;
|
|
386
|
+
case 'setBlacklisted':
|
|
387
|
+
result = await user.setBlacklist(data.value);
|
|
388
|
+
break;
|
|
389
|
+
default:
|
|
390
|
+
throw new Error(`Неизвестное действие: ${action}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
child.send({ type: 'user_action_response', requestId, payload: result });
|
|
394
|
+
} catch (error) {
|
|
395
|
+
this.logger.error({ botId, action, username: targetUsername, error }, 'Ошибка действия пользователя');
|
|
396
|
+
child.send({ type: 'user_action_response', requestId, error: error.message });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
_handleProcessExit(botId, botConfig, code, signal) {
|
|
401
|
+
this.processManager.remove(botId);
|
|
402
|
+
this.resourceMonitor.clearResourceUsage(botId);
|
|
403
|
+
this.cache.clearBotCache(botId);
|
|
404
|
+
|
|
405
|
+
this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
409
|
+
const { broadcastBotStatus } = require('../../real-time/botApi');
|
|
410
|
+
broadcastBotStatus(getIOSafe(), botId, false);
|
|
411
|
+
} catch (e) { /* Socket.IO может быть не инициализирован */ }
|
|
412
|
+
|
|
413
|
+
// Автоперезапуск при критических ошибках
|
|
414
|
+
if (code === 1) {
|
|
415
|
+
this._handleCrashRestart(botId, botConfig);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
_handleCrashRestart(botId, botConfig) {
|
|
420
|
+
const MAX_RESTART_ATTEMPTS = 5;
|
|
421
|
+
const RESTART_COOLDOWN = 60000;
|
|
422
|
+
|
|
423
|
+
const counter = this.crashCounters.get(botId) || { count: 0, firstCrash: Date.now() };
|
|
424
|
+
const timeSinceFirstCrash = Date.now() - counter.firstCrash;
|
|
425
|
+
|
|
426
|
+
if (timeSinceFirstCrash > RESTART_COOLDOWN) {
|
|
427
|
+
counter.count = 0;
|
|
428
|
+
counter.firstCrash = Date.now();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
counter.count++;
|
|
432
|
+
this.crashCounters.set(botId, counter);
|
|
433
|
+
|
|
434
|
+
if (counter.count >= MAX_RESTART_ATTEMPTS) {
|
|
435
|
+
this.logger.warn({ botId, attempts: counter.count }, 'Автоперезапуск остановлен');
|
|
436
|
+
this.appendLog(botId, `[SYSTEM] Обнаружено ${counter.count} критических ошибок подряд.`);
|
|
437
|
+
this.appendLog(botId, `[SYSTEM] Исправьте проблему и запустите бота вручную.`);
|
|
438
|
+
this.crashCounters.delete(botId);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
this.logger.info({ botId, attempt: counter.count, max: MAX_RESTART_ATTEMPTS }, 'Перезапуск через 5 секунд');
|
|
443
|
+
this.appendLog(botId, `[SYSTEM] Обнаружена критическая ошибка, перезапуск через 5 секунд... (попытка ${counter.count}/${MAX_RESTART_ATTEMPTS})`);
|
|
444
|
+
|
|
445
|
+
setTimeout(() => {
|
|
446
|
+
this.logger.info({ botId }, 'Выполняется перезапуск');
|
|
447
|
+
this.startBot(botConfig);
|
|
448
|
+
}, 5000);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async loadConfigForBot(botId) {
|
|
452
|
+
this.logger.info({ botId }, 'Загрузка конфигурации');
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const [commands, permissions] = await Promise.all([
|
|
456
|
+
this.commandRepository.findByBotId(botId),
|
|
457
|
+
this.permissionRepository.findByBotId(botId),
|
|
458
|
+
]);
|
|
459
|
+
|
|
460
|
+
const config = {
|
|
461
|
+
commands: new Map(commands.map(cmd => [cmd.name, cmd])),
|
|
462
|
+
permissionsById: new Map(permissions.map(p => [p.id, p])),
|
|
463
|
+
commandAliases: new Map()
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
for (const cmd of commands) {
|
|
467
|
+
const aliases = JSON.parse(cmd.aliases || '[]');
|
|
468
|
+
for (const alias of aliases) {
|
|
469
|
+
config.commandAliases.set(alias, cmd.name);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
this.cache.setBotConfig(botId, config);
|
|
474
|
+
this.logger.info({ botId }, 'Конфигурация загружена');
|
|
475
|
+
return config;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
this.logger.error({ botId, error }, 'Ошибка загрузки конфигурации');
|
|
478
|
+
throw new Error(`Failed to load/cache bot configuration for botId ${botId}: ${error.message}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
invalidateConfigCache(botId) {
|
|
483
|
+
if (this.cache.getBotConfig(botId)) {
|
|
484
|
+
this.cache.deleteBotConfig(botId);
|
|
485
|
+
this.logger.debug({ botId }, 'Кеш конфигурации инвалидирован');
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
reloadBotConfigInRealTime(botId) {
|
|
490
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
491
|
+
this.invalidateConfigCache(botId);
|
|
492
|
+
|
|
493
|
+
if (this.processManager.sendMessage(botId, { type: 'config:reload' })) {
|
|
494
|
+
this.logger.info({ botId }, 'Отправлен config:reload');
|
|
495
|
+
getIOSafe().emit('bot:config_reloaded', { botId });
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async _syncSystemPermissions(botId) {
|
|
500
|
+
const systemPermissions = [
|
|
501
|
+
{ name: "admin.*", description: "Все права администратора", owner: "system" },
|
|
502
|
+
{ name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд", owner: "system" },
|
|
503
|
+
{ name: "user.*", description: "Все права обычного пользователя", owner: "system" },
|
|
504
|
+
{ name: "user.say", description: "Доступ к простым командам", owner: "system" },
|
|
505
|
+
{ name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд", owner: "system" },
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
this.logger.debug({ botId }, 'Синхронизация системных прав');
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
for (const perm of systemPermissions) {
|
|
512
|
+
const existing = await this.permissionRepository.findByName(botId, perm.name);
|
|
513
|
+
if (existing) {
|
|
514
|
+
// Обновляем описание если изменилось
|
|
515
|
+
if (existing.description !== perm.description) {
|
|
516
|
+
await this.permissionRepository.update(existing.id, {
|
|
517
|
+
description: perm.description
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
// Создаем новое системное право
|
|
522
|
+
await this.permissionRepository.create({
|
|
523
|
+
botId,
|
|
524
|
+
name: perm.name,
|
|
525
|
+
description: perm.description,
|
|
526
|
+
owner: perm.owner,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.logger.debug({ botId }, 'Системные права синхронизированы');
|
|
531
|
+
} catch (error) {
|
|
532
|
+
this.logger.error({ botId, error }, 'Ошибка синхронизации системных прав');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
appendLog(botId, logContent) {
|
|
537
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
538
|
+
const logEntry = {
|
|
539
|
+
id: Date.now() + Math.random(),
|
|
540
|
+
content: logContent,
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const currentLogs = this.logCache.get(botId) || [];
|
|
544
|
+
const newLogs = [...currentLogs.slice(-199), logEntry];
|
|
545
|
+
this.logCache.set(botId, newLogs);
|
|
546
|
+
|
|
547
|
+
getIOSafe().emit('bot:log', { botId, log: logEntry });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
getBotLogs(botId) {
|
|
551
|
+
return this.logCache.get(botId) || [];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
emitStatusUpdate(botId, status, message = null) {
|
|
555
|
+
const { getIOSafe, broadcastToPanelNamespace } = require('../../real-time/socketHandler');
|
|
556
|
+
if (message) this.appendLog(botId, `[SYSTEM] ${message}`);
|
|
557
|
+
|
|
558
|
+
getIOSafe().emit('bot:status', { botId, status, message });
|
|
559
|
+
|
|
560
|
+
broadcastToPanelNamespace(getIOSafe(), 'bots:status', {
|
|
561
|
+
botId,
|
|
562
|
+
status,
|
|
563
|
+
message,
|
|
564
|
+
timestamp: new Date().toISOString()
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
getFullState() {
|
|
569
|
+
const processes = this.processManager.getAllProcesses();
|
|
570
|
+
const statuses = {};
|
|
571
|
+
for (const [id, child] of processes.entries()) {
|
|
572
|
+
statuses[id] = child.killed ? 'stopped' : 'running';
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const logs = {};
|
|
576
|
+
for (const [botId, logArray] of this.logCache.entries()) {
|
|
577
|
+
logs[botId] = logArray;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return { statuses, logs };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
isBotRunning(botId) {
|
|
584
|
+
return this.processManager.isRunning(botId);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
sendMessageToBot(botId, message, chatType = 'command', username = null) {
|
|
588
|
+
const child = this.processManager.getProcess(botId);
|
|
589
|
+
if (child && child.api) {
|
|
590
|
+
child.api.sendMessage(chatType, message, username);
|
|
591
|
+
return { success: true };
|
|
592
|
+
}
|
|
593
|
+
return { success: false, message: 'Бот не найден или не запущен' };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
lookAt(botId, position) {
|
|
597
|
+
if (this.processManager.sendMessage(botId, { type: 'action', name: 'lookAt', payload: { position } })) {
|
|
598
|
+
return { success: true };
|
|
599
|
+
}
|
|
600
|
+
this.logger.error({ botId }, 'Не удалось выполнить lookAt');
|
|
601
|
+
return { success: false };
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async reloadPlugins(botId) {
|
|
605
|
+
if (this.processManager.sendMessage(botId, { type: 'plugins:reload' })) {
|
|
606
|
+
this.logger.info({ botId }, 'Отправлен plugins:reload');
|
|
607
|
+
const { getIOSafe } = require('../../real-time/socketHandler');
|
|
608
|
+
getIOSafe().emit('bot:plugins_reloaded', { botId });
|
|
609
|
+
return { success: true, message: 'Команда на перезагрузку плагинов отправлена.' };
|
|
610
|
+
}
|
|
611
|
+
return { success: false, message: 'Бот не запущен.' };
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
sendServerCommandToBot(botId, command) {
|
|
615
|
+
this.processManager.sendMessage(botId, { type: 'server_command', payload: { command } });
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async getPlayerList(botId) {
|
|
619
|
+
const PLAYER_LIST_CACHE_TTL = 2000;
|
|
620
|
+
|
|
621
|
+
if (!this.processManager.isRunning(botId)) {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const cachedPlayers = this.cache.getPlayerList(botId);
|
|
626
|
+
if (cachedPlayers) {
|
|
627
|
+
return cachedPlayers;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return new Promise((resolve) => {
|
|
631
|
+
const { v4: uuidv4 } = require('uuid');
|
|
632
|
+
const requestId = uuidv4();
|
|
633
|
+
|
|
634
|
+
const timeout = setTimeout(() => {
|
|
635
|
+
resolve([]);
|
|
636
|
+
}, 5000);
|
|
637
|
+
|
|
638
|
+
this.processManager.addPlayerListRequest(requestId, {
|
|
639
|
+
resolve: (playerList) => {
|
|
640
|
+
clearTimeout(timeout);
|
|
641
|
+
this.cache.setPlayerList(botId, playerList);
|
|
642
|
+
resolve(playerList);
|
|
643
|
+
},
|
|
644
|
+
timeout
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
this.processManager.sendMessage(botId, { type: 'system:get_player_list', requestId });
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async getNearbyEntities(botId, position = null, radius = 32) {
|
|
652
|
+
if (!this.processManager.isRunning(botId)) {
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return new Promise((resolve) => {
|
|
657
|
+
const { v4: uuidv4 } = require('uuid');
|
|
658
|
+
const requestId = uuidv4();
|
|
659
|
+
|
|
660
|
+
const timeout = setTimeout(() => {
|
|
661
|
+
resolve([]);
|
|
662
|
+
}, 5000);
|
|
663
|
+
|
|
664
|
+
this.processManager.addNearbyEntitiesRequest(requestId, {
|
|
665
|
+
resolve: (entities) => {
|
|
666
|
+
clearTimeout(timeout);
|
|
667
|
+
resolve(entities);
|
|
668
|
+
},
|
|
669
|
+
timeout
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
this.processManager.sendMessage(botId, {
|
|
673
|
+
type: 'system:get_nearby_entities',
|
|
674
|
+
requestId,
|
|
675
|
+
payload: { position, radius }
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
invalidateUserCache(botId, username) {
|
|
681
|
+
UserService.clearCache(username, botId);
|
|
682
|
+
this.processManager.sendMessage(botId, { type: 'invalidate_user_cache', username });
|
|
683
|
+
return { success: true };
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
invalidateAllUserCache(botId) {
|
|
687
|
+
for (const [cacheKey] of UserService.cache.entries()) {
|
|
688
|
+
if (cacheKey.startsWith(`${botId}:`)) {
|
|
689
|
+
UserService.cache.delete(cacheKey);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
this.logger.info({ botId }, 'Кеш пользователей очищен');
|
|
694
|
+
this.processManager.sendMessage(botId, { type: 'invalidate_all_user_cache' });
|
|
695
|
+
|
|
696
|
+
return { success: true };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
async _handleTraceCompleted(botId, trace) {
|
|
700
|
+
try {
|
|
701
|
+
const { getTraceCollector } = require('../services/TraceCollectorService');
|
|
702
|
+
const traceCollector = getTraceCollector();
|
|
703
|
+
|
|
704
|
+
// Сохраняем трассировку в главном TraceCollectorService
|
|
705
|
+
await traceCollector._storeCompletedTrace(trace);
|
|
706
|
+
} catch (error) {
|
|
707
|
+
this.logger.error({ botId, error }, 'Ошибка обработки завершённой трассировки');
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async _handleDebugBreakpointCheck(botId, child, message) {
|
|
712
|
+
const { requestId, payload } = message;
|
|
713
|
+
const { graphId, nodeId, nodeType, inputs, executedSteps, context } = payload;
|
|
714
|
+
|
|
715
|
+
try {
|
|
716
|
+
const { getGlobalDebugManager } = require('../services/DebugSessionManager');
|
|
717
|
+
const debugManager = getGlobalDebugManager();
|
|
718
|
+
|
|
719
|
+
const debugState = debugManager.get(graphId);
|
|
720
|
+
if (!debugState) {
|
|
721
|
+
// Нет debug сессии для этого графа - просто продолжаем выполнение
|
|
722
|
+
child.send({
|
|
723
|
+
type: 'debug:breakpoint_response',
|
|
724
|
+
requestId,
|
|
725
|
+
overrides: null
|
|
726
|
+
});
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const breakpoint = debugState.breakpoints.get(nodeId);
|
|
731
|
+
if (!breakpoint || !breakpoint.enabled) {
|
|
732
|
+
// Нет брейкпоинта для этой ноды или он отключен
|
|
733
|
+
child.send({
|
|
734
|
+
type: 'debug:breakpoint_response',
|
|
735
|
+
requestId,
|
|
736
|
+
overrides: null
|
|
737
|
+
});
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Проверяем условие брейкпоинта (пока всегда срабатывает)
|
|
742
|
+
// TODO: добавить evaluateBreakpointCondition
|
|
743
|
+
|
|
744
|
+
breakpoint.hitCount++;
|
|
745
|
+
|
|
746
|
+
// Приостанавливаем выполнение и ждём действий от пользователя
|
|
747
|
+
const overrides = await debugState.pause({
|
|
748
|
+
nodeId,
|
|
749
|
+
nodeType,
|
|
750
|
+
inputs,
|
|
751
|
+
executedSteps,
|
|
752
|
+
context,
|
|
753
|
+
breakpoint: {
|
|
754
|
+
condition: breakpoint.condition,
|
|
755
|
+
hitCount: breakpoint.hitCount
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// Отправляем результат обратно в дочерний процесс
|
|
760
|
+
child.send({
|
|
761
|
+
type: 'debug:breakpoint_response',
|
|
762
|
+
requestId,
|
|
763
|
+
overrides: overrides || null
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
} catch (error) {
|
|
767
|
+
this.logger.error({ botId, error }, 'Ошибка обработки debug breakpoint check');
|
|
768
|
+
// В случае ошибки отправляем null чтобы продолжить выполнение
|
|
769
|
+
child.send({
|
|
770
|
+
type: 'debug:breakpoint_response',
|
|
771
|
+
requestId,
|
|
772
|
+
overrides: null
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async _handleDebugStepModeCheck(botId, child, message) {
|
|
778
|
+
const { requestId, payload } = message;
|
|
779
|
+
const { graphId, nodeId, nodeType, inputs, executedSteps, context } = payload;
|
|
780
|
+
|
|
781
|
+
try {
|
|
782
|
+
const { getGlobalDebugManager } = require('../services/DebugSessionManager');
|
|
783
|
+
const debugManager = getGlobalDebugManager();
|
|
784
|
+
|
|
785
|
+
const debugState = debugManager.get(graphId);
|
|
786
|
+
if (!debugState) {
|
|
787
|
+
// Нет debug сессии - продолжаем выполнение
|
|
788
|
+
child.send({
|
|
789
|
+
type: 'debug:breakpoint_response', // Используем тот же тип ответа
|
|
790
|
+
requestId,
|
|
791
|
+
overrides: null
|
|
792
|
+
});
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Проверяем, нужно ли остановиться в step mode
|
|
797
|
+
if (!debugState.shouldStepPause(nodeId)) {
|
|
798
|
+
// Step mode не активен или не нужно останавливаться на этой ноде
|
|
799
|
+
child.send({
|
|
800
|
+
type: 'debug:breakpoint_response',
|
|
801
|
+
requestId,
|
|
802
|
+
overrides: null
|
|
803
|
+
});
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Приостанавливаем выполнение и ждём действий от пользователя
|
|
808
|
+
const overrides = await debugState.pause({
|
|
809
|
+
nodeId,
|
|
810
|
+
nodeType,
|
|
811
|
+
inputs,
|
|
812
|
+
executedSteps,
|
|
813
|
+
context
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Отправляем результат обратно в дочерний процесс
|
|
817
|
+
child.send({
|
|
818
|
+
type: 'debug:breakpoint_response',
|
|
819
|
+
requestId,
|
|
820
|
+
overrides: overrides || null
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
} catch (error) {
|
|
824
|
+
this.logger.error({ botId, error }, 'Ошибка обработки debug step mode check');
|
|
825
|
+
// В случае ошибки отправляем null чтобы продолжить выполнение
|
|
826
|
+
child.send({
|
|
827
|
+
type: 'debug:breakpoint_response',
|
|
828
|
+
requestId,
|
|
829
|
+
overrides: null
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
module.exports = BotLifecycleService;
|