From 8432279bdf47914d365ed8bdbb7e977019c0e122 Mon Sep 17 00:00:00 2001 From: elias Date: Sun, 7 Jun 2026 11:29:58 +0200 Subject: [PATCH] =?UTF-8?q?AstraOS=20Ver.=201.28:=20-=20Dashboard=20ver?= =?UTF-8?q?=C3=A4ndert=20-=20AyranTracker=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AstraOS/Frontend/HTML/ayran_tracker.html | 287 +++++++++++++++++++++++ AstraOS/Frontend/HTML/dashboard.html | 114 +++------ AstraOS/astra_os.py | 202 ++++++++++++++-- 3 files changed, 506 insertions(+), 97 deletions(-) create mode 100644 AstraOS/Frontend/HTML/ayran_tracker.html diff --git a/AstraOS/Frontend/HTML/ayran_tracker.html b/AstraOS/Frontend/HTML/ayran_tracker.html new file mode 100644 index 0000000..554e7a1 --- /dev/null +++ b/AstraOS/Frontend/HTML/ayran_tracker.html @@ -0,0 +1,287 @@ + + + + + + Ayran Tracker - Professional Valorant Analytics | AstraOS + + + + + + {% include 'sidebar.html' %} + +
+
+
+
+ 🥛 + + AYRANTRACKER + +
+
+
+ +
+ +
+
+ + Live Match Analytics + +

+ Track Your Performance +

+
+ +
+
+
+ +
+ +
+
+
+ + +
+
+ + + + + diff --git a/AstraOS/Frontend/HTML/dashboard.html b/AstraOS/Frontend/HTML/dashboard.html index d1a514d..6676178 100644 --- a/AstraOS/Frontend/HTML/dashboard.html +++ b/AstraOS/Frontend/HTML/dashboard.html @@ -12,18 +12,18 @@ box-shadow: 0 0 20px rgba(192,132,252,0.3); transform: translateY(-4px); } -#system-status-text.small { - font-size: 2rem; - text-align: center; - margin: 0 auto; - display: block; +.quick-link { + padding: 0.75rem 1rem; + border-radius: 0.5rem; + background: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.3); + transition: all 0.2s ease; + text-decoration: none; + color: #e6e6e6; } -#system-status-desc .wartung-desc { - font-size: 0.75rem; - display: block; - text-align: center; - margin: 0 auto; - font-weight: 500; +.quick-link:hover { + background: rgba(139, 92, 246, 0.2); + border-color: rgba(139, 92, 246, 0.6); } {% endblock %} @@ -36,28 +36,14 @@
-

🚀 Willkommen bei AstraOS!

+

🚀 Willkommen bei AstraOS

- AstraOS ist da! Wir haben alles von Grund auf neu gebaut, damit du deine Systeme noch intuitiver und effizienter verwalten kannst. Wir hoffen, dir gefallen das neue Design und die vielen praktischen Funktionen. + Schön, dass du da bist. Deine Systeme sind einsatzbereit.

-
-

Aktive Benutzer

-

{{ stats.active_users.value }}

-

- {% set pct = stats.active_users.percent %} - {% if pct is not none %} - {{ '+' if pct>=0 else '' }}{{ pct }}% seit letzter Woche - {% else %} - - - {% endif %} -

-
-

Server-Auslastung

{{ stats.server_load.percent }}%

@@ -66,28 +52,14 @@
-

Offene Tickets

-

{{ stats.open_tickets.count }}

-

Keine offenen Anfragen

-
- -
-

Systemstatus

- {% set status = stats.system_status.status %} -

- {{ status }} -

-

- {% if 'wartung' in status.lower() or 'systeme im wartungsmodus' in status.lower() %} - Systeme im Wartungsmodus - {% elif 'online' in status.lower() %} - Alle Systeme funktionsfähig +

Aktive Benutzer

+

{{ stats.active_users.value }}

+

+ {% set pct = stats.active_users.percent %} + {% if pct is not none %} + {{ '+' if pct>=0 else '' }}{{ pct }}% vs. letzte Woche {% else %} - Teilweise oder komplett offline + - {% endif %}

@@ -101,21 +73,31 @@

{{ maint.status if maint.status else 'Wartungsarbeiten laufen' }}

{% elif maint and maint.time_str %}

{{ maint.time_str }}

-

{{ maint.status if maint and maint.status else 'Geplant' }} {% if maint and maint.duration_minutes %}— Dauer: {{ maint.duration_minutes }} Min.{% endif %}

+

{{ maint.status if maint and maint.status else 'Geplant' }} {% if maint and maint.duration_minutes %}— {{ maint.duration_minutes }} Min.{% endif %}

{% else %}

Keine geplant

-

Alle Systeme sind betriebsbereit.

+

Alles läuft normal.

{% endif %}
+
+
-

Letzte Benutzererstellung

- {% set last = stats.last_user_created %} -

{{ last.time_str if last and last.time_str else 'Keine Daten' }}

-

Erstellter Benutzer: {{ last.by if last and last.by else 'N/A' }}

+

⚡ Quick Access

+
+ {% for tool in available_tools %} + +
{{ tool.label }}
+
+ {% endfor %} +
+ {% if not available_tools %} +
Keine verfügbaren Tools für dich.
+ {% endif %}
+ {% endblock %} diff --git a/AstraOS/astra_os.py b/AstraOS/astra_os.py index a92afa0..44e30a6 100644 --- a/AstraOS/astra_os.py +++ b/AstraOS/astra_os.py @@ -91,7 +91,8 @@ try: "instranet", "view_ip_analyzer", "view_age_verification", - "manage_age_verification" + "manage_age_verification", + "view_ayran_tracker" ] if roles_collection.count_documents({}) == 0: print("WARNUNG: Keine Rollen in der Datenbank gefunden. Erstelle Standard-Rollen...") @@ -616,21 +617,34 @@ def dashboard(): return v.strftime("%d. %b %Y, %H:%M") except Exception: return str(v) - maint = scheduled_maintenance or {"time": None, "status": "Keine", "duration_minutes": None} - maint["time_str"] = _dt_to_str(maint.get("time")) - stats = { - "active_users": {"value": user_count, "percent": 0}, - "server_load": server_load, - "open_tickets": {"count": open_tickets}, - "system_status": system_status, - "scheduled_maintenance": maint, - "last_user_created": { - "time": last_user_time, - "by": last_user_by, - "time_str": _dt_to_str(last_user_time) - } - } - return render_template('dashboard.html', stats=stats, active_page='dashboard') + maint = scheduled_maintenance or {"time": None, "status": "Keine", "duration_minutes": None} + maint["time_str"] = _dt_to_str(maint.get("time")) + + user_perms = session.get('permissions', []) + all_tools = [ + {"label": "👤 User Mgmt", "url": "user_management", "perm": "view_user_management"}, + {"label": "🤖 Bots", "url": "discord_bots", "perm": "manage_discord_bots"}, + {"label": "🚚 ETS2 DLCs", "url": "ets2_dlcs", "perm": "view_ets2_dlcs"}, + {"label": "🌐 IP Analyzer", "url": "ip_analyzer", "perm": "view_ip_analyzer"}, + {"label": "📜 Logs", "url": "logs", "perm": "view_logs"}, + {"label": "🎂 Age Verify", "url": "age_verification", "perm": "view_age_verification"}, + {"label": "🥛 Ayran Tracker", "url": "ayran_tracker", "perm": "view_ayran_tracker"}, + ] + available_tools = [t for t in all_tools if t["perm"] in user_perms] + + stats = { + "active_users": {"value": user_count, "percent": 0}, + "server_load": server_load, + "open_tickets": {"count": open_tickets}, + "system_status": system_status, + "scheduled_maintenance": maint, + "last_user_created": { + "time": last_user_time, + "by": last_user_by, + "time_str": _dt_to_str(last_user_time) + } + } + return render_template('dashboard.html', stats=stats, active_page='dashboard', available_tools=available_tools) @app.route('/logout') @login_required def logout(): @@ -1513,9 +1527,159 @@ def update_maintenance(): {"$set": update_data}, upsert=True ) - add_log("Wartungseinstellungen aktualisiert", f"Aktiviert: {is_enabled}, Nachricht: {status_message}") - flash("Wartungseinstellungen wurden aktualisiert.", "success") - return redirect(url_for('maintenance_management')) + add_log("Wartungseinstellungen aktualisiert", f"Aktiviert: {is_enabled}, Nachricht: {status_message}") + flash("Wartungseinstellungen wurden aktualisiert.", "success") + return redirect(url_for('maintenance_management')) + +@app.route('/ayran_tracker') +@login_required +@permission_required('view_ayran_tracker') +def ayran_tracker(): + return render_template('ayran_tracker.html', active_page='ayran_tracker') + +@app.route('/api/v1/profile//', methods=['GET']) +@login_required +@permission_required('view_ayran_tracker') +def get_valorant_stats(name, tag): + VALORANT_API_KEY = os.environ.get('VALORANT_API_KEY') + VALORANT_API_BASE = "https://api.henrikdev.xyz/valorant" + mode = request.args.get('mode', 'competitive').lower() + + if not VALORANT_API_KEY: + return jsonify({"status": "error", "message": "API-Schlüssel nicht konfiguriert"}), 500 + + headers = {"Authorization": VALORANT_API_KEY} + + try: + acc_res = requests.get(f"{VALORANT_API_BASE}/v1/account/{name}/{tag}?force=true", headers=headers, timeout=10) + if acc_res.status_code != 200: + return jsonify({"status": "error", "message": "Spieler nicht gefunden oder API-Limit erreicht."}), acc_res.status_code + + acc_data = acc_res.json().get('data', {}) or {} + region = acc_data.get("region", "eu") + + mmr_res = requests.get(f"{VALORANT_API_BASE}/v1/mmr/{region}/{name}/{tag}", headers=headers, timeout=10) + mmr_data = mmr_res.json().get('data', {}) if mmr_res.status_code == 200 else {} + if not mmr_data: mmr_data = {} + + matches_res = requests.get(f"{VALORANT_API_BASE}/v3/matches/{region}/{name}/{tag}?size=15&mode={mode}", headers=headers, timeout=10) + matches_list = matches_res.json().get('data', []) if matches_res.status_code == 200 else [] + if not matches_list: matches_list = [] + + mmr_hist_res = requests.get(f"{VALORANT_API_BASE}/v1/mmr-history/{region}/{name}/{tag}", headers=headers, timeout=10) + mmr_history = mmr_hist_res.json().get('data', []) if mmr_hist_res.status_code == 200 else [] + if not mmr_history: mmr_history = [] + + mmr_history_dict = {} + for entry in mmr_history: + m_id = entry.get("match_id") + if m_id: + mmr_history_dict[m_id] = entry + + total_kills = 0 + total_deaths = 0 + wins = 0 + total_damage = 0 + total_rounds = 0 + agent_counts = {} + processed_matches = [] + + for match in matches_list: + if not match: continue + metadata = match.get('metadata', {}) or {} + match_id = metadata.get('matchid') + players = match.get('players', {}).get('all_players', []) or [] + teams = match.get('teams', {}) or {} + + me = next((p for p in players if p.get('name', '').lower() == name.lower() and p.get('tag', '').lower() == tag.lower()), None) + if not me: + continue + + stats = me.get('stats', {}) or {} + kills = stats.get('kills', 0) or 0 + deaths = stats.get('deaths', 0) or 0 + assists = stats.get('assists', 0) or 0 + score = stats.get('score', 0) or 0 + damage = stats.get('damage', 0) or 0 + + total_kills += kills + total_deaths += deaths + total_damage += damage + + agent_name = me.get('character', 'Unknown') or 'Unknown' + agent_counts[agent_name] = agent_counts.get(agent_name, 0) + 1 + + my_team = (me.get('team', '') or '').lower() + rounds_won = teams.get(my_team, {}).get('rounds_won', 0) if teams.get(my_team) else 0 + rounds_won = rounds_won or 0 + + enemy_team = 'red' if my_team == 'blue' else 'blue' + rounds_lost = teams.get(enemy_team, {}).get('rounds_won', 0) if teams.get(enemy_team) else 0 + rounds_lost = rounds_lost or 0 + + total_rounds += (rounds_won + rounds_lost) + + is_win = rounds_won > rounds_lost + if is_win: + wins += 1 + + start_time = metadata.get('game_start', 0) or 0 + date_str = datetime.fromtimestamp(start_time).strftime('%d.%m.%Y') if start_time else "Unbekannt" + + rr_change = 0 + if match_id and match_id in mmr_history_dict: + rr_change = mmr_history_dict[match_id].get('mmr_change_to_last_game', 0) or 0 + + processed_matches.append({ + "map": metadata.get('map', 'Unknown') or 'Unknown', + "mode": metadata.get('mode', 'Competitive') or 'Competitive', + "date": date_str, + "result": "WIN" if is_win else "LOSS", + "rounds_won": rounds_won, + "rounds_lost": rounds_lost, + "kills": kills, + "deaths": deaths, + "assists": assists, + "score": score, + "agent": agent_name, + "rr_change": rr_change + }) + + match_count = len(processed_matches) + kd_calculated = round(total_kills / total_deaths, 2) if total_deaths > 0 else total_kills + winrate_calculated = round((wins / match_count) * 100, 1) if match_count > 0 else 0 + adr_calculated = round(total_damage / total_rounds, 1) if total_rounds > 0 else 0 + + sorted_agents = sorted(agent_counts.items(), key=lambda x: x[1], reverse=True) + top_agents = [{"name": a[0], "count": a[1]} for a in sorted_agents[:2]] + + add_log("Ayran Tracker verwendet", f"Spieler: {name}#{tag}, Mode: {mode}") + + return jsonify({ + "status": "success", + "account": { + "name": acc_data.get('name'), + "tag": acc_data.get('tag'), + "level": acc_data.get('account_level'), + "card_url": acc_data.get('card', {}).get('small') if acc_data.get('card') else None + }, + "rank": { + "current_tier_name": mmr_data.get('currenttierpatched', 'Unranked') or 'Unranked', + "ranking_in_tier": mmr_data.get('ranking_in_tier', 0) or 0, + }, + "calculated_stats": { + "win_rate": f"{winrate_calculated}%", + "kd_ratio": str(kd_calculated), + "avg_damage_round": str(adr_calculated), + "matches_analyzed": match_count + }, + "top_agents": top_agents, + "matches": processed_matches + }) + + except Exception as e: + return jsonify({"status": "error", "message": f"Fehler bei der Berechnung: {str(e)}"}), 500 + @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404