projekt/ │ app.py │ requirements.txt │ Dockerfile │ └── static/ │ index.html │ login.html │ rules.html │ leaderboard.html │ history.html │ comparison.html # League of Legends Tracker mit Login, Leaderboard, Verlauf # Entwickler: ChatGPT - GPT-5 import os, time, sqlite3, requests from flask import Flask, request, jsonify, send_from_directory, session, abort from werkzeug.security import generate_password_hash, check_password_hash # --- Konfiguration --- RIOT_KEY = os.environ.get("RIOT_API_KEY", "").strip() if not RIOT_KEY: raise RuntimeError("Bitte setze deine Riot API Key als Umgebungsvariable: RIOT_API_KEY") SECRET_KEY = os.environ.get("SECRET_KEY", "super-secret-key") ADMIN_USER = os.environ.get("ADMIN_USER", "admin") ADMIN_PASS = os.environ.get("ADMIN_PASS", "admin123") HEADERS = {"X-Riot-Token": RIOT_KEY} DB = "tracked.db" # --- Punkteberechnung --- TIER_VALUES = { "IRON": 0, "BRONZE": 400, "SILVER": 800, "GOLD": 1200, "PLATINUM": 1600, "EMERALD": 2000, "DIAMOND": 2400, "MASTER": 2800, "GRANDMASTER": 3000, "CHALLENGER": 3200, } DIV_VALUES = {"I": 300, "II": 200, "III": 100, "IV": 0} REGION_ROUTING = { "euw": "euw1", "eune": "eun1", "na": "na1", "kr": "kr", "br": "br1", "lan": "la1", "las": "la2", "oc": "oc1", } def match_region(platform): p = platform.lower() if p.startswith(("euw", "eun", "tr", "ru")): return "europe" if p.startswith(("na", "br", "la", "oc")): return "americas" if p.startswith(("kr", "jp")): return "asia" return "americas" app = Flask(__name__, static_folder='static') app.secret_key = SECRET_KEY # --- DB initialisieren --- def init_db(): with sqlite3.connect(DB) as c: c.execute("""CREATE TABLE IF NOT EXISTS accounts( id INTEGER PRIMARY KEY, name TEXT, region TEXT, added_at INTEGER)""") c.execute("""CREATE TABLE IF NOT EXISTS history( id INTEGER PRIMARY KEY, account_id INTEGER, timestamp INTEGER, points INTEGER, lp INTEGER, tier TEXT, division TEXT)""") c.execute("""CREATE TABLE IF NOT EXISTS users( id INTEGER PRIMARY KEY, username TEXT UNIQUE, password_hash TEXT, created_at INTEGER)""") # Admin anlegen falls nicht vorhanden cur = c.execute("SELECT 1 FROM users WHERE username=?", (ADMIN_USER,)) if not cur.fetchone(): c.execute("INSERT INTO users(username,password_hash,created_at) VALUES(?,?,?)", (ADMIN_USER, generate_password_hash(ADMIN_PASS), int(time.time()))) init_db() # --- Helper --- def riot_get(url, params=None): r = requests.get(url, headers=HEADERS, params=params, timeout=10) r.raise_for_status() return r.json() def login_required(fn): def wrap(*a, **kw): if not session.get("user"): return jsonify({"error": "nicht eingeloggt"}), 401 return fn(*a, **kw) wrap.__name__ = fn.__name__ return wrap # --- Static --- @app.route("/") def index(): return send_from_directory("static", "index.html") @app.route("/

") def static_files(p): try: return send_from_directory("static", p) except: abort(404) # --- Auth --- @app.route("/api/login", methods=["POST"]) def login(): data = request.json user, pw = data.get("username",""), data.get("password","") with sqlite3.connect(DB) as c: cur = c.execute("SELECT password_hash FROM users WHERE username=?", (user,)) row = cur.fetchone() if not row or not check_password_hash(row[0], pw): return jsonify({"error":"Falsche Login-Daten"}),401 session["user"]=user return jsonify({"ok":True}) @app.route("/api/logout", methods=["POST"]) def logout(): session.pop("user",None) return jsonify({"ok":True}) @app.route("/api/whoami") def whoami(): return jsonify({"username":session.get("user")}) @app.route("/api/register", methods=["POST"]) def register(): data=request.json u,p=data.get("username",""),data.get("password","") if not u or not p: return jsonify({"error":"Fehlende Felder"}),400 try: with sqlite3.connect(DB) as c: c.execute("INSERT INTO users(username,password_hash,created_at) VALUES(?,?,?)", (u,generate_password_hash(p),int(time.time()))) return jsonify({"ok":True}) except sqlite3.IntegrityError: return jsonify({"error":"Name existiert"}),400 # --- Accounts --- @app.route("/api/add", methods=["POST"]) @login_required def add_acc(): d=request.json name,region=d.get("name","").strip(),d.get("region","").strip().lower() if not name or not region: return jsonify({"error":"Name/Region nötig"}),400 with sqlite3.connect(DB) as c: c.execute("INSERT INTO accounts(name,region,added_at) VALUES(?,?,?)",(name,region,int(time.time()))) return jsonify({"ok":True}) @app.route("/api/list") @login_required def list_acc(): with sqlite3.connect(DB) as c: cur=c.execute("SELECT id,name,region,added_at FROM accounts") res=[{"id":r[0],"name":r[1],"region":r[2],"added_at":r[3]} for r in cur.fetchall()] return jsonify(res) @app.route("/api/remove", methods=["POST"]) @login_required def rem_acc(): d=request.json aid=d.get("id") with sqlite3.connect(DB) as c: c.execute("DELETE FROM accounts WHERE id=?",(aid,)) return jsonify({"ok":True}) # --- Status & History --- @app.route("/api/status") @login_required def status(): with sqlite3.connect(DB) as c: cur=c.execute("SELECT id,name,region FROM accounts") accs=cur.fetchall() out=[] for aid,name,region in accs: try: platform=REGION_ROUTING.get(region,region) summ=riot_get(f"https://{platform}.api.riotgames.com/lol/summoner/v4/summoners/by-name/{requests.utils.requote_uri(name)}") sid,puuid=summ["id"],summ["puuid"] leagues=riot_get(f"https://{platform}.api.riotgames.com/lol/league/v4/entries/by-summoner/{sid}") solo=next((e for e in leagues if e["queueType"] in ("RANKED_SOLO_5x5","RANKED_SOLO_5v5")),None) points=0 if solo: points=TIER_VALUES.get(solo["tier"],0)+DIV_VALUES.get(solo["rank"],0)+int(solo["leaguePoints"]) with sqlite3.connect(DB) as c: c.execute("INSERT INTO history(account_id,timestamp,points,lp,tier,division) VALUES(?,?,?,?,?,?)", (aid,int(time.time()),points,solo.get("leaguePoints",0) if solo else 0, solo.get("tier","") if solo else "",solo.get("rank","") if solo else "")) out.append({"id":aid,"name":name,"region":region, "tier":solo.get("tier") if solo else None, "rank":solo.get("rank") if solo else None, "lp":solo.get("leaguePoints") if solo else None, "points":points}) except Exception as e: out.append({"id":aid,"name":name,"region":region,"error":str(e)}) return jsonify(out) @app.route("/api/history/") @login_required def hist(aid): with sqlite3.connect(DB) as c: cur=c.execute("SELECT timestamp,points,lp,tier,division FROM history WHERE account_id=? ORDER BY timestamp",(aid,)) rows=[{"timestamp":r[0],"points":r[1],"lp":r[2],"tier":r[3],"division":r[4]} for r in cur.fetchall()] return jsonify(rows) if __name__=="__main__": app.run(host="0.0.0.0",port=8080,debug=False)