modified: app.py
modified: bot.py modified: templates/server_admin_dashboard.html new file: templates/server_settings.html
This commit is contained in:
98
app.py
98
app.py
@@ -580,6 +580,104 @@ def edit_giveaway(guild_id, uuid):
|
|||||||
return render_template("edit_giveaway.html", giveaway=giveaway, guild_id=guild_id)
|
return render_template("edit_giveaway.html", giveaway=giveaway, guild_id=guild_id)
|
||||||
return redirect(url_for("landing_page"))
|
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>")
|
@app.route("/user_giveaways/<int:guild_id>")
|
||||||
def user_giveaways(guild_id):
|
def user_giveaways(guild_id):
|
||||||
"""Zeigt dem Benutzer die Giveaways an, die er auf einem bestimmten Server gewonnen hat."""
|
"""Zeigt dem Benutzer die Giveaways an, die er auf einem bestimmten Server gewonnen hat."""
|
||||||
|
|||||||
771
bot.py
771
bot.py
@@ -112,7 +112,7 @@ loop = asyncio.get_event_loop()
|
|||||||
def close_database_connection(connection):
|
def close_database_connection(connection):
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
def insert_user_data(user_id, guild_id, permission, points, ban, askmultus, filter_value, chat_history, xp=0, level=1, nickname="", profile_picture="", join_date=None, leave_date=None):
|
def insert_user_data(user_id, guild_id, permission, points, ban, askmultus, filter_value, chat_history, xp=0, level=1, nickname="", profile_picture="", join_date=None, leave_date=None, ai_ban=0, mutes=0, warns=0):
|
||||||
"""Fügt neue Benutzerdaten in die Datenbank ein mit Connection Pool"""
|
"""Fügt neue Benutzerdaten in die Datenbank ein mit Connection Pool"""
|
||||||
connection = None
|
connection = None
|
||||||
cursor = None
|
cursor = None
|
||||||
@@ -120,11 +120,11 @@ def insert_user_data(user_id, guild_id, permission, points, ban, askmultus, filt
|
|||||||
connection = connect_to_database()
|
connection = connect_to_database()
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
insert_query = """
|
insert_query = """
|
||||||
INSERT INTO user_data (user_id, guild_id, permission, points, ban, askmultus, filter_value, rank, chat_history, xp, level, nickname, profile_picture, join_date, leave_date)
|
INSERT INTO user_data (user_id, guild_id, permission, points, ban, askmultus, filter_value, rank, chat_history, xp, level, nickname, profile_picture, join_date, leave_date, ai_ban, mutes, warns)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
"""
|
"""
|
||||||
serialized_chat_history = json.dumps(chat_history)
|
serialized_chat_history = json.dumps(chat_history)
|
||||||
data = (user_id, guild_id, permission, points, ban, askmultus, filter_value, 0, serialized_chat_history, xp, level, nickname, profile_picture, join_date, leave_date)
|
data = (user_id, guild_id, permission, points, ban, askmultus, filter_value, 0, serialized_chat_history, xp, level, nickname, profile_picture, join_date, leave_date, ai_ban, mutes, warns)
|
||||||
|
|
||||||
cursor.execute(insert_query, data)
|
cursor.execute(insert_query, data)
|
||||||
connection.commit()
|
connection.commit()
|
||||||
@@ -246,7 +246,10 @@ async def create_user_data_with_member(user_id, guild_id, member=None):
|
|||||||
"asknotes_history": [],
|
"asknotes_history": [],
|
||||||
"xp": 0,
|
"xp": 0,
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"nickname": nickname
|
"nickname": nickname,
|
||||||
|
"ai_ban": 0,
|
||||||
|
"mutes": 0,
|
||||||
|
"warns": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_user_data(
|
insert_user_data(
|
||||||
@@ -292,7 +295,10 @@ def load_user_data_from_mysql(user_id, guild_id):
|
|||||||
"asknotes_history": json.loads(result[9]) if result[9] else [],
|
"asknotes_history": json.loads(result[9]) if result[9] else [],
|
||||||
"xp": int(result[10]) if result[10] is not None else 0,
|
"xp": int(result[10]) if result[10] is not None else 0,
|
||||||
"level": int(result[11]) if result[11] is not None else 1,
|
"level": int(result[11]) if result[11] is not None else 1,
|
||||||
"nickname": result[12]
|
"nickname": result[12],
|
||||||
|
"ai_ban": int(result[15]) if len(result) > 15 and result[15] is not None else 0,
|
||||||
|
"mutes": int(result[16]) if len(result) > 16 and result[16] is not None else 0,
|
||||||
|
"warns": int(result[17]) if len(result) > 17 and result[17] is not None else 0
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
user_data = {
|
user_data = {
|
||||||
@@ -308,7 +314,10 @@ def load_user_data_from_mysql(user_id, guild_id):
|
|||||||
"asknotes_history": [],
|
"asknotes_history": [],
|
||||||
"xp": 0,
|
"xp": 0,
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"nickname": ""
|
"nickname": "",
|
||||||
|
"ai_ban": 0,
|
||||||
|
"mutes": 0,
|
||||||
|
"warns": 0
|
||||||
}
|
}
|
||||||
insert_user_data(
|
insert_user_data(
|
||||||
user_data["user_id"],
|
user_data["user_id"],
|
||||||
@@ -404,7 +413,10 @@ def load_user_data_sync(user_id, guild_id):
|
|||||||
"asknotes_history": [],
|
"asknotes_history": [],
|
||||||
"xp": 0,
|
"xp": 0,
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"nickname": ""
|
"nickname": "",
|
||||||
|
"ai_ban": 0,
|
||||||
|
"mutes": 0,
|
||||||
|
"warns": 0
|
||||||
}
|
}
|
||||||
insert_user_data(
|
insert_user_data(
|
||||||
user_data["user_id"],
|
user_data["user_id"],
|
||||||
@@ -2146,6 +2158,749 @@ async def version(ctx):
|
|||||||
"""Displays the current version of the bot."""
|
"""Displays the current version of the bot."""
|
||||||
await ctx.send(f"The current version of the bot is: {__version__}")
|
await ctx.send(f"The current version of the bot is: {__version__}")
|
||||||
|
|
||||||
|
# ================================ GUILD SETTINGS SYSTEM ================================
|
||||||
|
|
||||||
|
def get_guild_settings(guild_id):
|
||||||
|
"""Lädt die Guild-Einstellungen aus der Datenbank"""
|
||||||
|
connection = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
connection = connect_to_database()
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
select_query = "SELECT * FROM guild_settings WHERE guild_id = %s"
|
||||||
|
cursor.execute(select_query, (guild_id,))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return {
|
||||||
|
"guild_id": result[0],
|
||||||
|
"mute_role_id": result[1],
|
||||||
|
"mute_role_name": result[2] or "Muted",
|
||||||
|
"auto_create_mute_role": bool(result[3]) if result[3] is not None else True,
|
||||||
|
"max_warn_threshold": result[4] or 3,
|
||||||
|
"auto_mute_on_warns": bool(result[5]) if result[5] is not None else False,
|
||||||
|
"auto_mute_duration": result[6] or "1h",
|
||||||
|
"log_channel_id": result[7],
|
||||||
|
"mod_log_enabled": bool(result[8]) if result[8] is not None else True
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Erstelle Default-Einstellungen
|
||||||
|
default_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
|
||||||
|
}
|
||||||
|
save_guild_settings(guild_id, default_settings)
|
||||||
|
return default_settings
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error loading guild settings: {e}")
|
||||||
|
# Return default settings on error
|
||||||
|
return {
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if connection:
|
||||||
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
def save_guild_settings(guild_id, settings):
|
||||||
|
"""Speichert Guild-Einstellungen in der Datenbank"""
|
||||||
|
connection = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
connection = connect_to_database()
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
insert_query = """
|
||||||
|
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)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
mute_role_id = VALUES(mute_role_id),
|
||||||
|
mute_role_name = VALUES(mute_role_name),
|
||||||
|
auto_create_mute_role = VALUES(auto_create_mute_role),
|
||||||
|
max_warn_threshold = VALUES(max_warn_threshold),
|
||||||
|
auto_mute_on_warns = VALUES(auto_mute_on_warns),
|
||||||
|
auto_mute_duration = VALUES(auto_mute_duration),
|
||||||
|
log_channel_id = VALUES(log_channel_id),
|
||||||
|
mod_log_enabled = VALUES(mod_log_enabled)
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(insert_query, (
|
||||||
|
guild_id,
|
||||||
|
settings.get("mute_role_id"),
|
||||||
|
settings.get("mute_role_name", "Muted"),
|
||||||
|
settings.get("auto_create_mute_role", True),
|
||||||
|
settings.get("max_warn_threshold", 3),
|
||||||
|
settings.get("auto_mute_on_warns", False),
|
||||||
|
settings.get("auto_mute_duration", "1h"),
|
||||||
|
settings.get("log_channel_id"),
|
||||||
|
settings.get("mod_log_enabled", True)
|
||||||
|
))
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
logger.info(f"Guild settings saved for guild {guild_id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error saving guild settings: {e}")
|
||||||
|
if connection:
|
||||||
|
connection.rollback()
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if connection:
|
||||||
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
async def get_or_create_mute_role(guild, settings):
|
||||||
|
"""Holt oder erstellt die Mute-Rolle basierend auf Guild-Einstellungen"""
|
||||||
|
mute_role = None
|
||||||
|
|
||||||
|
# Versuche zuerst über ID zu finden
|
||||||
|
if settings["mute_role_id"]:
|
||||||
|
mute_role = guild.get_role(settings["mute_role_id"])
|
||||||
|
if mute_role:
|
||||||
|
return mute_role
|
||||||
|
|
||||||
|
# Versuche über Name zu finden
|
||||||
|
mute_role = discord.utils.get(guild.roles, name=settings["mute_role_name"])
|
||||||
|
if mute_role:
|
||||||
|
# Update die ID in den Einstellungen
|
||||||
|
settings["mute_role_id"] = mute_role.id
|
||||||
|
save_guild_settings(guild.id, settings)
|
||||||
|
return mute_role
|
||||||
|
|
||||||
|
# Erstelle neue Rolle, falls auto_create_mute_role aktiviert ist
|
||||||
|
if settings["auto_create_mute_role"]:
|
||||||
|
try:
|
||||||
|
mute_role = await guild.create_role(
|
||||||
|
name=settings["mute_role_name"],
|
||||||
|
color=discord.Color.dark_gray(),
|
||||||
|
reason="Auto-created mute role for moderation system"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Konfiguriere Berechtigungen für alle Kanäle
|
||||||
|
for channel in guild.channels:
|
||||||
|
try:
|
||||||
|
await channel.set_permissions(
|
||||||
|
mute_role,
|
||||||
|
send_messages=False,
|
||||||
|
speak=False,
|
||||||
|
add_reactions=False,
|
||||||
|
create_private_threads=False,
|
||||||
|
create_public_threads=False,
|
||||||
|
send_messages_in_threads=False
|
||||||
|
)
|
||||||
|
except discord.Forbidden:
|
||||||
|
logger.warning(f"Could not set permissions for {channel.name} in guild {guild.id}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Speichere die neue Rolle-ID
|
||||||
|
settings["mute_role_id"] = mute_role.id
|
||||||
|
save_guild_settings(guild.id, settings)
|
||||||
|
|
||||||
|
logger.info(f"Created mute role '{settings['mute_role_name']}' for guild {guild.id}")
|
||||||
|
return mute_role
|
||||||
|
|
||||||
|
except discord.Forbidden:
|
||||||
|
logger.error(f"No permission to create mute role in guild {guild.id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ================================ MODERATION SYSTEM ================================
|
||||||
|
|
||||||
|
# Moderation Helper Functions
|
||||||
|
def check_moderation_permission(user_permission):
|
||||||
|
"""Überprüft, ob der Nutzer Moderationsrechte hat (Permission 5 oder höher)"""
|
||||||
|
return user_permission >= 5
|
||||||
|
|
||||||
|
async def save_user_roles(user_id, guild_id, roles):
|
||||||
|
"""Speichert die Rollen eines Users vor einem Mute"""
|
||||||
|
connection = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
connection = connect_to_database()
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
# Serialisiere die Rollen-IDs
|
||||||
|
role_ids = [str(role.id) for role in roles if not role.is_default()]
|
||||||
|
serialized_roles = json.dumps(role_ids)
|
||||||
|
|
||||||
|
insert_query = """
|
||||||
|
INSERT INTO user_saved_roles (user_id, guild_id, roles, saved_at)
|
||||||
|
VALUES (%s, %s, %s, %s)
|
||||||
|
ON DUPLICATE KEY UPDATE roles = %s, saved_at = %s
|
||||||
|
"""
|
||||||
|
current_time = datetime.now()
|
||||||
|
cursor.execute(insert_query, (user_id, guild_id, serialized_roles, current_time, serialized_roles, current_time))
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
logger.info(f"Saved roles for user {user_id} in guild {guild_id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error saving user roles: {e}")
|
||||||
|
if connection:
|
||||||
|
connection.rollback()
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if connection:
|
||||||
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
async def restore_user_roles(user, guild):
|
||||||
|
"""Stellt die gespeicherten Rollen eines Users wieder her"""
|
||||||
|
connection = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
connection = connect_to_database()
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
select_query = "SELECT roles FROM user_saved_roles WHERE user_id = %s AND guild_id = %s"
|
||||||
|
cursor.execute(select_query, (user.id, guild.id))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
role_ids = json.loads(result[0])
|
||||||
|
roles_to_add = []
|
||||||
|
|
||||||
|
for role_id in role_ids:
|
||||||
|
role = guild.get_role(int(role_id))
|
||||||
|
if role and role < guild.me.top_role: # Überprüfe, ob Bot die Rolle vergeben kann
|
||||||
|
roles_to_add.append(role)
|
||||||
|
|
||||||
|
if roles_to_add:
|
||||||
|
await user.add_roles(*roles_to_add, reason="Mute expired - restoring roles")
|
||||||
|
logger.info(f"Restored {len(roles_to_add)} roles for user {user.id}")
|
||||||
|
|
||||||
|
# Lösche die gespeicherten Rollen
|
||||||
|
delete_query = "DELETE FROM user_saved_roles WHERE user_id = %s AND guild_id = %s"
|
||||||
|
cursor.execute(delete_query, (user.id, guild.id))
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error restoring user roles: {e}")
|
||||||
|
if connection:
|
||||||
|
connection.rollback()
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if connection:
|
||||||
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def warn(ctx, user: discord.User, *, reason: str = "Keine Begründung angegeben"):
|
||||||
|
"""Warnt einen Benutzer (Benötigt Permission Level 5 oder höher)"""
|
||||||
|
try:
|
||||||
|
# Lade Moderator-Daten
|
||||||
|
mod_data = await load_user_data(ctx.author.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Überprüfe Moderationsrechte
|
||||||
|
if not check_moderation_permission(mod_data["permission"]):
|
||||||
|
await ctx.send("❌ Du hast keine Berechtigung, diesen Befehl zu verwenden. (Benötigt Permission Level 5 oder höher)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Lade User-Daten
|
||||||
|
user_data = await load_user_data(user.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Erhöhe Warn-Count
|
||||||
|
user_data["warns"] += 1
|
||||||
|
update_user_data(user.id, ctx.guild.id, "warns", user_data["warns"])
|
||||||
|
|
||||||
|
# Erstelle Embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="⚠️ Warnung erhalten",
|
||||||
|
description=f"{user.mention} wurde gewarnt.",
|
||||||
|
color=0xff9500,
|
||||||
|
timestamp=datetime.now()
|
||||||
|
)
|
||||||
|
embed.add_field(name="Grund", value=reason, inline=False)
|
||||||
|
embed.add_field(name="Moderator", value=ctx.author.mention, inline=True)
|
||||||
|
embed.add_field(name="Warn-Count", value=f"{user_data['warns']}", inline=True)
|
||||||
|
embed.set_footer(text=f"User ID: {user.id}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
# Log the action
|
||||||
|
logger.info(f"User {user.id} warned by {ctx.author.id} in guild {ctx.guild.id}. Reason: {reason}")
|
||||||
|
|
||||||
|
# Auto-Aktionen basierend auf Warn-Count
|
||||||
|
if user_data["warns"] >= 3:
|
||||||
|
embed_auto = discord.Embed(
|
||||||
|
title="🚨 Auto-Aktion ausgelöst",
|
||||||
|
description=f"{user.mention} hat {user_data['warns']} Warnungen erreicht!",
|
||||||
|
color=0xff0000
|
||||||
|
)
|
||||||
|
embed_auto.add_field(name="Empfehlung", value="Erwäge weitere Moderationsmaßnahmen", inline=False)
|
||||||
|
await ctx.send(embed=embed_auto)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in warn command: {e}")
|
||||||
|
await ctx.send("❌ Ein Fehler ist aufgetreten beim Warnen des Benutzers.")
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def mute(ctx, user: discord.User, duration: str, *, reason: str = "Keine Begründung angegeben"):
|
||||||
|
"""Mutet einen Benutzer für eine bestimmte Dauer (Benötigt Permission Level 5 oder höher)
|
||||||
|
|
||||||
|
Beispiele für Dauer:
|
||||||
|
- 10m = 10 Minuten
|
||||||
|
- 1h = 1 Stunde
|
||||||
|
- 2d = 2 Tage
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Lade Moderator-Daten
|
||||||
|
mod_data = await load_user_data(ctx.author.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Überprüfe Moderationsrechte
|
||||||
|
if not check_moderation_permission(mod_data["permission"]):
|
||||||
|
await ctx.send("❌ Du hast keine Berechtigung, diesen Befehl zu verwenden. (Benötigt Permission Level 5 oder höher)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse Dauer
|
||||||
|
time_units = {'m': 60, 'h': 3600, 'd': 86400}
|
||||||
|
if not duration[-1] in time_units or not duration[:-1].isdigit():
|
||||||
|
await ctx.send("❌ Ungültiges Zeitformat. Verwende: 10m, 1h, 2d")
|
||||||
|
return
|
||||||
|
|
||||||
|
duration_seconds = int(duration[:-1]) * time_units[duration[-1]]
|
||||||
|
end_time = datetime.now() + timedelta(seconds=duration_seconds)
|
||||||
|
|
||||||
|
# Hole Member-Objekt
|
||||||
|
member = ctx.guild.get_member(user.id)
|
||||||
|
if not member:
|
||||||
|
await ctx.send("❌ Benutzer nicht auf diesem Server gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Lade Guild-Einstellungen
|
||||||
|
guild_settings = get_guild_settings(ctx.guild.id)
|
||||||
|
|
||||||
|
# Speichere aktuelle Rollen
|
||||||
|
await save_user_roles(user.id, ctx.guild.id, member.roles)
|
||||||
|
|
||||||
|
# Entferne alle Rollen außer @everyone
|
||||||
|
roles_to_remove = [role for role in member.roles if not role.is_default()]
|
||||||
|
if roles_to_remove:
|
||||||
|
await member.remove_roles(*roles_to_remove, reason=f"Muted by {ctx.author}")
|
||||||
|
|
||||||
|
# Hole oder erstelle Mute-Rolle basierend auf Einstellungen
|
||||||
|
mute_role = await get_or_create_mute_role(ctx.guild, guild_settings)
|
||||||
|
if not mute_role:
|
||||||
|
await ctx.send("❌ Konnte Mute-Rolle nicht finden oder erstellen. Überprüfe die Server-Einstellungen.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Vergebe Mute-Rolle
|
||||||
|
await member.add_roles(mute_role, reason=f"Muted by {ctx.author} for {duration}")
|
||||||
|
|
||||||
|
# Update User-Daten
|
||||||
|
user_data = await load_user_data(user.id, ctx.guild.id)
|
||||||
|
user_data["mutes"] += 1
|
||||||
|
update_user_data(user.id, ctx.guild.id, "mutes", user_data["mutes"])
|
||||||
|
|
||||||
|
# Erstelle Active Process für Auto-Unmute
|
||||||
|
process_data = {
|
||||||
|
"user_id": user.id,
|
||||||
|
"guild_id": ctx.guild.id,
|
||||||
|
"channel_id": ctx.channel.id,
|
||||||
|
"reason": reason,
|
||||||
|
"moderator_id": ctx.author.id,
|
||||||
|
"mute_role_id": mute_role.id
|
||||||
|
}
|
||||||
|
|
||||||
|
process_uuid = create_active_process(
|
||||||
|
process_type="mute",
|
||||||
|
guild_id=ctx.guild.id,
|
||||||
|
channel_id=ctx.channel.id,
|
||||||
|
user_id=user.id,
|
||||||
|
target_id=user.id,
|
||||||
|
end_time=end_time,
|
||||||
|
data=process_data
|
||||||
|
)
|
||||||
|
|
||||||
|
# Erstelle Embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🔇 Benutzer gemutet",
|
||||||
|
description=f"{user.mention} wurde gemutet.",
|
||||||
|
color=0xff0000,
|
||||||
|
timestamp=datetime.now()
|
||||||
|
)
|
||||||
|
embed.add_field(name="Dauer", value=duration, inline=True)
|
||||||
|
embed.add_field(name="Endet am", value=end_time.strftime("%Y-%m-%d %H:%M:%S"), inline=True)
|
||||||
|
embed.add_field(name="Grund", value=reason, inline=False)
|
||||||
|
embed.add_field(name="Moderator", value=ctx.author.mention, inline=True)
|
||||||
|
embed.add_field(name="Mute-Count", value=f"{user_data['mutes']}", inline=True)
|
||||||
|
embed.set_footer(text=f"User ID: {user.id} | Process ID: {str(process_uuid)[:8]}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
# Log the action
|
||||||
|
logger.info(f"User {user.id} muted by {ctx.author.id} in guild {ctx.guild.id} for {duration}. Reason: {reason}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in mute command: {e}")
|
||||||
|
await ctx.send("❌ Ein Fehler ist aufgetreten beim Muten des Benutzers.")
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def unmute(ctx, user: discord.User):
|
||||||
|
"""Entmutet einen Benutzer manuell (Benötigt Permission Level 5 oder höher)"""
|
||||||
|
try:
|
||||||
|
# Lade Moderator-Daten
|
||||||
|
mod_data = await load_user_data(ctx.author.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Überprüfe Moderationsrechte
|
||||||
|
if not check_moderation_permission(mod_data["permission"]):
|
||||||
|
await ctx.send("❌ Du hast keine Berechtigung, diesen Befehl zu verwenden. (Benötigt Permission Level 5 oder höher)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Hole Member-Objekt
|
||||||
|
member = ctx.guild.get_member(user.id)
|
||||||
|
if not member:
|
||||||
|
await ctx.send("❌ Benutzer nicht auf diesem Server gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Lade Guild-Einstellungen
|
||||||
|
guild_settings = get_guild_settings(ctx.guild.id)
|
||||||
|
|
||||||
|
# Finde Mute-Rolle basierend auf Einstellungen
|
||||||
|
mute_role = None
|
||||||
|
if guild_settings["mute_role_id"]:
|
||||||
|
mute_role = ctx.guild.get_role(guild_settings["mute_role_id"])
|
||||||
|
|
||||||
|
if not mute_role:
|
||||||
|
mute_role = discord.utils.get(ctx.guild.roles, name=guild_settings["mute_role_name"])
|
||||||
|
|
||||||
|
if not mute_role or mute_role not in member.roles:
|
||||||
|
await ctx.send("❌ Benutzer ist nicht gemutet.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Entferne Mute-Rolle
|
||||||
|
await member.remove_roles(mute_role, reason=f"Unmuted by {ctx.author}")
|
||||||
|
|
||||||
|
# Stelle Rollen wieder her
|
||||||
|
await restore_user_roles(member, ctx.guild)
|
||||||
|
|
||||||
|
# Finde und aktualisiere aktiven Mute-Prozess
|
||||||
|
active_processes = get_active_processes(process_type="mute", guild_id=ctx.guild.id)
|
||||||
|
for process in active_processes:
|
||||||
|
if process["target_id"] == user.id:
|
||||||
|
update_process_status(process["uuid"], "cancelled_manual")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Erstelle Embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🔊 Benutzer entmutet",
|
||||||
|
description=f"{user.mention} wurde entmutet.",
|
||||||
|
color=0x00ff00,
|
||||||
|
timestamp=datetime.now()
|
||||||
|
)
|
||||||
|
embed.add_field(name="Moderator", value=ctx.author.mention, inline=True)
|
||||||
|
embed.set_footer(text=f"User ID: {user.id}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
# Log the action
|
||||||
|
logger.info(f"User {user.id} unmuted by {ctx.author.id} in guild {ctx.guild.id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in unmute command: {e}")
|
||||||
|
await ctx.send("❌ Ein Fehler ist aufgetreten beim Entmuten des Benutzers.")
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def modstats(ctx, user: discord.User = None):
|
||||||
|
"""Zeigt Moderationsstatistiken für einen Benutzer an"""
|
||||||
|
try:
|
||||||
|
# Falls kein User angegeben, zeige eigene Stats
|
||||||
|
target_user = user or ctx.author
|
||||||
|
|
||||||
|
# Lade User-Daten
|
||||||
|
user_data = await load_user_data(target_user.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Erstelle Embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=f"📊 Moderationsstatistiken",
|
||||||
|
description=f"Statistiken für {target_user.mention}",
|
||||||
|
color=0x3498db,
|
||||||
|
timestamp=datetime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="🤖 AI Bans", value=user_data.get("ai_ban", 0), inline=True)
|
||||||
|
embed.add_field(name="🔇 Mutes", value=user_data.get("mutes", 0), inline=True)
|
||||||
|
embed.add_field(name="⚠️ Warnungen", value=user_data.get("warns", 0), inline=True)
|
||||||
|
embed.add_field(name="🛡️ Permission Level", value=user_data.get("permission", 0), inline=True)
|
||||||
|
embed.add_field(name="⭐ Level", value=user_data.get("level", 1), inline=True)
|
||||||
|
embed.add_field(name="💰 Punkte", value=user_data.get("points", 0), inline=True)
|
||||||
|
|
||||||
|
embed.set_thumbnail(url=target_user.display_avatar.url)
|
||||||
|
embed.set_footer(text=f"User ID: {target_user.id}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in modstats command: {e}")
|
||||||
|
await ctx.send("❌ Ein Fehler ist aufgetreten beim Laden der Moderationsstatistiken.")
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def modconfig(ctx, setting: str = None, *, value: str = None):
|
||||||
|
"""Konfiguriert Moderationseinstellungen für den Server (Benötigt Permission Level 8 oder höher)
|
||||||
|
|
||||||
|
Verfügbare Einstellungen:
|
||||||
|
- mute_role <@role/role_name> - Setzt die Mute-Rolle
|
||||||
|
- mute_role_name <name> - Setzt den Namen für auto-erstellte Mute-Rollen
|
||||||
|
- auto_create_mute_role <true/false> - Auto-Erstellung von Mute-Rollen
|
||||||
|
- max_warn_threshold <number> - Anzahl Warnungen vor Auto-Aktion
|
||||||
|
- auto_mute_on_warns <true/false> - Auto-Mute bei zu vielen Warnungen
|
||||||
|
- auto_mute_duration <duration> - Dauer für Auto-Mutes (z.B. 1h, 30m)
|
||||||
|
- log_channel <#channel> - Kanal für Moderations-Logs
|
||||||
|
- mod_log_enabled <true/false> - Aktiviert/Deaktiviert Moderations-Logs
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Lade Moderator-Daten
|
||||||
|
mod_data = await load_user_data(ctx.author.id, ctx.guild.id)
|
||||||
|
|
||||||
|
# Überprüfe Admin-Rechte (Level 8+)
|
||||||
|
if mod_data["permission"] < 8:
|
||||||
|
await ctx.send("❌ Du hast keine Berechtigung, Moderationseinstellungen zu ändern. (Benötigt Permission Level 8 oder höher)")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Lade aktuelle Einstellungen
|
||||||
|
guild_settings = get_guild_settings(ctx.guild.id)
|
||||||
|
|
||||||
|
# Zeige Einstellungen an, falls keine Parameter
|
||||||
|
if not setting:
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🛠️ Moderationseinstellungen",
|
||||||
|
description=f"Aktuelle Einstellungen für **{ctx.guild.name}**",
|
||||||
|
color=0x3498db,
|
||||||
|
timestamp=datetime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mute-Rolle Info
|
||||||
|
mute_role_info = "Nicht gesetzt"
|
||||||
|
if guild_settings["mute_role_id"]:
|
||||||
|
role = ctx.guild.get_role(guild_settings["mute_role_id"])
|
||||||
|
mute_role_info = role.mention if role else f"❌ Rolle nicht gefunden (ID: {guild_settings['mute_role_id']})"
|
||||||
|
|
||||||
|
embed.add_field(name="🔇 Mute-Rolle", value=mute_role_info, inline=False)
|
||||||
|
embed.add_field(name="📝 Mute-Rollen-Name", value=guild_settings["mute_role_name"], inline=True)
|
||||||
|
embed.add_field(name="🔧 Auto-Erstellen", value="✅" if guild_settings["auto_create_mute_role"] else "❌", inline=True)
|
||||||
|
embed.add_field(name="⚠️ Warn-Limit", value=guild_settings["max_warn_threshold"], inline=True)
|
||||||
|
embed.add_field(name="🔄 Auto-Mute bei Warns", value="✅" if guild_settings["auto_mute_on_warns"] else "❌", inline=True)
|
||||||
|
embed.add_field(name="⏱️ Auto-Mute-Dauer", value=guild_settings["auto_mute_duration"], inline=True)
|
||||||
|
|
||||||
|
# Log-Kanal Info
|
||||||
|
log_info = "Nicht gesetzt"
|
||||||
|
if guild_settings["log_channel_id"]:
|
||||||
|
channel = ctx.guild.get_channel(guild_settings["log_channel_id"])
|
||||||
|
log_info = channel.mention if channel else f"❌ Kanal nicht gefunden (ID: {guild_settings['log_channel_id']})"
|
||||||
|
|
||||||
|
embed.add_field(name="📊 Log-Kanal", value=log_info, inline=True)
|
||||||
|
embed.add_field(name="📝 Logs Aktiviert", value="✅" if guild_settings["mod_log_enabled"] else "❌", inline=True)
|
||||||
|
|
||||||
|
embed.set_footer(text="Verwende -modconfig <setting> <value> zum Ändern")
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ändere Einstellungen
|
||||||
|
setting = setting.lower()
|
||||||
|
|
||||||
|
if setting == "mute_role":
|
||||||
|
if not value:
|
||||||
|
await ctx.send("❌ Bitte gib eine Rolle an: `-modconfig mute_role @MuteRole`")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse Rolle
|
||||||
|
role = None
|
||||||
|
if value.startswith("<@&") and value.endswith(">"):
|
||||||
|
role_id = int(value[3:-1])
|
||||||
|
role = ctx.guild.get_role(role_id)
|
||||||
|
else:
|
||||||
|
role = discord.utils.get(ctx.guild.roles, name=value)
|
||||||
|
|
||||||
|
if not role:
|
||||||
|
await ctx.send("❌ Rolle nicht gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild_settings["mute_role_id"] = role.id
|
||||||
|
guild_settings["mute_role_name"] = role.name
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Mute-Rolle auf {role.mention} gesetzt.")
|
||||||
|
|
||||||
|
elif setting == "mute_role_name":
|
||||||
|
if not value:
|
||||||
|
await ctx.send("❌ Bitte gib einen Namen an: `-modconfig mute_role_name Stumm`")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild_settings["mute_role_name"] = value
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Mute-Rollen-Name auf `{value}` gesetzt.")
|
||||||
|
|
||||||
|
elif setting == "auto_create_mute_role":
|
||||||
|
if value.lower() in ["true", "1", "ja", "yes", "on"]:
|
||||||
|
guild_settings["auto_create_mute_role"] = True
|
||||||
|
await ctx.send("✅ Auto-Erstellung von Mute-Rollen aktiviert.")
|
||||||
|
elif value.lower() in ["false", "0", "nein", "no", "off"]:
|
||||||
|
guild_settings["auto_create_mute_role"] = False
|
||||||
|
await ctx.send("✅ Auto-Erstellung von Mute-Rollen deaktiviert.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Ungültiger Wert. Verwende: true/false")
|
||||||
|
return
|
||||||
|
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
elif setting == "max_warn_threshold":
|
||||||
|
try:
|
||||||
|
threshold = int(value)
|
||||||
|
if threshold < 1 or threshold > 10:
|
||||||
|
await ctx.send("❌ Warn-Limit muss zwischen 1 und 10 liegen.")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild_settings["max_warn_threshold"] = threshold
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Warn-Limit auf {threshold} gesetzt.")
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
await ctx.send("❌ Ungültiger Wert. Verwende eine Nummer zwischen 1 und 10.")
|
||||||
|
return
|
||||||
|
|
||||||
|
elif setting == "auto_mute_on_warns":
|
||||||
|
if value.lower() in ["true", "1", "ja", "yes", "on"]:
|
||||||
|
guild_settings["auto_mute_on_warns"] = True
|
||||||
|
await ctx.send("✅ Auto-Mute bei zu vielen Warnungen aktiviert.")
|
||||||
|
elif value.lower() in ["false", "0", "nein", "no", "off"]:
|
||||||
|
guild_settings["auto_mute_on_warns"] = False
|
||||||
|
await ctx.send("✅ Auto-Mute bei zu vielen Warnungen deaktiviert.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Ungültiger Wert. Verwende: true/false")
|
||||||
|
return
|
||||||
|
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
elif setting == "auto_mute_duration":
|
||||||
|
# Validiere Dauer-Format
|
||||||
|
time_units = {'m': 60, 'h': 3600, 'd': 86400}
|
||||||
|
if not value or not value[-1] in time_units or not value[:-1].isdigit():
|
||||||
|
await ctx.send("❌ Ungültiges Zeitformat. Verwende: 10m, 1h, 2d")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild_settings["auto_mute_duration"] = value
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Auto-Mute-Dauer auf {value} gesetzt.")
|
||||||
|
|
||||||
|
elif setting == "log_channel":
|
||||||
|
if not value:
|
||||||
|
await ctx.send("❌ Bitte gib einen Kanal an: `-modconfig log_channel #mod-log`")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse Kanal
|
||||||
|
channel = None
|
||||||
|
if value.startswith("<#") and value.endswith(">"):
|
||||||
|
channel_id = int(value[2:-1])
|
||||||
|
channel = ctx.guild.get_channel(channel_id)
|
||||||
|
else:
|
||||||
|
channel = discord.utils.get(ctx.guild.channels, name=value.replace("#", ""))
|
||||||
|
|
||||||
|
if not channel:
|
||||||
|
await ctx.send("❌ Kanal nicht gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild_settings["log_channel_id"] = channel.id
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Log-Kanal auf {channel.mention} gesetzt.")
|
||||||
|
|
||||||
|
elif setting == "mod_log_enabled":
|
||||||
|
if value.lower() in ["true", "1", "ja", "yes", "on"]:
|
||||||
|
guild_settings["mod_log_enabled"] = True
|
||||||
|
await ctx.send("✅ Moderations-Logs aktiviert.")
|
||||||
|
elif value.lower() in ["false", "0", "nein", "no", "off"]:
|
||||||
|
guild_settings["mod_log_enabled"] = False
|
||||||
|
await ctx.send("✅ Moderations-Logs deaktiviert.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Ungültiger Wert. Verwende: true/false")
|
||||||
|
return
|
||||||
|
|
||||||
|
save_guild_settings(ctx.guild.id, guild_settings)
|
||||||
|
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Unbekannte Einstellung. Verwende `-modconfig` ohne Parameter für eine Liste aller Einstellungen.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in modconfig command: {e}")
|
||||||
|
await ctx.send("❌ Ein Fehler ist aufgetreten beim Konfigurieren der Moderationseinstellungen.")
|
||||||
|
|
||||||
|
# Erweitere handle_expired_mute Funktion
|
||||||
|
async def handle_expired_mute(process_uuid, data):
|
||||||
|
"""Handles expired mute processes"""
|
||||||
|
try:
|
||||||
|
guild_id = data.get("guild_id")
|
||||||
|
user_id = data.get("user_id")
|
||||||
|
mute_role_id = data.get("mute_role_id")
|
||||||
|
|
||||||
|
if not all([guild_id, user_id]):
|
||||||
|
logger.error(f"Missing data in mute process {process_uuid}")
|
||||||
|
update_process_status(process_uuid, "failed")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Hole Guild und Member
|
||||||
|
guild = client.get_guild(guild_id)
|
||||||
|
if not guild:
|
||||||
|
logger.error(f"Guild {guild_id} not found for mute process {process_uuid}")
|
||||||
|
update_process_status(process_uuid, "failed")
|
||||||
|
return
|
||||||
|
|
||||||
|
member = guild.get_member(user_id)
|
||||||
|
if not member:
|
||||||
|
logger.warning(f"Member {user_id} not found in guild {guild_id} for mute process {process_uuid}")
|
||||||
|
update_process_status(process_uuid, "completed")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Finde Mute-Rolle basierend auf Guild-Einstellungen
|
||||||
|
guild_settings = get_guild_settings(guild_id)
|
||||||
|
mute_role = None
|
||||||
|
|
||||||
|
if mute_role_id:
|
||||||
|
mute_role = guild.get_role(mute_role_id)
|
||||||
|
|
||||||
|
if not mute_role and guild_settings["mute_role_id"]:
|
||||||
|
mute_role = guild.get_role(guild_settings["mute_role_id"])
|
||||||
|
|
||||||
|
if not mute_role:
|
||||||
|
mute_role = discord.utils.get(guild.roles, name=guild_settings["mute_role_name"])
|
||||||
|
|
||||||
|
# Entferne Mute-Rolle falls vorhanden
|
||||||
|
if mute_role and mute_role in member.roles:
|
||||||
|
await member.remove_roles(mute_role, reason="Mute expired automatically")
|
||||||
|
|
||||||
|
# Stelle Rollen wieder her
|
||||||
|
await restore_user_roles(member, guild)
|
||||||
|
|
||||||
|
update_process_status(process_uuid, "completed")
|
||||||
|
logger.info(f"Successfully unmuted user {user_id} in guild {guild_id} (expired mute)")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error handling expired mute {process_uuid}: {e}")
|
||||||
|
update_process_status(process_uuid, "failed")
|
||||||
|
|
||||||
# Cache-Ordner für Notizen
|
# Cache-Ordner für Notizen
|
||||||
CACHE_DIR = "cache"
|
CACHE_DIR = "cache"
|
||||||
if not os.path.exists(CACHE_DIR):
|
if not os.path.exists(CACHE_DIR):
|
||||||
|
|||||||
@@ -173,6 +173,7 @@
|
|||||||
|
|
||||||
.giveaway-icon { color: #ffd700; }
|
.giveaway-icon { color: #ffd700; }
|
||||||
.user-icon { color: #4299e1; }
|
.user-icon { color: #4299e1; }
|
||||||
|
.settings-icon { color: #9f7aea; }
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -267,6 +268,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Server Settings Section -->
|
||||||
|
<div class="admin-card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">
|
||||||
|
<i class="fas fa-cogs section-icon" style="color: #9f7aea;"></i>Server Settings
|
||||||
|
</h5>
|
||||||
|
<p class="card-text">Configure server-specific settings for moderation, logging, and automation.</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div style="background: rgba(159, 122, 234, 0.1); border: 1px solid rgba(159, 122, 234, 0.2); border-radius: 10px; padding: 1.5rem;">
|
||||||
|
<h6 style="color: #9f7aea; margin-bottom: 0.75rem;">
|
||||||
|
<i class="fas fa-shield-alt"></i> Moderation Settings
|
||||||
|
</h6>
|
||||||
|
<p style="color: #a0aec0; margin-bottom: 1rem; font-size: 0.9rem;">
|
||||||
|
Configure mute roles, warn thresholds, and automatic moderation actions.
|
||||||
|
</p>
|
||||||
|
<a href="{{ url_for('server_settings', guild_id=guild_id) }}" class="admin-btn">
|
||||||
|
<i class="fas fa-cog"></i> Configure Settings
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div style="background: rgba(66, 153, 225, 0.1); border: 1px solid rgba(66, 153, 225, 0.2); border-radius: 10px; padding: 1.5rem;">
|
||||||
|
<h6 style="color: #4299e1; margin-bottom: 0.75rem;">
|
||||||
|
<i class="fas fa-clipboard-list"></i> Log Configuration
|
||||||
|
</h6>
|
||||||
|
<p style="color: #a0aec0; margin-bottom: 1rem; font-size: 0.9rem;">
|
||||||
|
Set up moderation logs and monitoring channels for server activity.
|
||||||
|
</p>
|
||||||
|
<a href="{{ url_for('server_settings', guild_id=guild_id) }}#log-settings" class="admin-btn">
|
||||||
|
<i class="fas fa-list"></i> Setup Logging
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- User Management Section -->
|
<!-- User Management Section -->
|
||||||
<div class="admin-card">
|
<div class="admin-card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|||||||
398
templates/server_settings.html
Normal file
398
templates/server_settings.html
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Server-Einstellungen - {{ guild_name }}</title>
|
||||||
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #0c1426 0%, #1a1f2e 25%, #2d3748 75%, #0c1426 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
background: rgba(26, 31, 46, 0.8);
|
||||||
|
backdrop-filter: blur(15px);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
padding: 2rem;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-name {
|
||||||
|
color: #a0aec0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
background: rgba(45, 55, 72, 0.6);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-icon {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
background: rgba(45, 55, 72, 0.8);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #e2e8f0;
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
background: rgba(45, 55, 72, 0.9);
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control::placeholder {
|
||||||
|
color: #a0aec0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(45, 55, 72, 0.4);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-input {
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-label {
|
||||||
|
color: #e2e8f0;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
color: #a0aec0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(45, 55, 72, 0.6);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
color: #e2e8f0;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.1);
|
||||||
|
color: #e2e8f0;
|
||||||
|
text-decoration: none;
|
||||||
|
border-color: rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: rgba(72, 187, 120, 0.1);
|
||||||
|
color: #48bb78;
|
||||||
|
border: 1px solid rgba(72, 187, 120, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
background: rgba(245, 101, 101, 0.1);
|
||||||
|
color: #f56565;
|
||||||
|
border: 1px solid rgba(245, 101, 101, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
background: rgba(56, 178, 172, 0.1);
|
||||||
|
border: 1px solid rgba(56, 178, 172, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card-title {
|
||||||
|
color: #38b2ac;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card-text {
|
||||||
|
color: #a0aec0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main-container {
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% include 'navigation.html' %}
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="header-section">
|
||||||
|
<h1 class="header-title">
|
||||||
|
<i class="fas fa-cogs header-icon"></i>
|
||||||
|
Server-Einstellungen
|
||||||
|
</h1>
|
||||||
|
<p class="server-name">{{ guild_name }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Flash Messages -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ 'success' if category == 'success' else 'danger' }}">
|
||||||
|
<i class="fas fa-{{ 'check-circle' if category == 'success' else 'exclamation-triangle' }}"></i>
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<!-- Mute-Einstellungen -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<i class="fas fa-volume-mute section-icon"></i>
|
||||||
|
Mute-Einstellungen
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="info-card">
|
||||||
|
<div class="info-card-title">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
Hinweis zu Rollen-IDs
|
||||||
|
</div>
|
||||||
|
<p class="info-card-text">
|
||||||
|
Um eine Rollen-ID zu finden: Rechtsklick auf die Rolle → "ID kopieren" (Entwicklermodus muss aktiviert sein)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="mute_role_id">Mute-Rollen-ID</label>
|
||||||
|
<input type="text" class="form-control" id="mute_role_id" name="mute_role_id"
|
||||||
|
value="{{ settings.mute_role_id or '' }}" placeholder="z.B. 123456789012345678">
|
||||||
|
<small class="help-text">Leer lassen für automatische Suche nach Name</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="mute_role_name">Mute-Rollen-Name</label>
|
||||||
|
<input type="text" class="form-control" id="mute_role_name" name="mute_role_name"
|
||||||
|
value="{{ settings.mute_role_name }}" placeholder="Muted" required>
|
||||||
|
<small class="help-text">Name für automatisch erstellte Mute-Rollen</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="auto_create_mute_role"
|
||||||
|
name="auto_create_mute_role" {% if settings.auto_create_mute_role %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="auto_create_mute_role">
|
||||||
|
Mute-Rolle automatisch erstellen
|
||||||
|
</label>
|
||||||
|
<small class="help-text d-block">Erstellt automatisch eine Mute-Rolle, falls keine gefunden wird</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Warn-Einstellungen -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<i class="fas fa-exclamation-triangle section-icon"></i>
|
||||||
|
Warn-Einstellungen
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="max_warn_threshold">Maximale Warnungen</label>
|
||||||
|
<input type="number" class="form-control" id="max_warn_threshold" name="max_warn_threshold"
|
||||||
|
value="{{ settings.max_warn_threshold }}" min="1" max="10" required>
|
||||||
|
<small class="help-text">Anzahl Warnungen vor automatischen Aktionen</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="auto_mute_duration">Auto-Mute-Dauer</label>
|
||||||
|
<input type="text" class="form-control" id="auto_mute_duration" name="auto_mute_duration"
|
||||||
|
value="{{ settings.auto_mute_duration }}" placeholder="1h" required>
|
||||||
|
<small class="help-text">Format: 10m, 1h, 2d (Minuten, Stunden, Tage)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="auto_mute_on_warns"
|
||||||
|
name="auto_mute_on_warns" {% if settings.auto_mute_on_warns %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="auto_mute_on_warns">
|
||||||
|
Automatischer Mute bei zu vielen Warnungen
|
||||||
|
</label>
|
||||||
|
<small class="help-text d-block">Mutet Benutzer automatisch bei Erreichen des Warn-Limits</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Log-Einstellungen -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3 class="section-title">
|
||||||
|
<i class="fas fa-clipboard-list section-icon"></i>
|
||||||
|
Log-Einstellungen
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="log_channel_id">Log-Kanal-ID</label>
|
||||||
|
<input type="text" class="form-control" id="log_channel_id" name="log_channel_id"
|
||||||
|
value="{{ settings.log_channel_id or '' }}" placeholder="z.B. 123456789012345678">
|
||||||
|
<small class="help-text">Kanal für Moderations-Logs (leer lassen zum Deaktivieren)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="mod_log_enabled"
|
||||||
|
name="mod_log_enabled" {% if settings.mod_log_enabled %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="mod_log_enabled">
|
||||||
|
Moderations-Logs aktiviert
|
||||||
|
</label>
|
||||||
|
<small class="help-text d-block">Protokolliert alle Moderationsaktionen</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-primary mr-3">
|
||||||
|
<i class="fas fa-save"></i> Einstellungen speichern
|
||||||
|
</button>
|
||||||
|
<a href="{{ url_for('server_admin_dashboard', guild_id=guild_id) }}" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left"></i> Zurück zum Dashboard
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Formular-Validierung
|
||||||
|
document.querySelector('form').addEventListener('submit', function(e) {
|
||||||
|
const muteRoleId = document.getElementById('mute_role_id').value;
|
||||||
|
const logChannelId = document.getElementById('log_channel_id').value;
|
||||||
|
const muteDuration = document.getElementById('auto_mute_duration').value;
|
||||||
|
|
||||||
|
// Validiere Rollen-ID Format (falls angegeben)
|
||||||
|
if (muteRoleId && !/^\d{17,19}$/.test(muteRoleId)) {
|
||||||
|
alert('Mute-Rollen-ID muss eine 17-19 stellige Zahl sein');
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiere Log-Kanal-ID Format (falls angegeben)
|
||||||
|
if (logChannelId && !/^\d{17,19}$/.test(logChannelId)) {
|
||||||
|
alert('Log-Kanal-ID muss eine 17-19 stellige Zahl sein');
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiere Zeitformat
|
||||||
|
if (muteDuration && !/^\d+[mhd]$/.test(muteDuration)) {
|
||||||
|
alert('Auto-Mute-Dauer muss im Format "10m", "1h" oder "2d" sein');
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user