master #7
|
|
@ -1 +1 @@
|
|||
{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"r","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"}
|
||||
{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Stolpernder Meta-Chaser","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"}
|
||||
|
|
@ -11,7 +11,12 @@ def change_display_name(player_id, new_name):
|
|||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
|
||||
def generate_silly_name():
|
||||
adjectives = ["Verwirrter", "Blinder", "Heulender", "Zorniger", "Chaos", "Verzweifelter", "Schreiender", "Stolpernder", "Schwitzender"]
|
||||
nouns = ["Grot", "Kultist", "Servitor", "Snotling", "Guardmen", "Würfellecker", "Regelvergesser", "Meta-Chaser", "Klebschnüffler"]
|
||||
adj = random.choice(adjectives)
|
||||
noun = random.choice(nouns)
|
||||
return f"{adj} {noun}"
|
||||
|
||||
|
||||
def get_or_create_player(discord_id, discord_name, avatar_url):
|
||||
|
|
@ -25,12 +30,6 @@ def get_or_create_player(discord_id, discord_name, avatar_url):
|
|||
if player is None:
|
||||
|
||||
# Random Silly Name Generator für neue Spieler. Damit sie angeregt werden ihren richtigen Namen einzutragen.
|
||||
def generate_silly_name():
|
||||
adjectives = ["Verwirrter", "Blinder", "Heulender", "Zorniger", "Chaos", "Verzweifelter", "Schreiender", "Stolpernder", "Schwitzender"]
|
||||
nouns = ["Grot", "Kultist", "Servitor", "Snotling", "Guardmen", "Würfellecker", "Regelvergesser", "Meta-Chaser", "Klebschnüffler"]
|
||||
adj = random.choice(adjectives)
|
||||
noun = random.choice(nouns)
|
||||
return f"{adj} {noun}"
|
||||
|
||||
silly_name = generate_silly_name()
|
||||
cursor.execute("INSERT INTO players (discord_id, discord_name, display_name, discord_avatar_url) VALUES (?, ?, ?, ?)", (discord_id, discord_name, silly_name, avatar_url))
|
||||
|
|
@ -456,13 +455,14 @@ def get_leaderboard(system_name):
|
|||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Wir holen ID, Namen, Discord und MMR, sortiert vom höchsten MMR zum niedrigsten
|
||||
# WIR HABEN HIER EINE BEDINGUNG HINZUGEFÜGT: AND stat.games_in_system > 0
|
||||
# Dadurch filtert die Datenbank direkt auf dem Server schon alle "0-Spiele"-Accounts raus.
|
||||
query = """
|
||||
SELECT p.id, p.display_name, p.discord_name, stat.mmr
|
||||
FROM players p
|
||||
JOIN player_game_statistic stat ON p.id = stat.player_id
|
||||
JOIN gamesystems sys ON stat.gamesystem_id = sys.id
|
||||
WHERE sys.name = ?
|
||||
WHERE sys.name = ? AND stat.games_in_system > 0
|
||||
ORDER BY stat.mmr DESC
|
||||
"""
|
||||
|
||||
|
|
@ -536,3 +536,27 @@ def set_match_counted(match_id):
|
|||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
|
||||
|
||||
def get_submitted_matches(player_id):
|
||||
"""Holt alle offenen Matches, die der Spieler selbst eingetragen hat, aber vom Gegner noch nicht bestätigt wurden."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
# ACHTUNG: Wir joinen hier p2 (player2), weil wir den Namen des Gegners anzeigen wollen!
|
||||
query = """
|
||||
SELECT m.id AS match_id, m.score_player1, m.score_player2, m.played_at,
|
||||
sys.name AS system_name,
|
||||
p2.display_name AS p2_name
|
||||
FROM matches m
|
||||
JOIN gamesystems sys ON m.gamesystem_id = sys.id
|
||||
JOIN players p2 ON m.player2_id = p2.id
|
||||
WHERE m.player1_id = ? AND m.player2_check = 0
|
||||
"""
|
||||
|
||||
cursor.execute(query, (player_id,))
|
||||
rows = cursor.fetchall()
|
||||
connection.close()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ from data import database, data_api
|
|||
from gui import gui_style
|
||||
|
||||
def setup_routes():
|
||||
@ui.page('/admin')
|
||||
@ui.page('/admin', dark=True)
|
||||
def home_page():
|
||||
gui_style.apply_design()
|
||||
gui_style.apply_design()
|
||||
if app.storage.user.get('authenticated', False):
|
||||
ui.card().classes("w-full")
|
||||
|
|
@ -11,8 +11,9 @@ def get_auth_url():
|
|||
redirect_uri = f"{app_url}/login/discord"
|
||||
encoded_redirect_uri = urllib.parse.quote(redirect_uri, safe="")
|
||||
|
||||
# NEU: scope=identify%20guilds fragt Profilbild UND Server ab
|
||||
return f"https://discord.com/api/oauth2/authorize?client_id={client_id}&redirect_uri={encoded_redirect_uri}&response_type=code&scope=identify%20guilds"
|
||||
# NEU: guilds.members.read erlaubt uns, die Rollen des Users in einem bestimmten Server abzufragen
|
||||
return f"https://discord.com/api/oauth2/authorize?client_id={client_id}&redirect_uri={encoded_redirect_uri}&response_type=code&scope=identify%20guilds.members.read"
|
||||
|
||||
|
||||
def setup_login_routes():
|
||||
client_id = os.getenv("DISCORD_CLIENT_ID")
|
||||
|
|
@ -20,7 +21,7 @@ def setup_login_routes():
|
|||
app_url = os.getenv("APP_URL")
|
||||
redirect_uri = f"{app_url}/login/discord"
|
||||
|
||||
@ui.page('/login/discord')
|
||||
@ui.page('/login/discord', dark=True)
|
||||
def discord_callback(code: str = None):
|
||||
if not code:
|
||||
ui.label('Fehler: Kein Code erhalten.').classes('text-red-500')
|
||||
|
|
@ -39,8 +40,35 @@ def setup_login_routes():
|
|||
|
||||
if 'access_token' in token_json:
|
||||
access_token = token_json['access_token']
|
||||
|
||||
user_headers = {'Authorization': f"Bearer {access_token}"}
|
||||
|
||||
# 1. Die IDs aus der .env laden (Passe die Namen an, falls sie bei dir anders heißen!)
|
||||
guild_id = os.getenv("DISCORD_SERVER_ID")
|
||||
role_diceghosts = os.getenv("DISCORD_SERVER_DICEGHOST_ID")
|
||||
role_friend = os.getenv("DISCORD_SERVER_FRIEND_ID")
|
||||
|
||||
# 2. Prüfen: Ist der Nutzer überhaupt auf unserem Server?
|
||||
# Wir fragen Discord gezielt nach dem Profil des Nutzers auf DEINEM Server.
|
||||
member_response = requests.get(f'https://discord.com/api/users/@me/guilds/{guild_id}/member', headers=user_headers)
|
||||
|
||||
# Ein HTTP Status-Code 200 bedeutet "OK". Alles andere (z.B. 404 Not Found) bedeutet: Er ist nicht auf dem Server!
|
||||
if member_response.status_code != 200:
|
||||
ui.label('Zugriff verweigert: Du bist nicht auf dem Diceghosts Server!').classes('text-red-500 text-xl font-bold p-4')
|
||||
return # Bricht die Funktion hier ab. Kein Login!
|
||||
|
||||
# 3. Prüfen: Hat er die richtige Rolle?
|
||||
member_json = member_response.json()
|
||||
# Wir holen die Liste aller Rollen des Nutzers. Wenn er keine hat, nehmen wir eine leere Liste []
|
||||
user_roles = member_json.get('roles', [])
|
||||
|
||||
# Wir prüfen, ob mindestens eine der beiden Rollen-IDs in seiner Liste auftaucht
|
||||
if role_diceghosts not in user_roles and role_friend not in user_roles:
|
||||
ui.label('Zugriff verweigert: Du hast nicht die benötigte Rolle (Diceghosts oder Friend)!').classes('text-red-500 text-xl font-bold p-4')
|
||||
return # Bricht die Funktion hier ab. Kein Login!
|
||||
|
||||
# --- AB HIER: ZUGANG GEWÄHRT! ---
|
||||
|
||||
# Jetzt laden wir noch seine allgemeinen Discord-Daten (für Name und Profilbild)
|
||||
user_response = requests.get('https://discord.com/api/users/@me', headers=user_headers)
|
||||
user_json = user_response.json()
|
||||
|
||||
|
|
@ -63,8 +91,8 @@ def setup_login_routes():
|
|||
app.storage.user['discord_name'] = discord_name
|
||||
app.storage.user['db_id'] = player[0]
|
||||
app.storage.user['display_name'] = player[2]
|
||||
app.storage.user['discord_avatar_url'] = player[3] # Bild speichern!
|
||||
app.storage.user['discord_avatar_url'] = player[3]
|
||||
|
||||
ui.navigate.to('/')
|
||||
else:
|
||||
ui.label('Fehler beim Login!').classes('text-red-500')
|
||||
ui.label('Fehler beim Login!').classes('text-red-500')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from nicegui import ui
|
||||
|
||||
def apply_design():
|
||||
ui.add_css('body { background-color: #18181b; }')
|
||||
|
||||
# 1. Dark Mode aktivieren
|
||||
ui.dark_mode(True)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from data import data_api
|
|||
|
||||
def setup_routes():
|
||||
# 1. Die {}-Klammern definieren eine dynamische Variable in der URL
|
||||
@ui.page('/statistic/{systemname}')
|
||||
@ui.page('/statistic/{systemname}', dark=True)
|
||||
def gamesystem_statistic_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf!
|
||||
|
||||
# Sicherheitscheck: Ist der User eingeloggt?
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from gui import discord_login, gui_style
|
|||
from match_calculations import calc_match
|
||||
|
||||
def setup_routes(admin_discord_id):
|
||||
@ui.page('/')
|
||||
@ui.page('/', dark=True)
|
||||
def home_page():
|
||||
gui_style.apply_design()
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ def setup_routes(admin_discord_id):
|
|||
ui.button(icon='edit', color='primary', on_click=toggle_edit_mode).props('round dense')
|
||||
|
||||
# --- ANSICHT 2: Das Eingabefeld (startet unsichtbar!) ---
|
||||
with ui.row().classes('items-center gap-2') as edit_row:
|
||||
with ui.row().classes('items-center gap-5') as edit_row:
|
||||
edit_row.visible = False # Am Anfang verstecken
|
||||
|
||||
def save_new_name():
|
||||
|
|
@ -65,8 +65,14 @@ def setup_routes(admin_discord_id):
|
|||
# Wenn nichts geändert wurde, einfach wieder einklappen
|
||||
toggle_edit_mode()
|
||||
|
||||
def generate_random_silly_name():
|
||||
silly_name = data_api.generate_silly_name()
|
||||
name_input.value=silly_name
|
||||
|
||||
|
||||
name_input = ui.input('Neuer Name', value=display_name).on('keydown.enter', save_new_name)
|
||||
ui.button(icon='save', color='positive', on_click=save_new_name).props('round dense')
|
||||
ui.button(icon='casino', on_click=generate_random_silly_name).props('round dense')
|
||||
ui.button(icon='close', color='negative', on_click=toggle_edit_mode).props('round dense')
|
||||
|
||||
avatar = app.storage.user.get('avatar_url')
|
||||
|
|
@ -125,6 +131,33 @@ def setup_routes(admin_discord_id):
|
|||
# BESTÄTIGEN und spiel berechnen lassen
|
||||
ui.button("Bestätigen", color="positive", icon="check", on_click=lambda e, m_id=match['match_id']: acccept_match(m_id))
|
||||
|
||||
# ---------------------------
|
||||
# --- Selbst eingetragene, offene Spiele ---
|
||||
# ---------------------------
|
||||
submitted_matches = data_api.get_submitted_matches(player_id)
|
||||
|
||||
if len(submitted_matches) > 0:
|
||||
# Eine etwas dezentere Karte (grau)
|
||||
with ui.card().classes('w-full bg-zinc-800 border border-gray-600 mb-6'):
|
||||
ui.label(f"Warten auf Gegner: Du hast {len(submitted_matches)} offene(s) Spiel(e) eingetragen.").classes('text-xl font-bold text-gray-300 mb-2')
|
||||
|
||||
for match in submitted_matches:
|
||||
|
||||
# Die Lösch-Funktion, die beim Klick ausgeführt wird
|
||||
def retract_match(m_id):
|
||||
data_api.delete_match(m_id)
|
||||
ui.notify("Eingetragenes Spiel zurückgezogen!", color="warning")
|
||||
ui.navigate.reload()
|
||||
|
||||
# Für jedes Match machen wir eine kleine Reihe
|
||||
with ui.row().classes('w-full items-center justify-between bg-zinc-900 p-3 rounded shadow-inner'):
|
||||
|
||||
# Info-Text: Auf wen warten wir?
|
||||
info_text = f"[{match['system_name']}] Warten auf Bestätigung von {match['p2_name']} ({match['score_player1']} : {match['score_player2']})"
|
||||
ui.label(info_text).classes('text-lg text-gray-400')
|
||||
|
||||
# Der Zurückziehen-Button (wieder mit unserem lambda m_id=... Trick!)
|
||||
ui.button("Zurückziehen", color="warning", icon="delete", on_click=lambda e, m_id=match['match_id']: retract_match(m_id))
|
||||
|
||||
|
||||
# ---------------------------
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from match_calculations import calc_match
|
|||
def setup_routes():
|
||||
|
||||
# 1. Die {}-Klammern definieren eine dynamische Variable in der URL
|
||||
@ui.page('/add-match/{systemname}')
|
||||
@ui.page('/add-match/{systemname}', dark=True)
|
||||
def match_form_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf!
|
||||
gui_style.apply_design()
|
||||
|
||||
|
|
|
|||
2
main.py
2
main.py
|
|
@ -25,4 +25,4 @@ match_gui.setup_routes()
|
|||
admin_gui.setup_routes()
|
||||
|
||||
# 4. Wir starten die NiceGUI App
|
||||
ui.run(title="Westside Diceghost Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies")
|
||||
ui.run(title="Westside Diceghost Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies", favicon="gui/pictures/wsdg.png")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user