import json
import random
import time
from pathlib import Path
from urllib.parse import quote
import requests
from flask import Flask, Response, render_template_string, request, send_from_directory
from flask_cors import CORS
from config import BASE_DIR, CONFIG
app = Flask(__name__)
CORS(app)
ALLOWED_STATIC_EXTENSIONS = {
".css",
".png",
".jpg",
".jpeg",
".gif",
".webp",
".bmp",
".svg",
".ico",
}
def safe_url(filename):
return quote(str(filename or ""), safe="/")
def generate_stars(count, opacity):
shadows = []
for _ in range(count):
x = random.randint(0, 2000)
y = random.randint(0, 2000)
shadows.append(f"{x}px {y}px rgba(255, 255, 255, {opacity})")
return ", ".join(shadows)
def fetch_teamspeak_data(cfg):
cache_file = Path(cfg["cache_file"])
if cache_file.exists() and (time.time() - cache_file.stat().st_mtime) < cfg["cache_time"]:
try:
return json.loads(cache_file.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
pass
base_url = f"https://{cfg['host']}:{cfg['port']}/{cfg['server_id']}"
headers = {"x-api-key": cfg["api_key"]}
try:
channels_res = requests.get(f"{base_url}/channellist", headers=headers, timeout=2)
clients_res = requests.get(f"{base_url}/clientlist?-groups", headers=headers, timeout=2)
channels_json = channels_res.json()
clients_json = clients_res.json()
except requests.RequestException:
return {"channels": [], "clients": []}
except ValueError:
return {"channels": [], "clients": []}
data = {
"channels": channels_json.get("body") or [],
"clients": clients_json.get("body") or [],
}
try:
cache_file.write_text(json.dumps(data), encoding="utf-8")
except OSError:
pass
return data
def same_id(left, right):
return str(left or 0) == str(right or 0)
def sort_ts_channels(channels, pid=0):
ordered = []
by_order = {}
for channel in channels:
if same_id(channel.get("pid", 0), pid):
by_order[str(channel.get("channel_order", 0))] = channel
current_order = "0"
max_iterations = len(channels) + 1
iterations = 0
while current_order in by_order and iterations < max_iterations:
channel = by_order[current_order]
ordered.append(channel)
ordered.extend(sort_ts_channels(channels, channel.get("cid")))
current_order = str(channel.get("cid"))
iterations += 1
return ordered
def build_context():
start_time = time.perf_counter()
ts_data = fetch_teamspeak_data(CONFIG["server"])
api_ping = round((time.perf_counter() - start_time) * 1000)
raw_channels = ts_data.get("channels") or []
for channel in raw_channels:
channel["channel_name"] = channel.get("channel_name").replace("[Cspacer]", "")
raw_clients = ts_data.get("clients") or []
is_online = bool(raw_channels)
channels = sort_ts_channels(raw_channels, 0)
clients_by_channel = {}
total_real_clients = 0
if is_online:
for client in raw_clients:
if int(client.get("client_type") or 0) == 1:
continue
clients_by_channel.setdefault(str(client.get("cid")), []).append(client)
total_real_clients += 1
return {
"config": CONFIG,
"api_ping": api_ping,
"channels": channels,
"clients_by_channel": clients_by_channel,
"is_online": is_online,
"safe_url": safe_url,
"small_stars": generate_stars(150, 0.4),
"medium_stars": generate_stars(50, 0.6),
"large_stars": generate_stars(15, 0.8),
"total_real_clients": total_real_clients,
}
FRAGMENT_TEMPLATE = """
{% if is_online %}
Users Online{{ total_real_clients }}
{% for channel in channels %}
{% set c_name = channel.get('channel_name', '') %}
{% set cid = channel.get('cid')|string %}
{% set channel_clients = clients_by_channel.get(cid, []) %}
{% set bg_img = config.channel_banners.get(c_name, config.generic_images.get('background', '')) %}
{% set icon_img = config.channel_icons.get(c_name, config.generic_images.get('icon', '')) %}
{% set max_clients = channel.get('channel_maxclients') %}
{% if bg_img %}
{% set bg_style = "background-image: linear-gradient(90deg, rgba(9, 18, 32, 0.96) 0%, rgba(9, 18, 32, 0.78) 45%, rgba(9, 18, 32, 0.94) 100%), url('" ~ safe_url(bg_img) ~ "');" %}
{% else %}
{% set bg_style = "" %}
{% endif %}
{% if icon_img %}
{% endif %}
{{ c_name }}
{# Only show client count for non-spacer channels #}
{% if '[spacer' not in c_name %}
{{ channel_clients|length }} / {{ '∞' if max_clients == -1 or max_clients == '-1' else max_clients }}
{% endif %}
{# Only show client list for non-spacer channels #}
{% if '[spacer' not in c_name|lower and '[cspacer' not in c_name|lower and channel_clients %}
{% for client in channel_clients %}
{% set role_icon = namespace(path='') %}
{% for group_id in (client.get('client_servergroups', '')|string).split(',') %}
{% if not role_icon.path and group_id in config.role_icons %}
{% set role_icon.path = config.role_icons[group_id] %}
{% endif %}
{% endfor %}
{% if role_icon.path %}
{% else %}
{% endif %}
{{ client.get('client_nickname', '') }}