Actualiser 7lna.py

This commit is contained in:
2026-03-05 10:18:26 +00:00
parent e1982dba0a
commit 51788eab62

328
7lna.py
View File

@@ -5,13 +5,14 @@ import threading
import subprocess import subprocess
import urllib.request import urllib.request
import time import time
import random
import platform
import datetime import datetime
import glob import glob
import hashlib import hashlib
import string import string
import secrets import secrets
import json
import platform
import shutil
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@@ -21,6 +22,7 @@ from watchdog.events import FileSystemEventHandler
UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py" UPDATE_URL = "https://git.7ka1.com/7ka1/7LnA_Antivirus_Linux_Free_ClamAV_Based/raw/branch/main/7lna.py"
QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine") QUARANTINE_DIR = os.path.expanduser("~/.7lna_quarantine")
WATCH_FOLDER = os.path.expanduser("~/Téléchargements") WATCH_FOLDER = os.path.expanduser("~/Téléchargements")
HISTORY_FILE = os.path.expanduser("~/.7lna_history.json")
ctk.set_appearance_mode("dark") ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue") ctk.set_default_color_theme("blue")
@@ -29,12 +31,11 @@ ctk.set_default_color_theme("blue")
# UTILITAIRES SYSTÈME # UTILITAIRES SYSTÈME
# ========================================== # ==========================================
def send_desktop_notification(title, message, is_critical=False): def send_desktop_notification(title, message, is_critical=False):
"""Envoie une notification native sous Linux"""
try: try:
urgency = 'critical' if is_critical else 'normal' urgency = 'critical' if is_critical else 'normal'
subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message]) subprocess.Popen(['notify-send', '-u', urgency, '-a', '7LnA Security', title, message])
except Exception: except Exception:
pass # Ignore si notify-send n'est pas installé pass
# ========================================== # ==========================================
# GESTIONNAIRE DU BOUCLIER TEMPS RÉEL # GESTIONNAIRE DU BOUCLIER TEMPS RÉEL
@@ -45,7 +46,7 @@ class RealTimeShieldHandler(FileSystemEventHandler):
def on_created(self, event): def on_created(self, event):
if not event.is_directory: if not event.is_directory:
time.sleep(1.5) # Laisse le temps au fichier d'être copié/téléchargé time.sleep(1.5)
self.app.trigger_realtime_scan(event.src_path) self.app.trigger_realtime_scan(event.src_path)
# ========================================== # ==========================================
@@ -54,17 +55,24 @@ class RealTimeShieldHandler(FileSystemEventHandler):
class Antivirus7LnA(ctk.CTk): class Antivirus7LnA(ctk.CTk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("7LnA Security Suite - Ultimate Edition V7") self.title("7LnA Security Suite - V8 Enterprise Edition")
self.geometry("1200x800") self.geometry("1250x850")
self.minsize(950, 650) self.minsize(1000, 700)
os.makedirs(QUARANTINE_DIR, exist_ok=True) os.makedirs(QUARANTINE_DIR, exist_ok=True)
if not os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "w") as f:
json.dump([], f)
self.shield_observer = None self.shield_observer = None
self.shield_active = False self.shield_active = False
self.check_dependencies() self.check_dependencies()
self.setup_ui() self.setup_ui()
# Lancement de la surveillance USB en arrière-plan
threading.Thread(target=self.monitor_usb_drives, daemon=True).start()
def check_dependencies(self): def check_dependencies(self):
try: try:
subprocess.run(['clamscan', '--version'], capture_output=True, check=True) subprocess.run(['clamscan', '--version'], capture_output=True, check=True)
@@ -79,7 +87,7 @@ class Antivirus7LnA(ctk.CTk):
# --- BARRE LATÉRALE --- # --- BARRE LATÉRALE ---
self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0, fg_color="#111827") self.sidebar = ctk.CTkFrame(self, width=260, corner_radius=0, fg_color="#111827")
self.sidebar.grid(row=0, column=0, sticky="nsew") self.sidebar.grid(row=0, column=0, sticky="nsew")
self.sidebar.grid_rowconfigure(8, weight=1) self.sidebar.grid_rowconfigure(10, weight=1)
ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=30, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=(30, 20)) ctk.CTkLabel(self.sidebar, text="🛡️ 7LnA Sec", font=ctk.CTkFont(size=30, weight="bold"), text_color="#3B82F6").grid(row=0, column=0, padx=20, pady=(30, 20))
@@ -89,10 +97,12 @@ class Antivirus7LnA(ctk.CTk):
self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit") self.btn_audit = self.create_nav_button("⚙️ Audit Réseau", 4, "audit")
self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools") self.btn_tools = self.create_nav_button("🧰 Outils Avancés", 5, "tools")
self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine") self.btn_quarantine = self.create_nav_button("📦 Quarantaine", 6, "quarantine")
self.btn_update = self.create_nav_button("🔄 Mise à jour", 7, "update") self.btn_schedule = self.create_nav_button("📅 Planification", 7, "schedule") # NOUVEAU
self.btn_history = self.create_nav_button("📜 Rapports", 8, "history") # NOUVEAU
self.btn_update = self.create_nav_button("🔄 Mise à jour", 9, "update")
self.version_label = ctk.CTkLabel(self.sidebar, text="v7.0 - Ultimate", text_color="#6B7280", font=ctk.CTkFont(weight="bold")) self.version_label = ctk.CTkLabel(self.sidebar, text="v8.0 - Enterprise", text_color="#6B7280", font=ctk.CTkFont(weight="bold"))
self.version_label.grid(row=8, column=0, pady=20, sticky="s") self.version_label.grid(row=10, column=0, pady=20, sticky="s")
# --- CONTENEUR DES VUES --- # --- CONTENEUR DES VUES ---
self.views = {} self.views = {}
@@ -102,6 +112,8 @@ class Antivirus7LnA(ctk.CTk):
self.init_audit_view() self.init_audit_view()
self.init_tools_view() self.init_tools_view()
self.init_quarantine_view() self.init_quarantine_view()
self.init_schedule_view() # NOUVEAU
self.init_history_view() # NOUVEAU
self.init_update_view() self.init_update_view()
self.select_view("dashboard") self.select_view("dashboard")
@@ -118,8 +130,8 @@ class Antivirus7LnA(ctk.CTk):
view.grid_forget() view.grid_forget()
if view_name in self.views: if view_name in self.views:
self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30) self.views[view_name].grid(row=0, column=1, sticky="nsew", padx=30, pady=30)
if view_name == "quarantine": if view_name == "quarantine": self.refresh_quarantine_list()
self.refresh_quarantine_list() if view_name == "history": self.refresh_history_list()
# --- UTILITAIRES DE CONSOLE --- # --- UTILITAIRES DE CONSOLE ---
def setup_console_tags(self, console): def setup_console_tags(self, console):
@@ -131,16 +143,48 @@ class Antivirus7LnA(ctk.CTk):
def get_time_prefix(self): def get_time_prefix(self):
return datetime.datetime.now().strftime("[%H:%M:%S] ") return datetime.datetime.now().strftime("[%H:%M:%S] ")
def log_threat_to_history(self, filepath, threat_name):
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
data.append({
"date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"file": filepath,
"threat": threat_name
})
with open(HISTORY_FILE, "w") as f: json.dump(data, f, indent=4)
except Exception as e: print("Erreur d'historique:", e)
# --- NOUVEAU : AUTO-DETECT USB ---
def monitor_usb_drives(self):
media_dir = f"/media/{os.getlogin()}"
if not os.path.exists(media_dir): return
known_mounts = set(os.listdir(media_dir))
while True:
time.sleep(3)
try:
current_mounts = set(os.listdir(media_dir))
new_mounts = current_mounts - known_mounts
for mount in new_mounts:
full_path = os.path.join(media_dir, mount)
self.after(0, self.prompt_usb_scan, full_path, mount)
known_mounts = current_mounts
except Exception: pass
def prompt_usb_scan(self, path, name):
send_desktop_notification("USB Détectée", f"Disque {name} branché.")
if messagebox.askyesno("Protection USB", f"Nouveau périphérique USB détecté :\n{name}\n\nVoulez-vous lancer une analyse antivirus dessus ?"):
self.select_view("scanner")
threading.Thread(target=self.run_clamav_scan, args=(path, True, self.scan_console), daemon=True).start()
# --- VUE : TABLEAU DE BORD --- # --- VUE : TABLEAU DE BORD ---
def init_dashboard_view(self): def init_dashboard_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["dashboard"] = frame self.views["dashboard"] = frame
ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="État du Système", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15) status_card = ctk.CTkFrame(frame, fg_color="#064E3B" if self.clamav_installed else "#7F1D1D", corner_radius=15)
status_card.pack(fill="x", pady=10, ipady=20) status_card.pack(fill="x", pady=10, ipady=20)
status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable" status_text = "Moteur ClamAV Opérationnel" if self.clamav_installed else "Moteur ClamAV Introuvable"
ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=24, weight="bold"), text_color="white").pack(expand=True) ctk.CTkLabel(status_card, text=f"{'' if self.clamav_installed else ''} {status_text}", font=ctk.CTkFont(size=24, weight="bold"), text_color="white").pack(expand=True)
@@ -149,67 +193,80 @@ class Antivirus7LnA(ctk.CTk):
sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}" sys_info = f"🖥️ OS : {platform.system()} {platform.release()} | 👤 Compte : {os.getlogin()}"
ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w") ctk.CTkLabel(sys_frame, text=sys_info, font=ctk.CTkFont(size=16, weight="bold")).pack(padx=20, pady=10, anchor="w")
info_frame = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
info_frame.pack(fill="x", pady=10, ipady=10)
ctk.CTkLabel(info_frame, text=f"📂 Dossier Quarantaine : {QUARANTINE_DIR}\n📁 Dossier Surveillé : {WATCH_FOLDER}", justify="left", font=ctk.CTkFont(size=14)).pack(padx=20, pady=10, anchor="w")
# --- VUE : SCANNER MANUEL --- # --- VUE : SCANNER MANUEL ---
def init_scanner_view(self): def init_scanner_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(3, weight=1) frame.grid_rowconfigure(4, weight=1)
frame.grid_columnconfigure((0, 1, 2), weight=1) frame.grid_columnconfigure((0, 1, 2), weight=1)
self.views["scanner"] = frame self.views["scanner"] = frame
ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Analyse Profonde", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, columnspan=3, sticky="w", pady=(0, 20))
# Ligne 1 des boutons
self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8") self.btn_scan_f = ctk.CTkButton(frame, text="📄 Analyser Fichier", command=lambda: self.start_manual_scan(is_dir=False), height=45, fg_color="#2563EB", hover_color="#1D4ED8")
self.btn_scan_f.grid(row=1, column=0, padx=(0, 5), sticky="ew") self.btn_scan_f.grid(row=1, column=0, padx=(0, 5), pady=5, sticky="ew")
self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA") self.btn_scan_d = ctk.CTkButton(frame, text="📁 Analyser Dossier", command=lambda: self.start_manual_scan(is_dir=True), height=45, fg_color="#4F46E5", hover_color="#4338CA")
self.btn_scan_d.grid(row=1, column=1, padx=(5, 5), sticky="ew") self.btn_scan_d.grid(row=1, column=1, padx=(5, 5), pady=5, sticky="ew")
self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures (freshclam)", command=self.update_virus_db, height=45, fg_color="#059669", hover_color="#047857") self.btn_db_update = ctk.CTkButton(frame, text="🔄 MaJ Signatures (freshclam)", command=self.update_virus_db, height=45, fg_color="#059669", hover_color="#047857")
self.btn_db_update.grid(row=1, column=2, padx=(5, 0), sticky="ew") self.btn_db_update.grid(row=1, column=2, padx=(5, 0), pady=5, sticky="ew")
# NOUVEAU : Scanner Rootkit
self.btn_rootkit = ctk.CTkButton(frame, text="🕵️ Chasse aux Rootkits (rkhunter)", command=self.run_rootkit_scan, height=45, fg_color="#7C3AED", hover_color="#6D28D9")
self.btn_rootkit.grid(row=2, column=0, columnspan=3, pady=(5, 10), sticky="ew")
self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10) self.scan_progress = ctk.CTkProgressBar(frame, mode="indeterminate", height=10)
self.scan_progress.grid(row=2, column=0, columnspan=3, pady=(20, 0), sticky="ew") self.scan_progress.grid(row=3, column=0, columnspan=3, pady=(10, 0), sticky="ew")
self.scan_progress.set(0) self.scan_progress.set(0)
self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) self.scan_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.scan_console.grid(row=3, column=0, columnspan=3, pady=20, sticky="nsew") self.scan_console.grid(row=4, column=0, columnspan=3, pady=20, sticky="nsew")
self.setup_console_tags(self.scan_console) self.setup_console_tags(self.scan_console)
self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V7 prêt...\n", "info") self.scan_console.insert("end", f"{self.get_time_prefix()}[*] Moteur de détection V8 prêt...\n", "info")
def run_rootkit_scan(self):
threading.Thread(target=self._exec_rootkit, daemon=True).start()
def _exec_rootkit(self):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Lancement de l'analyse Rootkit (Requiert l'authentification)...\n", "info")
self.scan_progress.start()
try:
# pkexec permet de demander le mot de passe proprement en interface graphique Linux
process = subprocess.Popen(['pkexec', 'rkhunter', '-c', '--sk', '--report-warnings-only'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
for line in process.stdout:
self.scan_console.insert("end", line)
self.scan_console.see("end")
process.wait()
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Analyse Rootkit terminée.\n", "success")
except FileNotFoundError:
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] rkhunter n'est pas installé. (sudo apt install rkhunter)\n", "warning")
except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally:
self.scan_progress.stop()
self.scan_console.see("end")
def update_virus_db(self): def update_virus_db(self):
threading.Thread(target=self._run_freshclam, daemon=True).start() threading.Thread(target=self._run_freshclam, daemon=True).start()
def _run_freshclam(self): def _run_freshclam(self):
self.scan_console.insert("end", f"\n{self.get_time_prefix()}[*] Mise à jour des signatures...\n", "info")
self.scan_progress.start() self.scan_progress.start()
self.btn_db_update.configure(state="disabled")
try: try:
process = subprocess.Popen(['freshclam'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) process = subprocess.Popen(['freshclam'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
for line in process.stdout: for line in process.stdout:
self.scan_console.insert("end", line) self.scan_console.insert("end", line)
self.scan_console.see("end") self.scan_console.see("end")
process.wait() process.wait()
if process.returncode == 0: self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Signatures à jour.\n", "success")
self.scan_console.insert("end", f"{self.get_time_prefix()}[+] Base de données virale à jour.\n", "success")
send_desktop_notification("Mise à jour réussie", "Les signatures ClamAV sont à jour.")
else:
self.scan_console.insert("end", f"{self.get_time_prefix()}[-] Échec ou droits root requis (sudo freshclam).\n", "warning")
except Exception as e: except Exception as e:
self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger") self.scan_console.insert("end", f"{self.get_time_prefix()}❌ Erreur : {e}\n", "danger")
finally: finally:
self.scan_progress.stop() self.scan_progress.stop()
self.scan_progress.set(0)
self.btn_db_update.configure(state="normal")
self.scan_console.see("end")
def start_manual_scan(self, is_dir): def start_manual_scan(self, is_dir):
path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename() path = filedialog.askdirectory() if is_dir else filedialog.askopenfilename()
if path: if path: threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start()
threading.Thread(target=self.run_clamav_scan, args=(path, is_dir, self.scan_console), daemon=True).start()
# --- VUE : BOUCLIER TEMPS RÉEL --- # --- VUE : BOUCLIER TEMPS RÉEL ---
def init_realtime_view(self): def init_realtime_view(self):
@@ -228,7 +285,6 @@ class Antivirus7LnA(ctk.CTk):
self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) self.rt_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.rt_console.grid(row=2, column=0, sticky="nsew") self.rt_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.rt_console) self.setup_console_tags(self.rt_console)
self.rt_console.insert("end", f"{self.get_time_prefix()}[-] Sentinelle en attente d'activation.\n")
def toggle_shield(self): def toggle_shield(self):
if not self.shield_active: if not self.shield_active:
@@ -238,28 +294,21 @@ class Antivirus7LnA(ctk.CTk):
self.shield_active = True self.shield_active = True
self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C") self.btn_toggle_shield.configure(text="Arrêter le Bouclier", fg_color="#DC2626", hover_color="#B91C1C")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success") self.rt_console.insert("end", f"\n{self.get_time_prefix()}[+] BOUCLIER ARMÉ : {WATCH_FOLDER}\n", "success")
send_desktop_notification("Bouclier Activé", f"Surveillance en direct de {WATCH_FOLDER}")
else: else:
self.shield_observer.stop() self.shield_observer.stop()
self.shield_active = False self.shield_active = False
self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857") self.btn_toggle_shield.configure(text="Démarrer la Surveillance", fg_color="#059669", hover_color="#047857")
self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning") self.rt_console.insert("end", f"\n{self.get_time_prefix()}[-] Bouclier désactivé.\n", "warning")
send_desktop_notification("Bouclier Désactivé", "La surveillance en temps réel est coupée.")
self.rt_console.see("end")
def trigger_realtime_scan(self, filepath): def trigger_realtime_scan(self, filepath):
self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info") self.rt_console.insert("end", f"\n{self.get_time_prefix()}⚡ Nouveau fichier : {os.path.basename(filepath)}\n", "info")
threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start() threading.Thread(target=self.run_clamav_scan, args=(filepath, False, self.rt_console), daemon=True).start()
# --- MOTEUR CLAMAV (Cœur) --- # --- MOTEUR CLAMAV ---
def run_clamav_scan(self, path, is_dir, console): def run_clamav_scan(self, path, is_dir, console):
if not self.clamav_installed: if not self.clamav_installed: return
console.insert("end", f"{self.get_time_prefix()}❌ Moteur introuvable.\n", "danger")
return
console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n") console.insert("end", f"{self.get_time_prefix()}[*] Analyse : {path}\n")
if console == self.scan_console: if console == self.scan_console: self.scan_progress.start()
self.scan_progress.start()
try: try:
cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}'] cmd = ['clamscan', '-i', '--no-summary', f'--move={QUARANTINE_DIR}']
@@ -271,26 +320,25 @@ class Antivirus7LnA(ctk.CTk):
for line in process.stdout: for line in process.stdout:
clean_line = line.strip() clean_line = line.strip()
if "FOUND" in clean_line: if "FOUND" in clean_line:
threat_details = clean_line.split(":")
threat_name = threat_details[-1].replace("FOUND", "").strip() if len(threat_details) > 1 else "Malware Inconnu"
console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger") console.insert("end", f"{self.get_time_prefix()}☠️ MENACE DÉTECTÉE : {clean_line}\n", "danger")
console.insert("end", f"{self.get_time_prefix()}🛡️ Action : Fichier mis en quarantaine.\n", "warning")
infected += 1 infected += 1
self.log_threat_to_history(path, threat_name) # Enregistrement dans l'historique
elif clean_line: elif clean_line:
console.insert("end", f" -> {clean_line}\n") console.insert("end", f" -> {clean_line}\n")
console.see("end") console.see("end")
process.wait() process.wait()
if infected == 0: if infected == 0:
console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success") console.insert("end", f"{self.get_time_prefix()}[+] Fichier propre.\n", "success")
else: else:
console.insert("end", f"{self.get_time_prefix()}🚨 {infected} MENACE(S) NEUTRALISÉE(S) !\n", "danger") send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s) et isolée(s).", is_critical=True)
# Envoi d'une notification critique au système
send_desktop_notification("🚨 VIRUS NEUTRALISÉ", f"{infected} menace(s) trouvée(s) et isolée(s) dans {os.path.basename(path)}.", is_critical=True)
except Exception as e: except Exception as e:
console.insert("end", f"{self.get_time_prefix()}❌ Erreur Moteur : {e}\n", "danger") console.insert("end", f"{self.get_time_prefix()}❌ Erreur Moteur : {e}\n", "danger")
finally: finally:
if console == self.scan_console: if console == self.scan_console: self.scan_progress.stop()
self.scan_progress.stop()
self.scan_progress.set(0)
console.see("end") console.see("end")
# --- VUE : AUDIT & PARE-FEU --- # --- VUE : AUDIT & PARE-FEU ---
@@ -299,15 +347,11 @@ class Antivirus7LnA(ctk.CTk):
frame.grid_rowconfigure(2, weight=1) frame.grid_rowconfigure(2, weight=1)
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["audit"] = frame self.views["audit"] = frame
ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Audit Réseau & Pare-feu", font=ctk.CTkFont(size=34, weight="bold")).grid(row=0, column=0, sticky="w", pady=(0, 20))
btn_frame = ctk.CTkFrame(frame, fg_color="transparent") btn_frame = ctk.CTkFrame(frame, fg_color="transparent")
btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10)) btn_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
ctk.CTkButton(btn_frame, text="Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309", height=40).pack(side="left", padx=(0, 10)) ctk.CTkButton(btn_frame, text="Lancer l'Audit Processus", command=self.run_audit_thread, fg_color="#D97706", hover_color="#B45309", height=40).pack(side="left", padx=(0, 10))
ctk.CTkButton(btn_frame, text="Vérifier Statut UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left") ctk.CTkButton(btn_frame, text="Vérifier Statut UFW", command=self.check_firewall, fg_color="#4B5563", hover_color="#374151", height=40).pack(side="left")
self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) self.audit_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.audit_console.grid(row=2, column=0, sticky="nsew") self.audit_console.grid(row=2, column=0, sticky="nsew")
self.setup_console_tags(self.audit_console) self.setup_console_tags(self.audit_console)
@@ -318,10 +362,8 @@ class Antivirus7LnA(ctk.CTk):
def perform_audit(self): def perform_audit(self):
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Recherche de connexions fantômes (Ports ouverts)...\n", "info") self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Recherche de connexions fantômes (Ports ouverts)...\n", "info")
try: try: self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
self.audit_console.insert("end", subprocess.check_output(['ss', '-tuln'], text=True))
except: pass except: pass
self.audit_console.insert("end", f"\n{self.get_time_prefix()}[*] Top 10 Processus (Charge CPU)...\n", "info") self.audit_console.insert("end", f"\n{self.get_time_prefix()}[*] Top 10 Processus (Charge CPU)...\n", "info")
try: try:
res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True) res = subprocess.check_output(['ps', '-eo', 'pid,user,%cpu,%mem,cmd', '--sort=-%cpu'], text=True)
@@ -330,91 +372,134 @@ class Antivirus7LnA(ctk.CTk):
def check_firewall(self): def check_firewall(self):
self.audit_console.delete("0.0", "end") self.audit_console.delete("0.0", "end")
self.audit_console.insert("end", f"{self.get_time_prefix()}[*] Interrogation du pare-feu Ubuntu (UFW)...\n", "info")
try: try:
res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip() res = subprocess.check_output(['systemctl', 'is-active', 'ufw'], text=True).strip()
if res == "active": if res == "active": self.audit_console.insert("end", f"{self.get_time_prefix()}✅ PARE-FEU UFW ACTIF.\n", "success")
self.audit_console.insert("end", f"{self.get_time_prefix()} PARE-FEU UFW ACTIF.\n", "success") else: self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\n", "danger")
else: except: self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Impossible de déterminer l'état de UFW.\n", "warning")
self.audit_console.insert("end", f"{self.get_time_prefix()}⚠️ PARE-FEU INACTIF.\nTerminal: 'sudo ufw enable'.\n", "danger")
except Exception:
self.audit_console.insert("end", f"{self.get_time_prefix()}[-] Impossible de déterminer l'état de UFW.\n", "warning")
# --- VUE : OUTILS AVANCÉS (V7 - Scrollable) --- # --- VUE : OUTILS AVANCÉS (Avec Nettoyeur) ---
def init_tools_view(self): def init_tools_view(self):
# On utilise un CTkScrollableFrame pour empiler les outils proprement
frame = ctk.CTkScrollableFrame(self, fg_color="transparent") frame = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.views["tools"] = frame self.views["tools"] = frame
ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Boîte à Outils", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
# --- Outil 1 : Destructeur de fichiers --- # NOUVEAU : Nettoyeur Système
clean_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
clean_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(clean_card, text="🧹 Nettoyeur Système", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(clean_card, text="Vide la corbeille, les fichiers temporaires et le cache de l'utilisateur pour libérer de l'espace.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
ctk.CTkButton(clean_card, text="Nettoyer l'ordinateur", fg_color="#059669", hover_color="#047857", command=self.run_cleaner).pack(anchor="w", padx=20)
# Destructeur de Fichiers
shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) shred_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
shred_card.pack(fill="x", pady=10, ipady=15) shred_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers (File Shredder)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0)) ctk.CTkLabel(shred_card, text="🔥 Destructeur de Fichiers (File Shredder)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#EF4444").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(shred_card, text="Écrase le fichier avec des données aléatoires (3 passes) pour empêcher sa récupération.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10)) ctk.CTkLabel(shred_card, text="Écrase le fichier avec des données aléatoires (3 passes) pour empêcher sa récupération.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
ctk.CTkButton(shred_card, text="Détruire un fichier à jamais", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20) ctk.CTkButton(shred_card, text="Détruire un fichier à jamais", fg_color="#DC2626", hover_color="#991B1B", command=self.run_shredder).pack(anchor="w", padx=20)
# --- Outil 2 : Calculateur de Hash --- # Calculateur de Hash
hash_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) hash_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
hash_card.pack(fill="x", pady=10, ipady=15) hash_card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(hash_card, text="🧬 Extracteur d'Empreintes (Hash)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#3B82F6").pack(anchor="w", padx=20, pady=(10,0)) ctk.CTkLabel(hash_card, text="🧬 Extracteur d'Empreintes (Hash)", font=ctk.CTkFont(size=18, weight="bold"), text_color="#3B82F6").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(hash_card, text="Calcule les empreintes MD5 et SHA-256 d'un fichier pour vérifier sa légitimité.", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
self.hash_result_var = ctk.StringVar(value="Aucun fichier sélectionné.") self.hash_result_var = ctk.StringVar(value="Aucun fichier sélectionné.")
ctk.CTkButton(hash_card, text="Sélectionner un fichier", fg_color="#2563EB", hover_color="#1D4ED8", command=self.calculate_hash).pack(anchor="w", padx=20, pady=(0, 10)) ctk.CTkButton(hash_card, text="Sélectionner un fichier", fg_color="#2563EB", hover_color="#1D4ED8", command=self.calculate_hash).pack(anchor="w", padx=20, pady=(0, 10))
ctk.CTkEntry(hash_card, textvariable=self.hash_result_var, state="readonly", width=500, fg_color="#111827").pack(anchor="w", padx=20) ctk.CTkEntry(hash_card, textvariable=self.hash_result_var, state="readonly", width=500, fg_color="#111827").pack(anchor="w", padx=20)
# --- Outil 3 : Générateur de Mot de Passe --- def run_cleaner(self):
pwd_card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10) freed_space = 0
pwd_card.pack(fill="x", pady=10, ipady=15) paths_to_clean = [os.path.expanduser("~/.local/share/Trash/files"), os.path.expanduser("~/.cache/thumbnails")]
ctk.CTkLabel(pwd_card, text="🔑 Générateur de Mot de Passe Blindé", font=ctk.CTkFont(size=18, weight="bold"), text_color="#10B981").pack(anchor="w", padx=20, pady=(10,0))
ctk.CTkLabel(pwd_card, text="Génère un mot de passe cryptographiquement sécurisé (20 caractères).", text_color="#9CA3AF").pack(anchor="w", padx=20, pady=(5, 10))
self.pwd_result_var = ctk.StringVar(value="*****") for path in paths_to_clean:
btn_pwd_frame = ctk.CTkFrame(pwd_card, fg_color="transparent") if os.path.exists(path):
btn_pwd_frame.pack(anchor="w", padx=20, fill="x") for item in os.listdir(path):
item_path = os.path.join(path, item)
try:
freed_space += os.path.getsize(item_path)
if os.path.isfile(item_path): os.remove(item_path)
elif os.path.isdir(item_path): shutil.rmtree(item_path)
except: pass
ctk.CTkButton(btn_pwd_frame, text="Générer", fg_color="#059669", hover_color="#047857", command=self.generate_password).pack(side="left", padx=(0, 10)) freed_mb = freed_space / (1024 * 1024)
ctk.CTkEntry(btn_pwd_frame, textvariable=self.pwd_result_var, state="normal", width=300, fg_color="#111827", font=ctk.CTkFont(family="Consolas", size=14)).pack(side="left") messagebox.showinfo("Nettoyage Terminé", f"Le nettoyage est terminé.\nEspace libéré : {freed_mb:.2f} MB")
def run_shredder(self): def run_shredder(self):
filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ LE FICHIER À DÉTRUIRE") filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ LE FICHIER À DÉTRUIRE")
if not filepath: return if not filepath: return
if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"): if messagebox.askyesno("DANGER", f"Détruire DÉFINITIVEMENT ?\n\n{filepath}"):
threading.Thread(target=self.shred_file, args=(filepath,), daemon=True).start() try:
file_size = os.path.getsize(filepath)
def shred_file(self, filepath): for _ in range(3):
try: with open(filepath, "ba+", buffering=0) as f:
file_size = os.path.getsize(filepath) f.seek(0)
for _ in range(3): f.write(os.urandom(file_size))
with open(filepath, "ba+", buffering=0) as f: os.remove(filepath)
f.seek(0) messagebox.showinfo("Succès", "Fichier détruit.")
f.write(os.urandom(file_size)) except Exception as e: messagebox.showerror("Erreur", str(e))
os.remove(filepath)
messagebox.showinfo("Succès", "Fichier détruit de manière irréversible.")
except Exception as e:
messagebox.showerror("Erreur", f"Erreur : {e}")
def calculate_hash(self): def calculate_hash(self):
filepath = filedialog.askopenfilename(title="SÉLECTIONNEZ UN FICHIER") filepath = filedialog.askopenfilename()
if not filepath: return if not filepath: return
try: try:
sha256_hash = hashlib.sha256() sha256_hash = hashlib.sha256()
with open(filepath, "rb") as f: with open(filepath, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""): for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block)
sha256_hash.update(byte_block)
self.hash_result_var.set(f"SHA-256: {sha256_hash.hexdigest()}") self.hash_result_var.set(f"SHA-256: {sha256_hash.hexdigest()}")
except Exception as e: except: self.hash_result_var.set("Erreur de lecture.")
self.hash_result_var.set(f"Erreur de lecture.")
def generate_password(self): # --- NOUVEAU : VUE PLANIFICATION (CRON) ---
alphabet = string.ascii_letters + string.digits + "!@#$%^&*" def init_schedule_view(self):
pwd = ''.join(secrets.choice(alphabet) for i in range(20)) frame = ctk.CTkFrame(self, fg_color="transparent")
self.pwd_result_var.set(pwd) self.views["schedule"] = frame
ctk.CTkLabel(frame, text="Planification de Scans", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Automatisez l'analyse de votre système via les tâches planifiées Linux (Cron).", text_color="#9CA3AF").pack(anchor="w", pady=(0, 20))
card = ctk.CTkFrame(frame, fg_color="#1F2937", corner_radius=10)
card.pack(fill="x", pady=10, ipady=15)
ctk.CTkLabel(card, text="Programmer un scan automatique de ~/Téléchargements", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=10)
ctk.CTkButton(card, text="Activer le Scan Quotidien (12h00)", fg_color="#2563EB", hover_color="#1D4ED8", command=self.setup_cronjob).pack(pady=10)
ctk.CTkButton(card, text="Désactiver les scans programmés", fg_color="#4B5563", hover_color="#374151", command=self.remove_cronjob).pack(pady=10)
def setup_cronjob(self):
cron_command = f"0 12 * * * /usr/bin/clamscan -r --move={QUARANTINE_DIR} {WATCH_FOLDER} > /dev/null 2>&1"
try:
# Récupère l'ancien crontab, enlève nos anciennes tâches, ajoute la nouvelle
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}"; echo "{cron_command}") | crontab -')
messagebox.showinfo("Succès", "Scan automatique configuré tous les jours à 12h00.")
except Exception as e:
messagebox.showerror("Erreur", "Impossible de configurer Cron.")
def remove_cronjob(self):
try:
os.system(f'(crontab -l 2>/dev/null | grep -v "clamscan -r --move={QUARANTINE_DIR}") | crontab -')
messagebox.showinfo("Succès", "Planification désactivée.")
except: pass
# --- NOUVEAU : VUE HISTORIQUE ---
def init_history_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent")
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1)
self.views["history"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="📜 Registre des Menaces", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
self.history_console = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.history_console.grid(row=1, column=0, sticky="nsew")
def refresh_history_list(self):
self.history_console.delete("0.0", "end")
try:
with open(HISTORY_FILE, "r") as f: data = json.load(f)
if not data:
self.history_console.insert("end", "\n ✅ Historique vierge. Aucun virus n'a été détecté sur cette machine.\n")
else:
for entry in reversed(data): # Du plus récent au plus ancien
self.history_console.insert("end", f"[{entry['date']}] ☠️ {entry['threat']}\n -> Fichier : {entry['file']}\n\n")
except: self.history_console.insert("end", "Impossible de lire l'historique.")
# --- VUE : QUARANTAINE --- # --- VUE : QUARANTAINE ---
def init_quarantine_view(self): def init_quarantine_view(self):
@@ -422,21 +507,17 @@ class Antivirus7LnA(ctk.CTk):
frame.grid_rowconfigure(1, weight=1) frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(0, weight=1)
self.views["quarantine"] = frame self.views["quarantine"] = frame
header = ctk.CTkFrame(frame, fg_color="transparent") header = ctk.CTkFrame(frame, fg_color="transparent")
header.grid(row=0, column=0, sticky="ew", pady=(0, 20)) header.grid(row=0, column=0, sticky="ew", pady=(0, 20))
ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left") ctk.CTkLabel(header, text="📦 Quarantaine", font=ctk.CTkFont(size=34, weight="bold")).pack(side="left")
ctk.CTkButton(header, text="🗑️ Vider", fg_color="#DC2626", hover_color="#991B1B", command=self.empty_quarantine, height=40).pack(side="right") ctk.CTkButton(header, text="🗑️ Vider", fg_color="#DC2626", hover_color="#991B1B", command=self.empty_quarantine, height=40).pack(side="right")
self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10) self.quarantine_listbox = ctk.CTkTextbox(frame, font=ctk.CTkFont(family="Consolas", size=14), fg_color="#111827", corner_radius=10)
self.quarantine_listbox.grid(row=1, column=0, sticky="nsew") self.quarantine_listbox.grid(row=1, column=0, sticky="nsew")
def refresh_quarantine_list(self): def refresh_quarantine_list(self):
self.quarantine_listbox.delete("0.0", "end") self.quarantine_listbox.delete("0.0", "end")
files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files: if not files: self.quarantine_listbox.insert("end", "\n ✅ Aucun fichier malveillant isolé.\n")
self.quarantine_listbox.insert("end", "\n ✅ Aucun fichier malveillant isolé.\n")
else: else:
self.quarantine_listbox.insert("end", f" ⚠️ {len(files)} menace(s) :\n\n") self.quarantine_listbox.insert("end", f" ⚠️ {len(files)} menace(s) :\n\n")
for f in files: for f in files:
@@ -446,7 +527,6 @@ class Antivirus7LnA(ctk.CTk):
def empty_quarantine(self): def empty_quarantine(self):
files = glob.glob(os.path.join(QUARANTINE_DIR, "*")) files = glob.glob(os.path.join(QUARANTINE_DIR, "*"))
if not files: return if not files: return
if messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"): if messagebox.askyesno("Purger", "Supprimer définitivement les virus isolés ?"):
for f in files: for f in files:
try: os.remove(f) try: os.remove(f)
@@ -457,25 +537,19 @@ class Antivirus7LnA(ctk.CTk):
def init_update_view(self): def init_update_view(self):
frame = ctk.CTkFrame(self, fg_color="transparent") frame = ctk.CTkFrame(self, fg_color="transparent")
self.views["update"] = frame self.views["update"] = frame
ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20)) ctk.CTkLabel(frame, text="Mise à jour Cloud", font=ctk.CTkFont(size=34, weight="bold")).pack(anchor="w", pady=(0, 20))
ctk.CTkLabel(frame, text="Synchronisation avec le dépôt 7ka1 Git.", text_color="#9CA3AF", font=ctk.CTkFont(size=14)).pack(anchor="w", pady=(0, 20))
self.btn_update_soft = ctk.CTkButton(frame, text="⬇️ Mettre à jour", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45) self.btn_update_soft = ctk.CTkButton(frame, text="⬇️ Mettre à jour", command=self.run_software_update, fg_color="#8B5CF6", hover_color="#7C3AED", height=45)
self.btn_update_soft.pack(anchor="w") self.btn_update_soft.pack(anchor="w")
self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10) self.update_console = ctk.CTkTextbox(frame, height=200, font=ctk.CTkFont(family="Consolas", size=13), fg_color="#111827", corner_radius=10)
self.update_console.pack(fill="x", pady=20) self.update_console.pack(fill="x", pady=20)
self.setup_console_tags(self.update_console) self.setup_console_tags(self.update_console)
def run_software_update(self): def run_software_update(self):
self.btn_update_soft.configure(state="disabled") self.btn_update_soft.configure(state="disabled")
self.update_console.insert("end", f"{self.get_time_prefix()}[*] Négociation avec git.7ka1.com...\n", "info") self.update_console.insert("end", f"{self.get_time_prefix()}[*] Négociation avec serveur...\n", "info")
try: try:
current_file_path = os.path.abspath(__file__) current_file_path = os.path.abspath(__file__)
urllib.request.urlretrieve(UPDATE_URL, current_file_path) urllib.request.urlretrieve(UPDATE_URL, current_file_path)
self.update_console.insert("end", f"{self.get_time_prefix()}[+] Code source mis à jour !\n", "success") self.update_console.insert("end", f"{self.get_time_prefix()}[+] Code source mis à jour !\n", "success")
messagebox.showinfo("Succès", "Redémarrage requis.") messagebox.showinfo("Succès", "Redémarrage requis.")
self.destroy() self.destroy()