blockmine 1.5.7 → 1.5.9
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/README.md +2 -3
- package/backend/src/api/routes/bots.js +18 -11
- package/backend/src/core/BotManager.js +707 -714
- package/backend/src/core/BotProcess.js +5 -0
- package/backend/src/core/EventGraphManager.js +3 -0
- package/backend/src/core/GraphExecutionEngine.js +18 -9
- package/backend/src/core/NodeRegistry.js +14 -0
- package/backend/src/core/TaskScheduler.js +8 -3
- package/backend/src/lib/prisma.js +5 -0
- package/backend/src/real-time/socketHandler.js +9 -11
- package/backend/src/server.js +25 -20
- package/frontend/dist/assets/{index-BRG5IJlS.js → index-Cb7r5FoV.js} +1650 -1650
- package/frontend/dist/assets/index-OIucIMTn.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-CxAe5KlR.css +0 -1
|
@@ -1,715 +1,708 @@
|
|
|
1
|
-
const { fork } = require('child_process');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { getIO } = require('../real-time/socketHandler');
|
|
4
|
-
const
|
|
5
|
-
const pidusage = require('pidusage');
|
|
6
|
-
const DependencyService = require('./DependencyService');
|
|
7
|
-
const config = require('../config');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const os = require('os');
|
|
10
|
-
const { v4: uuidv4 } = require('uuid');
|
|
11
|
-
const crypto = require('crypto');
|
|
12
|
-
const { decrypt } = require('./utils/crypto');
|
|
13
|
-
const EventGraphManager = require('./EventGraphManager');
|
|
14
|
-
const nodeRegistry = require('./NodeRegistry');
|
|
15
|
-
const GraphExecutionEngine = require('./GraphExecutionEngine');
|
|
16
|
-
|
|
17
|
-
const UserService = require('./UserService');
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
prisma.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
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
|
-
this.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
let
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
nonce
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
{ name: "admin
|
|
174
|
-
{ name: "
|
|
175
|
-
{ name: "user
|
|
176
|
-
{ name: "user.
|
|
177
|
-
|
|
178
|
-
];
|
|
179
|
-
const
|
|
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
|
-
const
|
|
229
|
-
const
|
|
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
|
-
this.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
getIO().emit('bot:
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
break;
|
|
367
|
-
case '
|
|
368
|
-
result =
|
|
369
|
-
break;
|
|
370
|
-
case '
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
owner: commandConfig.owner,
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const child = this.bots.get(botId);
|
|
709
|
-
if (child) {
|
|
710
|
-
child.send({ type: 'server_command', payload: { command } });
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
|
|
1
|
+
const { fork } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getIO } = require('../real-time/socketHandler');
|
|
4
|
+
const prisma = require('../lib/prisma');
|
|
5
|
+
const pidusage = require('pidusage');
|
|
6
|
+
const DependencyService = require('./DependencyService');
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const { v4: uuidv4 } = require('uuid');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const { decrypt } = require('./utils/crypto');
|
|
13
|
+
const EventGraphManager = require('./EventGraphManager');
|
|
14
|
+
const nodeRegistry = require('./NodeRegistry');
|
|
15
|
+
const GraphExecutionEngine = require('./GraphExecutionEngine');
|
|
16
|
+
|
|
17
|
+
const UserService = require('./UserService');
|
|
18
|
+
|
|
19
|
+
const cooldowns = new Map();
|
|
20
|
+
const warningCache = new Map();
|
|
21
|
+
const WARNING_COOLDOWN = 10 * 1000;
|
|
22
|
+
|
|
23
|
+
const STATS_SERVER_URL = 'http://185.65.200.184:3000';
|
|
24
|
+
let instanceId = null;
|
|
25
|
+
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
26
|
+
const INSTANCE_ID_PATH = path.join(DATA_DIR, '.instance_id');
|
|
27
|
+
|
|
28
|
+
function getInstanceId() {
|
|
29
|
+
if (instanceId) return instanceId;
|
|
30
|
+
try {
|
|
31
|
+
if (fs.existsSync(INSTANCE_ID_PATH)) {
|
|
32
|
+
instanceId = fs.readFileSync(INSTANCE_ID_PATH, 'utf-8');
|
|
33
|
+
} else {
|
|
34
|
+
instanceId = uuidv4();
|
|
35
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
36
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
fs.writeFileSync(INSTANCE_ID_PATH, instanceId, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('[Telemetry] Ошибка при загрузке/создании Instance ID:', error);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return instanceId;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class BotManager {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.bots = new Map();
|
|
50
|
+
this.logCache = new Map();
|
|
51
|
+
this.resourceUsage = new Map();
|
|
52
|
+
this.botConfigs = new Map();
|
|
53
|
+
this.nodeRegistry = nodeRegistry;
|
|
54
|
+
this.pendingPlayerListRequests = new Map();
|
|
55
|
+
this.playerListCache = new Map();
|
|
56
|
+
this.graphEngine = new GraphExecutionEngine(this.nodeRegistry, this);
|
|
57
|
+
this.eventGraphManager = null;
|
|
58
|
+
|
|
59
|
+
getInstanceId();
|
|
60
|
+
setInterval(() => this.updateAllResourceUsage(), 5000);
|
|
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
|
+
async loadConfigForBot(botId) {
|
|
73
|
+
console.log(`[BotManager] Caching configuration for bot ID ${botId}...`);
|
|
74
|
+
try {
|
|
75
|
+
const [commands, permissions] = await Promise.all([
|
|
76
|
+
prisma.command.findMany({ where: { botId } }),
|
|
77
|
+
prisma.permission.findMany({ where: { botId } }),
|
|
78
|
+
]);
|
|
79
|
+
const config = {
|
|
80
|
+
commands: new Map(commands.map(cmd => [cmd.name, cmd])),
|
|
81
|
+
permissionsById: new Map(permissions.map(p => [p.id, p])),
|
|
82
|
+
commandAliases: new Map()
|
|
83
|
+
};
|
|
84
|
+
for (const cmd of commands) {
|
|
85
|
+
const aliases = JSON.parse(cmd.aliases || '[]');
|
|
86
|
+
for (const alias of aliases) {
|
|
87
|
+
config.commandAliases.set(alias, cmd.name);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.botConfigs.set(botId, config);
|
|
91
|
+
console.log(`[BotManager] Configuration for bot ID ${botId} cached successfully.`);
|
|
92
|
+
return config;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error(`[BotManager] Failed to cache configuration for bot ${botId}:`, error);
|
|
95
|
+
throw new Error(`Failed to load/cache bot configuration for botId ${botId}: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async _ensureDefaultEventGraphs(botId) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
invalidateConfigCache(botId) {
|
|
104
|
+
if (this.botConfigs.has(botId)) {
|
|
105
|
+
this.botConfigs.delete(botId);
|
|
106
|
+
console.log(`[BotManager] Invalidated config cache for bot ID ${botId}. It will be reloaded on next command.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
reloadBotConfigInRealTime(botId) {
|
|
111
|
+
this.invalidateConfigCache(botId);
|
|
112
|
+
const child = this.bots.get(botId);
|
|
113
|
+
if (child && !child.killed) {
|
|
114
|
+
child.send({ type: 'config:reload' });
|
|
115
|
+
console.log(`[BotManager] Sent config:reload to bot process ${botId}`);
|
|
116
|
+
getIO().emit('bot:config_reloaded', { botId });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
triggerHeartbeat() {
|
|
121
|
+
if (!config.telemetry?.enabled) return;
|
|
122
|
+
if (this.heartbeatDebounceTimer) {
|
|
123
|
+
clearTimeout(this.heartbeatDebounceTimer);
|
|
124
|
+
}
|
|
125
|
+
this.heartbeatDebounceTimer = setTimeout(() => {
|
|
126
|
+
this.sendHeartbeat();
|
|
127
|
+
}, 3000);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async sendHeartbeat() {
|
|
131
|
+
if (!config.telemetry?.enabled || !instanceId) return;
|
|
132
|
+
try {
|
|
133
|
+
const runningBots = Array.from(this.bots.values())
|
|
134
|
+
.filter(p => p.botConfig)
|
|
135
|
+
.map(p => ({
|
|
136
|
+
username: p.botConfig.username,
|
|
137
|
+
serverHost: p.botConfig.server.host,
|
|
138
|
+
serverPort: p.botConfig.server.port
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
if (runningBots.length === 0) return;
|
|
142
|
+
|
|
143
|
+
const challengeRes = await fetch(`${STATS_SERVER_URL}/api/challenge?uuid=${instanceId}`);
|
|
144
|
+
if (!challengeRes.ok) throw new Error(`Challenge server error: ${challengeRes.statusText}`);
|
|
145
|
+
|
|
146
|
+
const { challenge, difficulty, prefix } = await challengeRes.json();
|
|
147
|
+
let nonce = 0;
|
|
148
|
+
let hash = '';
|
|
149
|
+
do {
|
|
150
|
+
nonce++;
|
|
151
|
+
hash = crypto.createHash('sha256').update(prefix + challenge + nonce).digest('hex');
|
|
152
|
+
} while (!hash.startsWith('0'.repeat(difficulty)));
|
|
153
|
+
|
|
154
|
+
const packageJson = require('../../../package.json');
|
|
155
|
+
await fetch(`${STATS_SERVER_URL}/api/heartbeat`, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
body: JSON.stringify({
|
|
159
|
+
instanceUuid: instanceId,
|
|
160
|
+
appVersion: packageJson.version,
|
|
161
|
+
bots: runningBots,
|
|
162
|
+
nonce: nonce
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error(`[Telemetry] Не удалось отправить heartbeat: ${error.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async _syncSystemPermissions(botId) {
|
|
171
|
+
const systemPermissions = [
|
|
172
|
+
{ name: "admin.*", description: "Все права администратора" },
|
|
173
|
+
{ name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
|
|
174
|
+
{ name: "user.*", description: "Все права обычного пользователя" },
|
|
175
|
+
{ name: "user.say", description: "Доступ к простым командам" },
|
|
176
|
+
{ name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
|
|
177
|
+
];
|
|
178
|
+
const systemGroups = ["User", "Admin"];
|
|
179
|
+
const systemGroupPermissions = {
|
|
180
|
+
"User": ["user.say"],
|
|
181
|
+
"Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
|
|
182
|
+
};
|
|
183
|
+
console.log(`[Permission Sync] Синхронизация системных прав для бота ID ${botId}...`);
|
|
184
|
+
for (const perm of systemPermissions) {
|
|
185
|
+
await prisma.permission.upsert({
|
|
186
|
+
where: { botId_name: { botId, name: perm.name } },
|
|
187
|
+
update: { description: perm.description },
|
|
188
|
+
create: { ...perm, botId, owner: 'system' }
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
for (const groupName of systemGroups) {
|
|
192
|
+
await prisma.group.upsert({
|
|
193
|
+
where: { botId_name: { botId, name: groupName } },
|
|
194
|
+
update: {},
|
|
195
|
+
create: { name: groupName, botId, owner: 'system' }
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
for (const [groupName, permNames] of Object.entries(systemGroupPermissions)) {
|
|
199
|
+
const group = await prisma.group.findUnique({ where: { botId_name: { botId, name: groupName } } });
|
|
200
|
+
if (group) {
|
|
201
|
+
for (const permName of permNames) {
|
|
202
|
+
const permission = await prisma.permission.findUnique({ where: { botId_name: { botId, name: permName } } });
|
|
203
|
+
if (permission) {
|
|
204
|
+
await prisma.groupPermission.upsert({
|
|
205
|
+
where: { groupId_permissionId: { groupId: group.id, permissionId: permission.id } },
|
|
206
|
+
update: {},
|
|
207
|
+
create: { groupId: group.id, permissionId: permission.id }
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.log(`[Permission Sync] Синхронизация для бота ID ${botId} завершена.`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async updateAllResourceUsage() {
|
|
217
|
+
if (this.bots.size === 0) {
|
|
218
|
+
if (this.resourceUsage.size > 0) {
|
|
219
|
+
this.resourceUsage.clear();
|
|
220
|
+
getIO().emit('bots:usage', []);
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const pids = Array.from(this.bots.values()).map(child => child.pid).filter(Boolean);
|
|
225
|
+
if (pids.length === 0) return;
|
|
226
|
+
try {
|
|
227
|
+
const stats = await pidusage(pids);
|
|
228
|
+
const usageData = [];
|
|
229
|
+
for (const pid in stats) {
|
|
230
|
+
if (!stats[pid]) continue;
|
|
231
|
+
const botId = this.getBotIdByPid(parseInt(pid, 10));
|
|
232
|
+
if (botId) {
|
|
233
|
+
const usage = {
|
|
234
|
+
botId: botId,
|
|
235
|
+
cpu: parseFloat(stats[pid].cpu.toFixed(1)),
|
|
236
|
+
memory: parseFloat((stats[pid].memory / 1024 / 1024).toFixed(1)),
|
|
237
|
+
};
|
|
238
|
+
this.resourceUsage.set(botId, usage);
|
|
239
|
+
usageData.push(usage);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
getIO().emit('bots:usage', usageData);
|
|
243
|
+
} catch (error) {}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
getBotIdByPid(pid) {
|
|
247
|
+
for (const [botId, child] of this.bots.entries()) {
|
|
248
|
+
if (child.pid === pid) {
|
|
249
|
+
return botId;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getFullState() {
|
|
256
|
+
return {
|
|
257
|
+
statuses: Object.fromEntries(Array.from(this.bots.entries()).map(([id, child]) => [id, child.killed ? 'stopped' : 'running'])),
|
|
258
|
+
logs: Object.fromEntries(this.logCache)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
emitStatusUpdate(botId, status, message = null) {
|
|
263
|
+
// console.log(`[BotManager] Отправка статуса для бота ${botId}: status=${status}, message=${message || 'null'}`);
|
|
264
|
+
if (message) this.appendLog(botId, `[SYSTEM] ${message}`);
|
|
265
|
+
getIO().emit('bot:status', { botId, status, message });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
appendLog(botId, log) {
|
|
269
|
+
const currentLogs = this.logCache.get(botId) || [];
|
|
270
|
+
const newLogs = [...currentLogs.slice(-499), log];
|
|
271
|
+
this.logCache.set(botId, newLogs);
|
|
272
|
+
getIO().emit('bot:log', { botId, log });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async startBot(botConfig) {
|
|
276
|
+
if (this.bots.has(botConfig.id) && !this.bots.get(botConfig.id).killed) {
|
|
277
|
+
this.appendLog(botConfig.id, `[SYSTEM-ERROR] Попытка повторного запуска. Запуск отменен.`);
|
|
278
|
+
return { success: false, message: 'Бот уже запущен или запускается.' };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
await this._syncSystemPermissions(botConfig.id);
|
|
282
|
+
await this.loadConfigForBot(botConfig.id);
|
|
283
|
+
this.logCache.set(botConfig.id, []);
|
|
284
|
+
this.emitStatusUpdate(botConfig.id, 'starting', '');
|
|
285
|
+
|
|
286
|
+
const allPluginsForBot = await prisma.installedPlugin.findMany({ where: { botId: botConfig.id, isEnabled: true } });
|
|
287
|
+
const { sortedPlugins, hasCriticalIssues, pluginInfo } = DependencyService.resolveDependencies(allPluginsForBot, allPluginsForBot);
|
|
288
|
+
|
|
289
|
+
if (hasCriticalIssues) {
|
|
290
|
+
this.appendLog(botConfig.id, '[DependencyManager] Обнаружены критические проблемы с зависимостями, запуск отменен.');
|
|
291
|
+
|
|
292
|
+
const criticalIssueTypes = new Set(['missing_dependency', 'version_mismatch', 'circular_dependency']);
|
|
293
|
+
|
|
294
|
+
for (const pluginId in pluginInfo) {
|
|
295
|
+
const info = pluginInfo[pluginId];
|
|
296
|
+
if (info.issues.length === 0) continue;
|
|
297
|
+
|
|
298
|
+
const criticalIssues = info.issues.filter(issue => criticalIssueTypes.has(issue.type));
|
|
299
|
+
|
|
300
|
+
if (criticalIssues.length > 0) {
|
|
301
|
+
this.appendLog(botConfig.id, `* Плагин "${info.name}":`);
|
|
302
|
+
for (const issue of criticalIssues) {
|
|
303
|
+
this.appendLog(botConfig.id, ` - ${issue.message}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.emitStatusUpdate(botConfig.id, 'stopped', 'Ошибка зависимостей плагинов.');
|
|
309
|
+
return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const decryptedConfig = { ...botConfig };
|
|
313
|
+
if (decryptedConfig.password) decryptedConfig.password = decrypt(decryptedConfig.password);
|
|
314
|
+
if (decryptedConfig.proxyPassword) decryptedConfig.proxyPassword = decrypt(decryptedConfig.proxyPassword);
|
|
315
|
+
|
|
316
|
+
const fullBotConfig = { ...decryptedConfig, plugins: sortedPlugins };
|
|
317
|
+
const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
|
|
318
|
+
const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
|
|
319
|
+
|
|
320
|
+
child.botConfig = botConfig;
|
|
321
|
+
|
|
322
|
+
child.on('message', async (message) => {
|
|
323
|
+
const botId = botConfig.id;
|
|
324
|
+
try {
|
|
325
|
+
switch (message.type) {
|
|
326
|
+
case 'event':
|
|
327
|
+
if (this.eventGraphManager) {
|
|
328
|
+
this.eventGraphManager.handleEvent(botId, message.eventType, message.args);
|
|
329
|
+
}
|
|
330
|
+
break;
|
|
331
|
+
case 'log':
|
|
332
|
+
this.appendLog(botId, message.content);
|
|
333
|
+
break;
|
|
334
|
+
case 'status':
|
|
335
|
+
this.emitStatusUpdate(botId, message.status);
|
|
336
|
+
break;
|
|
337
|
+
case 'validate_and_run_command':
|
|
338
|
+
await this.handleCommandValidation(botConfig, message);
|
|
339
|
+
break;
|
|
340
|
+
case 'register_command':
|
|
341
|
+
await this.handleCommandRegistration(botId, message.commandConfig);
|
|
342
|
+
break;
|
|
343
|
+
case 'register_group':
|
|
344
|
+
await this.handleGroupRegistration(botId, message.groupConfig);
|
|
345
|
+
break;
|
|
346
|
+
case 'request_user_action':
|
|
347
|
+
const { requestId, payload } = message;
|
|
348
|
+
const { targetUsername, action, data } = payload;
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const user = await UserService.getUser(targetUsername, botConfig.id);
|
|
352
|
+
if (!user) throw new Error(`Пользователь ${targetUsername} не найден.`);
|
|
353
|
+
|
|
354
|
+
let result;
|
|
355
|
+
|
|
356
|
+
switch (action) {
|
|
357
|
+
case 'addGroup':
|
|
358
|
+
result = await user.addGroup(data.group);
|
|
359
|
+
break;
|
|
360
|
+
case 'removeGroup':
|
|
361
|
+
result = await user.removeGroup(data.group);
|
|
362
|
+
break;
|
|
363
|
+
case 'addPermission':
|
|
364
|
+
break;
|
|
365
|
+
case 'removePermission':
|
|
366
|
+
break;
|
|
367
|
+
case 'getGroups':
|
|
368
|
+
result = user.groups;
|
|
369
|
+
break;
|
|
370
|
+
case 'getPermissions':
|
|
371
|
+
result = Array.from(user.permissionsSet);
|
|
372
|
+
break;
|
|
373
|
+
case 'isBlacklisted':
|
|
374
|
+
result = user.isBlacklisted;
|
|
375
|
+
break;
|
|
376
|
+
case 'setBlacklisted':
|
|
377
|
+
result = await user.setBlacklist(data.value);
|
|
378
|
+
break;
|
|
379
|
+
default:
|
|
380
|
+
throw new Error(`Неизвестное действие: ${action}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
child.send({ type: 'user_action_response', requestId, payload: result });
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error(`[BotManager] Ошибка выполнения действия '${action}' для пользователя '${targetUsername}':`, error);
|
|
386
|
+
child.send({ type: 'user_action_response', requestId, error: error.message });
|
|
387
|
+
}
|
|
388
|
+
break;
|
|
389
|
+
case 'playerListUpdate':
|
|
390
|
+
break;
|
|
391
|
+
case 'get_player_list_response': {
|
|
392
|
+
const { requestId, payload } = message;
|
|
393
|
+
const request = this.pendingPlayerListRequests.get(requestId);
|
|
394
|
+
if (request) {
|
|
395
|
+
clearTimeout(request.timeout);
|
|
396
|
+
request.resolve(payload.players);
|
|
397
|
+
this.pendingPlayerListRequests.delete(requestId);
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
this.appendLog(botId, `[SYSTEM-ERROR] Критическая ошибка в обработчике сообщений от бота: ${error.stack}`);
|
|
404
|
+
console.error(`[BotManager] Критическая ошибка в обработчике сообщений от бота ${botId}:`, error);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
child.on('error', (err) => this.appendLog(botConfig.id, `[PROCESS FATAL] ${err.stack}`));
|
|
409
|
+
child.stderr.on('data', (data) => this.appendLog(botConfig.id, `[STDERR] ${data.toString()}`));
|
|
410
|
+
|
|
411
|
+
child.on('exit', (code, signal) => {
|
|
412
|
+
const botId = botConfig.id;
|
|
413
|
+
this.bots.delete(botId);
|
|
414
|
+
this.resourceUsage.delete(botId);
|
|
415
|
+
this.botConfigs.delete(botId);
|
|
416
|
+
this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
|
|
417
|
+
this.updateAllResourceUsage();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
this.bots.set(botConfig.id, child);
|
|
421
|
+
child.send({ type: 'start', config: fullBotConfig });
|
|
422
|
+
|
|
423
|
+
await this.eventGraphManager.loadGraphsForBot(botConfig.id);
|
|
424
|
+
|
|
425
|
+
this.triggerHeartbeat();
|
|
426
|
+
getIO().emit('bot:status', { botId: botConfig.id, status: 'starting' });
|
|
427
|
+
return child;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async handleCommandValidation(botConfig, message) {
|
|
431
|
+
const { commandName, username, args, typeChat } = message;
|
|
432
|
+
const botId = botConfig.id;
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
let botConfigCache = this.botConfigs.get(botId);
|
|
436
|
+
if (!botConfigCache) {
|
|
437
|
+
console.log(`[BotManager] No cache for ${botId}, loading...`);
|
|
438
|
+
botConfigCache = await this.loadConfigForBot(botId);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const user = await UserService.getUser(username, botId, botConfig);
|
|
442
|
+
|
|
443
|
+
const child = this.bots.get(botId);
|
|
444
|
+
if (!child) return;
|
|
445
|
+
|
|
446
|
+
if (user.isBlacklisted) {
|
|
447
|
+
this.sendMessageToBot(botId, 'Вы находитесь в черном списке и не можете использовать команды.', 'private', username);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const mainCommandName = botConfigCache.commandAliases.get(commandName) || commandName;
|
|
452
|
+
const dbCommand = botConfigCache.commands.get(mainCommandName);
|
|
453
|
+
|
|
454
|
+
if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
|
|
459
|
+
if (!allowedTypes.includes(typeChat) && !user.isOwner) {
|
|
460
|
+
if (typeChat === 'global') return;
|
|
461
|
+
this.sendMessageToBot(botId, 'Эту команду нельзя использовать в этом чате.', 'private', username);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const permission = dbCommand.permissionId ? botConfigCache.permissionsById.get(dbCommand.permissionId) : null;
|
|
466
|
+
if (permission && !user.hasPermission(permission.name)) {
|
|
467
|
+
this.sendMessageToBot(botId, 'У вас нет прав на использование этой команды.', 'private', username);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const domain = (permission?.name || '').split('.')[0] || 'user';
|
|
472
|
+
const bypassCooldownPermission = `${domain}.cooldown.bypass`;
|
|
473
|
+
|
|
474
|
+
if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
|
|
475
|
+
const cooldownKey = `${botId}:${dbCommand.name}:${user.id}`;
|
|
476
|
+
const now = Date.now();
|
|
477
|
+
const lastUsed = cooldowns.get(cooldownKey);
|
|
478
|
+
|
|
479
|
+
if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
|
|
480
|
+
const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
|
|
481
|
+
this.sendMessageToBot(botId, `Подождите еще ${timeLeft} сек. перед использованием команды.`, 'private', username);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
cooldowns.set(cooldownKey, now);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (dbCommand.isVisual) {
|
|
488
|
+
const graph = dbCommand.graphJson;
|
|
489
|
+
if (graph) {
|
|
490
|
+
const players = await this.getPlayerList(botId);
|
|
491
|
+
console.log('[BotManager] Received player list for graph:', players);
|
|
492
|
+
|
|
493
|
+
const botAPI = {
|
|
494
|
+
sendMessage: (type, message, recipient) => {
|
|
495
|
+
this.sendMessageToBot(botId, message, type, recipient);
|
|
496
|
+
},
|
|
497
|
+
executeCommand: (command) => {
|
|
498
|
+
this.sendServerCommandToBot(botId, command);
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const graphContext = {
|
|
503
|
+
bot: botAPI,
|
|
504
|
+
botId: botId,
|
|
505
|
+
user,
|
|
506
|
+
args,
|
|
507
|
+
typeChat,
|
|
508
|
+
players,
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
try {
|
|
512
|
+
const resultContext = await this.graphEngine.execute(graph, graphContext, 'command');
|
|
513
|
+
} catch (e) {
|
|
514
|
+
console.error(`[BotManager] Ошибка выполнения визуальной команды '${commandName}':`, e);
|
|
515
|
+
this.sendMessageToBot(botId, 'Произошла внутренняя ошибка при выполнении команды.', 'private', username);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(`[BotManager] Command validation error for botId: ${botId}`, {
|
|
524
|
+
command: commandName, user: username, error: error.message, stack: error.stack
|
|
525
|
+
});
|
|
526
|
+
this.sendMessageToBot(botId, `Произошла внутренняя ошибка при выполнении команды.`, 'private', username);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async handleCommandRegistration(botId, commandConfig) {
|
|
531
|
+
try {
|
|
532
|
+
let permissionId = null;
|
|
533
|
+
if (commandConfig.permissions) {
|
|
534
|
+
let permission = await prisma.permission.findUnique({
|
|
535
|
+
where: { botId_name: { botId, name: commandConfig.permissions } }
|
|
536
|
+
});
|
|
537
|
+
if (!permission) {
|
|
538
|
+
permission = await prisma.permission.create({
|
|
539
|
+
data: {
|
|
540
|
+
botId,
|
|
541
|
+
name: commandConfig.permissions,
|
|
542
|
+
description: `Автоматически создано для команды ${commandConfig.name}`,
|
|
543
|
+
owner: commandConfig.owner,
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
permissionId = permission.id;
|
|
548
|
+
}
|
|
549
|
+
const createData = {
|
|
550
|
+
botId,
|
|
551
|
+
name: commandConfig.name,
|
|
552
|
+
description: commandConfig.description,
|
|
553
|
+
aliases: JSON.stringify(commandConfig.aliases || []),
|
|
554
|
+
owner: commandConfig.owner,
|
|
555
|
+
permissionId: permissionId,
|
|
556
|
+
allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
|
|
557
|
+
cooldown: commandConfig.cooldown || 0,
|
|
558
|
+
};
|
|
559
|
+
const updateData = {
|
|
560
|
+
description: commandConfig.description,
|
|
561
|
+
owner: commandConfig.owner,
|
|
562
|
+
};
|
|
563
|
+
await prisma.command.upsert({
|
|
564
|
+
where: { botId_name: { botId, name: commandConfig.name } },
|
|
565
|
+
update: updateData,
|
|
566
|
+
create: createData,
|
|
567
|
+
});
|
|
568
|
+
this.invalidateConfigCache(botId);
|
|
569
|
+
} catch (error) {
|
|
570
|
+
console.error(`[BotManager] Ошибка при регистрации команды '${commandConfig.name}':`, error);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
async handleGroupRegistration(botId, groupConfig) {
|
|
575
|
+
try {
|
|
576
|
+
await prisma.group.upsert({
|
|
577
|
+
where: { botId_name: { botId, name: groupConfig.name } },
|
|
578
|
+
update: {
|
|
579
|
+
owner: groupConfig.owner,
|
|
580
|
+
},
|
|
581
|
+
create: {
|
|
582
|
+
botId,
|
|
583
|
+
name: groupConfig.name,
|
|
584
|
+
owner: groupConfig.owner,
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
this.invalidateConfigCache(botId);
|
|
588
|
+
} catch (error) {
|
|
589
|
+
console.error(`[BotManager] Ошибка при регистрации группы '${groupConfig.name}':`, error);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
stopBot(botId) {
|
|
594
|
+
const child = this.bots.get(botId);
|
|
595
|
+
if (child) {
|
|
596
|
+
this.eventGraphManager.unloadGraphsForBot(botId);
|
|
597
|
+
child.send({ type: 'stop' });
|
|
598
|
+
this.botConfigs.delete(botId);
|
|
599
|
+
return { success: true };
|
|
600
|
+
}
|
|
601
|
+
return { success: false, message: 'Бот не найден или уже остановлен' };
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
sendMessageToBot(botId, message, chatType = 'command', username = null) {
|
|
605
|
+
const child = this.bots.get(botId);
|
|
606
|
+
if (child) {
|
|
607
|
+
child.send({ type: 'chat', payload: { message, chatType, username } });
|
|
608
|
+
return { success: true };
|
|
609
|
+
}
|
|
610
|
+
return { success: false, message: 'Бот не найден или не запущен' };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
invalidateUserCache(botId, username) {
|
|
614
|
+
UserService.clearCache(username, botId);
|
|
615
|
+
const child = this.bots.get(botId);
|
|
616
|
+
if (child) {
|
|
617
|
+
child.send({ type: 'invalidate_user_cache', username });
|
|
618
|
+
}
|
|
619
|
+
return { success: true };
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async getPlayerList(botId) {
|
|
623
|
+
const PLAYER_LIST_CACHE_TTL = 2000;
|
|
624
|
+
|
|
625
|
+
const child = this.bots.get(botId);
|
|
626
|
+
if (!child || child.killed) {
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const cachedEntry = this.playerListCache.get(botId);
|
|
631
|
+
if (cachedEntry && (Date.now() - cachedEntry.timestamp < PLAYER_LIST_CACHE_TTL)) {
|
|
632
|
+
return cachedEntry.promise;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const newPromise = new Promise((resolve) => {
|
|
636
|
+
const requestId = uuidv4();
|
|
637
|
+
const timeout = setTimeout(() => {
|
|
638
|
+
this.pendingPlayerListRequests.delete(requestId);
|
|
639
|
+
if (this.playerListCache.get(botId)?.promise === newPromise) {
|
|
640
|
+
this.playerListCache.delete(botId);
|
|
641
|
+
}
|
|
642
|
+
resolve([]);
|
|
643
|
+
}, 5000);
|
|
644
|
+
|
|
645
|
+
this.pendingPlayerListRequests.set(requestId, {
|
|
646
|
+
resolve: (playerList) => {
|
|
647
|
+
clearTimeout(timeout);
|
|
648
|
+
this.pendingPlayerListRequests.delete(requestId);
|
|
649
|
+
this.playerListCache.set(botId, {
|
|
650
|
+
promise: Promise.resolve(playerList),
|
|
651
|
+
timestamp: Date.now()
|
|
652
|
+
});
|
|
653
|
+
resolve(playerList);
|
|
654
|
+
},
|
|
655
|
+
reject: (error) => {
|
|
656
|
+
clearTimeout(timeout);
|
|
657
|
+
this.pendingPlayerListRequests.delete(requestId);
|
|
658
|
+
if (this.playerListCache.get(botId)?.promise === newPromise) {
|
|
659
|
+
this.playerListCache.delete(botId);
|
|
660
|
+
}
|
|
661
|
+
resolve([]);
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
child.send({ type: 'system:get_player_list', requestId });
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
this.playerListCache.set(botId, {
|
|
669
|
+
promise: newPromise,
|
|
670
|
+
timestamp: Date.now()
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
return newPromise;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
setEventGraphManager(manager) {
|
|
677
|
+
this.eventGraphManager = manager;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
lookAt(botId, position) {
|
|
681
|
+
const botProcess = this.bots.get(botId);
|
|
682
|
+
if (botProcess && !botProcess.killed) {
|
|
683
|
+
botProcess.send({ type: 'action', name: 'lookAt', payload: { position } });
|
|
684
|
+
} else {
|
|
685
|
+
console.error(`[BotManager] Не удалось найти запущенный процесс для бота ${botId}, чтобы выполнить lookAt.`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
async reloadPlugins(botId) {
|
|
690
|
+
const child = this.bots.get(botId);
|
|
691
|
+
if (child && !child.killed) {
|
|
692
|
+
child.send({ type: 'plugins:reload' });
|
|
693
|
+
console.log(`[BotManager] Sent plugins:reload to bot process ${botId}`);
|
|
694
|
+
getIO().emit('bot:plugins_reloaded', { botId });
|
|
695
|
+
return { success: true, message: 'Команда на перезагрузку плагинов отправлена.' };
|
|
696
|
+
}
|
|
697
|
+
return { success: false, message: 'Бот не запущен.' };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
sendServerCommandToBot(botId, command) {
|
|
701
|
+
const child = this.bots.get(botId);
|
|
702
|
+
if (child) {
|
|
703
|
+
child.send({ type: 'server_command', payload: { command } });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
715
708
|
module.exports = BotManager;
|