2026-02-19 06:08:41 +01:00
|
|
|
from fastapi import APIRouter, Request, Response
|
|
|
|
|
import yaml
|
|
|
|
|
import json
|
2026-03-21 17:12:06 +01:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2026-03-25 00:31:32 +01:00
|
|
|
from fastapi_cache.decorator import cache
|
2026-02-19 06:08:41 +01:00
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
#ripped straight from splatcord. sorry not sorry?
|
|
|
|
|
MAP_DATA = {
|
|
|
|
|
0: {"it-IT": "Periferia urbana", "de-DE": "Dekabahnstation", "en-US": "Urchin Underpass", "es-ES": "Parque Viaducto", "ja-JP": "デカライン高架下"},
|
|
|
|
|
1: {"it-IT": "Magazzino", "de-DE": "Kofferfisch-Lager", "en-US": "Walleye Warehouse", "es-ES": "Almacén Rodaballo", "ja-JP": "ハコフグ倉庫"},
|
|
|
|
|
2: {"it-IT": "Raffineria", "de-DE": "Bohrinsel Nautilus", "en-US": "Saltspray Rig", "es-ES": "Plataforma Gaviota", "ja-JP": "シオノメ油田"},
|
|
|
|
|
3: {"it-IT": "Centro commerciale", "de-DE": "Arowana-Center", "en-US": "Arowana Mall", "es-ES": "Plazuela del Calamar", "ja-JP": "アロワナモール"},
|
|
|
|
|
4: {"it-IT": "Pista Polposkate", "de-DE": "Punkasius-Skatepark", "en-US": "Blackbelly Skatepark", "es-ES": "Parque Lubina", "ja-JP": "Bバスパーク"},
|
|
|
|
|
5: {"it-IT": "Campeggio Totan", "de-DE": "Camp Schützenfisch", "en-US": "Camp Triggerfish", "es-ES": "Campamento Arowana", "ja-JP": "モンガラキャンプ場"},
|
|
|
|
|
6: {"it-IT": "Porto Polpo", "de-DE": "Heilbutt-Hafen", "en-US": "Port Mackerel", "es-ES": "Puerto Jurel", "ja-JP": "ホッケふ頭"},
|
|
|
|
|
7: {"it-IT": "Serra di alghe", "de-DE": "Tümmlerkuppel", "en-US": "Kelp Dome", "es-ES": "Jardín botánico", "ja-JP": "モズク農園"},
|
|
|
|
|
8: {"it-IT": "Torri cittadine", "de-DE": "Muränentürme", "en-US": "Moray Towers", "es-ES": "Torres Merluza", "ja-JP": "タチウオパーキング"},
|
|
|
|
|
9: {"it-IT": "Molo Mollusco", "de-DE": "Blauflossen-Depot", "en-US": "Bluefin Depot", "es-ES": "Mina costera", "ja-JP": "ネギトロ炭鉱"},
|
|
|
|
|
10: {"it-IT": "Ponte Sgombro", "de-DE": "Makrelenbrücke", "en-US": "Hammerhead Bridge", "es-ES": "Puente Salmón", "ja-JP": "マサバ海峡大橋"},
|
|
|
|
|
11: {"it-IT": "Cime sogliolose", "de-DE": "Schollensiedlung", "en-US": "Flounder Heights", "es-ES": "Complejo Medusa", "ja-JP": "ヒラメが丘団地"},
|
|
|
|
|
12: {"it-IT": "Museo di Cefalò", "de-DE": "Pinakoithek", "en-US": "Museum d'Alfonsino", "es-ES": "Museo del Pargo", "ja-JP": "キンメダイ美術館"},
|
|
|
|
|
13: {"it-IT": "Acciugames", "de-DE": "Anchobit Games HQ", "en-US": "Ancho-V Games", "es-ES": "Estudios Esturión", "ja-JP": "アンチョビットゲームズ"},
|
|
|
|
|
14: {"it-IT": "Miniera d'Orata", "de-DE": "Steinköhler-Grube", "en-US": "Piranha Pit", "es-ES": "Cantera Tintorera", "ja-JP": "ショッツル鉱山"},
|
|
|
|
|
15: {"it-IT": "Villanguilla", "de-DE": "Mahi-Mahi Resort", "en-US": "Mahi-Mahi Resort", "es-ES": "Spa Cala Bacalao", "ja-JP": "マヒマヒリゾート&スパ"}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RULE_NAMES = {
|
|
|
|
|
"cVar": "SplatZones",
|
|
|
|
|
"cVgl": "Rainmaker",
|
|
|
|
|
"cVlf": "TowerControl",
|
|
|
|
|
"cPnt": "TurfWar"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@router.api_route('/boss', methods=['GET', 'POST'])
|
2026-03-25 00:31:32 +01:00
|
|
|
@cache(expire=3660)
|
2026-02-19 06:08:41 +01:00
|
|
|
async def boss_rotation(request: Request):
|
|
|
|
|
try:
|
2026-04-01 00:39:47 +02:00
|
|
|
def format_stages(stage_list):
|
|
|
|
|
formatted = []
|
|
|
|
|
for s in stage_list:
|
|
|
|
|
mid = s.get("MapID")
|
|
|
|
|
formatted.append({
|
|
|
|
|
"mapID": mid,
|
|
|
|
|
"translatedNames": MAP_DATA.get(mid, {"en-US": "Unknown Stage"})
|
|
|
|
|
})
|
|
|
|
|
return formatted
|
|
|
|
|
|
2026-04-21 06:09:22 +02:00
|
|
|
with open("boss.yaml", "r", encoding='utf-8') as f:
|
2026-02-19 06:08:41 +01:00
|
|
|
yaml_data = yaml.safe_load(f)
|
|
|
|
|
|
2026-03-21 17:12:06 +01:00
|
|
|
start_time = yaml_data.get("DateTime")
|
|
|
|
|
if isinstance(start_time, str):
|
|
|
|
|
start_time = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
|
|
|
|
|
|
|
|
|
|
current_rotation_start = start_time
|
2026-02-19 06:08:41 +01:00
|
|
|
rotations = {}
|
|
|
|
|
|
|
|
|
|
for phase in yaml_data.get("Phases", []):
|
2026-03-21 17:12:06 +01:00
|
|
|
ts_ms = str(int(current_rotation_start.timestamp() * 1000))
|
|
|
|
|
|
|
|
|
|
duration = phase.get("Time", 4)
|
2026-02-19 06:08:41 +01:00
|
|
|
rotations[ts_ms] = {
|
2026-03-21 17:12:06 +01:00
|
|
|
"startTime": current_rotation_start.isoformat(),
|
|
|
|
|
"duration": duration,
|
|
|
|
|
"turfStages": format_stages(phase.get("RegularStages", [])),
|
|
|
|
|
"rankedStages": format_stages(phase.get("GachiStages", [])),
|
2026-02-19 06:08:41 +01:00
|
|
|
"rankedMode": RULE_NAMES.get(phase.get("GachiRule"), "SplatZones")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 17:12:06 +01:00
|
|
|
current_rotation_start += timedelta(hours=duration)
|
2026-02-19 06:08:41 +01:00
|
|
|
|
|
|
|
|
response_data = {
|
|
|
|
|
"nintendo": {
|
|
|
|
|
"notice": "Nintendo Network has been shut down. Thanks for your interest."
|
|
|
|
|
},
|
|
|
|
|
"pretendo": {
|
|
|
|
|
"rotations": rotations
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-01 00:39:47 +02:00
|
|
|
|
|
|
|
|
try:
|
2026-04-21 06:09:22 +02:00
|
|
|
with open("fes_boss.yaml", "r", encoding='utf-8') as f:
|
2026-04-01 00:39:47 +02:00
|
|
|
fes_yaml = yaml.safe_load(f)
|
|
|
|
|
|
|
|
|
|
if fes_yaml:
|
|
|
|
|
time_cfg = fes_yaml.get("Time", {})
|
|
|
|
|
|
|
|
|
|
def parse_iso(iso_str):
|
|
|
|
|
if not iso_str: return None
|
|
|
|
|
return int(datetime.fromisoformat(iso_str.replace('Z', '+00:00')).timestamp())
|
|
|
|
|
|
|
|
|
|
fest_start = parse_iso(time_cfg.get("Start"))
|
|
|
|
|
fest_end = parse_iso(time_cfg.get("End"))
|
|
|
|
|
fest_announce = parse_iso(time_cfg.get("Announce"))
|
|
|
|
|
fest_result = parse_iso(time_cfg.get("Result"))
|
|
|
|
|
|
|
|
|
|
teams = fes_yaml.get("Teams", [])
|
|
|
|
|
team_shortnames = []
|
|
|
|
|
for team in teams:
|
|
|
|
|
team_shortnames.append(team.get("ShortName", {}))
|
|
|
|
|
|
|
|
|
|
response_data["splatfestivalSplatfest"] = {
|
|
|
|
|
"stages": format_stages(fes_yaml.get("Stages", [])),
|
|
|
|
|
"mode": RULE_NAMES.get(fes_yaml.get("Rule"), "TurfWar"),
|
|
|
|
|
"teams": team_shortnames,
|
|
|
|
|
"time": {
|
|
|
|
|
"start": fest_start,
|
|
|
|
|
"end": fest_end,
|
|
|
|
|
"annoucement": fest_announce,
|
|
|
|
|
"results": fest_result
|
|
|
|
|
},
|
|
|
|
|
"festivalID": fes_yaml.get("FestivalId", 0)
|
|
|
|
|
}
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
pass
|
2026-02-19 06:08:41 +01:00
|
|
|
|
2026-03-25 00:31:32 +01:00
|
|
|
return response_data
|
2026-02-19 06:08:41 +01:00
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return Response(content=json.dumps({"error": str(e)}), status_code=500, media_type="application/json")
|