OrbitMC commited on
Commit
00f2929
·
verified ·
1 Parent(s): d7cb46d

Rename index.j to index.html

Browse files
Files changed (2) hide show
  1. index.html +784 -0
  2. index.j +0 -338
index.html ADDED
@@ -0,0 +1,784 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Bot Manager - OrbitMC</title>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ color: #fff;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1400px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ .header {
28
+ text-align: center;
29
+ margin-bottom: 30px;
30
+ }
31
+
32
+ .header h1 {
33
+ font-size: 42px;
34
+ margin-bottom: 10px;
35
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
36
+ }
37
+
38
+ .header p {
39
+ font-size: 18px;
40
+ opacity: 0.9;
41
+ }
42
+
43
+ /* Server Status Card */
44
+ .server-card {
45
+ background: rgba(255, 255, 255, 0.1);
46
+ backdrop-filter: blur(10px);
47
+ border-radius: 20px;
48
+ padding: 30px;
49
+ margin-bottom: 25px;
50
+ border: 1px solid rgba(255, 255, 255, 0.2);
51
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
52
+ }
53
+
54
+ .server-header {
55
+ display: flex;
56
+ justify-content: space-between;
57
+ align-items: center;
58
+ margin-bottom: 20px;
59
+ }
60
+
61
+ .server-title {
62
+ font-size: 24px;
63
+ font-weight: bold;
64
+ }
65
+
66
+ .status-badge {
67
+ padding: 8px 20px;
68
+ border-radius: 20px;
69
+ font-weight: bold;
70
+ font-size: 14px;
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 8px;
74
+ }
75
+
76
+ .status-badge.online {
77
+ background: #10b981;
78
+ animation: pulse 2s infinite;
79
+ }
80
+
81
+ .status-badge.offline {
82
+ background: #ef4444;
83
+ }
84
+
85
+ @keyframes pulse {
86
+ 0%, 100% { opacity: 1; }
87
+ 50% { opacity: 0.7; }
88
+ }
89
+
90
+ .status-dot {
91
+ width: 10px;
92
+ height: 10px;
93
+ border-radius: 50%;
94
+ background: white;
95
+ }
96
+
97
+ .server-info {
98
+ display: grid;
99
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
100
+ gap: 15px;
101
+ }
102
+
103
+ .info-item {
104
+ background: rgba(255, 255, 255, 0.1);
105
+ padding: 15px;
106
+ border-radius: 10px;
107
+ text-align: center;
108
+ }
109
+
110
+ .info-label {
111
+ font-size: 12px;
112
+ opacity: 0.8;
113
+ margin-bottom: 5px;
114
+ }
115
+
116
+ .info-value {
117
+ font-size: 20px;
118
+ font-weight: bold;
119
+ }
120
+
121
+ /* Rotation Card */
122
+ .rotation-card {
123
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
124
+ border-radius: 20px;
125
+ padding: 30px;
126
+ margin-bottom: 25px;
127
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
128
+ text-align: center;
129
+ }
130
+
131
+ .rotation-title {
132
+ font-size: 18px;
133
+ opacity: 0.9;
134
+ margin-bottom: 15px;
135
+ }
136
+
137
+ .current-bot {
138
+ font-size: 48px;
139
+ font-weight: bold;
140
+ margin: 15px 0;
141
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
142
+ }
143
+
144
+ .rotation-timer {
145
+ font-size: 36px;
146
+ font-weight: bold;
147
+ margin: 20px 0;
148
+ font-family: 'Courier New', monospace;
149
+ }
150
+
151
+ .progress-bar {
152
+ background: rgba(255, 255, 255, 0.3);
153
+ border-radius: 10px;
154
+ height: 20px;
155
+ overflow: hidden;
156
+ margin: 20px 0;
157
+ }
158
+
159
+ .progress-fill {
160
+ background: rgba(255, 255, 255, 0.9);
161
+ height: 100%;
162
+ border-radius: 10px;
163
+ transition: width 1s linear;
164
+ }
165
+
166
+ .next-bot-info {
167
+ background: rgba(255, 255, 255, 0.2);
168
+ padding: 15px;
169
+ border-radius: 10px;
170
+ margin-top: 15px;
171
+ font-size: 16px;
172
+ }
173
+
174
+ .controls {
175
+ display: flex;
176
+ justify-content: center;
177
+ gap: 10px;
178
+ margin-top: 20px;
179
+ }
180
+
181
+ button {
182
+ background: rgba(255, 255, 255, 0.3);
183
+ border: 2px solid rgba(255, 255, 255, 0.5);
184
+ color: white;
185
+ padding: 12px 30px;
186
+ border-radius: 25px;
187
+ cursor: pointer;
188
+ font-size: 16px;
189
+ font-weight: bold;
190
+ transition: all 0.3s;
191
+ }
192
+
193
+ button:hover {
194
+ background: rgba(255, 255, 255, 0.5);
195
+ transform: translateY(-2px);
196
+ box-shadow: 0 5px 15px rgba(0,0,0,0.3);
197
+ }
198
+
199
+ button:active {
200
+ transform: translateY(0);
201
+ }
202
+
203
+ button.primary {
204
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
205
+ border: none;
206
+ }
207
+
208
+ button.primary:hover {
209
+ background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
210
+ }
211
+
212
+ /* Bot Grid */
213
+ .bot-grid {
214
+ display: grid;
215
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
216
+ gap: 20px;
217
+ margin-bottom: 30px;
218
+ }
219
+
220
+ .bot-card {
221
+ background: rgba(255, 255, 255, 0.1);
222
+ backdrop-filter: blur(10px);
223
+ border-radius: 15px;
224
+ padding: 20px;
225
+ border: 2px solid rgba(255, 255, 255, 0.2);
226
+ transition: all 0.3s;
227
+ position: relative;
228
+ overflow: hidden;
229
+ }
230
+
231
+ .bot-card::before {
232
+ content: '';
233
+ position: absolute;
234
+ top: 0;
235
+ left: 0;
236
+ right: 0;
237
+ height: 4px;
238
+ background: #6b7280;
239
+ }
240
+
241
+ .bot-card.active::before {
242
+ background: linear-gradient(90deg, #f093fb 0%, #f5576c 100%);
243
+ animation: shimmer 2s infinite;
244
+ }
245
+
246
+ .bot-card.online::before {
247
+ background: #10b981;
248
+ }
249
+
250
+ .bot-card.connecting::before {
251
+ background: #f59e0b;
252
+ }
253
+
254
+ .bot-card.offline::before {
255
+ background: #6b7280;
256
+ }
257
+
258
+ @keyframes shimmer {
259
+ 0% { transform: translateX(-100%); }
260
+ 100% { transform: translateX(100%); }
261
+ }
262
+
263
+ .bot-card.active {
264
+ border-color: rgba(240, 147, 251, 0.5);
265
+ background: rgba(240, 147, 251, 0.15);
266
+ transform: scale(1.05);
267
+ box-shadow: 0 10px 30px rgba(240, 147, 251, 0.3);
268
+ }
269
+
270
+ .bot-header {
271
+ display: flex;
272
+ justify-content: space-between;
273
+ align-items: center;
274
+ margin-bottom: 15px;
275
+ }
276
+
277
+ .bot-name {
278
+ font-size: 20px;
279
+ font-weight: bold;
280
+ }
281
+
282
+ .active-badge {
283
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
284
+ padding: 4px 12px;
285
+ border-radius: 12px;
286
+ font-size: 11px;
287
+ font-weight: bold;
288
+ }
289
+
290
+ .bot-stats {
291
+ display: grid;
292
+ grid-template-columns: 1fr 1fr;
293
+ gap: 10px;
294
+ margin: 15px 0;
295
+ }
296
+
297
+ .stat-item {
298
+ background: rgba(255, 255, 255, 0.1);
299
+ padding: 10px;
300
+ border-radius: 8px;
301
+ text-align: center;
302
+ }
303
+
304
+ .stat-label {
305
+ font-size: 11px;
306
+ opacity: 0.7;
307
+ margin-bottom: 3px;
308
+ }
309
+
310
+ .stat-value {
311
+ font-size: 18px;
312
+ font-weight: bold;
313
+ }
314
+
315
+ .bot-status-text {
316
+ text-align: center;
317
+ padding: 8px;
318
+ border-radius: 8px;
319
+ font-weight: bold;
320
+ margin-top: 10px;
321
+ }
322
+
323
+ .bot-status-text.online {
324
+ background: rgba(16, 185, 129, 0.3);
325
+ color: #10b981;
326
+ }
327
+
328
+ .bot-status-text.offline {
329
+ background: rgba(107, 114, 128, 0.3);
330
+ color: #9ca3af;
331
+ }
332
+
333
+ .bot-status-text.connecting {
334
+ background: rgba(245, 158, 11, 0.3);
335
+ color: #f59e0b;
336
+ }
337
+
338
+ .bot-status-text.active {
339
+ background: rgba(240, 147, 251, 0.3);
340
+ color: #f093fb;
341
+ }
342
+
343
+ /* Queue Section */
344
+ .queue-section {
345
+ background: rgba(255, 255, 255, 0.1);
346
+ backdrop-filter: blur(10px);
347
+ border-radius: 20px;
348
+ padding: 25px;
349
+ margin-bottom: 25px;
350
+ border: 1px solid rgba(255, 255, 255, 0.2);
351
+ }
352
+
353
+ .queue-title {
354
+ font-size: 20px;
355
+ font-weight: bold;
356
+ margin-bottom: 20px;
357
+ text-align: center;
358
+ }
359
+
360
+ .queue-list {
361
+ display: flex;
362
+ justify-content: center;
363
+ align-items: center;
364
+ gap: 15px;
365
+ flex-wrap: wrap;
366
+ }
367
+
368
+ .queue-item {
369
+ background: rgba(255, 255, 255, 0.1);
370
+ padding: 15px 25px;
371
+ border-radius: 15px;
372
+ font-weight: bold;
373
+ transition: all 0.3s;
374
+ border: 2px solid transparent;
375
+ }
376
+
377
+ .queue-item.active {
378
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
379
+ transform: scale(1.15);
380
+ box-shadow: 0 5px 20px rgba(240, 147, 251, 0.4);
381
+ }
382
+
383
+ .queue-item.next {
384
+ border-color: rgba(245, 158, 11, 0.6);
385
+ background: rgba(245, 158, 11, 0.2);
386
+ }
387
+
388
+ .queue-arrow {
389
+ font-size: 24px;
390
+ opacity: 0.5;
391
+ }
392
+
393
+ .msg {
394
+ position: fixed;
395
+ top: 20px;
396
+ right: 20px;
397
+ background: #10b981;
398
+ color: white;
399
+ padding: 15px 25px;
400
+ border-radius: 10px;
401
+ box-shadow: 0 5px 20px rgba(0,0,0,0.3);
402
+ z-index: 1000;
403
+ animation: slideIn 0.3s;
404
+ }
405
+
406
+ .msg.error {
407
+ background: #ef4444;
408
+ }
409
+
410
+ @keyframes slideIn {
411
+ from {
412
+ transform: translateX(400px);
413
+ opacity: 0;
414
+ }
415
+ to {
416
+ transform: translateX(0);
417
+ opacity: 1;
418
+ }
419
+ }
420
+
421
+ @media (max-width: 768px) {
422
+ .header h1 { font-size: 28px; }
423
+ .current-bot { font-size: 32px; }
424
+ .rotation-timer { font-size: 24px; }
425
+ .bot-grid { grid-template-columns: 1fr; }
426
+ }
427
+ </style>
428
+ </head>
429
+ <body>
430
+ <div class="container">
431
+ <div class="header">
432
+ <h1>🎮 Minecraft Bot Manager</h1>
433
+ <p>OrbitMC Server Monitor & Bot Rotation System</p>
434
+ </div>
435
+
436
+ <!-- Server Status -->
437
+ <div class="server-card">
438
+ <div class="server-header">
439
+ <div class="server-title">📡 Server Status</div>
440
+ <div class="status-badge" id="server-badge">
441
+ <div class="status-dot"></div>
442
+ <span id="server-status-text">Checking...</span>
443
+ </div>
444
+ </div>
445
+ <div class="server-info">
446
+ <div class="info-item">
447
+ <div class="info-label">Address</div>
448
+ <div class="info-value" id="server-address">-</div>
449
+ </div>
450
+ <div class="info-item">
451
+ <div class="info-label">Players Online</div>
452
+ <div class="info-value" id="server-players">-</div>
453
+ </div>
454
+ <div class="info-item">
455
+ <div class="info-label">Latency</div>
456
+ <div class="info-value" id="server-latency">-</div>
457
+ </div>
458
+ <div class="info-item">
459
+ <div class="info-label">Version</div>
460
+ <div class="info-value" id="server-version">-</div>
461
+ </div>
462
+ </div>
463
+ </div>
464
+
465
+ <!-- Current Rotation -->
466
+ <div class="rotation-card">
467
+ <div class="rotation-title">🔄 CURRENT ACTIVE BOT</div>
468
+ <div class="current-bot" id="current-bot">-</div>
469
+ <div class="rotation-timer" id="rotation-timer">00:00:00</div>
470
+
471
+ <div class="progress-bar">
472
+ <div class="progress-fill" id="progress-fill"></div>
473
+ </div>
474
+
475
+ <div class="next-bot-info">
476
+ <strong>Next Bot:</strong> <span id="next-bot">-</span> in <span id="time-until-next">-</span>
477
+ </div>
478
+
479
+ <div class="controls">
480
+ <button class="primary" onclick="forceRotation()">⏭️ Switch to Next Bot</button>
481
+ <button onclick="refreshStatus()">🔄 Refresh</button>
482
+ </div>
483
+ </div>
484
+
485
+ <!-- Queue -->
486
+ <div class="queue-section">
487
+ <div class="queue-title">📋 Rotation Queue</div>
488
+ <div class="queue-list" id="queue-list"></div>
489
+ </div>
490
+
491
+ <!-- Bot Grid -->
492
+ <div class="bot-grid" id="bot-grid"></div>
493
+
494
+ <div id="msg-container"></div>
495
+ </div>
496
+
497
+ <script>
498
+ // Simulated data for demo (replace with actual API calls)
499
+ let data = {};
500
+ let updateTimer;
501
+ let serverCheckTimer;
502
+ let simulatedTime = 0;
503
+ let currentBotIndex = 0;
504
+
505
+ const BOT_NAMES = ["moderator_1", "moderator_2", "moderator_3", "moderator_4", "moderator_5"];
506
+ const ROTATION_DURATION = 3600; // 1 hour
507
+
508
+ // Initialize with demo data
509
+ function initializeDemoData() {
510
+ data = {
511
+ server_info: {
512
+ host: "orbitmc.progamer.me",
513
+ port: 40675,
514
+ version: "1.21.1"
515
+ },
516
+ server_status: {
517
+ online: true,
518
+ players: "12/100",
519
+ latency: 45,
520
+ last_check: new Date()
521
+ },
522
+ rotation: {
523
+ current_bot: BOT_NAMES[currentBotIndex],
524
+ next_bot: BOT_NAMES[(currentBotIndex + 1) % BOT_NAMES.length],
525
+ elapsed: simulatedTime,
526
+ remaining: ROTATION_DURATION - simulatedTime,
527
+ queue: BOT_NAMES
528
+ },
529
+ bots: BOT_NAMES.map((name, index) => ({
530
+ name: name,
531
+ status: index === currentBotIndex ? 'online' : 'offline',
532
+ is_active: index === currentBotIndex,
533
+ uptime: index === currentBotIndex ? simulatedTime : 0,
534
+ deaths: Math.floor(Math.random() * 5),
535
+ reconnects: Math.floor(Math.random() * 3),
536
+ position: index === currentBotIndex ? `${Math.floor(Math.random() * 100)}, 64, ${Math.floor(Math.random() * 100)}` : '0, 0, 0'
537
+ }))
538
+ };
539
+ }
540
+
541
+ // Simulate time passing
542
+ function updateSimulation() {
543
+ simulatedTime++;
544
+
545
+ if (simulatedTime >= ROTATION_DURATION) {
546
+ simulatedTime = 0;
547
+ currentBotIndex = (currentBotIndex + 1) % BOT_NAMES.length;
548
+ showMsg('🔄 Rotated to next bot!', 'success');
549
+ }
550
+
551
+ // Update data
552
+ data.rotation.elapsed = simulatedTime;
553
+ data.rotation.remaining = ROTATION_DURATION - simulatedTime;
554
+ data.rotation.current_bot = BOT_NAMES[currentBotIndex];
555
+ data.rotation.next_bot = BOT_NAMES[(currentBotIndex + 1) % BOT_NAMES.length];
556
+
557
+ // Update bots
558
+ data.bots = BOT_NAMES.map((name, index) => ({
559
+ name: name,
560
+ status: index === currentBotIndex ? 'online' : 'offline',
561
+ is_active: index === currentBotIndex,
562
+ uptime: index === currentBotIndex ? simulatedTime : 0,
563
+ deaths: data.bots[index]?.deaths || 0,
564
+ reconnects: data.bots[index]?.reconnects || 0,
565
+ position: index === currentBotIndex ? `${100 + Math.floor(Math.random() * 10)}, 64, ${100 + Math.floor(Math.random() * 10)}` : '0, 0, 0'
566
+ }));
567
+
568
+ // Simulate random server status changes
569
+ if (Math.random() > 0.98) {
570
+ data.server_status.online = !data.server_status.online;
571
+ }
572
+
573
+ if (data.server_status.online) {
574
+ data.server_status.latency = 30 + Math.floor(Math.random() * 30);
575
+ data.server_status.players = `${10 + Math.floor(Math.random() * 20)}/100`;
576
+ }
577
+ }
578
+
579
+ // For production, use this instead:
580
+ async function fetchStatus() {
581
+ try {
582
+ const r = await fetch('/api/status');
583
+ data = await r.json();
584
+ renderStatus();
585
+ } catch(e) {
586
+ console.error('Failed to fetch status', e);
587
+ // Fallback to demo mode
588
+ initializeDemoData();
589
+ renderStatus();
590
+ }
591
+ }
592
+
593
+ function renderStatus() {
594
+ // Server Status
595
+ const server = data.server_status;
596
+ const badge = document.getElementById('server-badge');
597
+
598
+ if (server.online) {
599
+ badge.className = 'status-badge online';
600
+ document.getElementById('server-status-text').textContent = 'Online';
601
+ } else {
602
+ badge.className = 'status-badge offline';
603
+ document.getElementById('server-status-text').textContent = 'Offline';
604
+ }
605
+
606
+ document.getElementById('server-address').textContent = `${data.server_info.host}:${data.server_info.port}`;
607
+ document.getElementById('server-players').textContent = server.players;
608
+ document.getElementById('server-latency').textContent = server.latency > 0 ? `${server.latency}ms` : 'N/A';
609
+ document.getElementById('server-version').textContent = data.server_info.version;
610
+
611
+ // Rotation Info
612
+ const rotation = data.rotation;
613
+ document.getElementById('current-bot').textContent = rotation.current_bot || '-';
614
+ document.getElementById('rotation-timer').textContent = formatTime(rotation.elapsed);
615
+ document.getElementById('next-bot').textContent = rotation.next_bot || '-';
616
+ document.getElementById('time-until-next').textContent = formatTime(rotation.remaining);
617
+
618
+ // Progress Bar
619
+ const progress = (rotation.elapsed / 3600) * 100;
620
+ document.getElementById('progress-fill').style.width = progress + '%';
621
+
622
+ // Queue
623
+ renderQueue(rotation.queue, rotation.current_bot, rotation.next_bot);
624
+
625
+ // Bot Grid
626
+ renderBots(data.bots);
627
+ }
628
+
629
+ function renderQueue(queue, current, next) {
630
+ const queueList = document.getElementById('queue-list');
631
+ queueList.innerHTML = '';
632
+
633
+ queue.forEach((bot, index) => {
634
+ const item = document.createElement('div');
635
+ item.className = 'queue-item';
636
+
637
+ if (bot === current) {
638
+ item.className += ' active';
639
+ } else if (bot === next) {
640
+ item.className += ' next';
641
+ }
642
+
643
+ item.textContent = bot;
644
+ queueList.appendChild(item);
645
+
646
+ if (index < queue.length - 1) {
647
+ const arrow = document.createElement('div');
648
+ arrow.className = 'queue-arrow';
649
+ arrow.textContent = '→';
650
+ queueList.appendChild(arrow);
651
+ }
652
+ });
653
+ }
654
+
655
+ function renderBots(bots) {
656
+ const grid = document.getElementById('bot-grid');
657
+ grid.innerHTML = '';
658
+
659
+ bots.forEach(bot => {
660
+ const card = document.createElement('div');
661
+ card.className = `bot-card ${bot.status}`;
662
+
663
+ if (bot.is_active) {
664
+ card.className += ' active';
665
+ }
666
+
667
+ const activeBadge = bot.is_active ? '<div class="active-badge">⚡ ACTIVE NOW</div>' : '';
668
+
669
+ const statusClass = bot.is_active ? 'active' : bot.status;
670
+ const statusText = bot.is_active ? '⚡ ACTIVE & ONLINE' : bot.status.toUpperCase();
671
+
672
+ card.innerHTML = `
673
+ <div class="bot-header">
674
+ <div class="bot-name">${bot.name}</div>
675
+ ${activeBadge}
676
+ </div>
677
+ <div class="bot-stats">
678
+ <div class="stat-item">
679
+ <div class="stat-label">Uptime</div>
680
+ <div class="stat-value">${formatTimeShort(bot.uptime)}</div>
681
+ </div>
682
+ <div class="stat-item">
683
+ <div class="stat-label">Deaths</div>
684
+ <div class="stat-value">${bot.deaths}</div>
685
+ </div>
686
+ <div class="stat-item">
687
+ <div class="stat-label">Reconnects</div>
688
+ <div class="stat-value">${bot.reconnects}</div>
689
+ </div>
690
+ <div class="stat-item">
691
+ <div class="stat-label">Position</div>
692
+ <div class="stat-value">${bot.position}</div>
693
+ </div>
694
+ </div>
695
+ <div class="bot-status-text ${statusClass}">${statusText}</div>
696
+ `;
697
+
698
+ grid.appendChild(card);
699
+ });
700
+ }
701
+
702
+ function formatTime(seconds) {
703
+ if (!seconds || seconds < 0) return '00:00:00';
704
+ const h = Math.floor(seconds / 3600);
705
+ const m = Math.floor((seconds % 3600) / 60);
706
+ const s = seconds % 60;
707
+ return `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;
708
+ }
709
+
710
+ function formatTimeShort(seconds) {
711
+ if (!seconds || seconds < 0) return '0m';
712
+ const h = Math.floor(seconds / 3600);
713
+ const m = Math.floor((seconds % 3600) / 60);
714
+ if (h > 0) return `${h}h ${m}m`;
715
+ return `${m}m`;
716
+ }
717
+
718
+ async function forceRotation() {
719
+ if (!confirm('Switch to the next bot now?')) return;
720
+
721
+ // For demo mode
722
+ simulatedTime = ROTATION_DURATION;
723
+ updateSimulation();
724
+ renderStatus();
725
+ showMsg('✅ Switching to next bot...', 'success');
726
+
727
+ // For production, uncomment this:
728
+ /*
729
+ try {
730
+ const r = await fetch('/api/next_rotation', {method: 'POST'});
731
+ const res = await r.json();
732
+
733
+ if (res.success) {
734
+ showMsg('✅ Switching to next bot...', 'success');
735
+ setTimeout(fetchStatus, 1000);
736
+ } else {
737
+ showMsg('❌ ' + (res.error || 'Failed to rotate'), 'error');
738
+ }
739
+ } catch(e) {
740
+ showMsg('❌ Failed to switch bot', 'error');
741
+ }
742
+ */
743
+ }
744
+
745
+ function refreshStatus() {
746
+ renderStatus();
747
+ showMsg('🔄 Refreshing...', 'success');
748
+ }
749
+
750
+ function showMsg(text, type = 'success') {
751
+ const container = document.getElementById('msg-container');
752
+ const msg = document.createElement('div');
753
+ msg.className = 'msg' + (type === 'error' ? ' error' : '');
754
+ msg.textContent = text;
755
+ container.appendChild(msg);
756
+
757
+ setTimeout(() => {
758
+ msg.remove();
759
+ }, 3000);
760
+ }
761
+
762
+ function startAutoUpdate() {
763
+ if (updateTimer) clearInterval(updateTimer);
764
+
765
+ // Update every second for demo
766
+ updateTimer = setInterval(() => {
767
+ updateSimulation();
768
+ renderStatus();
769
+ }, 1000);
770
+ }
771
+
772
+ // Initialize
773
+ initializeDemoData();
774
+ renderStatus();
775
+ startAutoUpdate();
776
+
777
+ // For production with backend API, use this instead:
778
+ /*
779
+ fetchStatus();
780
+ setInterval(fetchStatus, 2000); // Update every 2 seconds
781
+ */
782
+ </script>
783
+ </body>
784
+ </html>
index.j DELETED
@@ -1,338 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Bot Manager</title>
7
- <script src="/socket.io/socket.io.js"></script>
8
- <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: Arial, sans-serif;
17
- background: #1a1a1a;
18
- color: #e0e0e0;
19
- padding: 10px;
20
- }
21
-
22
- .header {
23
- background: #2a2a2a;
24
- padding: 15px;
25
- margin-bottom: 10px;
26
- border: 1px solid #333;
27
- }
28
-
29
- h1 {
30
- font-size: 20px;
31
- margin-bottom: 10px;
32
- color: #fff;
33
- }
34
-
35
- .stats {
36
- display: flex;
37
- gap: 15px;
38
- flex-wrap: wrap;
39
- }
40
-
41
- .stat {
42
- background: #333;
43
- padding: 8px 12px;
44
- border: 1px solid #444;
45
- color: #ccc;
46
- }
47
-
48
- .stat strong {
49
- color: #fff;
50
- }
51
-
52
- .controls {
53
- background: #2a2a2a;
54
- padding: 10px;
55
- margin-bottom: 10px;
56
- border: 1px solid #333;
57
- }
58
-
59
- .btn {
60
- background: #4CAF50;
61
- color: white;
62
- border: none;
63
- padding: 8px 16px;
64
- cursor: pointer;
65
- }
66
-
67
- .btn:hover {
68
- background: #45a049;
69
- }
70
-
71
- .btn:disabled {
72
- background: #555;
73
- color: #999;
74
- cursor: not-allowed;
75
- }
76
-
77
- .bot-grid {
78
- display: grid;
79
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
80
- gap: 10px;
81
- }
82
-
83
- .bot-card {
84
- background: #2a2a2a;
85
- border: 1px solid #333;
86
- padding: 10px;
87
- }
88
-
89
- .bot-header {
90
- display: flex;
91
- justify-content: space-between;
92
- margin-bottom: 8px;
93
- align-items: center;
94
- }
95
-
96
- .bot-name {
97
- font-weight: bold;
98
- font-size: 14px;
99
- color: #fff;
100
- }
101
-
102
- .status {
103
- font-size: 12px;
104
- padding: 2px 6px;
105
- border: 1px solid;
106
- }
107
-
108
- .status-connected {
109
- background: #1b4332;
110
- color: #4ade80;
111
- border-color: #16a34a;
112
- }
113
-
114
- .status-disconnected, .status-dead {
115
- background: #450a0a;
116
- color: #f87171;
117
- border-color: #dc2626;
118
- }
119
-
120
- .bot-info {
121
- font-size: 12px;
122
- color: #aaa;
123
- margin-bottom: 8px;
124
- }
125
-
126
- .bot-info div {
127
- margin: 2px 0;
128
- }
129
-
130
- .info-highlight {
131
- color: #4ade80;
132
- }
133
-
134
- .btn-rejoin {
135
- background: #2196F3;
136
- color: white;
137
- border: none;
138
- padding: 6px 12px;
139
- cursor: pointer;
140
- font-size: 12px;
141
- width: 100%;
142
- }
143
-
144
- .btn-rejoin:hover {
145
- background: #1976D2;
146
- }
147
-
148
- .btn-rejoin:disabled {
149
- background: #555;
150
- color: #999;
151
- cursor: not-allowed;
152
- }
153
-
154
- .timer {
155
- font-size: 11px;
156
- color: #888;
157
- text-align: center;
158
- margin-top: 4px;
159
- }
160
-
161
- .loading {
162
- padding: 20px;
163
- color: #888;
164
- text-align: center;
165
- }
166
-
167
- @media (max-width: 600px) {
168
- .bot-grid {
169
- grid-template-columns: 1fr;
170
- }
171
-
172
- h1 {
173
- font-size: 18px;
174
- }
175
-
176
- .stats {
177
- font-size: 12px;
178
- }
179
- }
180
- </style>
181
- </head>
182
- <body>
183
- <div class="header">
184
- <h1>🤖 Bot Manager</h1>
185
- <div class="stats">
186
- <div class="stat">Total: <strong id="totalBots">0</strong></div>
187
- <div class="stat">Connected: <strong id="connectedBots">0</strong></div>
188
- <div class="stat">Disconnected: <strong id="disconnectedBots">0</strong></div>
189
- <div class="stat">Deaths: <strong id="totalDeaths">0</strong></div>
190
- </div>
191
- </div>
192
-
193
- <div class="controls">
194
- <button class="btn" onclick="refreshSheet()">Refresh Sheet</button>
195
- </div>
196
-
197
- <div id="botContainer" class="bot-grid">
198
- <div class="loading">Loading...</div>
199
- </div>
200
-
201
- <script>
202
- const socket = io();
203
- let botsData = [];
204
-
205
- socket.on('connect', () => {
206
- console.log('Connected to server');
207
- });
208
-
209
- socket.on('botUpdate', (data) => {
210
- botsData = data;
211
- updateUI();
212
- });
213
-
214
- socket.on('reconnectResult', (result) => {
215
- if (!result.success) {
216
- alert(`Cannot reconnect ${result.botName} yet. Wait 1 hour between reconnects.`);
217
- }
218
- });
219
-
220
- function updateUI() {
221
- const container = document.getElementById('botContainer');
222
-
223
- // Filter out bots not in sheet
224
- const validBots = botsData.filter(bot => bot.inSheet);
225
-
226
- if (validBots.length === 0) {
227
- container.innerHTML = '<div class="loading">No bots configured in sheet.</div>';
228
- updateStats(0, 0, 0, 0);
229
- return;
230
- }
231
-
232
- let html = '';
233
- let totalBots = 0;
234
- let connectedBots = 0;
235
- let disconnectedBots = 0;
236
- let totalDeaths = 0;
237
-
238
- validBots.forEach(bot => {
239
- totalBots++;
240
- if (bot.status === 'Connected') {
241
- connectedBots++;
242
- } else {
243
- disconnectedBots++;
244
- }
245
- totalDeaths += bot.deathCount;
246
-
247
- const statusClass = bot.status === 'Connected' ? 'status-connected' :
248
- bot.status === 'Connecting...' ? 'status-connected' :
249
- 'status-disconnected';
250
-
251
- const showRejoinButton = (bot.status === 'Disconnected' || bot.status === 'Dead' ||
252
- bot.status === 'Connection Failed' || bot.status === 'Server already has a bot') &&
253
- bot.inSheet;
254
- const canRejoinNow = bot.canReconnect;
255
- const timeRemaining = bot.timeUntilReconnect;
256
-
257
- html += `
258
- <div class="bot-card">
259
- <div class="bot-header">
260
- <div class="bot-name">${bot.botName}</div>
261
- <div class="status ${statusClass}">${bot.status}</div>
262
- </div>
263
- <div class="bot-info">
264
- <div>Deaths: ${bot.deathCount}</div>
265
- ${bot.status === 'Connected' && bot.connectedDuration > 0 ?
266
- `<div class="info-highlight">Connected: ${formatUptime(bot.connectedDuration)}</div>` : ''}
267
- </div>
268
- ${showRejoinButton ? `
269
- <button class="btn-rejoin"
270
- onclick="reconnectBot('${bot.botName}')"
271
- ${!canRejoinNow ? 'disabled' : ''}>
272
- ${canRejoinNow ? 'Rejoin' : 'Wait to Rejoin'}
273
- </button>
274
- ${!canRejoinNow && timeRemaining > 0 ?
275
- `<div class="timer">Can rejoin in: ${formatUptime(timeRemaining)}</div>` : ''}
276
- ` : ''}
277
- </div>
278
- `;
279
- });
280
-
281
- container.innerHTML = html;
282
- updateStats(totalBots, connectedBots, disconnectedBots, totalDeaths);
283
- }
284
-
285
- function updateStats(total, connected, disconnected, deaths) {
286
- document.getElementById('totalBots').textContent = total;
287
- document.getElementById('connectedBots').textContent = connected;
288
- document.getElementById('disconnectedBots').textContent = disconnected;
289
- document.getElementById('totalDeaths').textContent = deaths;
290
- }
291
-
292
- function formatUptime(seconds) {
293
- if (seconds === 0) return '0s';
294
- const days = Math.floor(seconds / 86400);
295
- const hours = Math.floor((seconds % 86400) / 3600);
296
- const minutes = Math.floor((seconds % 3600) / 60);
297
- const secs = seconds % 60;
298
-
299
- if (days > 0) {
300
- return `${days}d ${hours}h ${minutes}m`;
301
- } else if (hours > 0) {
302
- return `${hours}h ${minutes}m`;
303
- } else if (minutes > 0) {
304
- return `${minutes}m ${secs}s`;
305
- } else {
306
- return `${secs}s`;
307
- }
308
- }
309
-
310
- function reconnectBot(botName) {
311
- socket.emit('reconnectBot', botName);
312
- }
313
-
314
- function refreshSheet() {
315
- socket.emit('refreshSheet');
316
- }
317
-
318
- // Update UI every 2 seconds to show timer countdown and connection duration
319
- setInterval(() => {
320
- if (botsData.length > 0) {
321
- botsData.forEach(bot => {
322
- if (bot.timeUntilReconnect > 0) {
323
- bot.timeUntilReconnect = Math.max(0, bot.timeUntilReconnect - 2);
324
- }
325
- if (bot.status === 'Connected' && bot.connectedDuration >= 0) {
326
- bot.connectedDuration += 2;
327
- }
328
- bot.canReconnect = bot.timeUntilReconnect === 0 &&
329
- (bot.status === 'Disconnected' || bot.status === 'Dead' ||
330
- bot.status === 'Connection Failed' || bot.status === 'Server already has a bot') &&
331
- bot.inSheet;
332
- });
333
- updateUI();
334
- }
335
- }, 2000);
336
- </script>
337
- </body>
338
- </html>