mirror of
https://github.com/JonasunderscoreJones/turbo-octo-potato.git
synced 2025-10-24 10:39:19 +02:00
165 lines
7.2 KiB
Python
165 lines
7.2 KiB
Python
import json
|
|
import time
|
|
import importlib
|
|
import os
|
|
import logging
|
|
import threading
|
|
import sys
|
|
import requests
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
class ScriptConfig:
|
|
def __init__(self, name, filename, method, interval, retry_on_error, launch_flags):
|
|
self.name = name
|
|
self.filename = filename
|
|
self.method = method
|
|
self.interval = interval
|
|
self.retry_on_error = retry_on_error
|
|
self.launch_flags = launch_flags # This will store the flags
|
|
self.failure_count = 0 # Track the failure count
|
|
|
|
def __repr__(self):
|
|
return f"ScriptConfig(name={self.name}, filename={self.filename}, method={self.method}, interval={self.interval}, retry_on_error={self.retry_on_error}, launch_flags={self.launch_flags}, failure_count={self.failure_count})"
|
|
|
|
|
|
class ScriptRunner:
|
|
def __init__(self, config_file):
|
|
self.scripts = []
|
|
self.load_config(config_file)
|
|
# Retrieve the Discord webhook URLs from the environment variables
|
|
self.logs_webhook_url = os.getenv('SCRIPT_LOGS_DISCORD_WEBHOOK_URL')
|
|
self.errors_webhook_url = os.getenv('SCRIPT_ERROR_DISCORD_WEBHOOK_URL')
|
|
|
|
def load_config(self, config_file):
|
|
"""Load script configurations from a JSON file."""
|
|
if not os.path.exists(config_file):
|
|
logging.error(f"Config file '{config_file}' not found.")
|
|
return
|
|
|
|
with open(config_file, 'r') as f:
|
|
data = json.load(f)
|
|
for item in data:
|
|
# Read launch_flags as a list (it could be an empty list if no flags are present)
|
|
launch_flags = item.get('launch_flags', [])
|
|
|
|
script_config = ScriptConfig(
|
|
name=item['name'],
|
|
filename=item['filename'],
|
|
method=item['method'],
|
|
interval=item['interval'],
|
|
retry_on_error=item['retry_on_error'].lower() == 'true',
|
|
launch_flags=launch_flags # Store launch flags
|
|
)
|
|
self.scripts.append(script_config)
|
|
|
|
def send_to_discord(self, webhook_url, message):
|
|
"""Send a message to a Discord webhook."""
|
|
if webhook_url:
|
|
try:
|
|
payload = {'content': message}
|
|
requests.post(webhook_url, json=payload)
|
|
except Exception as e:
|
|
logging.error(f"Failed to send message to Discord: {e}")
|
|
|
|
def run_script(self, script_config):
|
|
"""Run the script as per configuration."""
|
|
script_path = os.path.join(os.getcwd(), script_config.filename)
|
|
|
|
if not os.path.exists(script_path):
|
|
logging.error(f"Script file '{script_config.filename}' not found in the current directory.")
|
|
return
|
|
|
|
try:
|
|
# Import the script as a module
|
|
script_name = script_config.filename[:-3] # Strip ".py" from the filename
|
|
module = importlib.import_module(script_name)
|
|
|
|
# Temporarily modify sys.argv to simulate command-line arguments
|
|
original_argv = sys.argv
|
|
sys.argv = [script_name] + script_config.launch_flags # Simulate command-line args
|
|
|
|
# Check if method exists
|
|
if hasattr(module, script_config.method):
|
|
method = getattr(module, script_config.method)
|
|
retry_count = 0
|
|
while True:
|
|
try:
|
|
logging.info(f"Running {script_config.name} with flags: {script_config.launch_flags}...")
|
|
# Call the method without needing to pass arguments (sys.argv is used)
|
|
method()
|
|
# Log successful run to Discord
|
|
if self.logs_webhook_url:
|
|
self.send_to_discord(self.logs_webhook_url, f"Successfully ran {script_config.name}")
|
|
logging.info(f"Completed {script_config.name}.")
|
|
time.sleep(script_config.interval) # Sleep after execution before next run
|
|
script_config.failure_count = 0 # Reset failure count after a successful run
|
|
except Exception as e:
|
|
logging.error(f"Error running {script_config.name}: {e}")
|
|
script_config.failure_count += 1
|
|
if script_config.failure_count >= 3: # Notify on third failure
|
|
error_message = f"{script_config.name} has failed 3 times. Last error: {e}"
|
|
if self.errors_webhook_url:
|
|
self.send_to_discord(self.errors_webhook_url, error_message)
|
|
logging.error(f"Max retry attempts reached for {script_config.name}. Error sent to Discord.")
|
|
return 500 # return error code
|
|
if script_config.retry_on_error:
|
|
retry_count += 1
|
|
if retry_count >= 3: # Retry 3 times
|
|
logging.error(f"Max retry attempts reached for {script_config.name}.")
|
|
return 500
|
|
logging.info(f"Retrying {script_config.name}... ({retry_count}/3)")
|
|
else:
|
|
break
|
|
else:
|
|
logging.error(f"Method '{script_config.method}' not found in {script_config.filename}")
|
|
|
|
# Restore the original sys.argv
|
|
sys.argv = original_argv
|
|
except Exception as e:
|
|
logging.error(f"Failed to run script {script_config.filename}: {e}")
|
|
return 404 # return not found if script not found
|
|
|
|
return 200 # return success
|
|
|
|
def run_script_in_thread(self, script_config):
|
|
"""Run the script in a separate thread with an endless loop."""
|
|
def target():
|
|
error_count = 0
|
|
while error_count < 3:
|
|
error_code = self.run_script(script_config)
|
|
if error_code == 404:
|
|
error_count += 1
|
|
time.sleep(5)
|
|
elif error_code == 500:
|
|
break
|
|
else:
|
|
error_count = 0 # Reset error count on success
|
|
|
|
logging.error(f"Script {script_config.name} has failed 3 times. Stopping execution.")
|
|
|
|
thread = threading.Thread(target=target)
|
|
thread.daemon = True # Allow the thread to exit when the main program exits
|
|
thread.start()
|
|
return thread
|
|
|
|
def run_all_scripts(self):
|
|
"""Start all scripts concurrently in separate threads with an endless loop."""
|
|
threads = []
|
|
for script_config in self.scripts:
|
|
thread = self.run_script_in_thread(script_config)
|
|
threads.append(thread)
|
|
|
|
# The main thread only needs to start the loops, it doesn't need to join since the threads are infinite
|
|
logging.info("All scripts have been started and are running in their respective threads.")
|
|
|
|
# Main execution
|
|
if __name__ == "__main__":
|
|
config_file = 'scripts_config.json'
|
|
runner = ScriptRunner(config_file)
|
|
runner.run_all_scripts()
|
|
|
|
# Keep the main program running (this is important for the daemon threads to keep running)
|
|
while True:
|
|
time.sleep(1)
|