diff --git a/app/fetcher/__init__.py b/app/fetcher/__init__.py index 6d8e46d..a2390f8 100644 --- a/app/fetcher/__init__.py +++ b/app/fetcher/__init__.py @@ -1,8 +1,8 @@ from __future__ import annotations from .beatmap import BeatmapFetcher +from .beatmap_raw import BeatmapRawFetcher from .beatmapset import BeatmapsetFetcher -from .osu_dot_direct import BeatmapRawFetcher class Fetcher(BeatmapFetcher, BeatmapsetFetcher, BeatmapRawFetcher): diff --git a/app/fetcher/osu_dot_direct.py b/app/fetcher/beatmap_raw.py similarity index 86% rename from app/fetcher/osu_dot_direct.py rename to app/fetcher/beatmap_raw.py index 529eae4..f650df3 100644 --- a/app/fetcher/osu_dot_direct.py +++ b/app/fetcher/beatmap_raw.py @@ -2,7 +2,7 @@ from __future__ import annotations from ._base import BaseFetcher -from httpx import AsyncClient +from httpx import AsyncClient, HTTPError from httpx._models import Response from loguru import logger import redis.asyncio as redis @@ -22,12 +22,10 @@ class BeatmapRawFetcher(BaseFetcher): f"[BeatmapRawFetcher] get_beatmap_raw: {req_url}" ) resp = await self._request(req_url) - if resp.status_code == 429: + if resp.status_code >= 400: continue - elif resp.status_code < 400: - return resp.text - else: - resp.raise_for_status() + return resp.text + raise HTTPError("Failed to fetch beatmap") async def _request(self, url: str) -> Response: async with AsyncClient() as client: diff --git a/app/router/chat/banchobot.py b/app/router/chat/banchobot.py index 65f1c8b..104e958 100644 --- a/app/router/chat/banchobot.py +++ b/app/router/chat/banchobot.py @@ -16,7 +16,7 @@ from app.database.score import Score from app.database.statistics import UserStatistics, get_rank from app.dependencies.fetcher import get_fetcher from app.exception import InvokeException -from app.models.mods import APIMod +from app.models.mods import APIMod, mod_to_save from app.models.multiplayer_hub import ( ChangeTeamRequest, ServerMultiplayerRoom, @@ -30,7 +30,8 @@ from app.signalr.hub.hub import Client from .server import server from httpx import HTTPError -from sqlmodel import func, select +from sqlalchemy.orm import joinedload +from sqlmodel import col, func, select from sqlmodel.ext.asyncio.session import AsyncSession HandlerResult = str | None | Awaitable[str | None] @@ -571,3 +572,57 @@ async def _mp(user: User, args: list[str], session: AsyncSession, channel: ChatC return f"No such command: {command}" return await _MP_COMMANDS[command](signalr_client, room, args[1:], session) + + +async def _score( + user_id: int, + session: AsyncSession, + include_fail: bool = False, + gamemode: GameMode | None = None, +) -> str: + q = ( + select(Score) + .where(Score.user_id == user_id) + .order_by(col(Score.id).desc()) + .options(joinedload(Score.beatmap)) + ) + if not include_fail: + q = q.where(Score.passed.is_(True)) + if gamemode is not None: + q = q.where(Score.gamemode == gamemode) + + score = (await session.exec(q)).first() + if score is None: + return "You have no scores." + + result = f"""{score.beatmap.beatmapset.title} [{score.beatmap.version}] ({score.gamemode.name.lower()}) +Played at {score.started_at} +{score.pp:.2f}pp {score.accuracy:.2%} {",".join(mod_to_save(score.mods))} {score.rank.name.upper()} +Great: {score.n300}, Good: {score.n100}, Meh: {score.n50}, Miss: {score.nmiss}""" # noqa: E501 + if score.gamemode == GameMode.MANIA: + keys = next( + (mod["acronym"] for mod in score.mods if mod["acronym"].endswith("K")), None + ) + if keys is None: + keys = f"{int(score.beatmap.cs)}K" + p_d_g = f"{score.ngeki / score.n300:.2f}:1" if score.n300 > 0 else "inf:1" + result += ( + f"\nKeys: {keys}, Perfect: {score.ngeki}, Ok: {score.nkatu}, P/G: {p_d_g}" + ) + return result + + +@bot.command("re") +async def _re(user: User, args: list[str], session: AsyncSession, channel: ChatChannel): + gamemode = None + if len(args) >= 1: + gamemode = GameMode.parse(args[0]) + return await _score(user.id, session, include_fail=True, gamemode=gamemode) + + +@bot.command("pr") +async def _pr(user: User, args: list[str], session: AsyncSession, channel: ChatChannel): + gamemode = None + if len(args) >= 1: + gamemode = GameMode.parse(args[0]) + return await _score(user.id, session, include_fail=False, gamemode=gamemode)