Files
Discord-ai-chatbot/app.py
2025-08-24 01:10:44 +02:00

887 lines
35 KiB
Python

__version__ = "dev-0.8.3"
__all__ = ["Discordbot-chatai-webpanel (Discord)"]
__author__ = "SimolZimol"
from flask import Flask, render_template, redirect, url_for, request, session, jsonify, send_file, flash, g
from requests_oauthlib import OAuth2Session
import os
import subprocess
import psutil
import mysql.connector
from datetime import datetime, timedelta
from flask_session import Session
import logging
import time
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY")
# Disable Flask/Werkzeug logging for static files and 304 responses
logging.getLogger('werkzeug').setLevel(logging.ERROR)
app.logger.setLevel(logging.ERROR)
# Configure custom logging to only show important messages
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
# Override Flask's logger to filter out static file requests
class NoStaticFilter(logging.Filter):
def filter(self, record):
# Filter out static file requests and 304 responses
if hasattr(record, 'msg'):
msg = str(record.msg)
if '/static/' in msg and ('304' in msg or '200' in msg):
return False
if 'GET /static/' in msg:
return False
return True
# Apply filter to werkzeug logger
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.addFilter(NoStaticFilter())
LOG_FILE_PATH = os.path.join("logs", f"{datetime.now().strftime('%Y-%m-%d')}.log")
app.config["SESSION_TYPE"] = "filesystem" # Oder 'redis' für Redis-basierte Speicherung
Session(app)
print(f"Session Type: {app.config['SESSION_TYPE']}")
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_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"
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
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 robuste Verbindung zur MySQL-Datenbank her mit Retry-Logik."""
retry_count = 3
retry_delay = 1
for attempt in range(retry_count):
try:
connection = mysql.connector.connect(
host=DB_HOST,
port=DB_PORT,
user=DB_USER,
password=DB_PASS,
database=DB_NAME,
autocommit=True,
pool_reset_session=True,
connect_timeout=10,
charset='utf8mb4',
collation='utf8mb4_unicode_ci',
use_unicode=True
)
# Test the connection
if connection.is_connected():
return connection
else:
connection.close()
raise mysql.connector.Error("Connection test failed")
except mysql.connector.Error as e:
print(f"Datenbankverbindung Versuch {attempt + 1} fehlgeschlagen: {e}")
if attempt < retry_count - 1:
time.sleep(retry_delay)
retry_delay *= 2 # Exponential backoff
else:
print("Alle Datenbankverbindungsversuche fehlgeschlagen")
raise
except Exception as e:
print(f"Unerwarteter Fehler bei Datenbankverbindung: {e}")
if attempt < retry_count - 1:
time.sleep(retry_delay)
retry_delay *= 2
else:
raise
def safe_close_connection(connection):
"""Schließt eine Datenbankverbindung sicher."""
try:
if connection and connection.is_connected():
connection.close()
except Exception as e:
print(f"Fehler beim Schließen der Datenbankverbindung: {e}")
class DatabaseConnection:
"""Context Manager für Datenbankverbindungen."""
def __init__(self):
self.connection = None
def __enter__(self):
self.connection = get_db_connection()
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
safe_close_connection(self.connection)
def token_updater(token):
session['oauth_token'] = token
def make_discord_session(token=None, state=None):
return OAuth2Session(
DISCORD_CLIENT_ID,
token=token or session.get("oauth_token"),
state=state,
redirect_uri=DISCORD_REDIRECT_URI,
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
)
@app.route("/logs")
def view_logs():
"""Zeigt die Logs des Bots im Admin-Panel an."""
if is_bot_admin():
return render_template("logs.html")
return redirect(url_for("landing_page"))
@app.route("/get_logs")
def get_logs():
"""Liest den Inhalt der Log-Datei und gibt ihn zurück."""
if is_bot_admin():
try:
with open(LOG_FILE_PATH, 'r', encoding='utf-8') as file:
logs = file.read()
return jsonify({"logs": logs})
except FileNotFoundError:
return jsonify({"logs": "Log file not found."})
return redirect(url_for("landing_page"))
@app.route("/download_logs")
def download_logs():
"""Bietet die Log-Datei zum Download an."""
if is_bot_admin():
return send_file(LOG_FILE_PATH, as_attachment=True)
return redirect(url_for("landing_page"))
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()
return user_data and user_data["global_permission"] >= 8
return False
def is_server_admin(guild_id):
"""Überprüft, ob der Benutzer Admin-Rechte auf einem bestimmten Server (Guild) 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 permission FROM user_data WHERE user_id = %s AND guild_id = %s", (user_id, guild_id))
user_data = cursor.fetchone()
cursor.close()
connection.close()
return user_data and user_data["permission"] >= 8
return False
@app.route("/")
def landing_page():
"""Landing Page"""
return render_template("landing.html")
@app.route("/about")
def about():
"""Öffentliche Über-uns-Seite"""
return render_template("about.html")
@app.route("/contact")
def contact():
"""Öffentliche Kontaktseite"""
return render_template("contact.html")
@app.route("/faq")
def faq():
"""Öffentliche FAQ-Seite"""
return render_template("faq.html")
@app.route("/help")
def help_page():
"""Öffentliche Hilfeseite"""
return render_template("help.html")
@app.route("/login")
def login():
"""Startet den Discord-OAuth2-Flow."""
discord = make_discord_session()
authorization_url, state = discord.authorization_url(DISCORD_OAUTH2_URL)
session['oauth_state'] = state
return redirect(authorization_url)
@app.before_request
def load_user_data():
"""Lädt Benutzerdaten vor jeder Anfrage für geschützte Routen."""
if "discord_user" in session:
g.user_info = session["discord_user"]
g.is_admin = session.get("is_admin", False)
g.bot_running = bot_status() # Lädt den Bot-Status in g
# Hole die Liste der Gilden aus der Datenbank mit robuster Verbindung
try:
with DatabaseConnection() as connection:
cursor = connection.cursor(dictionary=True)
# Lade die Gilden des Nutzers
user_guilds = session.get("discord_guilds", [])
user_guild_ids = [guild["id"] for guild in user_guilds]
if user_guild_ids:
# Finde nur die Gilden, die auch in der Datenbank existieren
cursor.execute("SELECT guild_id FROM guilds WHERE guild_id IN (%s)" % ','.join(['%s'] * len(user_guild_ids)), user_guild_ids)
existing_guilds = cursor.fetchall()
# Filtere die Gilden des Nutzers basierend auf der Existenz in der Datenbank
g.guilds = [guild for guild in user_guilds if int(guild["id"]) in {g["guild_id"] for g in existing_guilds}]
else:
g.guilds = []
cursor.close()
except Exception as e:
print(f"Fehler beim Laden der Benutzerdaten: {e}")
g.guilds = []
else:
# Falls der Benutzer nicht eingeloggt ist, keine Daten setzen
g.user_info = None
g.is_admin = False
g.guilds = []
g.bot_running = False
@app.route("/callback")
def callback():
"""Verarbeitet den OAuth2-Rückruf von Discord."""
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
# Hole die Gilden (Server), auf denen der Benutzer ist
guilds_response = discord.get('https://discord.com/api/users/@me/guilds')
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
# Prüfe die Admin-Berechtigungen in der bot_data Tabelle
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
cursor.execute("SELECT global_permission FROM bot_data WHERE user_id = %s", (user_info["id"],))
bot_admin_data = cursor.fetchone()
# Speichere Admin-Rechte in der Session
session['is_admin'] = bool(bot_admin_data and bot_admin_data['global_permission'] >= 8)
cursor.close()
connection.close()
# Leite zur User-Landing-Page weiter
return redirect(url_for("user_landing_page"))
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"))
@app.route("/user_server_data/<int:guild_id>")
def user_server_data(guild_id):
"""Zeigt die serverbezogenen Nutzerdaten für den ausgewählten Server 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 die serverbezogenen Nutzerdaten
cursor.execute("SELECT * FROM user_data WHERE user_id = %s AND guild_id = %s", (user_id, guild_id))
user_data = cursor.fetchone()
cursor.close()
connection.close()
if user_data:
return render_template("user_server_data.html", user_info=user_info, user_data=user_data, guild_id=guild_id)
else:
flash("Keine Daten für diesen Server gefunden.", "warning")
return redirect(url_for("user_landing_page"))
return redirect(url_for("landing_page"))
@app.route("/server_admin_dashboard/<int:guild_id>")
def server_admin_dashboard(guild_id):
"""Serverbasiertes Admin-Dashboard für server-spezifische Admin-Rechte."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
# Giveaways für den Server abrufen
cursor.execute("SELECT * FROM giveaway_data WHERE guild_id = %s", (guild_id,))
giveaways = cursor.fetchall()
# Benutzer auf dem Server abrufen
cursor.execute("SELECT * FROM user_data WHERE guild_id = %s", (guild_id,))
server_users = cursor.fetchall()
# Servername aus der guilds-Tabelle holen
cursor.execute("SELECT name FROM guilds WHERE guild_id = %s", (guild_id,))
guild_name_result = cursor.fetchone()
guild_name = guild_name_result["name"] if guild_name_result else "Unknown Guild"
cursor.close()
connection.close()
return render_template("server_admin_dashboard.html", giveaways=giveaways, server_users=server_users, guild_id=guild_id, guild_name=guild_name)
flash("You do not have permission to access this server's admin dashboard.", "danger")
return redirect(url_for("user_landing_page"))
@app.route("/edit_user/<int:guild_id>/<int:user_id>", methods=["GET", "POST"])
def edit_user(guild_id, user_id):
"""Bearbeitet die Daten eines spezifischen Benutzers auf einem bestimmten Server."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
# Hole die Berechtigungen des aktuellen Admins
admin_user_id = session["discord_user"]["id"]
cursor.execute("SELECT permission FROM user_data WHERE guild_id = %s AND user_id = %s", (guild_id, admin_user_id))
admin_data = cursor.fetchone()
max_permission = admin_data["permission"] if admin_data else 0
if request.method == "POST":
points = int(request.form.get("points", 0))
level = int(request.form.get("level", 1))
ban = int(request.form.get("ban", 0))
permission = int(request.form.get("permission", 0))
askmultus = int(request.form.get("askmultus", 0))
filter_value = int(request.form.get("filter_value", 0))
rank = request.form.get("rank", "")
xp = int(request.form.get("xp", 0))
# Validierung der Berechtigungen
if permission > max_permission:
flash("You cannot assign a permission level higher than your own.", "danger")
return redirect(url_for("edit_user", guild_id=guild_id, user_id=user_id))
# Update der Benutzerdaten
cursor.execute("""
UPDATE user_data
SET points = %s, level = %s, ban = %s, permission = %s, askmultus = %s, filter_value = %s, rank = %s, xp = %s
WHERE guild_id = %s AND user_id = %s
""", (points, level, ban, permission, askmultus, filter_value, rank, xp, guild_id, user_id))
connection.commit()
flash("User data updated successfully!", "success")
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
# Daten des spezifischen Benutzers laden
cursor.execute("SELECT * FROM user_data WHERE guild_id = %s AND user_id = %s", (guild_id, user_id))
user_data = cursor.fetchone()
cursor.close()
connection.close()
return render_template("edit_user.html", user_data=user_data, guild_id=guild_id, max_permission=max_permission)
return redirect(url_for("landing_page"))
@app.route("/ban_user/<int:guild_id>/<int:user_id>")
def ban_user(guild_id, user_id):
"""Banned einen Benutzer auf einem spezifischen Server."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor()
try:
cursor.execute("UPDATE user_data SET ban = 1 WHERE user_id = %s AND guild_id = %s", (user_id, guild_id))
connection.commit()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
except Exception as e:
print(f"Error banning user: {e}")
connection.rollback()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
finally:
cursor.close()
connection.close()
return redirect(url_for("landing_page"))
@app.route("/update_points/<int:guild_id>/<int:user_id>", methods=["POST"])
def update_points(guild_id, user_id):
"""Aktualisiert die Punkte eines Benutzers auf einem spezifischen Server."""
if is_server_admin(guild_id):
points_change = int(request.form["points_change"])
connection = get_db_connection()
cursor = connection.cursor()
try:
cursor.execute("UPDATE user_data SET points = points + %s WHERE user_id = %s AND guild_id = %s", (points_change, user_id, guild_id))
connection.commit()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
except Exception as e:
print(f"Error updating points: {e}")
connection.rollback()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
finally:
cursor.close()
connection.close()
return redirect(url_for("landing_page"))
@app.route("/unban_user/<int:guild_id>/<int:user_id>")
def unban_user(guild_id, user_id):
"""Entbannt einen Benutzer auf einem spezifischen Server."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor()
try:
cursor.execute("UPDATE user_data SET ban = 0 WHERE user_id = %s AND guild_id = %s", (user_id, guild_id))
connection.commit()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
except Exception as e:
print(f"Error unbanning user: {e}")
connection.rollback()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
finally:
cursor.close()
connection.close()
return redirect(url_for("landing_page"))
@app.route("/update_role/<int:guild_id>/<int:user_id>", methods=["POST"])
def update_role(guild_id, user_id):
"""Aktualisiert die Rolle (Berechtigung) eines Benutzers auf einem spezifischen Server."""
if is_server_admin(guild_id):
new_permission = request.form["permission"]
connection = get_db_connection()
cursor = connection.cursor()
try:
cursor.execute("UPDATE user_data SET permission = %s WHERE user_id = %s AND guild_id = %s", (new_permission, user_id, guild_id))
connection.commit()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
except Exception as e:
print(f"Error updating role: {e}")
connection.rollback()
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
finally:
cursor.close()
connection.close()
return redirect(url_for("landing_page"))
@app.route("/user_dashboard/<int:guild_id>")
def user_dashboard(guild_id):
"""Serverbasiertes User-Dashboard"""
if g.user_info:
user_id = g.user_info["id"]
# Debugging-Ausgaben
print(f"Accessing user_dashboard for user_id: {user_id}, guild_id: {guild_id}")
try:
# Hole die serverbezogenen Nutzerdaten
with DatabaseConnection() as connection:
cursor = connection.cursor(dictionary=True)
# Überprüfe, ob der Benutzer Mitglied des Servers (Gilde) ist
cursor.execute("SELECT * FROM user_data WHERE user_id = %s AND guild_id = %s", (user_id, guild_id))
user_data = cursor.fetchone()
# Debugging-Ausgabe für user_data
print(f"user_data for user_id {user_id} on guild_id {guild_id}: {user_data}")
cursor.close()
if user_data:
# Falls `user_data` vorhanden ist, setze `g.guild_id` und `g.user_data`
g.guild_id = guild_id
g.user_data = user_data
print("Access granted to user_dashboard.")
return render_template("user_dashboard.html")
else:
# Debugging-Ausgabe für Fehlerfall
print(f"No access for user_id {user_id} on guild_id {guild_id}")
flash("You do not have access to this server.", "danger")
return redirect(url_for("user_landing_page"))
except Exception as e:
print(f"Fehler beim Laden des User-Dashboards: {e}")
flash("Fehler beim Laden des Dashboards. Bitte versuchen Sie es später erneut.", "error")
return redirect(url_for("user_landing_page"))
# Falls der Benutzer nicht eingeloggt ist
print("User not logged in, redirecting to landing page.")
flash("Please log in to view your dashboard.", "danger")
return redirect(url_for("landing_page"))
@app.route("/user_dashboard/<int:guild_id>/leaderboard")
def leaderboard(guild_id):
"""Zeigt das Level Leaderboard für einen bestimmten Server an."""
if "discord_user" in session:
try:
with DatabaseConnection() as connection:
cursor = connection.cursor(dictionary=True)
current_date = datetime.now()
one_month_ago = current_date - timedelta(days=30)
# Hole die Leaderboard-Daten
cursor.execute("""
SELECT nickname, profile_picture, level, xp, join_date
FROM user_data
WHERE guild_id = %s
AND ban = 0
AND (leave_date IS NULL OR leave_date > %s)
ORDER BY level DESC, xp DESC
""", (guild_id, one_month_ago))
leaderboard_data = cursor.fetchall()
# Hole den Server-Namen aus der guilds-Tabelle
cursor.execute("SELECT name FROM guilds WHERE guild_id = %s", (guild_id,))
guild_name_result = cursor.fetchone()
guild_name = guild_name_result["name"] if guild_name_result else f"Server {guild_id}"
cursor.close()
# Übergabe von enumerate und guild_name an das Template
return render_template("leaderboard.html",
leaderboard=leaderboard_data,
guild_id=guild_id,
guild_name=guild_name,
enumerate=enumerate)
except Exception as e:
print(f"Fehler beim Laden des Leaderboards: {e}")
flash("Fehler beim Laden des Leaderboards. Bitte versuchen Sie es später erneut.", "error")
return redirect(url_for("user_landing_page"))
return redirect(url_for("landing_page"))
@app.route("/server_giveaways/<int:guild_id>")
def server_giveaways(guild_id):
"""Serverbasiertes Giveaway-Management."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
# Hole die Giveaways für den spezifischen Server
cursor.execute("SELECT * FROM giveaway_data WHERE guild_id = %s", (guild_id,))
giveaways = cursor.fetchall()
cursor.close()
connection.close()
return render_template("server_giveaways.html", giveaways=giveaways, guild_id=guild_id)
return redirect(url_for("landing_page"))
@app.route("/edit_giveaway/<int:guild_id>/<string:uuid>", methods=["GET", "POST"])
def edit_giveaway(guild_id, uuid):
"""Bearbeitet ein spezifisches Giveaway für einen bestimmten Server."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
if request.method == "POST":
platform = request.form.get("platform")
name = request.form.get("name")
game_key = request.form.get("game_key")
winner_dc_id = request.form.get("winner_dc_id")
aktiv = bool(request.form.get("aktiv"))
# Update der Giveaways-Daten
cursor.execute("""
UPDATE giveaway_data
SET platform = %s, name = %s, game_key = %s, winner_dc_id = %s, aktiv = %s
WHERE guild_id = %s AND uuid = %s
""", (platform, name, game_key, winner_dc_id, aktiv, guild_id, uuid))
connection.commit()
flash("Giveaway updated successfully!", "success")
# Nach dem Speichern zum server_admin_dashboard weiterleiten
return redirect(url_for("server_admin_dashboard", guild_id=guild_id))
# Daten des spezifischen Giveaways laden
cursor.execute("SELECT * FROM giveaway_data WHERE guild_id = %s AND uuid = %s", (guild_id, uuid))
giveaway = cursor.fetchone()
cursor.close()
connection.close()
return render_template("edit_giveaway.html", giveaway=giveaway, guild_id=guild_id)
return redirect(url_for("landing_page"))
@app.route("/server_settings/<int:guild_id>", methods=["GET", "POST"])
def server_settings(guild_id):
"""Serverbasierte Einstellungen für Moderation und andere Features."""
if is_server_admin(guild_id):
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
if request.method == "POST":
# Verarbeite Formular-Daten
try:
# Hole aktuelle Einstellungen
cursor.execute("SELECT * FROM guild_settings WHERE guild_id = %s", (guild_id,))
current_settings = cursor.fetchone()
# Parse Formular-Daten
mute_role_id = request.form.get("mute_role_id")
mute_role_name = request.form.get("mute_role_name", "Muted")
auto_create_mute_role = bool(request.form.get("auto_create_mute_role"))
max_warn_threshold = int(request.form.get("max_warn_threshold", 3))
auto_mute_on_warns = bool(request.form.get("auto_mute_on_warns"))
auto_mute_duration = request.form.get("auto_mute_duration", "1h")
log_channel_id = request.form.get("log_channel_id")
mod_log_enabled = bool(request.form.get("mod_log_enabled"))
# Validierung
if max_warn_threshold < 1 or max_warn_threshold > 10:
flash("Warn-Limit muss zwischen 1 und 10 liegen.", "danger")
return redirect(url_for("server_settings", guild_id=guild_id))
# Zeitformat validieren
time_units = {'m': 60, 'h': 3600, 'd': 86400}
if auto_mute_duration and (not auto_mute_duration[-1] in time_units or not auto_mute_duration[:-1].isdigit()):
flash("Ungültiges Zeitformat für Auto-Mute-Dauer. Verwende: 10m, 1h, 2d", "danger")
return redirect(url_for("server_settings", guild_id=guild_id))
# Konvertiere leere Strings zu NULL
mute_role_id = int(mute_role_id) if mute_role_id and mute_role_id.isdigit() else None
log_channel_id = int(log_channel_id) if log_channel_id and log_channel_id.isdigit() else None
# Update oder Insert Einstellungen
if current_settings:
cursor.execute("""
UPDATE guild_settings
SET mute_role_id = %s, mute_role_name = %s, auto_create_mute_role = %s,
max_warn_threshold = %s, auto_mute_on_warns = %s, auto_mute_duration = %s,
log_channel_id = %s, mod_log_enabled = %s
WHERE guild_id = %s
""", (mute_role_id, mute_role_name, auto_create_mute_role, max_warn_threshold,
auto_mute_on_warns, auto_mute_duration, log_channel_id, mod_log_enabled, guild_id))
else:
cursor.execute("""
INSERT INTO guild_settings
(guild_id, mute_role_id, mute_role_name, auto_create_mute_role, max_warn_threshold,
auto_mute_on_warns, auto_mute_duration, log_channel_id, mod_log_enabled)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (guild_id, mute_role_id, mute_role_name, auto_create_mute_role, max_warn_threshold,
auto_mute_on_warns, auto_mute_duration, log_channel_id, mod_log_enabled))
connection.commit()
flash("Server-Einstellungen erfolgreich gespeichert!", "success")
except ValueError as e:
flash(f"Fehler bei der Eingabe: {str(e)}", "danger")
except Exception as e:
flash(f"Fehler beim Speichern der Einstellungen: {str(e)}", "danger")
connection.rollback()
# Lade aktuelle Einstellungen
cursor.execute("SELECT * FROM guild_settings WHERE guild_id = %s", (guild_id,))
settings = cursor.fetchone()
# Falls keine Einstellungen existieren, erstelle Default-Werte
if not settings:
settings = {
"guild_id": guild_id,
"mute_role_id": None,
"mute_role_name": "Muted",
"auto_create_mute_role": True,
"max_warn_threshold": 3,
"auto_mute_on_warns": False,
"auto_mute_duration": "1h",
"log_channel_id": None,
"mod_log_enabled": True
}
# Hole Servername
cursor.execute("SELECT name FROM guilds WHERE guild_id = %s", (guild_id,))
guild_name_result = cursor.fetchone()
guild_name = guild_name_result["name"] if guild_name_result else "Unknown Guild"
cursor.close()
connection.close()
return render_template("server_settings.html", settings=settings, guild_id=guild_id, guild_name=guild_name)
flash("Du hast keine Berechtigung, auf die Server-Einstellungen zuzugreifen.", "danger")
return redirect(url_for("user_landing_page"))
@app.route("/user_giveaways/<int:guild_id>")
def user_giveaways(guild_id):
"""Zeigt dem Benutzer die Giveaways an, die er auf einem bestimmten Server gewonnen 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)
# Suche nach Giveaways, bei denen der eingeloggte Benutzer der Gewinner ist
cursor.execute("""
SELECT * FROM giveaway_data WHERE winner_dc_id = %s AND guild_id = %s
""", (user_id, guild_id))
won_giveaways = cursor.fetchall()
# Hole den Servernamen aus der guilds-Tabelle
cursor.execute("SELECT name FROM guilds WHERE guild_id = %s", (guild_id,))
guild_name_result = cursor.fetchone()
guild_name = guild_name_result["name"] if guild_name_result else f"Server {guild_id}"
cursor.close()
connection.close()
return render_template("user_giveaways.html", won_giveaways=won_giveaways, guild_id=guild_id, guild_name=guild_name)
return redirect(url_for("landing_page"))
@app.route("/redeem_giveaway/<int:guild_id>/<string:uuid>", methods=["GET", "POST"])
def redeem_giveaway(guild_id, uuid):
"""Erlaubt dem Benutzer, einen gewonnenen Giveaway-Code für einen bestimmten Server einzulösen und erneut anzuzeigen, falls bereits eingelöst."""
if "discord_user" in session:
user_info = session["discord_user"]
user_id = user_info["id"]
connection = get_db_connection()
cursor = connection.cursor(dictionary=True)
# Überprüfen, ob der eingeloggte Benutzer der Gewinner ist
cursor.execute("""
SELECT * FROM giveaway_data WHERE guild_id = %s AND uuid = %s AND winner_dc_id = %s
""", (guild_id, uuid, user_id))
giveaway = cursor.fetchone()
if giveaway:
if request.method == "POST" and not giveaway["aktiv"]:
# Wenn der Benutzer den Key einlöst, setze `aktiv` auf TRUE
cursor.execute("UPDATE giveaway_data SET aktiv = TRUE WHERE guild_id = %s AND uuid = %s", (guild_id, uuid))
connection.commit()
giveaway["aktiv"] = True # Aktualisiere den Status in `giveaway` für die Anzeige
flash("Giveaway redeemed successfully!", "success")
# Zeige den Game Key an, ob er eingelöst ist oder gerade eingelöst wurde
return render_template("redeem_giveaway.html", giveaway=giveaway, key=giveaway["game_key"] if giveaway["aktiv"] else None)
else:
flash("You are not the winner of this giveaway or the giveaway is no longer available.", "danger")
cursor.close()
connection.close()
return redirect(url_for("user_giveaways", guild_id=guild_id))
return redirect(url_for("landing_page"))
@app.route("/user_landing_page")
def user_landing_page():
"""Zeigt die globale Benutzerdaten und die Liste der Server an."""
if g.user_info and "discord_guilds" in session:
guilds = session["discord_guilds"]
return render_template("user_landing_page.html", user_info=g.user_info, guilds=guilds)
return redirect(url_for("landing_page"))
@app.route("/global_admin_dashboard")
def global_admin_dashboard():
"""Globales Admin-Dashboard nur für globale Admins"""
if g.is_admin:
bot_running = bot_status() # Funktion, die den Status des Bots prüft
return render_template("global_admin_dashboard.html", user_info=g.user_info, bot_running=bot_running)
return redirect(url_for("user_landing_page"))
@app.route("/logout")
def logout():
"""Meldet den Benutzer ab."""
session.clear()
return redirect(url_for("landing_page"))
# Bot Management Routes
@app.route("/start_bot")
def start():
if g.is_admin:
start_bot()
return redirect(url_for("global_admin_dashboard"))
return redirect(url_for("landing_page"))
@app.route("/stop_bot")
def stop():
if g.is_admin:
stop_bot()
return redirect(url_for("global_admin_dashboard"))
return redirect(url_for("landing_page"))
if __name__ == "__main__":
# Disable default Flask logging for static files
app.logger.disabled = True
log = logging.getLogger('werkzeug')
log.disabled = True
# Start app with minimal logging
app.run(host="0.0.0.0", port=5000, debug=True)