import os import time import logging import json from typing import Optional from pathlib import Path import pymysql import requests from dotenv import load_dotenv BASE_DIR = Path("/home/debian/Gestion_sondes") load_dotenv(BASE_DIR / ".env") logging.basicConfig( level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s" ) log = logging.getLogger("journal_connexions") def env_str(name: str, default: Optional[str] = None) -> Optional[str]: value = os.getenv(name, default) if value is None: return None value = value.strip() return value if value else default DB_CONFIG = { "host": env_str("DB_HOST"), "port": int(env_str("DB_PORT", "3306")), "user": env_str("DB_USER2"), "password": env_str("DB_PASS2"), "database": env_str("DB_NAME2", "Acces"), "charset": "utf8mb4", "cursorclass": pymysql.cursors.DictCursor, "autocommit": True, } SYNO_CHAT_WEBHOOK = env_str("SYNO_CHAT_WEBHOOK_CONNEXIONS") SYNO_CHAT_BOTNAME = env_str("SYNO_CHAT_BOTNAME_CONNEXIONS", "Journal Connexions") SYNO_CHAT_WEBHOOK_SIMPLE = env_str("SYNO_CHAT_WEBHOOK_CONNEXIONS_SIMPLE") SYNO_CHAT_BOTNAME_SIMPLE = env_str("SYNO_CHAT_BOTNAME_CONNEXIONS_SIMPLE", "Connexions Simples") POLL_INTERVAL = int(env_str("POLL_INTERVAL", "10")) def get_connection(): return pymysql.connect(**DB_CONFIG) def format_message(row: dict) -> str: return ( f"[Connexion MySQL]\n" f"Utilisateur : {row.get('NomUtilisateur', '')}\n" f"Poste : {row.get('PosteClient', '')}\n" f"Tableur : {row.get('TableurSource', '')}\n" f"Windows : {row.get('UtilisateurWindows', '')}\n" f"Site : {row.get('SiteDemande', '')}\n" f"Service : {row.get('ServiceDemande', '')}\n" f"DSN : {row.get('DSN', '')}\n" f"BDD : {row.get('BDD', '')}\n" f"Statut : {row.get('Statut', '')}\n" f"Motif : {row.get('Motif', '')}\n" f"Heure : {row.get('DateHeure', '')}\n" f"Session : {row.get('SessionID', '')}" ) def format_simple_message(row: dict) -> str: user = row.get("NomUtilisateur", "") site = row.get("SiteDemande", "") statut = row.get("Statut", "") return f"[Connexion site]\n{user} -> {site} ({statut})" def send_synology_chat(message: str) -> None: if not SYNO_CHAT_WEBHOOK: raise RuntimeError("SYNO_CHAT_WEBHOOK_CONNEXIONS non configure") syno_payload = { "text": message, "botName": SYNO_CHAT_BOTNAME } response = requests.post( SYNO_CHAT_WEBHOOK, data={"payload": json.dumps(syno_payload, ensure_ascii=False)}, timeout=10 ) log.info("Synology Chat DETAIL HTTP=%s body=%s", response.status_code, response.text) response.raise_for_status() body = response.json() if not body.get("success", False): raise RuntimeError(f"Synology Chat DETAIL erreur: {body}") def send_synology_chat_simple(message: str) -> None: if not SYNO_CHAT_WEBHOOK_SIMPLE: return syno_payload = { "text": message, "botName": SYNO_CHAT_BOTNAME_SIMPLE } response = requests.post( SYNO_CHAT_WEBHOOK_SIMPLE, data={"payload": json.dumps(syno_payload, ensure_ascii=False)}, timeout=10 ) log.info("Synology Chat SIMPLE HTTP=%s body=%s", response.status_code, response.text) response.raise_for_status() body = response.json() if not body.get("success", False): raise RuntimeError(f"Synology Chat SIMPLE erreur: {body}") def fetch_pending_rows(conn) -> list[dict]: sql = """ SELECT Id_Journal, DateHeure, NomUtilisateur, PosteClient, TableurSource, UtilisateurWindows, SiteDemande, ServiceDemande, DSN, BDD, Statut, Motif, SessionID FROM `Acces`.`JournalConnexions` WHERE NotificationEnvoyee = 0 ORDER BY DateHeure ASC, Id_Journal ASC LIMIT 50 """ with conn.cursor() as cur: cur.execute(sql) return cur.fetchall() def mark_sent(conn, row_id: int) -> None: sql = """ UPDATE `Acces`.`JournalConnexions` SET NotificationEnvoyee = 1, DateNotification = NOW(), ErreurNotification = NULL WHERE Id_Journal = %s """ with conn.cursor() as cur: cur.execute(sql, (row_id,)) def mark_error(conn, row_id: int, error_msg: str) -> None: sql = """ UPDATE `Acces`.`JournalConnexions` SET ErreurNotification = %s WHERE Id_Journal = %s """ with conn.cursor() as cur: cur.execute(sql, (error_msg[:255], row_id)) def process_once() -> None: with get_connection() as conn: rows = fetch_pending_rows(conn) if not rows: log.info("Aucune nouvelle connexion à notifier.") return log.info("%s connexion(s) à notifier.", len(rows)) for row in rows: row_id = row["Id_Journal"] try: detailed_message = format_message(row) send_synology_chat(detailed_message) # Envoi simplifie uniquement pour la vraie connexion site, # pas pour la ligne meta Domo91 / Acces if row.get("SiteDemande") and row.get("DSN") != "Domo91": simple_message = format_simple_message(row) send_synology_chat_simple(simple_message) mark_sent(conn, row_id) log.info("Notification envoyee pour Id_Journal=%s", row_id) except Exception as exc: log.exception("Erreur d'envoi pour Id_Journal=%s", row_id) try: mark_error(conn, row_id, str(exc)) except Exception: log.exception("Impossible d'ecrire ErreurNotification pour Id_Journal=%s", row_id) def main(): missing = [k for k, v in DB_CONFIG.items() if v is None and k != "port"] if missing: raise RuntimeError(f"Variables d'environnement manquantes : {', '.join(missing)}") if not SYNO_CHAT_WEBHOOK: raise RuntimeError("SYNO_CHAT_WEBHOOK_CONNEXIONS manquant") log.info("Surveillance de JournalConnexions demarree.") while True: try: process_once() except Exception: log.exception("Erreur generale dans la boucle de surveillance") time.sleep(POLL_INTERVAL) if __name__ == "__main__": main()