aboutsummaryrefslogtreecommitdiffstats
path: root/config.py
blob: 96ef08ac99c74bef8928bb89169a23febae841ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""TeamSpeak Web Explorer configuration.

Settings live in ``config.toml`` (shared with the Node voice gateway in
``gateway/``) and secrets live in ``.env``. Environment variables override the
TOML values, using the same names the gateway reads (see ``ENV_OVERRIDES``).
"""

import os
import tomllib
from pathlib import Path

from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent
CONFIG_FILE = Path(os.environ.get("TS6_CONFIG_FILE") or BASE_DIR / "config.toml")

load_dotenv(BASE_DIR / ".env")

# (section, key, ENV_VAR, type). Keep in sync with gateway/src/config.js.
ENV_OVERRIDES = [
    ("web", "host", "WEB_HOST", str),
    ("web", "port", "WEB_PORT", int),
    ("web", "dashboard_port", "DASHBOARD_PORT", int),
    ("server", "host", "TS_HOST", str),
    ("server", "webquery_port", "TS_WEBQUERY_PORT", int),
    ("server", "server_id", "TS_SERVER_ID", str),
    ("server", "voice_port", "TS_VOICE_PORT", int),
    ("server", "voice_port", "TS_PORT", int),  # legacy name from the old .env.template
    ("server", "ts_domain", "TS_DOMAIN", str),
    ("voice", "enabled", "VOICE_ENABLED", bool),
    ("voice", "trust_proxy", "VOICE_TRUST_PROXY", bool),
]


def _env_value(raw, kind):
    if kind is bool:
        return raw.strip().lower() in {"1", "true", "yes", "on"}
    return kind(raw.strip())


def _load_toml():
    try:
        with CONFIG_FILE.open("rb") as fh:
            return tomllib.load(fh)
    except FileNotFoundError:
        print(f"[config] {CONFIG_FILE} not found, using defaults")
        return {}


def _build_config():
    raw = _load_toml()
    sections = {
        name: dict(raw.get(name) or {})
        for name in (
            "web", "server", "voice", "ui", "buttons", "external_links", "footer",
            "channel_banners", "channel_icons", "generic_images", "role_icons",
        )
    }

    applied = set()
    for section, key, env_name, kind in ENV_OVERRIDES:
        value = os.environ.get(env_name)
        if value is None or value.strip() == "" or (section, key) in applied:
            continue
        try:
            sections[section][key] = _env_value(value, kind)
            applied.add((section, key))
        except ValueError:
            print(f"[config] ignoring invalid {env_name}={value!r}")

    web = sections["web"]
    web.setdefault("host", "0.0.0.0")
    web.setdefault("port", 5000)
    web.setdefault("dashboard_port", 5001)

    server = sections["server"]
    server.setdefault("host", "")
    server["port"] = server.get("webquery_port", 8443)  # name used by the WebQuery fetcher
    server["server_id"] = str(server.get("server_id", 1))
    server.setdefault("voice_port", 9987)
    server["ts_domain"] = server.get("ts_domain") or server["host"]
    server.setdefault("cache_time", 30)
    server["cache_file"] = BASE_DIR / server.get("cache_file", "cache.json")
    server.setdefault("afk_status_max_clients", 3)
    server["api_key"] = os.environ.get("TS_API_KEY", "")

    voice = sections["voice"]
    voice.setdefault("enabled", True)

    ui = sections["ui"]
    for key, default in {
        "title": "", "subtitle": "", "background_color": "#07111f",
        "panel_color": "#101b2d", "accent_color": "#2589ff", "text_color": "#e8f2ff",
        "banner_url": "", "enable_stars": False,
    }.items():
        ui.setdefault(key, default)

    buttons = sections["buttons"]
    for key, default in {
        "join": "Connect in TeamSpeak", "web_join": "Join from browser",
        "download_text": "Teamspeak is not installed yet?",
        "download_link": "Download Teamspeak",
        "download_url": "https://www.teamspeak.com/en/downloads/",
    }.items():
        buttons.setdefault(key, default)

    footer = sections["footer"]
    for key in ("text", "author", "author_url"):
        footer.setdefault(key, "")

    generic = sections["generic_images"]
    generic.setdefault("background", "")
    generic.setdefault("icon", "")

    sections["role_icons"] = {str(k): v for k, v in sections["role_icons"].items()}
    return sections


CONFIG = _build_config()

# Web voice is only offered when enabled AND a server password is configured:
# the gateway refuses every join without one, so nobody can join without it.
# The password itself is intentionally kept out of CONFIG (which templates see).
VOICE_AVAILABLE = bool(CONFIG["voice"].get("enabled")) and bool(os.environ.get("TS_SERVER_PASSWORD"))
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage