@baipiaodajun/mcbots 1.0.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 (3) hide show
  1. package/README.md +38 -0
  2. package/package.json +25 -0
  3. package/server.js +864 -0
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # 🛡️ Minecraft 保活机器人及监控面板 - @baipiaodajun/mcbots
2
+
3
+ ## 用法
4
+ ### nodejs
5
+ 新建一个 index.js 文件,内容如下:
6
+ 其中内容替换成你自己的MC服务器地址和端口,其他可以根据情况改变,可以添加多台服务器。
7
+ ```
8
+ process.env.SERVERS_JSON='[{"host":"mc-yy.io","port":25565,"minBots":1,"maxBots":3,"version":"1.20.1"},{"host":"mc-xx.io","port":25565,"minBots":1,"maxBots":3,"version":"1.20.1"}]';
9
+
10
+ const mcbot = require('@baipiaodajun/mcbots');
11
+
12
+ ```
13
+
14
+ 再新建一个package.json,内容如下:
15
+ ```
16
+ {
17
+ "name": "mc",
18
+ "version": "1.0.0",
19
+ "description": "mcbots",
20
+ "main": "index.js",
21
+ "scripts": {
22
+ "start": "node index.js"
23
+ },
24
+ "dependencies": {
25
+ "@baipiaodajun/mcbots": "^1.0.0"
26
+ }
27
+ }
28
+ ```
29
+ ### docker
30
+ ```
31
+ docker run -d \
32
+ --name mcbot \
33
+ -e SERVERS_JSON='[{"host":"mc-yy.io","port":25565,"minBots":1,"maxBots":3,"version":"1.20.1"},{"host":"mc-xx.io","port":25565,"minBots":1,"maxBots":3,"version":"1.20.1"}]' \
34
+ -p 3000:3000 \
35
+ mingli2038/macbot:latest
36
+ ```
37
+ ## 来源
38
+ 这个东西不是我的原创,思路来自Tweek白嫖群的[这种事可以花点钱]用户给的镜像[ghcr.io/oprmg/mcbot:latest](https://ghcr.io/oprmg/mcbot:latest),我通过AI重构其实现并加入自己的想法从而重新发布出来。
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@baipiaodajun/mcbots",
3
+ "version": "1.0.0",
4
+ "description": "Minecraft bot and status dashboard for multi-server management",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "start": "node server.js"
8
+ },
9
+ "keywords": [
10
+ "minecraft",
11
+ "bot",
12
+ "mineflayer"
13
+ ],
14
+ "author": "mingli2038",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "mineflayer": "^4.14.0",
18
+ "node-fetch": "^2.7.0",
19
+ "express": "^4.18.2"
20
+ },
21
+ "files": [
22
+ "server.js",
23
+ "README.md"
24
+ ]
25
+ }
package/server.js ADDED
@@ -0,0 +1,864 @@
1
+ const mineflayer = require('mineflayer');
2
+ const { exec } = require('child_process');
3
+ const express = require('express');
4
+
5
+ // Minecraft服务器配置
6
+ const SERVERS = [
7
+ {
8
+ host: "127.0.0.1",
9
+ port: 25565,
10
+ minBots: 1,
11
+ maxBots: 3,
12
+ version: "1.20.1"
13
+ }
14
+ ];
15
+
16
+ // 从环境变量读取服务器配置
17
+ if (process && process.env && process.env.SERVERS_JSON) {
18
+ try {
19
+ const envConfig = JSON.parse(process.env.SERVERS_JSON);
20
+ SERVERS.length = 0;
21
+ SERVERS.push(...envConfig);
22
+ console.log('从环境变量加载服务器配置,数量:', SERVERS.length, '项');
23
+ } catch (error) {
24
+ console.error('❌ [错误] 无法解析环境变量 SERVERS_JSON');
25
+ console.error('原因:', error.message);
26
+ console.error('原始内容:\n', process.env.SERVERS_JSON);
27
+ console.error('请检查 JSON 格式是否正确,例如引号、逗号是否缺失');
28
+ console.error('容器即将退出...');
29
+ process.exit(1);
30
+ }
31
+ }
32
+
33
+ // 配置常量
34
+ const BOT_CONFIG = {
35
+ reconnectDelay: 2000,
36
+ maxReconnectAttempts: 3,
37
+ healthCheckInterval: 60000,
38
+ viewDistance: 4,
39
+ chat: 'enabled'
40
+ };
41
+
42
+ const SERVER_CONFIG = {
43
+ statusCheckInterval: 30000,
44
+ maxFailedAttempts: 3,
45
+ resetTimeout: 300000
46
+ };
47
+
48
+ // 全局状态存储
49
+ const globalServerStatus = {
50
+ servers: new Map()
51
+ };
52
+ function generateUsername() {
53
+ const adjectives = [
54
+ 'Clever', 'Swift', 'Brave', 'Sneaky', 'Happy', 'Crazy', 'Silky',
55
+ 'Fluffy', 'Shiny', 'Quick', 'Mighty', 'Tiny', 'Wise', 'Lazy'
56
+ ];
57
+
58
+ const animals = [
59
+ 'Fox', 'Wolf', 'Bear', 'Panda', 'Tiger', 'Eagle', 'Shark',
60
+ 'Mole', 'Badger', 'Otter', 'Raccoon', 'Frog', 'Hedgehog'
61
+ ];
62
+
63
+ const adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
64
+ const animal = animals[Math.floor(Math.random() * animals.length)];
65
+ const number = Math.random() > 0.5 ? Math.floor(Math.random() * 99) : ''; // 50% 加数字
66
+
67
+ return `${adjective}${animal}${number}`;
68
+ }
69
+ // Minecraft机器人管理器
70
+ class MinecraftBotManager {
71
+ constructor(host, port, minBots, maxBots, version) {
72
+ this.host = host;
73
+ this.port = port;
74
+ this.minBots = minBots;
75
+ this.maxBots = maxBots;
76
+ this.version = version || "1.20.1";
77
+ this.currentBots = 0;
78
+ this.activeBots = new Map();
79
+ this.failedAttempts = 0;
80
+ this.lastUpdate = Date.now();
81
+ this.status = 'initializing';
82
+ this.monitoringInterval = null;
83
+
84
+ // 机器人名称池
85
+ this.botNames = [
86
+ generateUsername(),
87
+ generateUsername(),
88
+ generateUsername()
89
+ ];
90
+
91
+ // 注册到全局状态
92
+ globalServerStatus.servers.set(`${host}:${port}`, {
93
+ host: host,
94
+ port: port,
95
+ minBots: minBots,
96
+ maxBots: maxBots,
97
+ currentBots: 0,
98
+ activeBots: [],
99
+ lastUpdate: Date.now(),
100
+ status: 'initializing'
101
+ });
102
+ }
103
+
104
+ // 生成随机机器人名称
105
+ generateBotName() {
106
+ const baseName = this.botNames[Math.floor(Math.random() * this.botNames.length)];
107
+ const randomNum = Math.floor(Math.random() * 1000);
108
+ return `${baseName}${randomNum}`;
109
+ }
110
+
111
+ // 创建Minecraft机器人
112
+ async createBot() {
113
+ if (this.currentBots >= this.maxBots) {
114
+ console.log(`[${this.host}:${this.port}] 已达到最大机器人限制: ${this.maxBots}`);
115
+ return null;
116
+ }
117
+
118
+ const botName = this.generateBotName();
119
+
120
+ try {
121
+ console.log(`[${this.host}:${this.port}] 创建机器人: ${botName}`);
122
+
123
+ const bot = mineflayer.createBot({
124
+ host: this.host,
125
+ port: this.port,
126
+ username: botName,
127
+ version: this.version,
128
+ viewDistance: BOT_CONFIG.viewDistance,
129
+ auth: 'offline'
130
+ });
131
+
132
+ // 设置机器人事件处理
133
+ this.setupBotEvents(bot, botName);
134
+
135
+ this.activeBots.set(botName, bot);
136
+ this.currentBots++;
137
+ this.failedAttempts = 0;
138
+ this.updateStatus();
139
+
140
+ return bot;
141
+
142
+ } catch (error) {
143
+ console.log(`[${this.host}:${this.port}] 创建机器人 ${botName} 失败:`, error.message);
144
+ this.handleBotFailure();
145
+ return null;
146
+ }
147
+ }
148
+
149
+ // 设置机器人事件
150
+ setupBotEvents(bot, botName) {
151
+ bot.on('login', () => {
152
+ console.log(`[${this.host}:${this.port}] 机器人 ${botName} 登录成功`);
153
+ this.updateStatus();
154
+ });
155
+
156
+ bot.on('spawn', () => {
157
+ console.log(`[${this.host}:${this.port}] 机器人 ${botName} 生成在世界中`);
158
+
159
+ // 机器人基础行为
160
+ this.setupBotBehavior(bot, botName);
161
+ });
162
+
163
+ bot.on('message', (message) => {
164
+ const text = message.toString();
165
+ console.log(`[${this.host}:${this.port}] ${botName} 收到消息: ${text}`);
166
+ });
167
+
168
+ bot.on('error', (error) => {
169
+ console.log(`[${this.host}:${this.port}] 机器人 ${botName} 错误:`, error.message);
170
+ this.handleBotDisconnect(botName);
171
+ });
172
+
173
+ bot.on('end', (reason) => {
174
+ console.log(`[${this.host}:${this.port}] 机器人 ${botName} 断开连接:`, reason);
175
+ this.handleBotDisconnect(botName);
176
+ });
177
+
178
+ bot.on('kicked', (reason) => {
179
+ console.log(`[${this.host}:${this.port}] 机器人 ${botName} 被踢出:`, reason);
180
+ this.handleBotDisconnect(botName);
181
+ });
182
+ }
183
+
184
+ // 设置机器人行为
185
+ setupBotBehavior(bot, botName) {
186
+ // 随机移动
187
+ setInterval(() => {
188
+ if (bot.entity && Math.random() < 0.3) {
189
+ const yaw = Math.random() * Math.PI * 2;
190
+ const pitch = Math.random() * Math.PI - Math.PI / 2;
191
+ bot.look(yaw, pitch, false);
192
+
193
+ if (Math.random() < 0.2) {
194
+ bot.setControlState('forward', true);
195
+ setTimeout(() => {
196
+ bot.setControlState('forward', false);
197
+ }, 1000);
198
+ }
199
+ }
200
+ }, 5000);
201
+
202
+ // 随机聊天(如果启用)
203
+ if (BOT_CONFIG.chat === 'enabled' && Math.random() < 0.1) {
204
+ setInterval(() => {
205
+ const messages = ['Hello!', 'Nice server!', 'What\'s up?', 'Good game!'];
206
+ const randomMessage = messages[Math.floor(Math.random() * messages.length)];
207
+ bot.chat(randomMessage);
208
+ }, 30000 + Math.random() * 60000);
209
+ }
210
+ }
211
+
212
+ // 处理机器人断开连接
213
+ handleBotDisconnect(botName) {
214
+ if (this.activeBots.has(botName)) {
215
+ this.activeBots.delete(botName);
216
+ this.currentBots = Math.max(0, this.currentBots - 1);
217
+ this.updateStatus();
218
+
219
+ // 自动重连
220
+ setTimeout(() => {
221
+ this.maintainBots();
222
+ }, BOT_CONFIG.reconnectDelay);
223
+ }
224
+ }
225
+
226
+ // 处理机器人失败
227
+ handleBotFailure() {
228
+ this.failedAttempts++;
229
+ this.updateStatus();
230
+
231
+ if (this.failedAttempts >= SERVER_CONFIG.maxFailedAttempts) {
232
+ console.log(`[${this.host}:${this.port}] 失败次数过多,暂停连接尝试`);
233
+ setTimeout(() => {
234
+ this.failedAttempts = 0;
235
+ this.maintainBots();
236
+ }, SERVER_CONFIG.resetTimeout);
237
+ return;
238
+ }
239
+
240
+ setTimeout(() => {
241
+ this.maintainBots();
242
+ }, BOT_CONFIG.reconnectDelay);
243
+ }
244
+
245
+ // 维护机器人数目
246
+ maintainBots() {
247
+ const neededBots = this.minBots - this.currentBots;
248
+
249
+ if (neededBots > 0 && this.failedAttempts < SERVER_CONFIG.maxFailedAttempts) {
250
+ console.log(`[${this.host}:${this.port}] 需要启动 ${neededBots} 个机器人`);
251
+
252
+ for (let i = 0; i < neededBots; i++) {
253
+ setTimeout(() => {
254
+ this.createBot();
255
+ }, i * 2000); // 每隔2秒启动一个
256
+ }
257
+ }
258
+
259
+ this.updateStatus();
260
+ }
261
+
262
+ // 更新状态
263
+ updateStatus() {
264
+ const serverInfo = globalServerStatus.servers.get(`${this.host}:${this.port}`);
265
+ if (serverInfo) {
266
+ serverInfo.currentBots = this.currentBots;
267
+ serverInfo.activeBots = Array.from(this.activeBots.keys());
268
+ serverInfo.lastUpdate = Date.now();
269
+ serverInfo.status = this.currentBots >= this.minBots ? 'healthy' :
270
+ this.failedAttempts >= SERVER_CONFIG.maxFailedAttempts ? 'failed' : 'degraded';
271
+ this.status = serverInfo.status;
272
+ }
273
+ }
274
+
275
+ // 启动监控
276
+ startMonitoring() {
277
+ this.maintainBots();
278
+
279
+ this.monitoringInterval = setInterval(() => {
280
+ this.maintainBots();
281
+ }, SERVER_CONFIG.statusCheckInterval);
282
+ }
283
+
284
+ // 停止所有机器人
285
+ stopAllBots() {
286
+ this.activeBots.forEach((bot, botName) => {
287
+ try {
288
+ bot.quit();
289
+ console.log(`[${this.host}:${this.port}] 停止机器人: ${botName}`);
290
+ } catch (error) {
291
+ console.log(`[${this.host}:${this.port}] 停止机器人 ${botName} 失败:`, error.message);
292
+ }
293
+ });
294
+
295
+ this.activeBots.clear();
296
+ this.currentBots = 0;
297
+ this.updateStatus();
298
+
299
+ if (this.monitoringInterval) {
300
+ clearInterval(this.monitoringInterval);
301
+ this.monitoringInterval = null;
302
+ }
303
+ }
304
+ // 获取状态信息
305
+ getStatus() {
306
+ return {
307
+ host: this.host,
308
+ port: this.port,
309
+ version: this.version,
310
+ minBots: this.minBots,
311
+ maxBots: this.maxBots,
312
+ currentBots: this.currentBots,
313
+ activeBots: Array.from(this.activeBots.keys()),
314
+ failedAttempts: this.failedAttempts,
315
+ status: this.status,
316
+ lastUpdate: this.lastUpdate
317
+ };
318
+ }
319
+ }
320
+
321
+ // 系统监控
322
+ class SystemMonitor {
323
+ constructor() {
324
+ this.memoryUsage = '未知';
325
+ this.uptime = 0;
326
+ this.monitoringInterval = null;
327
+ }
328
+
329
+ async getMemoryUsage() {
330
+ return new Promise((resolve) => {
331
+ const used = process.memoryUsage();
332
+ this.memoryUsage = `RSS: ${Math.round(used.rss / 1024 / 1024)}MB, Heap: ${Math.round(used.heapUsed / 1024 / 1024)}MB`;
333
+ resolve(this.memoryUsage);
334
+ });
335
+ }
336
+
337
+ startMonitoring() {
338
+ this.getMemoryUsage();
339
+ this.monitoringInterval = setInterval(async () => {
340
+ await this.getMemoryUsage();
341
+ this.uptime = process.uptime();
342
+ }, BOT_CONFIG.healthCheckInterval);
343
+ }
344
+
345
+ stopMonitoring() {
346
+ if (this.monitoringInterval) {
347
+ clearInterval(this.monitoringInterval);
348
+ }
349
+ }
350
+
351
+ getSystemInfo() {
352
+ return {
353
+ memoryUsage: this.memoryUsage,
354
+ uptime: this.uptime,
355
+ nodeVersion: process.version,
356
+ platform: process.platform
357
+ };
358
+ }
359
+ }
360
+
361
+ // Web状态服务器
362
+ class StatusServer {
363
+ constructor(port = 3000) {
364
+ this.port = port;
365
+ this.app = express();
366
+ this.server = null;
367
+ this.setupRoutes();
368
+ }
369
+
370
+ setupRoutes() {
371
+ this.app.get('/', (req, res) => {
372
+ const serversStatus = Array.from(globalServerStatus.servers.values());
373
+ const systemInfo = systemMonitor.getSystemInfo();
374
+
375
+ const html = `
376
+ <!DOCTYPE html>
377
+ <html lang="zh-CN">
378
+ <head>
379
+ <meta charset="UTF-8">
380
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
381
+ <title>Minecraft 机器人监控系统</title>
382
+ <style>
383
+ * {
384
+ margin: 0;
385
+ padding: 0;
386
+ box-sizing: border-box;
387
+ }
388
+
389
+ body {
390
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
391
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
392
+ min-height: 100vh;
393
+ padding: 20px;
394
+ color: #333;
395
+ }
396
+
397
+ .container {
398
+ max-width: 1200px;
399
+ margin: 0 auto;
400
+ }
401
+
402
+ .header {
403
+ text-align: center;
404
+ margin-bottom: 30px;
405
+ color: white;
406
+ }
407
+
408
+ .header h1 {
409
+ font-size: 2.5rem;
410
+ margin-bottom: 10px;
411
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
412
+ }
413
+
414
+ .header p {
415
+ font-size: 1.1rem;
416
+ opacity: 0.9;
417
+ }
418
+
419
+ .dashboard {
420
+ display: grid;
421
+ grid-template-columns: 1fr 2fr;
422
+ gap: 20px;
423
+ margin-bottom: 20px;
424
+ }
425
+
426
+ @media (max-width: 768px) {
427
+ .dashboard {
428
+ grid-template-columns: 1fr;
429
+ }
430
+ }
431
+
432
+ .system-card {
433
+ background: white;
434
+ border-radius: 15px;
435
+ padding: 25px;
436
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
437
+ backdrop-filter: blur(10px);
438
+ }
439
+
440
+ .system-card h3 {
441
+ color: #4a5568;
442
+ margin-bottom: 20px;
443
+ font-size: 1.3rem;
444
+ border-bottom: 2px solid #e2e8f0;
445
+ padding-bottom: 10px;
446
+ }
447
+
448
+ .stats-grid {
449
+ display: grid;
450
+ gap: 15px;
451
+ }
452
+
453
+ .stat-item {
454
+ display: flex;
455
+ justify-content: space-between;
456
+ align-items: center;
457
+ padding: 12px;
458
+ background: #f7fafc;
459
+ border-radius: 10px;
460
+ border-left: 4px solid #4299e1;
461
+ }
462
+
463
+ .stat-label {
464
+ font-weight: 600;
465
+ color: #4a5568;
466
+ }
467
+
468
+ .stat-value {
469
+ font-weight: 700;
470
+ color: #2d3748;
471
+ }
472
+
473
+ .servers-section {
474
+ background: white;
475
+ border-radius: 15px;
476
+ padding: 25px;
477
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
478
+ }
479
+
480
+ .servers-section h3 {
481
+ color: #4a5568;
482
+ margin-bottom: 20px;
483
+ font-size: 1.3rem;
484
+ }
485
+
486
+ .server-grid {
487
+ display: grid;
488
+ gap: 20px;
489
+ }
490
+
491
+ .server-card {
492
+ border-radius: 12px;
493
+ padding: 20px;
494
+ border: 1px solid #e2e8f0;
495
+ transition: all 0.3s ease;
496
+ background: white;
497
+ }
498
+
499
+ .server-card:hover {
500
+ transform: translateY(-2px);
501
+ box-shadow: 0 8px 25px rgba(0,0,0,0.1);
502
+ }
503
+
504
+ .server-card.healthy {
505
+ border-left: 4px solid #48bb78;
506
+ }
507
+
508
+ .server-card.degraded {
509
+ border-left: 4px solid #ed8936;
510
+ }
511
+
512
+ .server-card.failed {
513
+ border-left: 4px solid #f56565;
514
+ }
515
+
516
+ .server-header {
517
+ display: flex;
518
+ justify-content: space-between;
519
+ align-items: center;
520
+ margin-bottom: 15px;
521
+ }
522
+
523
+ .server-title {
524
+ font-size: 1.2rem;
525
+ font-weight: 600;
526
+ color: #2d3748;
527
+ }
528
+
529
+ .status-badge {
530
+ padding: 6px 12px;
531
+ border-radius: 20px;
532
+ font-size: 0.85rem;
533
+ font-weight: 600;
534
+ text-transform: uppercase;
535
+ }
536
+
537
+ .status-healthy {
538
+ background: #c6f6d5;
539
+ color: #276749;
540
+ }
541
+
542
+ .status-degraded {
543
+ background: #fed7d7;
544
+ color: #c53030;
545
+ }
546
+
547
+ .status-failed {
548
+ background: #fed7d7;
549
+ color: #c53030;
550
+ }
551
+
552
+ .server-info {
553
+ display: grid;
554
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
555
+ gap: 15px;
556
+ margin-bottom: 15px;
557
+ }
558
+
559
+ .info-item {
560
+ text-align: center;
561
+ padding: 10px;
562
+ background: #f7fafc;
563
+ border-radius: 8px;
564
+ }
565
+
566
+ .info-label {
567
+ font-size: 0.85rem;
568
+ color: #718096;
569
+ margin-bottom: 5px;
570
+ }
571
+
572
+ .info-value {
573
+ font-size: 1.1rem;
574
+ font-weight: 700;
575
+ color: #2d3748;
576
+ }
577
+
578
+ .bots-section {
579
+ margin-top: 15px;
580
+ }
581
+
582
+ .bots-title {
583
+ font-weight: 600;
584
+ margin-bottom: 10px;
585
+ color: #4a5568;
586
+ }
587
+
588
+ .bots-list {
589
+ display: flex;
590
+ flex-wrap: wrap;
591
+ gap: 8px;
592
+ }
593
+
594
+ .bot-tag {
595
+ background: #4299e1;
596
+ color: white;
597
+ padding: 4px 12px;
598
+ border-radius: 15px;
599
+ font-size: 0.8rem;
600
+ font-weight: 500;
601
+ }
602
+
603
+ .no-bots {
604
+ color: #a0aec0;
605
+ font-style: italic;
606
+ text-align: center;
607
+ padding: 10px;
608
+ }
609
+
610
+ .last-update {
611
+ text-align: right;
612
+ font-size: 0.8rem;
613
+ color: #a0aec0;
614
+ margin-top: 10px;
615
+ }
616
+
617
+ .refresh-info {
618
+ text-align: center;
619
+ color: white;
620
+ margin-top: 20px;
621
+ opacity: 0.8;
622
+ font-size: 0.9rem;
623
+ }
624
+
625
+ .progress-bar {
626
+ width: 100%;
627
+ height: 8px;
628
+ background: #e2e8f0;
629
+ border-radius: 4px;
630
+ overflow: hidden;
631
+ margin-top: 5px;
632
+ }
633
+
634
+ .progress-fill {
635
+ height: 100%;
636
+ border-radius: 4px;
637
+ transition: width 0.3s ease;
638
+ }
639
+
640
+ .progress-healthy {
641
+ background: linear-gradient(90deg, #48bb78, #68d391);
642
+ }
643
+
644
+ .progress-degraded {
645
+ background: linear-gradient(90deg, #ed8936, #f6ad55);
646
+ }
647
+
648
+ .progress-failed {
649
+ background: linear-gradient(90deg, #f56565, #fc8181);
650
+ }
651
+ </style>
652
+ </head>
653
+ <body>
654
+ <div class="container">
655
+ <div class="header">
656
+ <h1>🎮 Minecraft 机器人监控系统</h1>
657
+ <p>实时监控服务器状态和机器人连接</p>
658
+ </div>
659
+
660
+ <div class="dashboard">
661
+ <div class="system-card">
662
+ <h3>📊 系统概览</h3>
663
+ <div class="stats-grid">
664
+ <div class="stat-item">
665
+ <span class="stat-label">运行时间</span>
666
+ <span class="stat-value">${Math.floor(systemInfo.uptime / 60)} 分钟</span>
667
+ </div>
668
+ <div class="stat-item">
669
+ <span class="stat-label">内存使用</span>
670
+ <span class="stat-value">${systemInfo.memoryUsage.split(',')[0].replace('RSS: ', '')}</span>
671
+ </div>
672
+ <div class="stat-item">
673
+ <span class="stat-label">Node.js 版本</span>
674
+ <span class="stat-value">${systemInfo.nodeVersion}</span>
675
+ </div>
676
+ <div class="stat-item">
677
+ <span class="stat-label">监控服务器</span>
678
+ <span class="stat-value">${serversStatus.length} 个</span>
679
+ </div>
680
+ </div>
681
+ </div>
682
+
683
+ <div class="servers-section">
684
+ <h3>🖥️ 服务器状态</h3>
685
+ <div class="server-grid">
686
+ ${serversStatus.map(server => {
687
+ const progress = (server.currentBots / server.maxBots) * 100;
688
+ const statusClass = server.status === 'healthy' ? 'status-healthy' :
689
+ server.status === 'degraded' ? 'status-degraded' : 'status-failed';
690
+ const progressClass = server.status === 'healthy' ? 'progress-healthy' :
691
+ server.status === 'degraded' ? 'progress-degraded' : 'progress-failed';
692
+ const statusText = server.status === 'healthy' ? '正常' :
693
+ server.status === 'degraded' ? '降级' : '故障';
694
+
695
+ return `
696
+ <div class="server-card ${server.status}">
697
+ <div class="server-header">
698
+ <div class="server-title">${server.host}:${server.port}</div>
699
+ <div class="status-badge ${statusClass}">${statusText}</div>
700
+ </div>
701
+
702
+ <div class="server-info">
703
+ <div class="info-item">
704
+ <div class="info-label">机器人数量</div>
705
+ <div class="info-value">${server.currentBots} / ${server.maxBots}</div>
706
+ <div class="progress-bar">
707
+ <div class="progress-fill ${progressClass}" style="width: ${progress}%"></div>
708
+ </div>
709
+ </div>
710
+ <div class="info-item">
711
+ <div class="info-label">最小要求</div>
712
+ <div class="info-value">${server.minBots}</div>
713
+ </div>
714
+ <div class="info-item">
715
+ <div class="info-label">连接状态</div>
716
+ <div class="info-value" style="color: ${
717
+ server.status === 'healthy' ? '#48bb78' :
718
+ server.status === 'degraded' ? '#ed8936' : '#f56565'
719
+ }">${server.status}</div>
720
+ </div>
721
+ </div>
722
+
723
+ <div class="bots-section">
724
+ <div class="bots-title">🤖 活跃机器人</div>
725
+ <div class="bots-list">
726
+ ${server.activeBots.length > 0 ?
727
+ server.activeBots.map(bot => `
728
+ <div class="bot-tag">${bot}</div>
729
+ `).join('') :
730
+ '<div class="no-bots">暂无活跃机器人</div>'
731
+ }
732
+ </div>
733
+ </div>
734
+
735
+ <div class="last-update">
736
+ 最后更新: ${new Date(server.lastUpdate).toLocaleString('zh-CN')}
737
+ </div>
738
+ </div>
739
+ `;
740
+ }).join('')}
741
+ </div>
742
+ </div>
743
+ </div>
744
+
745
+ <div class="refresh-info">
746
+ 页面每30秒自动刷新 • Minecraft 机器人监控系统 v1.0
747
+ </div>
748
+ </div>
749
+
750
+ <script>
751
+ // 30秒自动刷新
752
+ setTimeout(() => {
753
+ location.reload();
754
+ }, 30000);
755
+
756
+ // 添加一些交互动画
757
+ document.addEventListener('DOMContentLoaded', function() {
758
+ const cards = document.querySelectorAll('.server-card');
759
+ cards.forEach(card => {
760
+ card.addEventListener('mouseenter', function() {
761
+ this.style.transform = 'translateY(-5px)';
762
+ });
763
+ card.addEventListener('mouseleave', function() {
764
+ this.style.transform = 'translateY(0)';
765
+ });
766
+ });
767
+ });
768
+ </script>
769
+ </body>
770
+ </html>`;
771
+
772
+ res.send(html);
773
+ });
774
+
775
+ this.app.get('/api/status', (req, res) => {
776
+ const serversStatus = Array.from(globalServerStatus.servers.values());
777
+ const systemInfo = systemMonitor.getSystemInfo();
778
+
779
+ res.json({
780
+ system: systemInfo,
781
+ servers: serversStatus,
782
+ timestamp: Date.now()
783
+ });
784
+ });
785
+ }
786
+
787
+ start() {
788
+ this.server = this.app.listen(this.port, () => {
789
+ console.log(`状态监控页面: http://localhost:${this.port}`);
790
+ });
791
+ }
792
+
793
+ stop() {
794
+ if (this.server) {
795
+ this.server.close();
796
+ }
797
+ }
798
+ }
799
+
800
+ // 全局实例
801
+ const systemMonitor = new SystemMonitor();
802
+ const statusServer = new StatusServer();
803
+ let botManagers = [];
804
+
805
+ // 主初始化
806
+ async function initialize() {
807
+ console.log('初始化Minecraft机器人管理系统...');
808
+
809
+ systemMonitor.startMonitoring();
810
+
811
+ // 创建机器人管理器
812
+ botManagers = SERVERS.map(server =>
813
+ new MinecraftBotManager(
814
+ server.host,
815
+ server.port,
816
+ server.minBots,
817
+ server.maxBots,
818
+ server.version
819
+ )
820
+ );
821
+
822
+ // 启动所有管理器
823
+ botManagers.forEach(manager => {
824
+ manager.startMonitoring();
825
+ console.log(`启动服务器: ${manager.host}:${manager.port} (${manager.minBots}-${manager.maxBots} 机器人)`);
826
+ });
827
+
828
+ statusServer.start();
829
+
830
+ console.log('Minecraft机器人管理系统初始化完成');
831
+
832
+ process.on('SIGINT', shutdown);
833
+ process.on('SIGTERM', shutdown);
834
+ }
835
+
836
+ function shutdown() {
837
+ console.log('正在关闭系统...');
838
+
839
+ botManagers.forEach(manager => {
840
+ manager.stopAllBots();
841
+ });
842
+
843
+ systemMonitor.stopMonitoring();
844
+ statusServer.stop();
845
+
846
+ console.log('系统已关闭');
847
+ process.exit(0);
848
+ }
849
+
850
+ // 启动
851
+ if (require.main === module) {
852
+ initialize().catch(error => {
853
+ console.error('初始化失败:', error);
854
+ process.exit(1);
855
+ });
856
+ }
857
+
858
+ module.exports = {
859
+ MinecraftBotManager,
860
+ SystemMonitor,
861
+ StatusServer,
862
+ initialize,
863
+ shutdown
864
+ };