Files
thc-webhook/templates.py
T

115 lines
3.1 KiB
Python

from __future__ import annotations
import json
from copy import deepcopy
from pathlib import Path
DEFAULT_TEMPLATES: dict[str, dict] = {
"420": {
"text": "Blaze it!",
"color": 0x2ECC71,
"image_url": "https://copyparty.allucanget.biz/img/weed.png",
},
"reminder": {
"text": "This is your 5 minute reminder to 420!",
"color": 0xE67E22,
},
"halftime": {
"text": "Half-time!",
"color": 0x2ECC71,
"image_url": "https://copyparty.allucanget.biz/img/weed.png",
},
"reminder_halftime": {
"text": "Half-time in 5 minutes!",
"color": 0xE67E22,
},
"test": {
"text": "This is a test notification.",
"color": 0x3498DB,
},
}
def _normalize_templates(raw: dict) -> dict[str, dict]:
out: dict[str, dict] = deepcopy(DEFAULT_TEMPLATES)
if not isinstance(raw, dict):
return out
for key, default in DEFAULT_TEMPLATES.items():
incoming = raw.get(key)
if not isinstance(incoming, dict):
continue
text = incoming.get("text")
if isinstance(text, str):
out[key]["text"] = text
color = incoming.get("color")
if isinstance(color, int):
out[key]["color"] = color
elif isinstance(color, str):
try:
out[key]["color"] = parse_color(color)
except ValueError:
pass
image_url = incoming.get("image_url")
if isinstance(image_url, str) and image_url.strip():
out[key]["image_url"] = image_url.strip()
elif "image_url" in default and image_url in (None, ""):
# Allow clearing image_url only if explicitly set to empty.
out[key].pop("image_url", None)
return out
def load_templates(path: str | Path) -> dict[str, dict]:
p = Path(path)
try:
if not p.exists():
return deepcopy(DEFAULT_TEMPLATES)
raw = json.loads(p.read_text(encoding="utf-8"))
return _normalize_templates(raw)
except Exception:
return deepcopy(DEFAULT_TEMPLATES)
def save_templates(path: str | Path, templates: dict) -> None:
p = Path(path)
normalized = _normalize_templates(templates)
serialized = deepcopy(normalized)
for tpl in serialized.values():
color = tpl.get("color")
if isinstance(color, int):
tpl["color"] = f"#{color:06X}"
p.parent.mkdir(parents=True, exist_ok=True)
tmp = p.with_suffix(p.suffix + ".tmp")
tmp.write_text(json.dumps(serialized, indent=2,
sort_keys=True) + "\n", encoding="utf-8")
tmp.replace(p)
def parse_color(value: str) -> int:
"""Parse color from '#RRGGBB', 'RRGGBB', '0xRRGGBB', or decimal."""
s = (value or "").strip().lower()
if not s:
raise ValueError("color is required")
if s.startswith("#"):
s = s[1:]
base = 16
if s.startswith("0x"):
s = s[2:]
elif all(c.isdigit() for c in s):
base = 10
color = int(s, base)
if color < 0 or color > 0xFFFFFF:
raise ValueError("color must be between 0 and 0xFFFFFF")
return color