diff --git a/database/__init__.py b/database/__init__.py
index 444d20f..77700d1 100644
--- a/database/__init__.py
+++ b/database/__init__.py
@@ -33,11 +33,11 @@ def _set_sqlite_pragma(dbapi_connection, connection_record):
except Exception:
pass
-def _tableExists(table_name:str, cursor:Cursor) -> bool:
+def _tableExists(table_name: str, cursor: Cursor) -> bool:
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
return cursor.fetchone() is not None
-def _tableHaveColumn(table_name:str, column_name:str, cursor:Cursor) -> bool:
+def _tableHaveColumn(table_name:str, column_name:str, cursor:Cursor) -> bool:
if not _tableExists(table_name, cursor):
return False
cursor.execute(f'PRAGMA table_info({table_name})')
@@ -70,9 +70,8 @@ def _doPostImportMigration(cursor:Cursor):
cursor.execute('INSERT INTO game_bundle(url, name, json) VALUES (?, ?, ?)', (url, name, json.dumps(json_data)))
logging.info("suppression de la table temporaire game_bundle_old")
_dropTable('game_bundle_old', cursor)
-
+
if _tableExists('youtube_notification', cursor):
- logging.info("Migration de la table youtube_notification: ajout des colonnes d'embed")
embed_columns = [
('embed_title', 'VARCHAR(256)'),
('embed_description', 'VARCHAR(2000)'),
@@ -81,7 +80,7 @@ def _doPostImportMigration(cursor:Cursor):
('embed_author_name', 'VARCHAR(256)'),
('embed_author_icon', 'VARCHAR(512)'),
('embed_thumbnail', 'BOOLEAN DEFAULT 1'),
- ('embed_image', 'BOOLEAN DEFAULT 1')
+ ('embed_image', 'BOOLEAN DEFAULT 1'),
]
for col_name, col_type in embed_columns:
if not _tableHaveColumn('youtube_notification', col_name, cursor):
@@ -89,8 +88,7 @@ def _doPostImportMigration(cursor:Cursor):
cursor.execute(f'ALTER TABLE youtube_notification ADD COLUMN {col_name} {col_type}')
logging.info(f"Colonne {col_name} ajoutée à youtube_notification")
except Exception as e:
- logging.error(f"Impossible d'ajouter la colonne {col_name}: {e}")
- raise
+ logging.warning(f"Colonne youtube_notification.{col_name}: {e}")
with webapp.app_context():
with open('database/schema.sql', 'r') as f:
diff --git a/database/models.py b/database/models.py
index 1562652..d144c7e 100644
--- a/database/models.py
+++ b/database/models.py
@@ -61,6 +61,7 @@ class AntiCheatCache(db.Model):
notes = db.Column(db.String(1024))
updated_at = db.Column(db.DateTime)
+
class YouTubeNotification(db.Model):
__tablename__ = 'youtube_notification'
id = db.Column(db.Integer, primary_key=True)
diff --git a/discordbot/__init__.py b/discordbot/__init__.py
index 9c48341..cffafb6 100644
--- a/discordbot/__init__.py
+++ b/discordbot/__init__.py
@@ -3,6 +3,7 @@ import discord
import logging
import random
+from webapp import webapp
from database import db
from database.helpers import ConfigurationHelper
from database.models import Configuration, Humeur, Commande
@@ -28,6 +29,8 @@ from protondb import searhProtonDb
class DiscordBot(discord.Client):
async def on_ready(self):
logging.info(f'Connecté en tant que {self.user} (ID: {self.user.id})')
+ webapp.config["BOT_STATUS"]["discord_connected"] = True
+ webapp.config["BOT_STATUS"]["discord_guild_count"] = len(self.guilds)
for c in self.get_all_channels() :
logging.info(f'{c.id} {c.name}')
@@ -37,6 +40,9 @@ class DiscordBot(discord.Client):
self.loop.create_task(self.updateStatus())
self.loop.create_task(self.updateHumbleBundle())
self.loop.create_task(self.updateYouTube())
+
+ async def on_disconnect(self):
+ webapp.config["BOT_STATUS"]["discord_connected"] = False
async def updateStatus(self):
while not self.is_closed():
@@ -52,11 +58,10 @@ class DiscordBot(discord.Client):
while not self.is_closed():
await checkHumbleBundleAndNotify(self)
await asyncio.sleep(30*60)
-
+
async def updateYouTube(self):
while not self.is_closed():
await checkYouTubeVideos()
- # Vérification toutes les 5 minutes (comme pour Twitch)
await asyncio.sleep(5*60)
def getAllTextChannel(self) -> list[TextChannel]:
diff --git a/twitchbot/__init__.py b/twitchbot/__init__.py
index 529bf0d..45796f6 100644
--- a/twitchbot/__init__.py
+++ b/twitchbot/__init__.py
@@ -14,8 +14,11 @@ USER_SCOPE = [AuthScope.CHAT_READ, AuthScope.CHAT_EDIT]
async def _onReady(ready_event: EventData):
logging.info('Bot Twitch prêt')
+ channel = ConfigurationHelper().getValue('twitch_channel')
+ webapp.config["BOT_STATUS"]["twitch_connected"] = True
+ webapp.config["BOT_STATUS"]["twitch_channel_name"] = channel
with webapp.app_context():
- await ready_event.chat.join_room(ConfigurationHelper().getValue('twitch_channel'))
+ await ready_event.chat.join_room(channel)
asyncio.get_event_loop().create_task(twitchBot._checkOnlineStreamers())
diff --git a/webapp/__init__.py b/webapp/__init__.py
index cdaaa26..0ff86c7 100644
--- a/webapp/__init__.py
+++ b/webapp/__init__.py
@@ -2,4 +2,12 @@ from flask import Flask
webapp = Flask(__name__)
+# État des bots (mis à jour par les bots, lu par le panneau)
+webapp.config["BOT_STATUS"] = {
+ "discord_connected": False,
+ "discord_guild_count": 0,
+ "twitch_connected": False,
+ "twitch_channel_name": None,
+}
+
from webapp import commandes, configurations, index, humeurs, protondb, live_alert, twitch_auth, moderation, youtube
diff --git a/webapp/index.py b/webapp/index.py
index 10f735b..ffe42b2 100644
--- a/webapp/index.py
+++ b/webapp/index.py
@@ -1,6 +1,16 @@
from flask import render_template
from webapp import webapp
+from database.models import ModerationEvent
@webapp.route("/")
def index():
- return render_template("index.html")
+ status = webapp.config["BOT_STATUS"]
+ sanctions_count = ModerationEvent.query.count()
+ return render_template(
+ "index.html",
+ discord_connected=status["discord_connected"],
+ discord_guild_count=status["discord_guild_count"],
+ sanctions_count=sanctions_count,
+ twitch_connected=status["twitch_connected"],
+ twitch_channel_name=status["twitch_channel_name"],
+ )
diff --git a/webapp/moderation.py b/webapp/moderation.py
index 5571487..e0d92f4 100644
--- a/webapp/moderation.py
+++ b/webapp/moderation.py
@@ -3,16 +3,58 @@ from webapp import webapp
from database import db
from database.models import ModerationEvent
+def _top_sanctioned():
+ return (
+ db.session.query(
+ ModerationEvent.discord_id,
+ db.func.max(ModerationEvent.username).label("username"),
+ db.func.count(ModerationEvent.id).label("count"),
+ )
+ .group_by(ModerationEvent.discord_id)
+ .order_by(db.func.count(ModerationEvent.id).desc())
+ .limit(3)
+ .all()
+ )
+
+def _top_moderators():
+ return (
+ db.session.query(
+ ModerationEvent.staff_id,
+ db.func.max(ModerationEvent.staff_name).label("staff_name"),
+ db.func.count(ModerationEvent.id).label("count"),
+ )
+ .group_by(ModerationEvent.staff_id)
+ .order_by(db.func.count(ModerationEvent.id).desc())
+ .limit(3)
+ .all()
+ )
+
@webapp.route("/moderation")
def moderation():
events = ModerationEvent.query.order_by(ModerationEvent.created_at.desc()).all()
- return render_template("moderation.html", events=events, event=None)
+ top_sanctioned = _top_sanctioned()
+ top_moderators = _top_moderators()
+ return render_template(
+ "moderation.html",
+ events=events,
+ event=None,
+ top_sanctioned=top_sanctioned,
+ top_moderators=top_moderators,
+ )
@webapp.route("/moderation/edit/ Nous devons définir ce que nous souhaitons afficher sur la page d'accueil. Peut-être l'historique des dernières
- modifications ? de la modération ?
+ Gérez les fonctionnalités de votre bot Discord et Twitch depuis cette interface.
+ Serveurs connectés {{ discord_guild_count }} Sanctions enregistrées {{ sanctions_count }} Canal connecté {% if twitch_channel_name %}{{ twitch_channel_name }}{% else %}—{% endif %} Sanctions — À venir Intégrations du bot Twitch à venir.
+ Mamie Henriette est un bot open source pour Discord et Twitch, développé par la communauté.
+ Cette interface vous permet de configurer et gérer toutes les fonctionnalités.
+
+ Historique des actions de modération sur le serveur Discord.
+
- Historique des actions de modération effectuées sur le serveur Discord.
+ Utilisateurs les plus sanctionnés Staff ayant effectué le plus d'actionsBienvenue sur l'interface d'administration de Mamie.
-
+ Panneau d'administration
+
+ Discord
+ Twitch
+ À propos
+ Modération Discord
+Modération
+ Top 3 sanctions
+ Top 3 modérateurs
+
-
-
-
-
-
-
- Commande
- Description
-
-
- !averto @utilisateur raison
-
Alias : !warn, !av, !avertissementAvertit un utilisateur et enregistre l'avertissement dans la base de données
-
-
- !delaverto id
-
Alias : !removewarn, !delwarnRetire un avertissement en utilisant son numéro d'ID
-
-
- !warnings ou !warnings @utilisateur
-
Alias : !listevent, !listwarnAffiche la liste des événements de modération (tous ou pour un utilisateur spécifique)
-
-
- !inspect @utilisateur ou !inspect id
- Affiche des informations détaillées sur un utilisateur : création du compte, date d'arrivée, historique de modération
-
-
- !kick @utilisateur raison
- Expulse un utilisateur du serveur
-
-
- !ban @utilisateur raison
- Bannit définitivement un utilisateur du serveur
-
-
- !unban discord_id ou !unban #sanction_id raison
- Révoque le bannissement d'un utilisateur et lui envoie une invitation
-
-
- !banlist
- Affiche la liste des utilisateurs actuellement bannis du serveur
-
-
-
- !aide
-
Alias : !helpAffiche l'aide avec toutes les commandes disponibles
-
!averto @user raison
+ Avertit un utilisateur
+ !delaverto id
+ Retire un avertissement
+ !warnings [@user]
+ Liste les événements de modération
+ !inspect @user
+ Informations sur un utilisateur
+ !kick @user raison
+ Expulse un utilisateur
+ !ban @user raison
+ Bannit un utilisateur
+ !unban id
+ Révoque un bannissement
+ !banlist
+ Liste des utilisateurs bannis
+ | Type | -Utilisateur | -Discord ID | -Date & Heure | -Raison | -Staff | -# | -
|---|---|---|---|---|---|---|
| {{ mod_event.type }} | -{{ mod_event.username }} | -{{ mod_event.discord_id }} | -{{ mod_event.created_at.strftime('%d/%m/%Y %H:%M') if mod_event.created_at else 'N/A' }} | -{{ mod_event.reason }} | -{{ mod_event.staff_name }} | -- ✐ - 🗑 - | -
| Type | +Utilisateur | +Date | +Raison | +Staff | +Actions | +
|---|---|---|---|---|---|
| + {% if mod_event.type == 'ban' %} + Ban + {% elif mod_event.type == 'kick' %} + Kick + {% elif mod_event.type == 'warn' or mod_event.type == 'warning' %} + Warn + {% elif mod_event.type == 'unban' %} + Unban + {% else %} + {{ mod_event.type }} + {% endif %} + | +
+
+ {{ mod_event.username }}
+ {{ mod_event.discord_id }}
+
+ |
+ + {{ mod_event.created_at.strftime('%d/%m/%Y %H:%M') if mod_event.created_at else 'N/A' }} + | +
+ {{ mod_event.reason }}
+ |
+ + {{ mod_event.staff_name }} + | +
+
+
+ Modifier
+
+
+ Supprimer
+
+
+ |
+
| + Aucun événement de modération + | +|||||