Files
Mow910 ec296dd226 Refactor Discord member statistics handling and remove unused components
- Removed the `guild_member_stats` table and related logic from the database and models, as well as the associated webapp routes and templates.
- Cleaned up the Discord bot initialization by eliminating unused imports and functions related to member statistics.
- Updated logging configuration in `run-web.py` to streamline the startup process and improve thread management.
2026-03-25 23:13:14 +01:00

330 lines
9.9 KiB
Python

# Paramètres webapp : rôles, permissions par page, inscriptions (super administrateur uniquement).
from flask import render_template, request, redirect, url_for, flash
from webapp import webapp
from webapp.auth import require_page
from database import db
from database.models import WebappRole, PagePermission, WebappUser
from database.helpers import ConfigurationHelper
# Métadonnées des pages : catégorie, label d'affichage, description
PAGE_METADATA = {
"index": {
"label": "Tableau de bord",
"category": "general",
"description": "Page d'accueil avec aperçu du système",
"icon": "home"
},
"commandes": {
"label": "Commandes",
"category": "content",
"description": "Gérer les commandes Discord et Twitch",
"icon": "terminal"
},
"configurations": {
"label": "Configurations",
"category": "config",
"description": "Paramètres généraux du bot",
"icon": "settings"
},
"humeurs": {
"label": "Humeurs",
"category": "content",
"description": "Gérer les statuts du bot Discord",
"icon": "smile"
},
"protondb": {
"label": "ProtonDB",
"category": "content",
"description": "Recherche de compatibilité des jeux Linux",
"icon": "gamepad"
},
"live_alert": {
"label": "Alertes Live",
"category": "content",
"description": "Notifications Discord pour les streams Twitch",
"icon": "bell"
},
"youtube": {
"label": "YouTube",
"category": "content",
"description": "Notifications Discord pour les vidéos YouTube",
"icon": "video"
},
"announcements": {
"label": "Annonces Twitch",
"category": "content",
"description": "Messages automatiques dans le chat Twitch",
"icon": "megaphone"
},
"moderation": {
"label": "Modération Discord",
"category": "moderation",
"description": "Historique de modération Discord",
"icon": "shield"
},
"twitch_moderation": {
"label": "Modération Twitch",
"category": "moderation",
"description": "Commandes et logs de modération Twitch",
"icon": "shield-check"
},
"link_filter": {
"label": "Filtre de liens",
"category": "moderation",
"description": "Filtrage automatique des liens Twitch",
"icon": "filter"
},
"twitch_events": {
"label": "Événements Twitch",
"category": "content",
"description": "Notifications subs, follows, raids, clips",
"icon": "star"
},
"freeloot": {
"label": "Free Loot",
"category": "content",
"description": "Flux RSS de jeux gratuits vers Discord",
"icon": "gift"
},
"users": {
"label": "Utilisateurs",
"category": "admin",
"description": "Gestion des comptes et rôles webapp",
"icon": "users"
},
"settings": {
"label": "Paramètres",
"category": "admin",
"description": "Rôles, permissions et inscriptions",
"icon": "cog"
},
}
# Labels des catégories
CATEGORY_LABELS = {
"general": {"label": "Général", "color": "#6B7280", "icon": "layout"},
"content": {"label": "Contenu", "color": "#3B82F6", "icon": "file-text"},
"moderation": {"label": "Modération", "color": "#EF4444", "icon": "shield"},
"config": {"label": "Configuration", "color": "#8B5CF6", "icon": "settings"},
"admin": {"label": "Administration", "color": "#F59E0B", "icon": "crown"},
}
# Rôles par défaut avec métadonnées
DEFAULT_ROLES = {
"viewer_twitch": {
"description": "Accès minimal, consultation uniquement",
"color": "#9146FF",
"icon": "eye"
},
"utilisateur_discord": {
"description": "Peut consulter et modifier du contenu basique",
"color": "#5865F2",
"icon": "user"
},
"moderateur_discord": {
"description": "Accès aux outils de modération Discord",
"color": "#57F287",
"icon": "shield"
},
"expert_discord": {
"description": "Gestion avancée du contenu et des configurations",
"color": "#FEE75C",
"icon": "star"
},
"moderateur_twitch": {
"description": "Accès aux outils de modération Twitch",
"color": "#9146FF",
"icon": "shield-check"
},
"super_administrateur": {
"description": "Accès complet à toutes les fonctionnalités",
"color": "#ED4245",
"icon": "crown"
},
}
PAGE_KEYS = [
("index", "Tableau de bord"),
("configurations", "Configurations"),
("commandes", "Commandes"),
("humeurs", "Humeurs"),
("live_alert", "Alerte live"),
("announcements", "Annonces Twitch"),
("twitch_moderation", "Modération Twitch"),
("link_filter", "Filtre de liens"),
("twitch_events", "Notifications événements Twitch"),
("youtube", "YouTube"),
("protondb", "ProtonDB"),
("freeloot", "FreeLoot"),
("moderation", "Modération Discord"),
("users", "Utilisateurs"),
("settings", "Paramètres"),
]
@webapp.route("/settings")
@require_page("settings")
def settings():
roles = WebappRole.query.order_by(WebappRole.level).all()
permissions = PagePermission.query.all()
perm_by_key = {p.page_key: p for p in permissions}
reg_enabled = ConfigurationHelper().getValue("registration_enabled") not in (None, "", "false", "0", "no", "off")
# Organiser les pages par catégorie
pages_by_category = {}
for page_key, meta in PAGE_METADATA.items():
category = meta.get("category", "general")
if category not in pages_by_category:
pages_by_category[category] = []
pages_by_category[category].append({
"key": page_key,
"meta": meta,
"permission": perm_by_key.get(page_key)
})
# Trier les pages dans chaque catégorie par label
for category in pages_by_category:
pages_by_category[category].sort(key=lambda x: x["meta"]["label"])
return render_template(
"settings.html",
roles=roles,
pages_by_category=pages_by_category,
category_labels=CATEGORY_LABELS,
perm_by_key=perm_by_key,
registration_enabled=reg_enabled,
page_metadata=PAGE_METADATA,
default_roles_meta=DEFAULT_ROLES,
)
@webapp.route("/settings/registration", methods=["POST"])
@require_page("settings")
def settings_toggle_registration():
enabled = request.form.get("enabled") in ("1", "true", "on", "yes")
ConfigurationHelper().createOrUpdate("registration_enabled", "true" if enabled else "false")
db.session.commit()
flash("Inscriptions " + ("activées" if enabled else "désactivées") + ".", "success")
return redirect(url_for("settings"))
@webapp.route("/settings/roles/add", methods=["POST"])
@require_page("settings")
def settings_role_add():
name = (request.form.get("name") or "").strip()
level_str = request.form.get("level", "0")
description = (request.form.get("description") or "").strip()
color = (request.form.get("color") or "#6B7280").strip()
icon = (request.form.get("icon") or "").strip()
if not name:
flash("Nom du rôle requis.", "error")
return redirect(url_for("settings"))
try:
level = int(level_str)
except ValueError:
level = 0
if WebappRole.query.filter_by(name=name).first():
flash(f"Le rôle « {name} » existe déjà.", "error")
return redirect(url_for("settings"))
role = WebappRole(
name=name,
level=level,
description=description if description else None,
color=color,
icon=icon if icon else None
)
db.session.add(role)
db.session.commit()
flash(f"Rôle « {name} » créé.", "success")
return redirect(url_for("settings"))
@webapp.route("/settings/roles/<int:role_id>/edit", methods=["POST"])
@require_page("settings")
def settings_role_edit(role_id):
role = WebappRole.query.get_or_404(role_id)
level_str = request.form.get("level")
description = request.form.get("description")
color = request.form.get("color")
icon = request.form.get("icon")
if level_str is not None:
try:
role.level = int(level_str)
except ValueError:
flash("Niveau invalide.", "error")
return redirect(url_for("settings"))
if description is not None:
role.description = description.strip() if description.strip() else None
if color is not None:
role.color = color.strip() if color.strip() else "#6B7280"
if icon is not None:
role.icon = icon.strip() if icon.strip() else None
db.session.commit()
flash(f"Rôle « {role.name} » mis à jour.", "success")
return redirect(url_for("settings"))
@webapp.route("/settings/roles/<int:role_id>/delete", methods=["POST"])
@require_page("settings")
def settings_role_delete(role_id):
role = WebappRole.query.get_or_404(role_id)
if WebappUser.query.filter_by(role=role.name).count() > 0:
flash(f"Impossible de supprimer le rôle « {role.name} » : des utilisateurs l'utilisent.", "error")
return redirect(url_for("settings"))
db.session.delete(role)
db.session.commit()
flash(f"Rôle « {role.name} » supprimé.", "success")
return redirect(url_for("settings"))
@webapp.route("/settings/permissions/update", methods=["POST"])
@require_page("settings")
def settings_permissions_update():
page_key = request.form.get("page_key")
role_name = request.form.get("role")
if not page_key:
return redirect(url_for("settings"))
role = WebappRole.query.filter_by(name=role_name).first()
level = role.level if role else 0
perm = PagePermission.query.filter_by(page_key=page_key).first()
if perm:
perm.min_level = level
perm.write_level = level
else:
perm = PagePermission(page_key=page_key, min_level=level, write_level=level)
db.session.add(perm)
db.session.commit()
flash(f"Accès à « {page_key} » mis à jour.", "success")
return redirect(url_for("settings"))
@webapp.route("/settings/permissions/bulk", methods=["POST"])
@require_page("settings")
def settings_permissions_bulk():
page_keys = request.form.getlist("page_keys")
role_name = request.form.get("role")
if not page_keys or not role_name:
flash("Sélectionnez au moins une page et un rôle.", "error")
return redirect(url_for("settings"))
role = WebappRole.query.filter_by(name=role_name).first()
level = role.level if role else 0
updated = 0
for page_key in page_keys:
perm = PagePermission.query.filter_by(page_key=page_key).first()
if perm:
perm.min_level = level
perm.write_level = level
else:
perm = PagePermission(page_key=page_key, min_level=level, write_level=level)
db.session.add(perm)
updated += 1
db.session.commit()
flash(f"Accès mis à jour pour {updated} page(s) avec le rôle « {role_name} ».", "success")
return redirect(url_for("settings"))