from PIL import Image import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from io import BytesIO import matplotlib.dates as mdates from flask import Flask, request, render_template, redirect, url_for, session, jsonify, Response, send_file from flask_socketio import SocketIO, join_room, leave_room, emit from pymongo import MongoClient from bson.objectid import ObjectId import uuid import datetime from flask_wtf.csrf import CSRFProtect from werkzeug.security import generate_password_hash, check_password_hash import subprocess import signal import threading import psutil import time import json import requests import os from datetime import datetime, timedelta app = Flask(__name__, static_folder='/var/www/admin_website/static', template_folder='/var/www/admin_website/templates') app.secret_key = os.urandom(24) socketio = SocketIO(app, cors_allowed_origins="*", async_mode="threading") csrf = CSRFProtect(app) client = MongoClient("mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/") MONGO_URI = "mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/" db = client["moonlab"] collection = db["anti_cheat"] accounts_collection = db["website_accounts"] delete_requests_collection = db["delete_requests"] logs_collection = db["logs"] user_ages_collection = db["user_ages"] projects_collection = db["projects"] changelogs_collection = db["changelogs"] appointments_collection = db["appointments"] scpsl_playerdata_collection = db["scpsl-playerdata"] ETS2_DB_NAME = "ets2_license_db" ETS2_COLLECTION_NAME = "licenses" ets2_db = client[ETS2_DB_NAME] ets2_licenses = ets2_db[ETS2_COLLECTION_NAME] SCP_SL_API_URL = "https://api.scplist.kr/api/servers/85734" app.config.update( SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SECURE=True, SESSION_COOKIE_SAMESITE='Lax', REMEMBER_COOKIE_HTTPONLY=True, REMEMBER_COOKIE_SECURE=True, REMEMBER_COOKIE_SAMESITE='Lax' ) if accounts_collection.count_documents({}) == 0: accounts_collection.insert_one({ "username": "Admin", "display_name": "Admin", "password": generate_password_hash("1812"), "permissions": ["user_management", "permission_management", "delete_requests", "logs", "create_license_key", "view_license_keys", "create_event_feedback", "view_event_feedback", "verify_age", "project_manager", "ip_checker", "changelogs"] }) def generate_license_key(): return str(uuid.uuid4()) def add_license_key_to_db(license_key): if collection.find_one({"license_key": license_key}): return False else: collection.insert_one({"license_key": license_key, "status": "unused"}) return True def add_log(message): logs_collection.insert_one({"timestamp": datetime.now(), "message": message}) def stream_logs(process): def generate(): for line in iter(process.stdout.readline, b''): yield line.decode('utf-8') return Response(generate(), mimetype='text/plain') def load_bot_config(): try: with open("bot_config.json", "r") as file: return json.load(file) except FileNotFoundError: return {} def save_bot_config(config): with open("bot_config.json", "w") as file: json.dump(config, file, indent=4) bot_config = load_bot_config() @app.route("/website.py", methods=["GET"]) def deny_access(): return "Access denied", 403 @app.route("/", methods=["GET", "POST"]) def login(): error = None if request.method == "POST": username = request.form["username"] password = request.form["password"] remember_me = "remember_me" in request.form user = accounts_collection.find_one({"username": username}) if user and check_password_hash(user["password"], password): session["user_id"] = str(user["_id"]) session["username"] = user["username"] session["display_name"] = user.get("display_name", user["username"]) session["permissions"] = user.get("permissions", []) add_log(f"{username} hat sich eingeloggt") if remember_me: session.permanent = True return redirect(url_for("dashboard")) else: error = "Du hast ein(en) falsches Passwort/Benutzernamen eingegeben" return render_template("login.html", error=error) @app.route("/dashboard") def dashboard(): if "username" not in session: return redirect(url_for("login")) user = accounts_collection.find_one({"_id": ObjectId(session["user_id"])}) if not user: return redirect(url_for("login")) session["permissions"] = user.get("permissions", []) if "Style" not in user: accounts_collection.update_one({"_id": ObjectId(session["user_id"])}, {"$set": {"Style": "lightmode"}}) session["theme"] = "lightmode" else: session["theme"] = user["Style"] if session["theme"] == "darkmode": return render_template("dashboard_dark.html") else: return render_template("dashboard_light.html") @app.route('/view_changelogs', methods=['GET']) def view_changelogs(): if "username" not in session: return redirect(url_for("login")) changelogs = [ {"text": changelog["text"], "timestamp": changelog["timestamp"]} for changelog in changelogs_collection.find().sort("timestamp", -1) ] return render_template("view_changelogs.html", changelogs=changelogs) @app.route("/user_management", methods=["GET", "POST"]) def user_management(): if "username" not in session or "user_management" not in session["permissions"]: return redirect(url_for("login")) users = accounts_collection.find() return render_template("user_management.html", users=users) @app.route("/add_user", methods=["POST"]) def add_user(): if "username" not in session or "user_management" not in session["permissions"]: return redirect(url_for("login")) new_username = request.form["username"] new_display_name = request.form["display_name"] new_password = request.form["password"] new_permissions = request.form.getlist("permissions") if accounts_collection.find_one({"username": new_username}): return "Benutzername existiert bereits", 400 accounts_collection.insert_one({ "username": new_username, "display_name": new_display_name, "password": generate_password_hash(new_password), "permissions": new_permissions }) add_log(f"{session['username']} created or modified user {new_username}") return redirect(url_for("user_management")) @app.route("/change_permissions/", methods=["POST"]) def change_permissions(user_id): if "username" not in session or "permission_management" not in session["permissions"]: return redirect(url_for("login")) user = accounts_collection.find_one({"_id": ObjectId(user_id)}) if not user: return "User not found", 404 if "remove_permission" in request.form: permission_to_remove = request.form["remove_permission"] accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$pull": {"permissions": permission_to_remove}}) add_log(f"{session['username']} removed permission {permission_to_remove} from user {user['username']}") if "add_permission" in request.form: permission_to_add = request.form["add_permission"] accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$addToSet": {"permissions": permission_to_add}}) add_log(f"{session['username']} added permission {permission_to_add} to user {user['username']}") if str(user["_id"]) == session.get("user_id"): updated_user = accounts_collection.find_one({"_id": ObjectId(user_id)}) session["permissions"] = updated_user.get("permissions", []) return redirect(url_for("user_management")) @app.route("/delete_user/", methods=["POST"]) def delete_user(user_id): if "username" not in session or "user_management" not in session["permissions"]: return redirect(url_for("login")) user = accounts_collection.find_one({"_id": ObjectId(user_id)}) if user: accounts_collection.delete_one({"_id": ObjectId(user_id)}) add_log(f"{session['username']} deleted user {user['username']}") return redirect(url_for("user_management")) UPLOAD_FOLDER = "/var/www/admin_website/static/pfps" ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg"} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route("/edit_user/", methods=["GET", "POST"]) def edit_user(user_id): if "username" not in session: return redirect(url_for("login")) user = accounts_collection.find_one({"_id": ObjectId(user_id)}) if "permission_management" not in session["permissions"] and session["user_id"] != str(user["_id"]): return "Access denied", 403 if request.method == "POST": new_display_name = request.form.get("display_name") new_password = request.form.get("password") profile_pic = request.files.get("pfp") update_data = {"display_name": new_display_name} pfp_uploaded = False pfp_url = None if new_password: update_data["password"] = generate_password_hash(new_password, method='scrypt') if profile_pic and profile_pic.filename != "": ext = profile_pic.filename.rsplit('.', 1)[-1].lower() if ext in ["png", "jpg", "jpeg"]: filename = f"{user_id}.png" upload_folder = "/var/www/admin_website/static/pfps" os.makedirs(upload_folder, exist_ok=True) save_path = os.path.join(upload_folder, filename) try: img = Image.open(profile_pic.stream) if img.mode == 'RGBA' or (img.mode == 'P' and 'transparency' in img.info): img.save(save_path, "PNG") else: img = img.convert("RGB") img.save(save_path, "PNG") website_data = user.get("website_accounts", {}) website_data["pfp_last_updated"] = int(time.time()) website_data["pfp_filename"] = filename update_data["website_accounts"] = website_data pfp_uploaded = True pfp_url = f"/static/pfps/{filename}" except Exception as e: print(f"Fehler beim Speichern oder Konvertieren des Profilbildes: {e}") else: print(f"Unerlaubter Dateityp für Profilbild: {ext}") accounts_collection.update_one({"_id": ObjectId(user_id)}, {"$set": update_data}) if user_id == session.get("user_id"): session["display_name"] = new_display_name if pfp_uploaded: session["pfp_last_updated"] = update_data["website_accounts"]["pfp_last_updated"] if pfp_uploaded: return jsonify({"success": True, "message": "Profilbild erfolgreich aktualisiert!", "pfp_url": pfp_url}) else: return jsonify({"success": True, "message": "Profil erfolgreich aktualisiert!", "pfp_url": None}) return render_template("edit_user.html", user=user) @app.route('/get_data', methods=['GET']) def get_data(): if "username" not in session: return redirect(url_for("login")) data = collection.find() result = [] for doc in data: if 'ingame_name' in doc and doc['ingame_name']: doc['_id'] = str(doc['_id']) result.append(doc) return render_template("get_data.html", data=result) @app.route('/license=', methods=['GET']) def view_license(license_key): if "username" not in session: return redirect(url_for("login")) license_data = collection.find_one({"license_key": license_key}) if license_data: license_data['_id'] = str(license_data['_id']) return render_template("view_license.html", license_data=license_data) else: return "Lizenzschlüssel nicht gefunden", 404 @app.route('/delete_license/', methods=['POST']) def delete_license(license_key): if "username" not in session: return redirect(url_for("login")) collection.delete_one({"license_key": license_key}) add_log(f"{session['username']} hat Lizenzschlüssel {license_key} gelöscht") return redirect(url_for("get_data")) @app.route('/request_delete_license/', methods=['POST']) def request_delete_license(license_key): if "username" not in session: return redirect(url_for("login")) delete_requests_collection.insert_one({"license_key": license_key, "username": session["username"]}) add_log(f"{session['username']} hat eine Delete-Request für Lizenzschlüssel {license_key} gestellt") return redirect(url_for("get_data")) @app.route('/delete_requests', methods=['GET']) def delete_requests(): if "username" not in session or "delete_requests" not in session["permissions"]: return redirect(url_for("login")) delete_requests = list(delete_requests_collection.find()) return render_template("delete_request.html", delete_requests=delete_requests) @app.route('/approve_delete_request/', methods=['POST']) def approve_delete_request(request_id): if "username" not in session or "delete_requests" not in session["permissions"]: return redirect(url_for("login")) request = delete_requests_collection.find_one({"_id": ObjectId(request_id)}) if request: collection.delete_one({"license_key": request["license_key"]}) delete_requests_collection.delete_one({"_id": ObjectId(request_id)}) add_log(f"{session['username']} hat die Delete-Request für Lizenzschlüssel {request['license_key']} genehmigt") return redirect(url_for("delete_requests")) @app.route('/deny_delete_request/', methods=['POST']) def deny_delete_request(request_id): if "username" not in session or "delete_requests" not in session["permissions"]: return redirect(url_for("login")) delete_requests_collection.delete_one({"_id": ObjectId(request_id)}) add_log(f"{session['username']} hat die Delete-Request für Lizenzschlüssel {request_id} abgelehnt") return redirect(url_for("delete_requests")) @app.route('/add_data', methods=['POST']) def add_data(): if "username" not in session: return redirect(url_for("login")) new_data = request.get_json() result = collection.insert_one(new_data) return jsonify({"message": "Daten erfolgreich hinzugefügt", "inserted_id": str(result.inserted_id)}) @app.route('/create_license_key', methods=['GET', 'POST']) def create_license_key(): if "username" not in session or "create_license_key" not in session["permissions"]: return redirect(url_for("login")) if request.method == "POST": license_key = generate_license_key() if add_license_key_to_db(license_key): add_log(f"{session['username']} hat Lizenzschlüssel {license_key} erstellt") return render_template("create_license_key.html", license_key=license_key) else: return render_template("create_license_key.html", error="Fehler beim Erstellen des Lizenzschlüssels") return render_template("create_license_key.html") @app.route('/database_entries', methods=['GET']) def database_entries(): if "username" not in session: return redirect(url_for("login")) data = collection.find() result = [] for doc in data: doc['_id'] = str(doc['_id']) result.append(doc) return render_template("view_license.html", data=result) @app.route('/license_keys', methods=['GET']) def license_keys(): if "username" not in session: return redirect(url_for("login")) return render_template("license_keys.html") @app.route('/search_license_key', methods=['GET']) def search_license_key(): license_key = request.args.get('license_key') license_data = collection.find_one({"license_key": license_key}) if license_data: return jsonify({"found": True}) else: return jsonify({"found": False}) @app.route('/logs', methods=['GET']) def logs(): if "username" not in session or "logs" not in session["permissions"]: return redirect(url_for("login")) logs = list(logs_collection.find().sort("timestamp", -1)) return render_template("logs.html", logs=logs) @app.route('/event_feedback', methods=['GET', 'POST']) def event_feedback(): if "username" not in session or "create_event_feedback" not in session["permissions"]: return redirect(url_for("login")) if request.method == "POST": event_name = request.form["event_name"] if event_name == "Anderes": event_name = request.form["other_event_name"] event_progress = request.form["event_progress"] if event_progress == "Anderes": event_progress = request.form["other_event_progress"] event_end = request.form["event_end"] if event_end == "Anderes": event_end = request.form["other_event_end"] community_feedback = request.form["community_feedback"] if community_feedback == "Anderes": community_feedback = request.form["other_community_feedback"] improvement_suggestions = request.form["improvement_suggestions"] feedback_data = { "event_name": event_name, "event_progress": event_progress, "event_end": event_end, "community_feedback": community_feedback, "improvement_suggestions": improvement_suggestions, "timestamp": datetime.now(), "username": session["username"] } db.event_feedback.insert_one(feedback_data) add_log(f"{session['username']} hat Feedback für das Event {event_name} abgegeben") return redirect(url_for("dashboard")) return render_template("event_feedback.html") @app.route('/event=', methods=['GET']) def view_event_feedback(event_id): if "username" not in session: return redirect(url_for("login")) feedback = db.event_feedback.find_one({"_id": ObjectId(event_id)}) if feedback: feedback['_id'] = str(feedback['_id']) return render_template("view_event_feedback.html", feedback=feedback) else: return "Event-Feedback nicht gefunden", 404 @app.route('/view_event_feedbacks', methods=['GET']) def view_event_feedbacks(): if "username" not in session or "view_event_feedback" not in session["permissions"]: return redirect(url_for("login")) feedbacks = list(db.event_feedback.find().sort("timestamp", -1)) return render_template("view_event_feedbacks.html", feedbacks=feedbacks) @app.route("/events", methods=["GET", "POST"]) @csrf.exempt def events(): if "username" not in session: return redirect(url_for("login")) if request.method == "POST": event_name = request.form["event_name"] community_feedback = request.form["community_feedback"] players = int(request.form["players"]) time = request.form["time"] weekday = request.form["weekday"] top = 1 if community_feedback == "top" else 0 flop = 1 if community_feedback == "flop" else 0 etwas = 1 if community_feedback == "etwas" else 0 rating = top - flop + etwas event = db.events.find_one({"name": event_name, "time": time, "weekday": weekday}) if event: db.events.update_one( {"_id": event["_id"]}, {"$inc": {"top": top, "flop": flop, "etwas": etwas, "rating": rating}} ) add_log(f"{session['username']} updated event {event_name} with rating {rating}") else: db.events.insert_one({ "name": event_name, "players": players, "time": time, "weekday": weekday, "top": top, "flop": flop, "etwas": etwas, "rating": rating, "timestamp": datetime.now() }) add_log(f"{session['username']} added event {event_name} with rating {rating}") events = list(db.events.find().sort("timestamp", -1)) best_event = None best_events = None if request.args.get("calculate_best"): current_players = int(request.args.get("current_players")) current_time = request.args.get("current_time") current_weekday = request.args.get("current_weekday") event_type = request.args.get("event_type", None) best_events = calculate_best_events(current_players, current_time, current_weekday, event_type) if best_events: best_event = best_events[0] return render_template("events.html", events=events, best_event=best_event) def calculate_best_events(current_players, current_time, current_weekday, event_type=None): events = list(db.events.find()) scored_events = [] for event in events: similarity_score = 0 similarity_score -= abs(event['players'] - current_players) * 0.5 if event['weekday'] != current_weekday: similarity_score -= 1 similarity_score -= abs(int(event['time'].split(':')[0]) - int(current_time.split(':')[0])) * 0.2 similarity_score += (event['rating'] * 1.0) if event_type and event.get('type') == event_type: similarity_score += 0.1 scored_events.append((similarity_score, event)) scored_events.sort(reverse=True, key=lambda x: x[0]) return [e for _, e in scored_events[:5]] @app.route("/dc_bots", methods=["GET"]) def dc_bots(): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bots = list(db.dc_bots.find()) return render_template("dc_bots.html", bots=bots) @app.route("/add_dc_bot", methods=["POST"]) def add_dc_bot(): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot_name = request.form["bot_name"] bot_directory = request.form["bot_directory"] db.dc_bots.insert_one({"name": bot_name, "directory": bot_directory}) add_log(f"{session['username']} added bot {bot_name}") return redirect(url_for("dc_bots")) @app.route("/upload_bot_version/", methods=["POST"]) def upload_bot_version(bot_name): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot = db.dc_bots.find_one({"name": bot_name}) if not bot: return f"Bot {bot_name} nicht gefunden", 404 if "bot_file" not in request.files: return "Keine Datei hochgeladen", 400 bot_file = request.files["bot_file"] if bot_file.filename == "": return "Keine Datei ausgewählt", 400 version = bot.get("version", "1.0") major, minor = map(int, version.split(".")) new_version = f"{major}.{minor + 1}" base_directory_name = bot['directory'].rsplit('_v', 1)[0] if '_v' in bot['directory'] else bot['directory'] bot_directory_with_version = f"{base_directory_name}_v{new_version}" os.makedirs(bot_directory_with_version, exist_ok=True) bot_file.save(os.path.join(bot_directory_with_version, bot_file.filename)) db.dc_bots.update_one({"name": bot_name}, {"$set": {"directory": bot_directory_with_version, "version": new_version}}) add_log(f"{session['username']} uploaded new version {new_version} for bot {bot_name}") return redirect(url_for("dc_bots")) @app.route("/revert_bot_version//", methods=["POST"]) def revert_bot_version(bot_name, version): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot = db.dc_bots.find_one({"name": bot_name}) if not bot: return f"Bot {bot_name} nicht gefunden", 404 base_directory_name = bot['directory'].rsplit('_v', 1)[0] if '_v' in bot['directory'] else bot['directory'] bot_directory = f"{base_directory_name}_v{version}" if not os.path.exists(bot_directory): return f"Version {version} für Bot {bot_name} nicht gefunden", 404 db.dc_bots.update_one({"name": bot_name}, {"$set": {"directory": bot_directory, "version": version}}) add_log(f"{session['username']} reverted bot {bot_name} to version {version}") return redirect(url_for("dc_bots")) def initialize_bot_versions(): bots = db.dc_bots.find({"version": {"$exists": False}}) for bot in bots: db.dc_bots.update_one({"_id": bot["_id"]}, {"$set": {"version": "1.0"}}) add_log(f"Initialized version 1.0 for bot {bot['name']}") initialize_bot_versions() if db.events.count_documents({}) == 0: db.events.insert_one({ "name": "Sample Event", "top": 0, "flop": 0, "rating": 0, "timestamp": datetime.now() }) def save_logs_to_file(process, log_file_path): try: with open(log_file_path, "w") as log_file: while True: output = process.stdout.readline() error = process.stderr.readline() if output: log_file.write(repr(output) + "\n") log_file.flush() print(output, end="") if error: log_file.write("[ERROR] " + repr(error) + "\n") log_file.flush() print("[ERROR] " + error, end="") if process.poll() is not None: for line in process.stdout.readlines(): log_file.write(repr(line) + "\n") for line in process.stderr.readlines(): log_file.write("[ERROR] " + repr(line) + "\n") break time.sleep(0.5) except Exception as e: print(f"Fehler beim Speichern der Logs: {str(e)}") @app.route("/turn_on_bot/", methods=["POST"]) def turn_on_bot(bot_name): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot = db.dc_bots.find_one({"name": bot_name}) if bot: if "pid" in bot and psutil.pid_exists(bot["pid"]): add_log(f"Bot {bot_name} is already running with PID {bot['pid']}") return f"Bot {bot_name} läuft bereits.", 400 bot_directory = bot["directory"] try: command = ["python", bot_directory] process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) log_file_path = f"/tmp/{bot_name}_log.txt" db.dc_bots.update_one({"name": bot_name}, { "$set": { "pid": process.pid, "log_file": log_file_path } }) threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start() bot_config[bot_name] = {"status": "on", "pid": process.pid} save_bot_config(bot_config) add_log(f"{session['username']} turned on {bot_name}") except Exception as e: add_log(f"Error starting bot {bot_name}: {str(e)}") return f"Fehler beim Starten des Bots {bot_name}: {str(e)}", 500 else: return f"Bot {bot_name} nicht gefunden", 404 return redirect(url_for("dc_bots")) @app.route("/turn_off_bot/", methods=["POST"]) def turn_off_bot(bot_name): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot = db.dc_bots.find_one({"name": bot_name}) if bot and "pid" in bot: try: pid = bot["pid"] if psutil.pid_exists(pid): os.kill(pid, signal.SIGTERM) db.dc_bots.update_one({"name": bot_name}, {"$unset": {"pid": ""}}) bot_config[bot_name] = {"status": "off"} save_bot_config(bot_config) add_log(f"{session['username']} turned off {bot_name}") else: add_log(f"Process for bot {bot_name} does not exist") db.dc_bots.update_one({"name": bot_name}, {"$unset": {"pid": ""}}) except Exception as e: add_log(f"Error stopping bot {bot_name}: {str(e)}") return f"Fehler beim Stoppen des Bots {bot_name}: {str(e)}", 500 else: return f"Bot {bot_name} nicht gefunden oder kein aktiver Prozess vorhanden", 404 return redirect(url_for("dc_bots")) @app.route("/turn_on_all_bots", methods=["POST"]) def turn_on_all_bots(): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bots = list(db.dc_bots.find()) for bot in bots: if "pid" not in bot or not psutil.pid_exists(bot["pid"]): bot_directory = bot["directory"] try: command = ["python", bot_directory] process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) log_file_path = f"/tmp/{bot['name']}_log.txt" db.dc_bots.update_one({"name": bot["name"]}, { "$set": { "pid": process.pid, "log_file": log_file_path } }) threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start() bot_config[bot["name"]] = {"status": "on", "pid": process.pid} save_bot_config(bot_config) add_log(f"{session['username']} turned on {bot['name']}") except Exception as e: add_log(f"Error starting bot {bot['name']}: {str(e)}") return redirect(url_for("dc_bots")) @app.route("/turn_off_all_bots", methods=["POST"]) def turn_off_all_bots(): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bots = list(db.dc_bots.find()) for bot in bots: if "pid" in bot: try: pid = bot["pid"] if psutil.pid_exists(pid): os.kill(pid, signal.SIGTERM) db.dc_bots.update_one({"name": bot["name"]}, {"$unset": {"pid": ""}}) bot_config[bot["name"]] = {"status": "off"} save_bot_config(bot_config) add_log(f"{session['username']} turned off {bot['name']}") else: add_log(f"Process for bot {bot['name']} does not exist") db.dc_bots.update_one({"name": bot["name"]}, {"$unset": {"pid": ""}}) except Exception as e: add_log(f"Error stopping bot {bot['name']}: {str(e)}") return redirect(url_for("dc_bots")) @app.route("/bot_logs/", methods=["GET"]) def bot_logs(bot_name): if "username" not in session or "discord_bots" not in session["permissions"]: return redirect(url_for("login")) bot = db.dc_bots.find_one({"name": bot_name}) if bot and "log_file" in bot: log_file_path = bot["log_file"] try: with open(log_file_path, "r") as log_file: logs = log_file.readlines() except FileNotFoundError: logs = ["Keine Logs gefunden."] return render_template("bot_logs.html", bot_name=bot_name, logs=logs) else: return f"Logs für Bot {bot_name} nicht gefunden", 404 def start_bots_on_server_start(): for bot_name, config in bot_config.items(): if config["status"] == "on": bot = db.dc_bots.find_one({"name": bot_name}) if bot: if "pid" in bot and psutil.pid_exists(bot["pid"]): add_log(f"Bot {bot_name} is already running with PID {bot['pid']}") continue bot_directory = bot["directory"] try: command = ["python", bot_directory] process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) log_file_path = f"/tmp/{bot_name}_log.txt" db.dc_bots.update_one({"name": bot_name}, { "$set": { "pid": process.pid, "log_file": log_file_path } }) threading.Thread(target=save_logs_to_file, args=(process, log_file_path)).start() bot_config[bot_name] = {"status": "on", "pid": process.pid} save_bot_config(bot_config) add_log(f"Bot {bot_name} automatically started on server start") except Exception as e: add_log(f"Error starting bot {bot_name} on server start: {str(e)}") start_bots_on_server_start() @app.route('/logout') def logout(): add_log(f"{session['username']} hat sich ausgeloggt") session.pop("username", None) session.pop("permissions", None) session.pop("user_id", None) return redirect(url_for("login")) @app.route('/toggle-theme', methods=['POST']) @csrf.exempt def toggle_theme(): if "username" not in session: return "Unauthorized", 401 user = accounts_collection.find_one({"_id": ObjectId(session["user_id"])}) if not user: return "User not found", 404 current_theme = user.get("Style", "lightmode") new_theme = "darkmode" if current_theme == "lightmode" else "lightmode" accounts_collection.update_one({"_id": ObjectId(session["user_id"])}, {"$set": {"Style": new_theme}}) session["theme"] = new_theme return "", 204 @app.route("/ip_checker", methods=["GET", "POST"]) def ip_checker(): if "username" not in session or "ip_checker" not in session["permissions"]: return redirect(url_for("login")) import ipaddress ip_data = None error = None similar_ips = [] is_cheater_network = False similar_cheater_ips = [] if request.method == "POST": ip_address = request.form.get("ip_address") is_cheater = request.form.get("is_cheater") == "yes" steam_id = request.form.get("steam_id") if is_cheater else None if not ip_address: error = "Bitte eine IP-Adresse eingeben." elif is_cheater and not steam_id: error = "SteamID ist erforderlich, wenn die IP als Cheater markiert wird." else: try: api_key = "015ada9472664329affb2f26d7036d47" response = requests.get(f"https://vpnapi.io/api/{ip_address}?key={api_key}") if response.status_code == 200: ip_data = response.json() if "security" not in ip_data or not isinstance(ip_data["security"], dict): ip_data["security"] = {} if "location" not in ip_data or not isinstance(ip_data["location"], dict): ip_data["location"] = {} if "network" not in ip_data or not isinstance(ip_data["network"], dict): ip_data["network"] = {} checked_network = None if "network" in ip_data and "network" in ip_data["network"]: cidr = str(ip_data["network"]["network"]).replace(" ", "") try: checked_network = ipaddress.ip_network(cidr, strict=False) except Exception: checked_network = None cheater_ips = list(db.ip_infos.find({"is_cheater": True})) for cheater in cheater_ips: cheater_data = cheater.get("data", {}) cheater_network = None if "network" in cheater_data and "network" in cheater_data["network"]: cheater_cidr = str(cheater_data["network"]["network"]).replace(" ", "") try: cheater_network = ipaddress.ip_network(cheater_cidr, strict=False) except Exception: cheater_network = None cheater_ip = cheater.get("ip") if cheater_network and checked_network: if checked_network.overlaps(cheater_network): is_cheater_network = True similar_cheater_ips.append(cheater) if cheater_ip and checked_network: try: if ipaddress.ip_address(cheater_ip) in checked_network: is_cheater_network = True similar_cheater_ips.append(cheater) except Exception: pass similar_ips = list(db.ip_infos.find({ "$or": [ {"ip": ip_address}, {"data.location.city": ip_data["location"].get("city")}, {"data.network.autonomous_system_number": ip_data["network"].get( "autonomous_system_number")} ] })) db.ip_infos.insert_one({ "ip": ip_address, "is_cheater": is_cheater, "steam_id": steam_id, "data": ip_data, "added_by": session["username"], "timestamp": datetime.now() }) else: error = "Fehler beim Abrufen der IP-Daten." except Exception as e: error = f"Fehler: {str(e)}" return render_template( "ip_checker.html", ip_data=ip_data, error=error, similar_ips=similar_ips, is_cheater_network=is_cheater_network, similar_cheater_ips=similar_cheater_ips ) @app.route('/add-changelog', methods=['GET', 'POST']) def add_changelog(): if 'changelogs' not in session.get('permissions', []): return "Unauthorized", 403 if request.method == 'POST': changelog_text = request.form.get('changelog') if changelog_text: changelogs_collection.insert_one({ 'text': changelog_text, 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) return redirect(url_for('dashboard')) return render_template('add_changelog.html') @app.route("/appointments", methods=["GET", "POST"]) def appointments(): if "username" not in session or "appointments" not in session["permissions"]: return redirect(url_for("login")) if request.method == "POST": title = request.form["title"] start_date = request.form["start_date"] end_date = request.form["end_date"] description = request.form.get("description", "") appointments_collection.insert_one({ "username": session["username"], "title": title, "start_date": start_date, "end_date": end_date, "description": description, "created_at": datetime.now() }) add_log(f"{session['username']} hat einen neuen Termin erstellt: {title}") return redirect(url_for("appointments")) user_appointments = list(appointments_collection.find({"username": session["username"]}).sort("start_date", 1)) return render_template("appointments.html", appointments=user_appointments) @app.route("/delete_appointment/", methods=["POST"]) def delete_appointment(appointment_id): if "username" not in session or "appointments" not in session["permissions"]: return redirect(url_for("login")) appointment = appointments_collection.find_one({"_id": ObjectId(appointment_id), "username": session["username"]}) if appointment: appointments_collection.delete_one({"_id": ObjectId(appointment_id)}) add_log(f"{session['username']} hat den Termin '{appointment['title']}' gelöscht") return redirect(url_for("appointments")) @app.route("/edit_appointment/", methods=["GET", "POST"]) def edit_appointment(appointment_id): if "username" not in session or "appointments" not in session["permissions"]: return redirect(url_for("login")) appointment = appointments_collection.find_one({"_id": ObjectId(appointment_id), "username": session["username"]}) if not appointment: return "Termin nicht gefunden", 404 if request.method == "POST": title = request.form["title"] start_date = request.form["start_date"] end_date = request.form["end_date"] description = request.form.get("description", "") appointments_collection.update_one( {"_id": ObjectId(appointment_id)}, {"$set": { "title": title, "start_date": start_date, "end_date": end_date, "description": description }} ) add_log(f"{session['username']} hat den Termin '{title}' bearbeitet") return redirect(url_for("appointments")) return render_template("edit_appointment.html", appointment=appointment) @app.route("/pcmachwas", methods=["GET", "POST"]) def pcmachwas(): return render_template("stinker.html") @app.route("/settings", methods=["GET"]) def settings(): if "username" not in session: return redirect(url_for("login")) return redirect(url_for("edit_user", user_id=session["user_id"])) @app.route("/ets2_map_cracked") def ets2_map_cracked(): scs_dir = "/var/www/admin_website/static/ets2_map_cracked" if not os.path.isdir(scs_dir): return f"Verzeichnis nicht gefunden oder keine Rechte: {scs_dir}", 404 try: files = [ f for f in os.listdir(scs_dir) if f.endswith(".scs") and os.path.isfile(os.path.join(scs_dir, f)) ] except Exception as e: return f"Fehler beim Lesen des Verzeichnisses: {e}", 500 if not files: return "Keine .scs Dateien gefunden.", 404 links = "".join( f'' for i, fname in enumerate(files) ) js = f""" """ return f"

ETS2 Maps Cracked Downloads

{links}{js}

Downloads werden automatisch gestartet...

" @app.route("/download_ets2_map_cracked/") def download_ets2_map_cracked(filename): scs_dir = "/var/www/admin_website/static/ets2_map_cracked" file_path = os.path.join(scs_dir, filename) if os.path.isfile(file_path) and filename.endswith(".scs"): return send_file(file_path, as_attachment=True) return "Datei nicht gefunden.", 404 # wallpaper @app.route("/wallpaper") def wallpaper(): return render_template("wallpaper.html") # NEUE PROXY-ROUTE für die SCP:SL API @app.route('/api/scpsl_proxy') def scpsl_proxy(): try: response = requests.get(SCP_SL_API_URL, timeout=5) response.raise_for_status() return jsonify(response.json()) except requests.exceptions.Timeout: return jsonify({"error": "Request timed out"}), 504 except requests.exceptions.ConnectionError: return jsonify({"error": "Connection error"}), 503 except requests.exceptions.RequestException as e: return jsonify({"error": f"Failed to fetch data: {e}"}), 500 def fetch_and_store_player_count(): with app.app_context(): try: response = requests.get(SCP_SL_API_URL, timeout=5) response.raise_for_status() data = response.json() players_str_full = data.get('players', '0/0') current_players = 0 if '/' in players_str_full: try: current_players = int(players_str_full.split('/')[0]) except ValueError: current_players = 0 else: try: current_players = int(players_str_full) except ValueError: current_players = 0 scpsl_playerdata_collection.insert_one({ "timestamp": datetime.utcnow(), "players": current_players }) scpsl_playerdata_collection.create_index("timestamp") except requests.exceptions.RequestException as e: print(f"Error fetching player count: {e}") except Exception as e: print(f"Unexpected error fetching/storing to MongoDB: {e}") def background_task(): while True: fetch_and_store_player_count() time.sleep(60) threading.Thread(target=background_task, daemon=True).start() @app.route('/api/player_history') def player_history_api(): graph_data = [] one_day_ago = datetime.utcnow() - timedelta(hours=24) history_cursor = scpsl_playerdata_collection.find( {"timestamp": {"$gte": one_day_ago}}, {"_id": 0, "timestamp": 1, "players": 1} ).sort("timestamp", 1) zero_start = None for doc in history_cursor: t = doc["timestamp"] p = doc["players"] if p == 0: if zero_start is None: zero_start = t continue else: if zero_start is not None: duration = (t - zero_start).total_seconds() if duration > 20: graph_data.append({ "time": zero_start.isoformat() + "Z", "players": 0 }) graph_data.append({ "time": t.isoformat() + "Z", "players": p }) zero_start = None graph_data.append({ "time": t.isoformat() + "Z", "players": p }) if zero_start is not None: last_time = datetime.utcnow() duration = (last_time - zero_start).total_seconds() if duration > 20: graph_data.append({ "time": zero_start.isoformat() + "Z", "players": 0 }) graph_data.append({ "time": last_time.isoformat() + "Z", "players": 0 }) return jsonify(graph_data) @app.route('/player_history_graph') def player_history_graph(): html = """ SCP:SL Player Count (Letzte 24h) """ return html @app.route('/player_history_graph_img') def player_history_graph_img(): one_day_ago = datetime.utcnow() - timedelta(hours=24) history_cursor = scpsl_playerdata_collection.find( {"timestamp": {"$gte": one_day_ago}}, {"_id": 0, "timestamp": 1, "players": 1} ).sort("timestamp", 1) zero_start = None filtered_times = [] filtered_players = [] for doc in history_cursor: t = doc["timestamp"] p = doc["players"] if p == 0: if zero_start is None: zero_start = t continue else: if zero_start is not None: duration = (t - zero_start).total_seconds() if duration > 20: filtered_times.append(zero_start) filtered_players.append(0) filtered_times.append(t) filtered_players.append(p) zero_start = None filtered_times.append(t) filtered_players.append(p) if zero_start is not None: last_time = datetime.utcnow() duration = (last_time - zero_start).total_seconds() if duration > 20: filtered_times.append(zero_start) filtered_players.append(0) filtered_times.append(last_time) filtered_players.append(0) fig, ax = plt.subplots(figsize=(10, 6)) if not filtered_times: ax.text(0.5, 0.5, 'No player data available for the last 24 hours.', horizontalalignment='center', verticalalignment='center', transform=ax.transAxes, fontsize=12, color='red') ax.set_ylim(bottom=0, top=36) else: ax.plot(filtered_times, filtered_players, color='lime', linewidth=2) max_players_in_history = max(filtered_players) if filtered_players else 0 if max_players_in_history > 0: ax.set_ylim(bottom=0, top=max_players_in_history + 5) else: ax.set_ylim(bottom=0, top=36) ax.set_title('SCP:SL Player Count (Letzte 24h)', color='white') ax.set_xlabel('Zeit', color='white') ax.set_ylabel('Spieleranzahl', color='white') ax.set_facecolor('#1a1a1a') fig.patch.set_facecolor('#1a1a1a') ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) ax.xaxis.set_major_locator(mdates.HourLocator(interval=3)) fig.autofmt_xdate() ax.tick_params(axis='x', colors='white') ax.tick_params(axis='y', colors='white') ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True)) ax.grid(True, linestyle='--', alpha=0.5, color='#444444') ax.spines['bottom'].set_color('white') ax.spines['top'].set_color('white') ax.spines['left'].set_color('white') ax.spines['right'].set_color('white') buffer = BytesIO() plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100) buffer.seek(0) plt.close(fig) return send_file(buffer, mimetype='image/png') # Voice-Channel Übersicht @app.route("/vcs") def vcs_overview(): # Beispiel: Channels können dynamisch sein, hier statisch channels = [ {"id": "talk1", "name": "Teamtalk"}, {"id": "talk2", "name": "Privat"}, {"id": "talk3", "name": "Privat 2"} ] return render_template("schisscord.html", channels=channels) # Voice-Channel Join @app.route("/join/") def join_vc(channel_id): if "username" not in session: return redirect(url_for("login")) return render_template("vc_room.html", channel_id=channel_id, username=session["username"]) # SocketIO: Nutzer tritt Channel bei @socketio.on("join_channel") def handle_join_channel(data): channel = data.get("channel") username = data.get("username") join_room(channel) emit("user_joined", {"username": username}, room=channel) @socketio.on("leave_channel") def handle_leave_channel(data): channel = data.get("channel") username = data.get("username") leave_room(channel) emit("user_left", {"username": username}, room=channel) @socketio.on("signal") def handle_signal(data): channel = data.get("channel") signal_data = data.get("signal") sender = data.get("username") emit("signal", {"signal": signal_data, "username": sender}, room=channel, include_self=False) MONGO_URI = "mongodb+srv://MrSniff:5169Galaxy@chainsaw.d0shx.mongodb.net/" client = MongoClient(MONGO_URI) ETS2_DB_NAME = "ets2_license_db" ETS2_COLLECTION_NAME = "licenses" ets2_db = client[ETS2_DB_NAME] ets2_licenses = ets2_db[ETS2_COLLECTION_NAME] print(f"[DEBUG] Using DB: {ETS2_DB_NAME}, Collection: {ETS2_COLLECTION_NAME}") # --- DLC Folder --- DLC_FOLDER = "/var/www/admin_website/static/ets2_dlcs" # --- License Check Endpoint --- @app.route("/checklicense", methods=["GET"]) def check_license(): key = request.args.get("key") user = request.args.get("user") print(f"[DEBUG] /checklicense called with key='{key}' user='{user}'") if not key or not user: print("[DEBUG] Missing key or user") return "invalid", 400 entry = ets2_licenses.find_one({"license_key": key}) print(f"[DEBUG] MongoDB entry found: {entry}") if entry: if not entry.get("windows_user"): ets2_licenses.update_one({"license_key": key}, {"$set": {"windows_user": user}}) print(f"[DEBUG] Key '{key}' registered to user '{user}'") return "valid" elif entry["windows_user"] == user: print(f"[DEBUG] Key '{key}' valid for user '{user}'") return "valid" else: print(f"[DEBUG] Key '{key}' already used by another user '{entry['windows_user']}'") return "invalid_user" else: print(f"[DEBUG] Key '{key}' not found in database") return "invalid" # --- DLC Download Endpoint --- @app.route("/download", methods=["GET"]) def download_dlc(): key = request.headers.get("License-Key") user = request.headers.get("Windows-User") dlc_type = request.args.get("type", "all") # "all_map_dlcs" oder "all_dlcs" print(f"[DEBUG] /download called with key='{key}', user='{user}', type='{dlc_type}'") if not key or not user: print("[DEBUG] Missing headers") return "missing_headers", 400 entry = ets2_licenses.find_one({"license_key": key}) print(f"[DEBUG] MongoDB entry for download: {entry}") if not entry or entry.get("windows_user") != user: print(f"[DEBUG] Unauthorized access for key '{key}' user '{user}'") return "unauthorized", 403 # Bestimme die Datei basierend auf DLC Typ if dlc_type == "all_map_dlcs": file_name = "all_map_dlcs.zip" else: file_name = "all_dlcs.zip" file_path = os.path.join(DLC_FOLDER, file_name) if not os.path.exists(file_path): print(f"[DEBUG] File not found at {file_path}") return "not_found", 404 print(f"[DEBUG] Sending file {file_path} to user '{user}'") return send_file(file_path, as_attachment=True) if __name__ == "__main__": print("[DEBUG] Starting ETS2 DLC Server") print(f"[DEBUG] Using DB: {ETS2_DB_NAME}, Collection: {ETS2_COLLECTION_NAME}") socketio.run(app, debug=True, host='0.0.0.0', port=5000)