Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simplify !with command argument parsing #356

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8965490
reworked !with command
minisbett Jan 9, 2023
b967689
Remove old parsing of !with
minisbett Jan 10, 2023
d37d112
Add acc validation
minisbett Jan 10, 2023
080cd56
Merge branch 'master' into with-rework
minisbett Jan 10, 2023
8ea50ab
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 10, 2023
a3f45b4
Merge branch 'osuAkatsuki:master' into with-rework
minisbett Jan 10, 2023
c3db130
Merge branch 'osuAkatsuki:master' into with-rework
minisbett Jan 13, 2023
7ff34ea
Fixed parameter handling in command
minisbett Jan 15, 2023
22f6346
Merge branch 'osuAkatsuki:master' into with-rework
minisbett Jan 15, 2023
e9799a5
Merge branch 'master' into with-rework
minisbett Jan 19, 2023
76a2fac
Merge branch 'master' into with-rework
NiceAesth Jan 31, 2023
643b029
Merge branch 'master' into with-rework
minisbett Sep 23, 2023
084d4ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 23, 2023
f7b8dd0
Merge branch 'osuAkatsuki:master' into with-rework
minisbett Sep 25, 2023
5047a14
Add mods to playr.last_np
minisbett Sep 25, 2023
42701ee
Merge branch 'master' into add-mods-to-np
minisbett Sep 25, 2023
e723e7b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 25, 2023
4488574
Merge remote-tracking branch 'origin/add-mods-to-np' into with-rework
minisbett Sep 25, 2023
3fe2a99
Bring code up to date, do further refactoring, add player.last_np["mo…
minisbett Sep 25, 2023
4c3a5b4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 25, 2023
6934419
Remove debug print
minisbett Sep 25, 2023
8d47f3f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 25, 2023
05bdd3c
Merge branch 'master' into with-rework
NiceAesth Nov 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/api/domains/cho.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,15 @@ async def handle(self, player: Player) -> None:
# use player mode if not specified
mode_vn = player.status.mode.as_vanilla

# parse the mods from regex
if r_match["mods"] is not None:
mods = Mods.from_np(r_match["mods"][1:], mode_vn)
else:
mods = None

player.last_np = {
"bmap": bmap,
"mods": mods,
"mode_vn": mode_vn,
"timeout": time.time() + 300, # /np's last 5mins
}
Expand Down Expand Up @@ -1214,9 +1221,16 @@ async def handle(self, player: Player) -> None:
# use player mode if not specified
mode_vn = player.status.mode.as_vanilla

# parse the mods from regex
if r_match["mods"] is not None:
mods = Mods.from_np(r_match["mods"][1:], mode_vn)
else:
mods = None

player.last_np = {
"bmap": bmap,
"mode_vn": mode_vn,
"mods": mods,
"timeout": time.time() + 300, # /np's last 5mins
}

Expand Down
117 changes: 35 additions & 82 deletions app/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,59 +422,6 @@ async def top(ctx: Context) -> str | None:
# TODO: !compare (compare to previous !last/!top post's map)


class ParsingError(str):
...


def parse__with__command_args(
mode: int,
args: Sequence[str],
) -> Mapping[str, Any] | ParsingError:
"""Parse arguments for the !with command."""

# tried to balance complexity vs correctness for this function
# TODO: it can surely be cleaned up further - need to rethink it?

if not args or len(args) > 4:
return ParsingError("Invalid syntax: !with <acc/nmiss/combo/mods ...>")

# !with 95% 1m 429x hddt
acc = mods = combo = nmiss = None

# parse acc, misses, combo and mods from arguments.
# tried to balance complexity vs correctness here
for arg in (str.lower(arg) for arg in args):
# mandatory suffix, combo & nmiss
if combo is None and arg.endswith("x") and arg[:-1].isdecimal():
combo = int(arg[:-1])
# if combo > bmap.max_combo:
# return "Invalid combo."
elif nmiss is None and arg.endswith("m") and arg[:-1].isdecimal():
nmiss = int(arg[:-1])
# TODO: store nobjects?
# if nmiss > bmap.combo:
# return "Invalid misscount."
else:
# optional prefix/suffix, mods & accuracy
arg_stripped = arg.removeprefix("+").removesuffix("%")
if mods is None and arg_stripped.isalpha() and len(arg_stripped) % 2 == 0:
mods = Mods.from_modstr(arg_stripped)
mods = mods.filter_invalid_combos(mode)
elif acc is None and arg_stripped.replace(".", "", 1).isdecimal():
acc = float(arg_stripped)
if not 0 <= acc <= 100:
return ParsingError("Invalid accuracy.")
else:
return ParsingError(f"Unknown argument: {arg}")

return {
"acc": acc,
"mods": mods,
"combo": combo,
"nmiss": nmiss,
}


@command(Privileges.UNRESTRICTED, aliases=["w"], hidden=True)
async def _with(ctx: Context) -> str | None:
"""Specify custom accuracy & mod combinations with `/np`."""
Expand All @@ -490,46 +437,52 @@ async def _with(ctx: Context) -> str | None:
if not await ensure_local_osu_file(osu_file_path, bmap.id, bmap.md5):
return "Mapfile could not be found; this incident has been reported."

mode_vn = ctx.player.last_np["mode_vn"]

command_args = parse__with__command_args(mode_vn, ctx.args)
if isinstance(command_args, ParsingError):
return str(command_args)

msg_fields = []

score_args = ScoreParams(mode=mode_vn)
score_args = ScoreParams(mode=ctx.player.last_np["mode_vn"])
attributes_table = {
"xgeki": "ngeki",
"xkatu": "nkatu",
"x100": "n100",
"x50": "n50",
"x": "combo",
"m": "nmiss",
"%": "acc",
}

mods = command_args["mods"]
if mods is not None:
score_args.mods = mods
msg_fields.append(f"{mods!r}")
# if the last np contains mod info, use it as the default
if ctx.player.last_np["mods"] is not None:
score_args.mods = ctx.player.last_np["mods"]

nmiss = command_args["nmiss"]
if nmiss:
score_args.nmiss = nmiss
msg_fields.append(f"{nmiss}m")
for arg in (arg.lower() for arg in ctx.args):
try:
# handle mods extra
if arg.startswith("+"):
score_args.mods = Mods.from_modstr(arg[1:]).filter_invalid_combos(
ctx.player.last_np["mode_vn"],
)
continue

combo = command_args["combo"]
if combo is not None:
score_args.combo = combo
msg_fields.append(f"{combo}x")
for suffix, attribute in attributes_table.items():
if arg.endswith(suffix):
valueStr = arg[: -len(suffix)]
value = (
min(max(float(valueStr), 0), 100)
if attribute == "acc"
else int(valueStr)
)
setattr(score_args, attribute, value)

acc = command_args["acc"]
if acc is not None:
score_args.acc = acc
msg_fields.append(f"{acc:.2f}%")
except ValueError as ex:
return f"Could not parse parameter '{arg}'."

result = app.usecases.performance.calculate_performances(
osu_file_path=str(osu_file_path),
scores=[score_args], # calculate one score
)

return "{msg}: {pp:.2f}pp ({stars:.2f}*)".format(
msg=" ".join(msg_fields),
return "{pp:.2f}pp ({stars:.2f}*)".format(
pp=result[0]["performance"]["pp"],
stars=result[0]["difficulty"]["stars"], # (first score result)
)
stars=result[0]["difficulty"]["stars"],
) # (first score result)


@command(Privileges.UNRESTRICTED, aliases=["req"])
Expand Down
Loading