From 016ac85bc787585249badcf083bf731ef26a1113 Mon Sep 17 00:00:00 2001 From: Mathis Date: Sun, 12 Jul 2026 15:01:47 +0200 Subject: [PATCH] V0.1 de la migration vers systemd --- README.md | 17 ++++++++++ bot.py | 2 +- commandes/bug_report.py | 49 +++++++++++++++++++++-------- scripts/install_systemd_services.sh | 20 ++++++++++++ scripts/run_kuby_bot.sh | 16 ++++++++++ scripts/run_kuby_flask.sh | 16 ++++++++++ serveur_flask.py | 12 ++----- systemd/kuby-bot.service | 17 ++++++++++ systemd/kuby-flask.service | 17 ++++++++++ 9 files changed, 142 insertions(+), 24 deletions(-) create mode 100755 scripts/install_systemd_services.sh create mode 100755 scripts/run_kuby_bot.sh create mode 100755 scripts/run_kuby_flask.sh create mode 100644 systemd/kuby-bot.service create mode 100644 systemd/kuby-flask.service diff --git a/README.md b/README.md index a7084a0..a084166 100755 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ Kuby est un bot Discord complet développé en Python avec `discord.py`. Il offr ### Prérequis +- systemd installé sur la machine cible +- utilisateur `discord` existant (ou adapter `User=` dans les fichiers de service) + - Python 3.10 ou supérieur - pip (gestionnaire de paquets Python) @@ -86,6 +89,20 @@ source venv/bin/activate # Linux/Mac pip install -r requirements.txt ``` +### 4. Installer les services systemd + +```bash +sudo chmod +x scripts/*.sh +sudo ./scripts/install_systemd_services.sh +``` + +### 5. Vérifier les services + +```bash +sudo systemctl status kuby-bot.service kuby-flask.service +sudo journalctl -u kuby-bot.service -f +``` + Les dépendances incluent : - `discord.py>=2.3.0` - Framework Discord - `python-dotenv>=1.0.0` - Variables d'environnement diff --git a/bot.py b/bot.py index 67a3a0a..5ff2710 100755 --- a/bot.py +++ b/bot.py @@ -102,7 +102,7 @@ class MyBot(commands.Bot): self.advanced_logger = AdvancedLogger(self) kuby_logger.info("✅ Advanced logger initialized") - # Flask est géré par PM2 (kuby-flask), pas besoin de le lancer ici + # Flask est géré par le service systemd kuby-flask, pas besoin de le lancer ici async def do_async_setup(self): kuby_logger.info("🚀 Configuration initiale du bot Kuby...") diff --git a/commandes/bug_report.py b/commandes/bug_report.py index 9c2f42a..d3083fc 100644 --- a/commandes/bug_report.py +++ b/commandes/bug_report.py @@ -29,7 +29,7 @@ def classify_webhook_event(payload: Dict[str, Any]) -> Optional[str]: if "comment" in payload or payload.get("comment") or "comment" in action: return "note" - if payload.get("issue") or payload.get("object_attributes") or action in {"open", "reopen", "close", "closed", "updated", "created", "edited"}: + if payload.get("issue") or payload.get("object_attributes") or action in {"open", "reopen", "close", "closed", "updated", "created", "edited", "label_updated", "label_added", "label_removed", "label_changed"}: return "issue" return None @@ -82,24 +82,47 @@ def normalize_labels(labels: Any) -> list[str]: def extract_label_change(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]: """Extrait le changement de labels d’un payload d’issue.""" - changes = payload.get("changes") or {} - labels_change = changes.get("labels") or changes.get("label") - if not isinstance(labels_change, dict): + action = str(payload.get("action") or payload.get("event") or "").lower() + if not action.startswith("label"): return None - previous = normalize_labels(labels_change.get("previous")) - current = normalize_labels(labels_change.get("current")) - if not previous and not current: + issue = payload.get("issue") or {} + current_labels = normalize_labels(issue.get("labels") or payload.get("labels")) + if not current_labels: return None - added = [label for label in current if label not in previous] - removed = [label for label in previous if label not in current] - if not added and not removed: - return None + label_payload = payload.get("label") + label_name = None + if isinstance(label_payload, dict): + label_name = label_payload.get("name") or label_payload.get("title") or label_payload.get("label") + elif isinstance(label_payload, str): + label_name = label_payload + + previous_labels = [] + if isinstance(payload.get("changes"), dict): + prev = payload["changes"].get("labels") or payload["changes"].get("label") + if isinstance(prev, dict): + previous_labels = normalize_labels(prev.get("previous")) + + if not previous_labels and issue.get("labels"): + previous_labels = [] + + if action in {"label_removed", "label_deleted"}: + return { + "previous": previous_labels or ([label_name] if label_name else []), + "current": current_labels, + "added": [], + "removed": [label_name] if label_name else [], + } + + added = [label for label in current_labels if label not in previous_labels] + removed = [label for label in previous_labels if label not in current_labels] + if not added and not removed and label_name: + added = [label_name] return { - "previous": previous, - "current": current, + "previous": previous_labels, + "current": current_labels, "added": added, "removed": removed, } diff --git a/scripts/install_systemd_services.sh b/scripts/install_systemd_services.sh new file mode 100755 index 0000000..d0e3d47 --- /dev/null +++ b/scripts/install_systemd_services.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +if [ "$EUID" -ne 0 ]; then + echo "Ce script doit être exécuté avec sudo." >&2 + exit 1 +fi + +mkdir -p /var/log/kuby + +for service in kuby-bot.service kuby-flask.service; do + sed "s#__KUBY_DIR__#${ROOT_DIR}#g" "$ROOT_DIR/systemd/$service" > "/etc/systemd/system/$service" +done + +systemctl daemon-reload +systemctl enable --now kuby-bot.service kuby-flask.service + +echo "Services systemd installés et démarrés." diff --git a/scripts/run_kuby_bot.sh b/scripts/run_kuby_bot.sh new file mode 100755 index 0000000..e3dbd02 --- /dev/null +++ b/scripts/run_kuby_bot.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +export PYTHONUNBUFFERED=1 + +if [ -x "$ROOT_DIR/.venv/bin/python" ]; then + exec "$ROOT_DIR/.venv/bin/python" "$ROOT_DIR/kuby.py" +elif command -v python3 >/dev/null 2>&1; then + exec python3 "$ROOT_DIR/kuby.py" +else + echo "python3 introuvable" >&2 + exit 1 +fi diff --git a/scripts/run_kuby_flask.sh b/scripts/run_kuby_flask.sh new file mode 100755 index 0000000..cfd917c --- /dev/null +++ b/scripts/run_kuby_flask.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +export PYTHONUNBUFFERED=1 + +if [ -x "$ROOT_DIR/.venv/bin/python" ]; then + exec "$ROOT_DIR/.venv/bin/python" "$ROOT_DIR/serveur_flask.py" +elif command -v python3 >/dev/null 2>&1; then + exec python3 "$ROOT_DIR/serveur_flask.py" +else + echo "python3 introuvable" >&2 + exit 1 +fi diff --git a/serveur_flask.py b/serveur_flask.py index 8739e77..ca04445 100644 --- a/serveur_flask.py +++ b/serveur_flask.py @@ -17,9 +17,6 @@ PROCESS_NAME = "kuby" # webhook de logs Discord DISCORD_LOG_URL = "https://discord.com/api/webhooks/1482148910888652910/IA9CcOWtjGswbuxMaOu_6uaclv5zojZo4ttxtV6RYyZzJ9gW7BF7xp_Zv3oPIjYEuh8o" -# Chemin vers PM2 -PM2_BINARY = "/home/discord/.nvm/versions/node/v24.15.0/bin/pm2" - # Bot Local Proxy (Relai vers le Cog de ton bot) BOT_LOCAL_URL = "http://127.0.0.1:5001/bot_event" @@ -88,13 +85,8 @@ def run_mep_logic(author, commit_msg, branch="main"): if use_rsync: subprocess.run(["rsync", "-a", "--exclude=.git", "--exclude=venv", "--exclude=.venv", "--exclude=__pycache__", f"{target_path}/", f"{PROJECT_PATH}/"], check=True) - # 3. PM2 Restart - subprocess.run( - [PM2_BINARY, "restart", PROCESS_NAME], - check=True, - capture_output=True, - text=True - ) + # 3. Redémarrage via systemd + subprocess.run(["systemctl", "restart", "kuby-bot.service"], check=True, capture_output=True, text=True) duration = (datetime.now() - start_time).total_seconds() msg = f"✅ **Déploiement réussi en {duration:.2f}s**\nLe bot est à jour sur Forgejo et redémarré." diff --git a/systemd/kuby-bot.service b/systemd/kuby-bot.service new file mode 100644 index 0000000..f5c13c1 --- /dev/null +++ b/systemd/kuby-bot.service @@ -0,0 +1,17 @@ +[Unit] +Description=Kuby Discord Bot +After=network.target + +[Service] +Type=simple +User=discord +WorkingDirectory=__KUBY_DIR__ +Environment=PYTHONUNBUFFERED=1 +ExecStart=__KUBY_DIR__/scripts/run_kuby_bot.sh +Restart=always +RestartSec=5 +StandardOutput=append:/var/log/kuby/bot.log +StandardError=append:/var/log/kuby/bot.log + +[Install] +WantedBy=multi-user.target diff --git a/systemd/kuby-flask.service b/systemd/kuby-flask.service new file mode 100644 index 0000000..0d1a64d --- /dev/null +++ b/systemd/kuby-flask.service @@ -0,0 +1,17 @@ +[Unit] +Description=Kuby Flask webhook service +After=network.target + +[Service] +Type=simple +User=discord +WorkingDirectory=__KUBY_DIR__ +Environment=PYTHONUNBUFFERED=1 +ExecStart=__KUBY_DIR__/scripts/run_kuby_flask.sh +Restart=always +RestartSec=5 +StandardOutput=append:/var/log/kuby/flask.log +StandardError=append:/var/log/kuby/flask.log + +[Install] +WantedBy=multi-user.target