")
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/
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("/