diff --git a/src/discord/discord_oauth.py b/src/discord/discord_oauth.py
index b52d128..2b736e4 100644
--- a/src/discord/discord_oauth.py
+++ b/src/discord/discord_oauth.py
@@ -1,14 +1,21 @@
-import requests
-import webbrowser
import os
-from urllib.parse import urlencode
-from http.server import HTTPServer, BaseHTTPRequestHandler
+import webbrowser
+from http.server import BaseHTTPRequestHandler, HTTPServer
+from urllib.parse import parse_qs, urlencode, urlparse
+
+from config.constants import AUTENTICATION_SUCCESS_MESSAGE, CLIENT_ID, REDIRECT_URI, SCOPES
from fivemserver.get_server_token import GetServerTokenForDiscord
-from config.constants import CLIENT_ID, REDIRECT_URI, SCOPES, AUTENTICATION_SUCCESS_MESSAGE
+from tools.http_client import ApiError, http_get, http_post
# Disable stderr output
os.environ['PYTHONWARNINGS'] = 'ignore'
+OAUTH_AUTHORIZE_URL = "https://discord.com/api/oauth2/authorize"
+OAUTH_TOKEN_URL = "https://discord.com/api/oauth2/token"
+DISCORD_ME_URL = "https://discord.com/api/users/@me"
+LOCAL_CALLBACK_HOST = "localhost"
+LOCAL_CALLBACK_PORT = 5000
+
class OAuthCallbackHandler(BaseHTTPRequestHandler):
code: str | None = None
@@ -20,15 +27,18 @@ class OAuthCallbackHandler(BaseHTTPRequestHandler):
"""
callback pour discord auth
"""
- if "/callback" in self.path:
- query = self.path.split("?")[1]
- params = dict(p.split("=") for p in query.split("&"))
- OAuthCallbackHandler.code = params.get("code")
+ parsed_url = urlparse(self.path)
+ if parsed_url.path != "/callback":
+ self.send_error(404)
+ return
- self.send_response(200)
- self.send_header("Content-type", "text/html")
- self.end_headers()
- self.wfile.write(AUTENTICATION_SUCCESS_MESSAGE)
+ query_params = parse_qs(parsed_url.query)
+ OAuthCallbackHandler.code = query_params.get("code", [None])[0]
+
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ self.wfile.write(AUTENTICATION_SUCCESS_MESSAGE)
# return discord application id (client id)
def get_discord_client_id() -> str:
@@ -38,13 +48,12 @@ def get_discord_client_id() -> str:
return CLIENT_ID
# return discord user id
-def get_discord_user_id() -> str:
+def get_discord_user_id() -> tuple[str, str]:
"""
Retourne l'id du compte discord de l'utilisateur via l'oauh discord.
"""
# récupération des infos serveur la tanière
- # récupération d
session_id = GetServerTokenForDiscord.authenticate()
client_secret = GetServerTokenForDiscord.get_token(session_id)
@@ -58,27 +67,47 @@ def get_discord_user_id() -> str:
webbrowser.open(f"{auth_url}?{urlencode(params)}")
- server = HTTPServer(("localhost", 5000), OAuthCallbackHandler)
- server.handle_request()
+ server = HTTPServer((LOCAL_CALLBACK_HOST, LOCAL_CALLBACK_PORT), OAuthCallbackHandler)
+
+ try:
+ server.handle_request()
+ finally:
+ server.server_close()
if not OAuthCallbackHandler.code:
- raise RuntimeError("OAuth échoué")
+ raise ApiError("OAuth échoué : aucun code de validation reçu.")
- token = requests.post(
- "https://discord.com/api/oauth2/token",
- data={
- "client_id": CLIENT_ID,
- "client_secret": client_secret,
- "grant_type": "authorization_code",
- "code": OAuthCallbackHandler.code,
- "redirect_uri": REDIRECT_URI,
- },
- headers={"Content-Type": "application/x-www-form-urlencoded"},
- ).json()
+ try:
+ token = http_post(
+ OAUTH_TOKEN_URL,
+ data={
+ "client_id": CLIENT_ID,
+ "client_secret": client_secret,
+ "grant_type": "authorization_code",
+ "code": OAuthCallbackHandler.code,
+ "redirect_uri": REDIRECT_URI,
+ },
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
+ ).json()
+ except ValueError as exc:
+ raise ApiError("OAuth échoué : réponse JSON invalide lors de la récupération du token.",
+ url=OAUTH_TOKEN_URL) from exc
- user = requests.get(
- "https://discord.com/api/users/@me",
- headers={"Authorization": f"Bearer {token['access_token']}"},
- ).json()
+ access_token = token.get("access_token")
+ if not access_token:
+ raise ApiError("OAuth échoué : access_token manquant.", url=OAUTH_TOKEN_URL)
- return user["id"]
+ try:
+ user = http_get(
+ DISCORD_ME_URL,
+ headers={"Authorization": f"Bearer {access_token}"},
+ ).json()
+ except ValueError as exc:
+ raise ApiError("OAuth échoué : réponse JSON invalide lors de la récupération du profil Discord.",
+ url=DISCORD_ME_URL) from exc
+
+ user_id = user.get("id")
+ if not user_id:
+ raise ApiError("OAuth échoué : id utilisateur manquant.", url=DISCORD_ME_URL)
+
+ return user_id, session_id
diff --git a/src/ui/error_dialog.py b/src/ui/error_dialog.py
new file mode 100644
index 0000000..b2592cc
--- /dev/null
+++ b/src/ui/error_dialog.py
@@ -0,0 +1,14 @@
+from PySide6.QtWidgets import QWidget
+
+from ui.custom_message_box import CustomMessageBox
+
+
+def show_qt_error(parent: QWidget | None, title: str, message: str) -> None:
+ msg = CustomMessageBox(
+ title=title,
+ message=message,
+ icon_type=CustomMessageBox.WARNING,
+ buttons=CustomMessageBox.OK,
+ parent=parent,
+ )
+ msg.exec()
diff --git a/src/ui/main_window.py b/src/ui/main_window.py
index d64c48c..103c9fd 100644
--- a/src/ui/main_window.py
+++ b/src/ui/main_window.py
@@ -10,6 +10,8 @@ from PySide6.QtCore import QThread, Signal
from config.config_manager import ConfigManager
from config.constants import PlayerServerInfo, Urls
+from tools.http_client import ApiError
+from ui.error_dialog import show_qt_error
from ui.hazard_stripes import HazardButton
from controllers.audio_controller import AudioController
from controllers.glow_animator import GlowAnimator
@@ -32,6 +34,7 @@ class MainWindow(QMainWindow):
super().__init__()
self.config = config_manager
+ self.stored_user_id = self.config.get_discord_user()
self.queue_thread = None
# UI
@@ -42,16 +45,30 @@ class MainWindow(QMainWindow):
# Par défaut on affiche la page normal pour la connexion au serveur
self.ui.stackedWidget.setCurrentIndex(0)
+ # On cache par défaut les infos liste d'attente
+ self.ui.queue_lbl.hide()
+ self.ui.queue_position.hide()
+
# Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
- if config_manager.get_discord_user() == "" or config_manager.get_discord_user().isspace():
- self.ui.queue_lbl.hide()
- self.ui.queue_position.hide()
+ if self.stored_user_id == "" or self.stored_user_id.isspace():
self.ui.stackedWidget.setCurrentIndex(1)
+ else:
+ try:
+ # on vérifie si le joueur est whitelisté
+ WhiteList.check_whitelist(Urls.API_URL.value, self.stored_user_id)
+ except ApiError as exc:
+ show_qt_error(self, "La Tanière", f"Impossible de vérifier la whitelist.\n\n{exc}")
+ PlayerServerInfo.is_whitelist = False
+ PlayerServerInfo.is_staff = False
- WhiteList.check_whitelist(Urls.API_URL.value, self.config.get_discord_user())
- if PlayerServerInfo.is_whitelist:
- self.start_queue()
+ # si on est whitelisté, on démarre la file d'attente
+ if PlayerServerInfo.is_whitelist:
+ self.start_queue()
+ self.ui.queue_lbl.show()
+ self.ui.queue_position.show()
+ else:
+ self.ui.stackedWidget.setCurrentIndex(2)
# Test bouton en contruction
en_chantier = False
@@ -100,12 +117,6 @@ class MainWindow(QMainWindow):
self._connect_signals()
self._center_window()
- # si le jouer n'est pas whitelisté, on affiche la page pour se faire whitelister
- if not PlayerServerInfo.is_whitelist:
- self.ui.stackedWidget.setCurrentIndex(2)
- else:
- self.start_queue() # ← Tout à la fin, UI complètement prête
-
self.show()
@@ -119,8 +130,8 @@ class MainWindow(QMainWindow):
self.ui.connexion_btn.clicked.connect(self._on_connexion)
self.ui.discord_btn.clicked.connect(self._on_discord)
self.ui.intranet_btn.clicked.connect(self._on_intranet)
-
self.ui.discord_auth_btn.clicked.connect(self._on_discord_auth_btn)
+ self.ui.no_whitelist_btn.clicked.connect(self.close)
def _center_window(self) -> None:
self.adjustSize()
@@ -137,9 +148,12 @@ class MainWindow(QMainWindow):
# ------------------------------------------------------------------
def _on_connexion(self) -> None:
- GetServerTokenForDiscord.register_discord_user(self.config.get_discord_user(),
- GetServerTokenForDiscord.authenticate(Urls.API_URL.value))
- FiveMLauncher.launch()
+ try:
+ session_id = GetServerTokenForDiscord.authenticate(Urls.API_URL.value)
+ GetServerTokenForDiscord.register_discord_user(self.stored_user_id, session_id)
+ FiveMLauncher.launch()
+ except ApiError as exc:
+ show_qt_error(self, "Connexion impossible", f"Erreur lors de la connexion.\n\n{exc}")
@staticmethod
def _on_discord() -> None:
@@ -150,12 +164,14 @@ class MainWindow(QMainWindow):
self._glow.start()
def _on_discord_auth_btn(self) -> None:
- test = discord_oauth.get_discord_user_id()
- self.config.set_discord_user(test[0])
- PlayerServerInfo.session_id = test[1]
- self.config.save()
- self.ui.stackedWidget.setCurrentIndex(0)
-
+ try:
+ test = discord_oauth.get_discord_user_id()
+ self.config.set_discord_user(test[0])
+ PlayerServerInfo.session_id = test[1]
+ self.config.save()
+ self.ui.stackedWidget.setCurrentIndex(0)
+ except ApiError as exc:
+ show_qt_error(self, "Connexion Discord", f"Impossible de récupérer ton compte Discord.\n\n{exc}")
# ------------------------------------------------------------------
# Mouse events → délégués au WindowDragger
# ------------------------------------------------------------------
@@ -196,14 +212,13 @@ class MainWindow(QMainWindow):
# Queue managment
# ------------------------------------------------------------------
def start_queue(self):
- user_id = self.config.get_discord_user()
- self.queue_thread = QueueThread(user_id)
- self.queue_thread = QueueThread(user_id, parent=self) # ← parent=self
+ self.queue_thread = QueueThread(self.stored_user_id)
+ self.queue_thread = QueueThread(self.stored_user_id, parent=self) # ← parent=self
self.queue_thread.update.connect(self.handle_update)
self.queue_thread.start()
# 🧪 TEMP - Simule une position en queue pour tester l'UI
- # self.handle_update("position:3:10")
+ self.handle_update("position:3:10")
def handle_update(self, message: str):
# print(f"[handle_update] reçu: {message}") # ← Debug
diff --git a/styles/styles.qss b/styles/styles.qss
index 3827aa1..6c6d813 100644
--- a/styles/styles.qss
+++ b/styles/styles.qss
@@ -103,7 +103,7 @@ QPushButton#staff_btn:pressed
QPushButton#discord_btn,
QPushButton#discord_auth_btn,
QPushButton#intranet_btn,
-QPushButton#whitelist_ok_boutton
+QPushButton#no_whitelist_btn
{
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
border: 1px solid rgba(255, 255, 255, 0.1);
@@ -115,7 +115,7 @@ QPushButton#whitelist_ok_boutton
QPushButton#discord_btn:hover,
QPushButton#discord_auth_btn:hover,
-QPushButton#whitelist_ok_boutton:hover
+QPushButton#no_whitelist_btn:hover
{
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
diff --git a/ui/mainwindow_vertical_pager.ui b/ui/mainwindow_vertical_pager.ui
index 5bedbda..f3be914 100644
--- a/ui/mainwindow_vertical_pager.ui
+++ b/ui/mainwindow_vertical_pager.ui
@@ -363,7 +363,7 @@
-
- 20
+ -
Qt::AlignmentFlag::AlignCenter
@@ -640,7 +640,7 @@
QFrame::Shape::NoFrame
- 0
+ 2
@@ -1607,7 +1607,7 @@ Inscris-toi sur Discord, puis relance le launcher.
-
-
+
380