feat(tools): add tools to add daily-challenge

This commit is contained in:
MingxuanGame
2025-08-20 08:59:14 +00:00
parent f1b0fa2c89
commit 6ac9a124ea
4 changed files with 104 additions and 23 deletions

View File

@@ -79,15 +79,13 @@ class Mod(TypedDict):
API_MODS: dict[Literal[0, 1, 2, 3], dict[str, Mod]] = {} API_MODS: dict[Literal[0, 1, 2, 3], dict[str, Mod]] = {}
mods_file = STATIC_DIR / "mods.json"
def init_mods(): raw_mods = json.loads(mods_file.read_text())
mods_file = STATIC_DIR / "mods.json" for ruleset in raw_mods:
raw_mods = json.loads(mods_file.read_text()) ruleset_mods = {}
for ruleset in raw_mods: for mod in ruleset["Mods"]:
ruleset_mods = {} ruleset_mods[mod["Acronym"]] = mod
for mod in ruleset["Mods"]: API_MODS[ruleset["RulesetID"]] = ruleset_mods
ruleset_mods[mod["Acronym"]] = mod
API_MODS[ruleset["RulesetID"]] = ruleset_mods
def int_to_mods(mods: int) -> list[APIMod]: def int_to_mods(mods: int) -> list[APIMod]:

View File

@@ -22,7 +22,7 @@ from app.dependencies.database import with_db
from app.dependencies.fetcher import get_fetcher from app.dependencies.fetcher import get_fetcher
from app.exception import InvokeException from app.exception import InvokeException
from .mods import APIMod from .mods import API_MODS, APIMod
from .room import ( from .room import (
DownloadState, DownloadState,
MatchType, MatchType,
@@ -121,19 +121,9 @@ class PlaylistItem(BaseModel):
star_rating: float star_rating: float
freestyle: bool freestyle: bool
def _get_api_mods(self):
from app.models.mods import API_MODS, init_mods
if not API_MODS:
init_mods()
return API_MODS
def _validate_mod_for_ruleset( def _validate_mod_for_ruleset(
self, mod: APIMod, ruleset_key: int, context: str = "mod" self, mod: APIMod, ruleset_key: int, context: str = "mod"
) -> None: ) -> None:
from typing import Literal, cast
API_MODS = self._get_api_mods()
typed_ruleset_key = cast(Literal[0, 1, 2, 3], ruleset_key) typed_ruleset_key = cast(Literal[0, 1, 2, 3], ruleset_key)
# Check if mod is valid for ruleset # Check if mod is valid for ruleset

View File

@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Literal, TypedDict, cast
from app.config import settings from app.config import settings
from .mods import API_MODS, APIMod, init_mods from .mods import API_MODS, APIMod
from pydantic import BaseModel, Field, ValidationInfo, field_validator from pydantic import BaseModel, Field, ValidationInfo, field_validator
@@ -193,8 +193,6 @@ class SoloScoreSubmissionInfo(BaseModel):
@field_validator("mods", mode="after") @field_validator("mods", mode="after")
@classmethod @classmethod
def validate_mods(cls, mods: list[APIMod], info: ValidationInfo): def validate_mods(cls, mods: list[APIMod], info: ValidationInfo):
if not API_MODS:
init_mods()
incompatible_mods = set() incompatible_mods = set()
# check incompatible mods # check incompatible mods
for mod in mods: for mod in mods:

View File

@@ -0,0 +1,95 @@
from __future__ import annotations
import datetime
import json
import os
import re
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from app.database import Beatmap
from app.dependencies.database import get_redis, with_db
from app.dependencies.fetcher import get_fetcher
from app.log import logger
from app.models.mods import APIMod, get_available_mods
from app.models.score import GameMode
logger.remove()
def mod_inp(name: str, default: list[APIMod]) -> list[APIMod]:
mods_inp = input(f"Enter {name} mods (JSON APIMod Array) >>> ")
while True:
if mods_inp.strip() == "":
mods = default
break
try:
mods = json.loads(mods_inp)
except json.JSONDecodeError:
mods_inp = input(f"Invalid input. Enter {name} mods (JSON APIMod) >>> ")
continue
if not isinstance(mods, list):
mods_inp = input(f"Invalid input. Enter {name} mods (JSON APIMod) >>> ")
continue
break
return mods
async def main():
async with with_db() as session:
redis = get_redis()
fetcher = await get_fetcher()
today = datetime.date.today()
input_date = input(f"Enter a date ({today}) >>> ")
while True:
if not input_date:
input_date = str(today)
elif not re.match(r"^\d{4}-\d{2}-\d{2}$", input_date):
input_date = input(f"Invalid date format. Enter a date ({today}) >>> ")
continue
break
beatmap_inp = input("Enter a beatmap ID >>> ")
while True:
if beatmap_inp.isdigit():
beatmap_id = int(beatmap_inp)
break
beatmap_inp = input("Invalid input. Enter a beatmap ID >>> ")
beatmap = await Beatmap.get_or_fetch(session, fetcher, beatmap_id)
ruleset_inp = input(f"Enter ruleset ID ({int(beatmap.mode)}) >>> ")
while True:
if not ruleset_inp:
ruleset_inp = str(int(beatmap.mode))
elif not ruleset_inp.isdigit():
ruleset_inp = input(
f"Invalid input. Enter ruleset ID ({int(beatmap.mode)}) >>> "
)
continue
ruleset_id = int(ruleset_inp)
if beatmap.mode != GameMode.OSU and ruleset_id != int(beatmap.mode):
ruleset_inp = input(
f"Invalid input. Enter ruleset ID ({int(beatmap.mode)}) >>> "
)
continue
break
required_mods = mod_inp("required", [])
allowed_mods = mod_inp("allowed", get_available_mods(ruleset_id, required_mods))
await redis.hset( # pyright: ignore[reportGeneralTypeIssues]
f"daily_challenge:{input_date}",
mapping={
"beatmap": beatmap.id,
"ruleset_id": ruleset_id,
"required_mods": json.dumps(required_mods),
"allowed_mods": json.dumps(allowed_mods),
},
)
if __name__ == "__main__":
import asyncio
asyncio.run(main())