diff --git a/app.py b/app.py index ab9bedf..5ea50a8 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,12 @@ -__version__ = "dev-0.4.7" +__version__ = "dev-0.4.6" __all__ = ["Discordbot-chatai-webpanel (Discord)"] __author__ = "SimolZimol" -from flask import Flask, render_template, redirect, url_for, request, session, jsonify, flash +from flask import Flask, render_template, redirect, url_for, request, session, jsonify, send_file, flash from requests_oauthlib import OAuth2Session import os import subprocess +import psutil import mysql.connector from datetime import datetime @@ -14,27 +15,49 @@ app.secret_key = os.getenv("FLASK_SECRET_KEY") LOG_FILE_PATH = os.path.join("logs", f"{datetime.now().strftime('%Y-%m-%d')}.log") -# Datenbankverbindung +# Verwende Umgebungsvariablen für die Datenbankverbindung DB_HOST = os.getenv("DB_HOST") DB_PORT = os.getenv("DB_PORT") DB_USER = os.getenv("DB_USER") DB_PASS = os.getenv("DB_PASSWORD") DB_NAME = os.getenv("DB_DATABASE") -# Discord OAuth2-Konfiguration DISCORD_CLIENT_ID = os.getenv("DISCORD_CLIENT_ID") DISCORD_CLIENT_SECRET = os.getenv("DISCORD_CLIENT_SECRET") DISCORD_REDIRECT_URI = os.getenv("DISCORD_REDIRECT_URI") DISCORD_OAUTH2_URL = "https://discord.com/api/oauth2/authorize" DISCORD_TOKEN_URL = "https://discord.com/api/oauth2/token" DISCORD_API_URL = "https://discord.com/api/users/@me" -DISCORD_GUILDS_URL = "https://discord.com/api/users/@me/guilds" +os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' -os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # nur für Entwicklungszwecke - -# Bot-Status +# Speichern der Prozess-ID bot_process = None +def bot_status(): + """Überprüft, ob der Bot läuft.""" + global bot_process + if bot_process is None: + return False + return bot_process.poll() is None # None bedeutet, dass der Prozess noch läuft + +def start_bot(): + """Startet den Bot.""" + global bot_process + if not bot_status(): + bot_process = subprocess.Popen(["python", "bot.py"], cwd=os.path.dirname(os.path.abspath(__file__))) + else: + print("Bot läuft bereits.") + +def stop_bot(): + """Stoppt den Bot.""" + global bot_process + if bot_process and bot_status(): + bot_process.terminate() + bot_process.wait() # Warten, bis der Prozess beendet ist + bot_process = None + else: + print("Bot läuft nicht.") + def get_db_connection(): """Stellt eine Verbindung zur MySQL-Datenbank her.""" return mysql.connector.connect( @@ -45,34 +68,37 @@ def get_db_connection(): database=DB_NAME ) +def token_updater(token): + session['oauth_token'] = token + def make_discord_session(token=None, state=None): - """Erstellt eine Discord OAuth2-Session.""" return OAuth2Session( DISCORD_CLIENT_ID, - token=token, + token=token or session.get("oauth_token"), state=state, redirect_uri=DISCORD_REDIRECT_URI, - scope=["identify", "guilds"] + scope=["identify", "guilds"], + auto_refresh_kwargs={ + 'client_id': DISCORD_CLIENT_ID, + 'client_secret': DISCORD_CLIENT_SECRET, + }, + auto_refresh_url=DISCORD_TOKEN_URL, + token_updater=token_updater ) -def fetch_discord_guilds(discord_session): - """Lädt die Gilden des Benutzers von der Discord-API.""" - try: - return discord_session.get(DISCORD_GUILDS_URL).json() - except Exception as e: - print(f"Fehler beim Abrufen der Gilden: {e}") - return None + def is_bot_admin(): """Überprüft, ob der Benutzer globale Admin-Rechte hat.""" if "discord_user" in session: user_info = session["discord_user"] user_id = user_info["id"] - + connection = get_db_connection() cursor = connection.cursor(dictionary=True) cursor.execute("SELECT global_permission FROM bot_data WHERE user_id = %s", (user_id,)) user_data = cursor.fetchone() + cursor.close() connection.close() @@ -84,11 +110,12 @@ def is_server_admin(guild_id): if "discord_user" in session: user_info = session["discord_user"] user_id = user_info["id"] - + connection = get_db_connection() cursor = connection.cursor(dictionary=True) cursor.execute("SELECT permission FROM user_data WHERE user_id = %s AND guild_id = %s", (user_id, guild_id)) user_data = cursor.fetchone() + cursor.close() connection.close() @@ -112,41 +139,42 @@ def login(): @app.route("/callback") def callback(): """Verarbeitet den OAuth2-Rückruf von Discord.""" - discord = make_discord_session(state=session.get("oauth_state")) - token = discord.fetch_token( - DISCORD_TOKEN_URL, - client_secret=DISCORD_CLIENT_SECRET, - authorization_response=request.url, - ) + try: + discord = make_discord_session(state=session.get("oauth_state")) + token = discord.fetch_token( + DISCORD_TOKEN_URL, + client_secret=DISCORD_CLIENT_SECRET, + authorization_response=request.url, + ) + + session['oauth_token'] = token + + # Abrufen der Benutzerinformationen von Discord + user_info = discord.get(DISCORD_API_URL).json() + session['discord_user'] = user_info - session['oauth_token'] = token - session['discord_user'] = discord.get(DISCORD_API_URL).json() + # Hole die Gilden (Server), auf denen der Benutzer ist + guilds_response = discord.get('https://discord.com/api/users/@me/guilds') - # Gilden (Server) nur einmal laden und in der Session speichern - session['discord_guilds'] = fetch_discord_guilds(discord) + if guilds_response.status_code != 200: + flash("Fehler beim Abrufen der Gilden.", "danger") + return redirect(url_for("landing_page")) + + guilds = guilds_response.json() + session['discord_guilds'] = guilds # Speichere die Gilden in der Session + + # Weiterleitung zur Server-Auswahl + return redirect(url_for("server_selection")) + + except Exception as e: + print(f"Error in OAuth2 callback: {e}") + flash("Ein Fehler ist beim Authentifizierungsprozess aufgetreten.", "danger") + return redirect(url_for("landing_page")) - return redirect(url_for("server_selection")) @app.route("/server_selection") def server_selection(): - """Zeigt dem Benutzer eine Liste aller Server an, auf denen er sich befindet.""" - if "discord_user" in session: - guilds = session.get('discord_guilds', []) - - if not guilds: - flash("Es wurden keine Server gefunden.", "danger") - return redirect(url_for("landing_page")) - - if len(guilds) == 1: - return redirect(url_for("user_dashboard", guild_id=guilds[0]['id'])) - else: - return render_template("server_selection.html", guilds=guilds) - - return redirect(url_for("login")) - -@app.route("/user_dashboard/") -def user_dashboard(guild_id): - """Zeigt das User-Dashboard für den ausgewählten Server an.""" + """Zeigt dem Benutzer eine Liste aller Server an, auf denen er sich befindet und die in der Datenbank vorhanden sind.""" if "discord_user" in session: user_info = session["discord_user"] user_id = user_info["id"] @@ -154,6 +182,48 @@ def user_dashboard(guild_id): connection = get_db_connection() cursor = connection.cursor(dictionary=True) + # Abfrage der Gilden, auf denen der Benutzer in der Datenbank Einträge hat + cursor.execute(""" + SELECT DISTINCT user_data.guild_id, guilds.name, guilds.icon + FROM user_data + JOIN guilds ON user_data.guild_id = guilds.guild_id + WHERE user_data.user_id = %s + """, (user_id,)) + guilds = cursor.fetchall() + + cursor.close() + connection.close() + + return render_template("server_selection.html", guilds=guilds) + + return redirect(url_for("login")) + + +@app.route("/admin_dashboard") +def admin_dashboard(): + """Zeigt das Bot-Admin-Dashboard an (nur für globale Admins).""" + if is_bot_admin(): + return render_template("admin_dashboard.html", bot_running=bot_status()) + return redirect(url_for("landing_page")) + +@app.route("/server_admin_dashboard/") +def server_admin_dashboard(guild_id): + """Zeigt das Server-Admin-Dashboard an (nur für Server-Admins).""" + if is_server_admin(guild_id): + return render_template("server_admin_dashboard.html", guild_id=guild_id) + return redirect(url_for("landing_page")) + +@app.route("/user_dashboard/") +def user_dashboard(guild_id): + """Zeigt das User-Dashboard für einen spezifischen Server (guild_id) an.""" + if "discord_user" in session: + user_info = session["discord_user"] + user_id = user_info["id"] + + connection = get_db_connection() + cursor = connection.cursor(dictionary=True) + + # Hole Benutzerinformationen für den ausgewählten Server cursor.execute("SELECT * FROM user_data WHERE user_id = %s AND guild_id = %s", (user_id, guild_id)) user_data = cursor.fetchone() @@ -163,9 +233,8 @@ def user_dashboard(guild_id): if user_data: return render_template("user_dashboard.html", user_info=user_info, user_data=user_data, guild_id=guild_id) else: - flash("Keine Daten für diesen Server gefunden.", "danger") - return redirect(url_for("server_selection")) - + return "User data not found", 404 + return redirect(url_for("login")) @app.route("/logout") @@ -174,17 +243,20 @@ def logout(): session.clear() return redirect(url_for("landing_page")) -# Verbesserte Fehlerbehandlung: Automatisches Logout bei ungültigem Token -@app.before_request -def check_oauth_token(): - if "oauth_token" in session: - discord = make_discord_session(token=session["oauth_token"]) - try: - discord.get(DISCORD_API_URL) # Test-API-Aufruf, um Token zu validieren - except Exception: - session.clear() - flash("Deine Sitzung ist abgelaufen. Bitte erneut einloggen.", "warning") - return redirect(url_for("login")) +# Bot Management Routes +@app.route("/start_bot") +def start(): + if is_bot_admin(): + start_bot() + return redirect(url_for("admin_dashboard")) + return redirect(url_for("landing_page")) + +@app.route("/stop_bot") +def stop(): + if is_bot_admin(): + stop_bot() + return redirect(url_for("admin_dashboard")) + return redirect(url_for("landing_page")) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/templates/server_selection.html b/templates/server_selection.html index 510f37d..234e1f4 100644 --- a/templates/server_selection.html +++ b/templates/server_selection.html @@ -4,18 +4,25 @@ Server Selection - + -
-

Wähle einen Server

-
    - {% for guild in guilds %} -
  • - {{ guild['name'] }} -
  • +
    +

    Select a Server

    +
    + {% for guild in session['discord_guilds'] %} +
    +
    + Server Icon +
    +
    {{ guild['name'] }}
    +

    You have permission level: {{ guild['permissions'] }}

    + Enter Server +
    +
    +
    {% endfor %} -
+