diff --git a/konabot/docs/concepts/中间答案.txt b/konabot/docs/concepts/中间答案.txt index 23ddaae..67c9e0b 100644 --- a/konabot/docs/concepts/中间答案.txt +++ b/konabot/docs/concepts/中间答案.txt @@ -7,3 +7,5 @@ - 例如:/^commit$/ 会匹配 commit,不会匹配 acommit、Commit 等。 - 而如果是 /commit/,则会匹配 commit、acommit,而不会匹配 Commit。 - 无法使用 Javascript 的字符串声明模式,例如,/case_insensitive/i 就不会被视作一个正则表达式。 + +一个提示是提示,还是中间答案,取决于它是否有 checkpoint 标记。如果有 checkpoint 标记,则会提示用户「你回答了一个中间答案」,并且这个中间答案的回答会在排行榜中显示。 diff --git a/konabot/plugins/kona_ph/core/message.py b/konabot/plugins/kona_ph/core/message.py index 72aa92a..b4833cf 100644 --- a/konabot/plugins/kona_ph/core/message.py +++ b/konabot/plugins/kona_ph/core/message.py @@ -63,7 +63,7 @@ def get_submission_message( hint_msg = "✨ 恭喜!这是本题的中间答案,加油!" else: hint_msg = "🤔 答错啦!请检查你的答案。" - return f"{hint_msg}\n\n提示:{hint.hint}" + return f"{hint_msg}\n\n提示:{hint.message}" return "❌ 答错啦!请检查你的答案。" @@ -162,3 +162,15 @@ def get_puzzle_info_message(manager: PuzzleManager, puzzle: Puzzle) -> UniMessag msg = msg.text(f"\n---------\n使用 `konaph ready {puzzle.raw_id}` 完成编辑") return msg + + +def get_puzzle_hint_list(puzzle: Puzzle) -> str: + msg = f"==== {puzzle.title} 提示与中间答案 ====\n" + if len(puzzle.hints) == 0: + msg += "\n你没有添加任何中间答案。" + return msg + for hint_id, hint in puzzle.hints.items(): + n = {False: "[提示]", True: "[中间答案]"}[hint.is_checkpoint] + msg += f"\n{n}[{hint_id}] {hint.pattern}" + msg += f"\n {hint.message}" + return msg diff --git a/konabot/plugins/kona_ph/core/storage.py b/konabot/plugins/kona_ph/core/storage.py index cca4ce6..0c6cac3 100644 --- a/konabot/plugins/kona_ph/core/storage.py +++ b/konabot/plugins/kona_ph/core/storage.py @@ -12,7 +12,7 @@ from konabot.plugins.kona_ph.core.path import KONAPH_DATA_JSON class PuzzleHint(BaseModel): pattern: str - hint: str + message: str is_checkpoint: bool @@ -59,13 +59,22 @@ class Puzzle(BaseModel): time=time, ) for hint_id, hint in self.hints.items(): - if re.match(hint.pattern, submission): - return PuzzleSubmission( - success=False, - flag=submission, - time=time, - hint_id=hint_id, - ) + if hint.pattern.startswith('/') and hint.pattern.endswith('/'): + if re.match(hint.pattern.strip('/'), submission): + return PuzzleSubmission( + success=False, + flag=submission, + time=time, + hint_id=hint_id, + ) + else: + if hint.pattern == submission: + return PuzzleSubmission( + success=False, + flag=submission, + time=time, + hint_id=hint_id, + ) return PuzzleSubmission( success=False, flag=submission, diff --git a/konabot/plugins/kona_ph/manager.py b/konabot/plugins/kona_ph/manager.py index bf30bb1..be9b5ba 100644 --- a/konabot/plugins/kona_ph/manager.py +++ b/konabot/plugins/kona_ph/manager.py @@ -3,18 +3,22 @@ from math import ceil from nonebot import get_plugin_config from nonebot_plugin_alconna import (Alconna, Args, Image, Option, Query, - Subcommand, SubcommandResult, UniMessage, on_alconna) + Subcommand, SubcommandResult, UniMessage, + on_alconna) from pydantic import BaseModel from konabot.common.longtask import DepLongTaskTarget +from konabot.common.nb.exc import BotExceptionMessage from konabot.common.nb.extract_image import download_image_bytes from konabot.common.nb.qq_broadcast import qq_broadcast from konabot.common.username import get_username from konabot.plugins.kona_ph.core.image import get_image_manager -from konabot.plugins.kona_ph.core.message import (get_puzzle_description, +from konabot.plugins.kona_ph.core.message import (get_puzzle_description, get_puzzle_hint_list, get_puzzle_info_message, get_submission_message) -from konabot.plugins.kona_ph.core.storage import get_today_date, puzzle_manager +from konabot.plugins.kona_ph.core.storage import (Puzzle, PuzzleHint, PuzzleManager, + get_today_date, + puzzle_manager) PUZZLE_PAGE_SIZE = 10 @@ -36,6 +40,17 @@ def is_puzzle_admin(target: DepLongTaskTarget): return target.target_id in config.plugin_puzzle_admin +def check_puzzle(manager: PuzzleManager, target: DepLongTaskTarget, raw_id: str) -> Puzzle: + if raw_id not in manager.puzzle_data: + raise BotExceptionMessage("没有这个谜题") + puzzle = manager.puzzle_data[raw_id] + if is_puzzle_admin(target): + return puzzle + if target.target_id != puzzle.author_id: + raise BotExceptionMessage("你没有权限查看或编辑这个谜题") + return puzzle + + def create_admin_commands(): cmd_admin = on_alconna( Alconna( @@ -73,21 +88,21 @@ def create_admin_commands(): "add", Args["raw_id", str], Args["pattern", str], - Args["hint", str], + Args["message", str], dest="add", ), Subcommand( "list", Args["raw_id", str], Args["page?", int], - dest="get", + dest="list", ), Subcommand( "modify", Args["raw_id", str], Args["hint_id", int], Option("--pattern", Args["pattern", str], alias=["-p"]), - Option("--hint", Args["hint", str], alias=["-h"]), + Option("--message", Args["message", str], alias=["-m"]), Option("--checkpoint", Args["is_checkpoint", bool], alias=["-c"]), dest="modify", ), @@ -140,15 +155,7 @@ def create_admin_commands(): @cmd_admin.assign("ready") async def _(raw_id: str, target: DepLongTaskTarget): async with puzzle_manager() as manager: - if raw_id not in manager.puzzle_data: - return await target.send_message(UniMessage.text( - "你输入的谜题不存在!输入 `konaph my` 查看你创建的谜题" - )) - p = manager.puzzle_data[raw_id] - if p.author_id != target.target_id and not is_puzzle_admin(target): - return await target.send_message(UniMessage.text( - "这不是你的题,你没有权限编辑!输入 `konaph my` 查看你创建的谜题" - )) + p = check_puzzle(manager, target, raw_id) if p.ready: return await target.send_message(UniMessage.text( "题目早就准备好啦!" @@ -161,15 +168,7 @@ def create_admin_commands(): @cmd_admin.assign("unready") async def _(raw_id: str, target: DepLongTaskTarget): async with puzzle_manager() as manager: - if raw_id not in manager.puzzle_data: - return await target.send_message(UniMessage.text( - "你输入的谜题不存在!输入 `konaph my` 查看你创建的谜题" - )) - p = manager.puzzle_data[raw_id] - if p.author_id != target.target_id and not is_puzzle_admin(target): - return await target.send_message(UniMessage.text( - "这不是你的题,你没有权限编辑!输入 `konaph my` 查看你创建的谜题" - )) + p = check_puzzle(manager, target, raw_id) if not p.ready: return await target.send_message(UniMessage.text( f"谜题「{p.title}」已经是未取消状态了!" @@ -187,16 +186,7 @@ def create_admin_commands(): @cmd_admin.assign("info") async def _(raw_id: str, target: DepLongTaskTarget): async with puzzle_manager() as manager: - if raw_id not in manager.puzzle_data: - return await target.send_message(UniMessage.text( - "你输入的谜题不存在!输入 `konaph my` 查看你创建的谜题" - )) - p = manager.puzzle_data[raw_id] - if p.author_id != target.target_id and not is_puzzle_admin(target): - return await target.send_message(UniMessage.text( - "这不是你的题,你没有权限查看详细信息!" - )) - + p = check_puzzle(manager, target, raw_id) await target.send_message(get_puzzle_info_message(manager, p)) @cmd_admin.assign("my") @@ -232,7 +222,9 @@ def create_admin_commands(): @cmd_admin.assign("all") async def _(target: DepLongTaskTarget, ready: Query[bool] = Query("all.ready"), page: int = 1): if not is_puzzle_admin(target): - return await target.send_message(UniMessage.text("你没有权限查看所有的哦")) + return await target.send_message(UniMessage.text( + "你没有权限使用该指令" + )) async with puzzle_manager() as manager: puzzles = [*manager.puzzle_data.values()] if ready.available: @@ -314,11 +306,7 @@ def create_admin_commands(): image_manager = get_image_manager() async with puzzle_manager() as manager: - if raw_id not in manager.puzzle_data: - return await target.send_message("没有这个谜题") - p = manager.puzzle_data[raw_id] - if not is_puzzle_admin(target) and target.target_id != p.author_id: - return await target.send_message("你没有权限编辑这个谜题") + p = check_puzzle(manager, target, raw_id) if title is not None: p.title = title if description is not None: @@ -342,6 +330,10 @@ def create_admin_commands(): @cmd_admin.assign("publish") async def _(target: DepLongTaskTarget, raw_id: str | None = None): + if not is_puzzle_admin(target): + return await target.send_message(UniMessage.text( + "你没有权限使用该指令" + )) today = get_today_date() async with puzzle_manager() as manager: if today in manager.daily_puzzle_of_date: @@ -358,12 +350,8 @@ def create_admin_commands(): @cmd_admin.assign("preview") async def _(target: DepLongTaskTarget, raw_id: str): async with puzzle_manager() as manager: - puzzle = manager.puzzle_data.get(raw_id) - if puzzle is None: - return await target.send_message("没有这个谜题") - if not is_puzzle_admin(target) and target.target_id != puzzle.author_id: - return await target.send_message("你没有权限预览这个谜题") - return await target.send_message(get_puzzle_description(puzzle)) + p = check_puzzle(manager, target, raw_id) + return await target.send_message(get_puzzle_description(p)) @cmd_admin.assign("get-submits") async def _(target: DepLongTaskTarget, raw_id: str): @@ -387,17 +375,10 @@ def create_admin_commands(): 测试一道谜题的回答,并给出结果 """ async with puzzle_manager() as manager: - puzzle = manager.puzzle_data.get(raw_id) - if puzzle is None: - return await target.send_message("没有这个谜题") - if not is_puzzle_admin(target) and target.target_id != puzzle.author_id: - return await target.send_message("你没有权限预览这个谜题") - - result = puzzle.check_submission(submission) - msg = get_submission_message(puzzle, result) - return await target.send_message( - UniMessage.text("[测试提交] ") + msg - ) + p = check_puzzle(manager, target, raw_id) + result = p.check_submission(submission) + msg = get_submission_message(p, result) + return await target.send_message("[测试提交] " + msg) @cmd_admin.assign("subcommands.hint") async def _(target: DepLongTaskTarget, subcommands: Query[SubcommandResult] = Query("subcommands.hint")): @@ -409,15 +390,75 @@ def create_admin_commands(): .text("- konaph hint add \n - 添加一个提示 / 中间答案\n") .text("- konaph hint modify \n") .text(" - --pattern \n - 更改匹配规则\n") - .text(" - --hint \n - 更改提示文本\n") + .text(" - --message \n - 更改提示文本\n") .text(" - --checkpoint [True|False]\n - 更改是否为中间答案\n") .text("- konaph hint delete \n - 删除一个提示 / 中间答案\n") .text("\n更多关于 pattern 和中间答案的信息,请见 man:中间答案(7)") ) - @cmd_admin.assign("hint.add") - async def _(target: DepLongTaskTarget): - await target.send_message("114514") + @cmd_admin.assign("subcommands.hint.add") + async def _( + target: DepLongTaskTarget, + raw_id: str, + pattern: str, + message: str, + ): + async with puzzle_manager() as manager: + p = check_puzzle(manager, target, raw_id) + p.hints[p.hint_id_max + 1] = PuzzleHint( + pattern=pattern, + message=message, + is_checkpoint=False, + ) + await target.send_message("创建成功!\n\n" + get_puzzle_hint_list(p)) + + @cmd_admin.assign("subcommands.hint.list") + async def _( + target: DepLongTaskTarget, + raw_id: str, + ): + async with puzzle_manager() as manager: + p = check_puzzle(manager, target, raw_id) + await target.send_message(get_puzzle_hint_list(p)) + + @cmd_admin.assign("subcommands.hint.modify") + async def _( + target: DepLongTaskTarget, + raw_id: str, + hint_id: int, + pattern: str | None = None, + message: str | None = None, + is_checkpoint: bool | None = None, + ): + async with puzzle_manager() as manager: + p = check_puzzle(manager, target, raw_id) + if hint_id not in p.hints: + raise BotExceptionMessage( + f"没有这个 hint_id。请使用 konaph hint list {raw_id} 了解 hint 清单" + ) + hint = p.hints[hint_id] + if pattern is not None: + hint.pattern = pattern + if message is not None: + hint.message = message + if is_checkpoint is not None: + hint.is_checkpoint = is_checkpoint + await target.send_message("更改成功!\n\n" + get_puzzle_hint_list(p)) + + @cmd_admin.assign("subcommands.hint.delete") + async def _( + target: DepLongTaskTarget, + raw_id: str, + hint_id: int, + ): + async with puzzle_manager() as manager: + p = check_puzzle(manager, target, raw_id) + if hint_id not in p.hints: + raise BotExceptionMessage( + f"没有这个 hint_id。请使用 konaph hint list {raw_id} 了解 hint 清单" + ) + del p.hints[hint_id] + await target.send_message("删除成功!\n\n" + get_puzzle_hint_list(p)) return cmd_admin