Tokipo commited on
Commit
78662ef
·
verified ·
1 Parent(s): 50ddab5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -16
app.py CHANGED
@@ -9,13 +9,17 @@ import subprocess
9
  import signal
10
  import sys
11
 
12
-
13
-
14
  app = Flask(__name__)
15
 
 
 
 
 
16
  # Google Sheets configuration
 
17
  SHEET_ID = os.environ.get("SHEET_ID")
18
  SHEET_URL = f"https://docs.google.com/spreadsheets/d/{SHEET_ID}/export?format=csv"
 
19
  # Bot storage
20
  bots = {}
21
  bot_processes = {}
@@ -297,6 +301,13 @@ HTML_TEMPLATE = """
297
  font-size: 1.2em;
298
  margin-top: 50px;
299
  }
 
 
 
 
 
 
 
300
  </style>
301
  </head>
302
  <body>
@@ -330,6 +341,8 @@ HTML_TEMPLATE = """
330
  <button class="reload-sheet-btn" onclick="reloadSheet()">📊 Reload from Sheet</button>
331
  </div>
332
 
 
 
333
  <div class="bot-grid" id="bot-grid">
334
  <div class="loading">Loading bots...</div>
335
  </div>
@@ -341,10 +354,13 @@ HTML_TEMPLATE = """
341
  async function fetchBots() {
342
  try {
343
  const response = await fetch('/api/bots');
 
344
  botsData = await response.json();
345
  updateUI();
 
346
  } catch (error) {
347
  console.error('Error fetching bots:', error);
 
348
  }
349
  }
350
 
@@ -360,8 +376,9 @@ HTML_TEMPLATE = """
360
  const result = await response.json();
361
  if (result.error) {
362
  alert(result.error);
 
 
363
  }
364
- fetchBots();
365
  } catch (error) {
366
  console.error('Error rejoining bot:', error);
367
  alert('Failed to rejoin bot');
@@ -375,12 +392,24 @@ HTML_TEMPLATE = """
375
  async function reloadSheet() {
376
  try {
377
  const response = await fetch('/api/refresh', {method: 'POST'});
 
378
  await fetchBots();
 
379
  } catch (error) {
380
  console.error('Error reloading sheet:', error);
 
381
  }
382
  }
383
 
 
 
 
 
 
 
 
 
 
384
  function formatTime(seconds) {
385
  const minutes = Math.floor(seconds / 60);
386
  const secs = seconds % 60;
@@ -413,14 +442,17 @@ HTML_TEMPLATE = """
413
  timeRemaining = `⏱️ ${formatTime(botInfo.time_until_rejoin)}`;
414
  }
415
 
 
 
 
416
  card.innerHTML = `
417
- <div class="bot-name" title="${botName}">🎮 ${botName}</div>
418
  <div class="bot-info">📦 Version: ${botInfo.version}</div>
419
  <div class="status ${statusClass}">${botInfo.status}</div>
420
  ${timeRemaining ? `<div class="timer">${timeRemaining}</div>` : ''}
421
  ${botInfo.status === 'disconnected' ?
422
  `<button class="btn btn-rejoin"
423
- onclick="rejoinBot('${botName}')"
424
  ${!canRejoin ? 'disabled' : ''}>
425
  ${canRejoin ? '🔄 Rejoin' : '⏳ ' + (timeRemaining || 'Cooldown')}
426
  </button>` : ''}
@@ -448,10 +480,19 @@ HTML_TEMPLATE = """
448
  """
449
 
450
  def write_bot_script():
451
- with open('bot.js', 'w') as f:
452
- f.write(BOT_SCRIPT)
 
 
 
 
 
 
 
 
453
 
454
  def fetch_sheet_data():
 
455
  try:
456
  response = requests.get(SHEET_URL, timeout=10)
457
  if response.status_code == 200:
@@ -459,27 +500,30 @@ def fetch_sheet_data():
459
  if len(lines) > 1:
460
  data = []
461
  for line in lines[1:]: # Skip header
 
462
  parts = line.split(',')
463
  if len(parts) >= 3:
464
- bot_name = parts[0].strip()
465
- ip = parts[1].strip()
466
- port = parts[2].strip()
467
- version = parts[3].strip() if len(parts) > 3 else "1.20.1"
468
 
469
  # Skip empty rows
470
- if bot_name and ip and port and ip != "" and port.isdigit():
471
  data.append({
472
  'bot_name': bot_name,
473
  'ip': ip,
474
  'port': port,
475
  'version': version if version else "1.20.1"
476
  })
 
477
  return data
478
  except Exception as e:
479
  print(f"Error fetching sheet: {e}")
480
  return []
481
 
482
  def start_bot(bot_data):
 
483
  bot_name = bot_data['bot_name']
484
  server_key = f"{bot_data['ip']}:{bot_data['port']}"
485
 
@@ -498,12 +542,14 @@ def start_bot(bot_data):
498
 
499
  # Start new bot process
500
  try:
 
501
  process = subprocess.Popen(
502
- ['node', 'bot.js', bot_name, bot_data['ip'], bot_data['port'], bot_data['version']],
503
  stdout=subprocess.PIPE,
504
  stderr=subprocess.STDOUT,
505
  text=True,
506
- bufsize=1
 
507
  )
508
 
509
  bot_processes[bot_name] = process
@@ -521,9 +567,11 @@ def start_bot(bot_data):
521
  threading.Thread(target=monitor_bot, args=(bot_name,), daemon=True).start()
522
  return True, "Bot started"
523
  except Exception as e:
 
524
  return False, str(e)
525
 
526
  def monitor_bot(bot_name):
 
527
  if bot_name not in bot_processes:
528
  return
529
 
@@ -555,6 +603,7 @@ def monitor_bot(bot_name):
555
  time.sleep(0.1)
556
 
557
  def update_bots_from_sheet():
 
558
  print("Updating bots from sheet...")
559
  sheet_data = fetch_sheet_data()
560
 
@@ -577,6 +626,7 @@ def update_bots_from_sheet():
577
  if server_key in server_bots:
578
  del server_bots[server_key]
579
  del bots[bot_name]
 
580
 
581
  # Add new bots
582
  for bot_data in sheet_data:
@@ -592,6 +642,7 @@ def index():
592
 
593
  @app.route('/api/bots')
594
  def get_bots():
 
595
  result = {}
596
  current_time = datetime.now()
597
 
@@ -616,6 +667,7 @@ def get_bots():
616
 
617
  @app.route('/api/rejoin', methods=['POST'])
618
  def rejoin_bot():
 
619
  data = request.json
620
  bot_name = data.get('bot_name')
621
 
@@ -651,10 +703,12 @@ def rejoin_bot():
651
 
652
  @app.route('/api/refresh', methods=['POST'])
653
  def refresh():
 
654
  update_bots_from_sheet()
655
  return jsonify({'success': True})
656
 
657
  def signal_handler(sig, frame):
 
658
  print("\nShutting down gracefully...")
659
  for bot_name, process in bot_processes.items():
660
  try:
@@ -667,8 +721,10 @@ if __name__ == '__main__':
667
  signal.signal(signal.SIGINT, signal_handler)
668
  signal.signal(signal.SIGTERM, signal_handler)
669
 
670
- # Write bot script
671
- write_bot_script()
 
 
672
 
673
  # Initial load from sheet
674
  print("Loading bots from Google Sheet...")
 
9
  import signal
10
  import sys
11
 
 
 
12
  app = Flask(__name__)
13
 
14
+ # Working directory for bot files
15
+ WORK_DIR = "/tmp"
16
+ BOT_SCRIPT_PATH = os.path.join(WORK_DIR, "bot.js")
17
+
18
  # Google Sheets configuration
19
+
20
  SHEET_ID = os.environ.get("SHEET_ID")
21
  SHEET_URL = f"https://docs.google.com/spreadsheets/d/{SHEET_ID}/export?format=csv"
22
+
23
  # Bot storage
24
  bots = {}
25
  bot_processes = {}
 
301
  font-size: 1.2em;
302
  margin-top: 50px;
303
  }
304
+ .error-msg {
305
+ background: #fee;
306
+ color: #c00;
307
+ padding: 10px;
308
+ border-radius: 5px;
309
+ margin: 10px 0;
310
+ }
311
  </style>
312
  </head>
313
  <body>
 
341
  <button class="reload-sheet-btn" onclick="reloadSheet()">📊 Reload from Sheet</button>
342
  </div>
343
 
344
+ <div id="error-container"></div>
345
+
346
  <div class="bot-grid" id="bot-grid">
347
  <div class="loading">Loading bots...</div>
348
  </div>
 
354
  async function fetchBots() {
355
  try {
356
  const response = await fetch('/api/bots');
357
+ if (!response.ok) throw new Error('Failed to fetch');
358
  botsData = await response.json();
359
  updateUI();
360
+ clearError();
361
  } catch (error) {
362
  console.error('Error fetching bots:', error);
363
+ showError('Failed to fetch bot status');
364
  }
365
  }
366
 
 
376
  const result = await response.json();
377
  if (result.error) {
378
  alert(result.error);
379
+ } else {
380
+ await fetchBots();
381
  }
 
382
  } catch (error) {
383
  console.error('Error rejoining bot:', error);
384
  alert('Failed to rejoin bot');
 
392
  async function reloadSheet() {
393
  try {
394
  const response = await fetch('/api/refresh', {method: 'POST'});
395
+ if (!response.ok) throw new Error('Failed to reload');
396
  await fetchBots();
397
+ clearError();
398
  } catch (error) {
399
  console.error('Error reloading sheet:', error);
400
+ showError('Failed to reload from sheet');
401
  }
402
  }
403
 
404
+ function showError(message) {
405
+ const container = document.getElementById('error-container');
406
+ container.innerHTML = `<div class="error-msg">${message}</div>`;
407
+ }
408
+
409
+ function clearError() {
410
+ document.getElementById('error-container').innerHTML = '';
411
+ }
412
+
413
  function formatTime(seconds) {
414
  const minutes = Math.floor(seconds / 60);
415
  const secs = seconds % 60;
 
442
  timeRemaining = `⏱️ ${formatTime(botInfo.time_until_rejoin)}`;
443
  }
444
 
445
+ // Escape bot name for HTML
446
+ const safeBotName = botName.replace(/'/g, "\\'").replace(/"/g, "&quot;");
447
+
448
  card.innerHTML = `
449
+ <div class="bot-name" title="${safeBotName}">🎮 ${safeBotName}</div>
450
  <div class="bot-info">📦 Version: ${botInfo.version}</div>
451
  <div class="status ${statusClass}">${botInfo.status}</div>
452
  ${timeRemaining ? `<div class="timer">${timeRemaining}</div>` : ''}
453
  ${botInfo.status === 'disconnected' ?
454
  `<button class="btn btn-rejoin"
455
+ onclick="rejoinBot('${safeBotName}')"
456
  ${!canRejoin ? 'disabled' : ''}>
457
  ${canRejoin ? '🔄 Rejoin' : '⏳ ' + (timeRemaining || 'Cooldown')}
458
  </button>` : ''}
 
480
  """
481
 
482
  def write_bot_script():
483
+ """Write the bot script to /tmp directory"""
484
+ try:
485
+ os.makedirs(WORK_DIR, exist_ok=True)
486
+ with open(BOT_SCRIPT_PATH, 'w') as f:
487
+ f.write(BOT_SCRIPT)
488
+ print(f"Bot script written to {BOT_SCRIPT_PATH}")
489
+ return True
490
+ except Exception as e:
491
+ print(f"Error writing bot script: {e}")
492
+ return False
493
 
494
  def fetch_sheet_data():
495
+ """Fetch bot configuration from Google Sheets"""
496
  try:
497
  response = requests.get(SHEET_URL, timeout=10)
498
  if response.status_code == 200:
 
500
  if len(lines) > 1:
501
  data = []
502
  for line in lines[1:]: # Skip header
503
+ # Handle CSV parsing properly
504
  parts = line.split(',')
505
  if len(parts) >= 3:
506
+ bot_name = parts[0].strip().strip('"')
507
+ ip = parts[1].strip().strip('"')
508
+ port = parts[2].strip().strip('"')
509
+ version = parts[3].strip().strip('"') if len(parts) > 3 else "1.20.1"
510
 
511
  # Skip empty rows
512
+ if bot_name and ip and port and port.isdigit():
513
  data.append({
514
  'bot_name': bot_name,
515
  'ip': ip,
516
  'port': port,
517
  'version': version if version else "1.20.1"
518
  })
519
+ print(f"Fetched {len(data)} bots from sheet")
520
  return data
521
  except Exception as e:
522
  print(f"Error fetching sheet: {e}")
523
  return []
524
 
525
  def start_bot(bot_data):
526
+ """Start a Minecraft bot"""
527
  bot_name = bot_data['bot_name']
528
  server_key = f"{bot_data['ip']}:{bot_data['port']}"
529
 
 
542
 
543
  # Start new bot process
544
  try:
545
+ # Change working directory to /app where node_modules are installed
546
  process = subprocess.Popen(
547
+ ['node', BOT_SCRIPT_PATH, bot_name, bot_data['ip'], bot_data['port'], bot_data['version']],
548
  stdout=subprocess.PIPE,
549
  stderr=subprocess.STDOUT,
550
  text=True,
551
+ bufsize=1,
552
+ cwd='/app' # Use /app as working directory
553
  )
554
 
555
  bot_processes[bot_name] = process
 
567
  threading.Thread(target=monitor_bot, args=(bot_name,), daemon=True).start()
568
  return True, "Bot started"
569
  except Exception as e:
570
+ print(f"Error starting bot {bot_name}: {e}")
571
  return False, str(e)
572
 
573
  def monitor_bot(bot_name):
574
+ """Monitor bot process output"""
575
  if bot_name not in bot_processes:
576
  return
577
 
 
603
  time.sleep(0.1)
604
 
605
  def update_bots_from_sheet():
606
+ """Update bots from Google Sheet"""
607
  print("Updating bots from sheet...")
608
  sheet_data = fetch_sheet_data()
609
 
 
626
  if server_key in server_bots:
627
  del server_bots[server_key]
628
  del bots[bot_name]
629
+ print(f"Removed bot: {bot_name}")
630
 
631
  # Add new bots
632
  for bot_data in sheet_data:
 
642
 
643
  @app.route('/api/bots')
644
  def get_bots():
645
+ """Get all bot statuses"""
646
  result = {}
647
  current_time = datetime.now()
648
 
 
667
 
668
  @app.route('/api/rejoin', methods=['POST'])
669
  def rejoin_bot():
670
+ """Rejoin a disconnected bot"""
671
  data = request.json
672
  bot_name = data.get('bot_name')
673
 
 
703
 
704
  @app.route('/api/refresh', methods=['POST'])
705
  def refresh():
706
+ """Refresh bots from Google Sheet"""
707
  update_bots_from_sheet()
708
  return jsonify({'success': True})
709
 
710
  def signal_handler(sig, frame):
711
+ """Handle shutdown gracefully"""
712
  print("\nShutting down gracefully...")
713
  for bot_name, process in bot_processes.items():
714
  try:
 
721
  signal.signal(signal.SIGINT, signal_handler)
722
  signal.signal(signal.SIGTERM, signal_handler)
723
 
724
+ # Write bot script to /tmp
725
+ if not write_bot_script():
726
+ print("Failed to write bot script, exiting...")
727
+ sys.exit(1)
728
 
729
  # Initial load from sheet
730
  print("Loading bots from Google Sheet...")