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 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."""
|
||||
|
||||
771
bot.py
771
bot.py
@@ -112,7 +112,7 @@ loop = asyncio.get_event_loop()
|
||||
def close_database_connection(connection):
|
||||
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"""
|
||||
connection = None
|
||||
cursor = None
|
||||
@@ -120,11 +120,11 @@ def insert_user_data(user_id, guild_id, permission, points, ban, askmultus, filt
|
||||
connection = connect_to_database()
|
||||
cursor = connection.cursor()
|
||||
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)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
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, %s, %s, %s)
|
||||
"""
|
||||
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)
|
||||
connection.commit()
|
||||
@@ -246,7 +246,10 @@ async def create_user_data_with_member(user_id, guild_id, member=None):
|
||||
"asknotes_history": [],
|
||||
"xp": 0,
|
||||
"level": 1,
|
||||
"nickname": nickname
|
||||
"nickname": nickname,
|
||||
"ai_ban": 0,
|
||||
"mutes": 0,
|
||||
"warns": 0
|
||||
}
|
||||
|
||||
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 [],
|
||||
"xp": int(result[10]) if result[10] is not None else 0,
|
||||
"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:
|
||||
user_data = {
|
||||
@@ -308,7 +314,10 @@ def load_user_data_from_mysql(user_id, guild_id):
|
||||
"asknotes_history": [],
|
||||
"xp": 0,
|
||||
"level": 1,
|
||||
"nickname": ""
|
||||
"nickname": "",
|
||||
"ai_ban": 0,
|
||||
"mutes": 0,
|
||||
"warns": 0
|
||||
}
|
||||
insert_user_data(
|
||||
user_data["user_id"],
|
||||
@@ -404,7 +413,10 @@ def load_user_data_sync(user_id, guild_id):
|
||||
"asknotes_history": [],
|
||||
"xp": 0,
|
||||
"level": 1,
|
||||
"nickname": ""
|
||||
"nickname": "",
|
||||
"ai_ban": 0,
|
||||
"mutes": 0,
|
||||
"warns": 0
|
||||
}
|
||||
insert_user_data(
|
||||
user_data["user_id"],
|
||||
@@ -2146,6 +2158,749 @@ async def version(ctx):
|
||||
"""Displays the current version of the bot."""
|
||||
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_DIR = "cache"
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
|
||||
.giveaway-icon { color: #ffd700; }
|
||||
.user-icon { color: #4299e1; }
|
||||
.settings-icon { color: #9f7aea; }
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
@@ -267,6 +268,44 @@
|
||||
</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 -->
|
||||
<div class="admin-card">
|
||||
<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