From 8d4f47417b99fc0493e13c50371ece66338d97a6 Mon Sep 17 00:00:00 2001 From: R1Yue Date: Thu, 28 May 2026 13:17:15 +0800 Subject: [PATCH 1/2] feat: fix Stats DoesNotExist, add -r/-p params to actions - Fix Stats.get_or_create to handle missing records - Add -r (replace suffix) parameter to /actions and \reverse_actions - Add -p (replace punctuation) parameter to /actions and \reverse_actions --- adapters/db/stats.py | 12 +++---- core/actions.py | 76 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/adapters/db/stats.py b/adapters/db/stats.py index 2319780..f94f37c 100644 --- a/adapters/db/stats.py +++ b/adapters/db/stats.py @@ -2,17 +2,17 @@ async def get_group_stats(chat_id: int) -> dict[str, dict]: """Retrieve statistics for a specific group chat by chat_id.""" - stats = await Stats.get(chat_id=chat_id).values() + stats, _ = await Stats.get_or_create(chat_id=chat_id) return stats async def get_24h_message_stats(chat_id: int) -> dict: """Retrieve 24-hour message statistics for a specific group chat.""" - stats = await Stats.get(chat_id=chat_id) + stats, _ = await Stats.get_or_create(chat_id=chat_id) return stats.messages_24h or {} async def get_user_stats(chat_id: int, user_id: int) -> dict: """Retrieve statistics for a specific user in a group chat.""" - stats = await Stats.get(chat_id=chat_id) + stats, _ = await Stats.get_or_create(chat_id=chat_id) users_data = stats.users if users_data and str(user_id) in users_data: return users_data[str(user_id)] @@ -21,7 +21,7 @@ async def get_user_stats(chat_id: int, user_id: int) -> dict: async def update_group_stats(chat_id: int, user_id: int) -> None: """Update statistics for a specific group chat.""" from datetime import datetime - stats = await Stats.get(chat_id=chat_id) + stats, _ = await Stats.get_or_create(chat_id=chat_id) # JSON 只能使用字符串作为键,所以将 user_id 转换为字符串 uid = str(user_id) stats.total_messages = (stats.total_messages or 0) + 1 @@ -34,13 +34,13 @@ async def update_group_stats(chat_id: int, user_id: int) -> None: async def update_24h_message(chat_id: int, data: dict) -> None: """Update the 24-hour message count for a specific group chat.""" - stats = await Stats.get(chat_id=chat_id) + stats, _ = await Stats.get_or_create(chat_id=chat_id) stats.messages_24h = data await stats.save() async def update_user_stats(chat_id: int, user_id: int, username: str, name: str,attr: None | str) -> None: """Update statistics for a specific user in a group chat.""" - stats = await Stats.get(chat_id=chat_id) + stats, _ = await Stats.get_or_create(chat_id=chat_id) users_data = stats.users or {} uid = str(user_id) user_data = users_data.get(uid, {}) diff --git a/core/actions.py b/core/actions.py index 94b8da0..490e126 100644 --- a/core/actions.py +++ b/core/actions.py @@ -6,6 +6,54 @@ import logging +def _parse_action(parts: list[str], default_ending: str = "了") -> tuple[str, str, str | None, str]: + """解析动作参数,支持 -r(替换后缀)和 -p(替换标点)。 + + -r:无参数删除默认后缀,有参数将默认后缀替换为参数。 + -p:无参数删除标点,有参数将默认标点替换为参数。 + + Returns: + (to_remove, replace_with, punctuation, content) + punctuation: None=默认"!",""=无标点,其他=自定义标点 + """ + # 分离 -r 和 -p 部分 + p_args = [] + has_p = "-p" in parts + if has_p: + p_idx = parts.index("-p") + p_args = parts[p_idx + 1:] + parts = parts[:p_idx] + + r_args = [] + if "-r" in parts: + r_idx = parts.index("-r") + content = " ".join(parts[:r_idx]) + r_args = parts[r_idx + 1:] + else: + content = " ".join(parts) + + # 处理 -r + if "-r" not in parts: + to_remove = "" + replace_with = default_ending + elif not r_args: + to_remove = default_ending + replace_with = "" + else: + to_remove = default_ending + replace_with = " ".join(r_args) + + # 处理 -p + if not has_p: + punctuation = None + elif not p_args: + punctuation = "" + else: + punctuation = " ".join(p_args) + + return (to_remove, replace_with, punctuation, content) + + async def handle_actions(message: Message) -> None: if not await config.is_feature_enabled('actions', message.chat.id): logging.debug(f"收到了命中 / 开头的的消息,但是 actions 功能未启用,跳过处理") @@ -24,9 +72,18 @@ async def handle_actions(message: Message) -> None: from_user = message.from_user.mention_html(message.sender_chat.title) if message.sender_chat else message.from_user.mention_html() replied_user = message.reply_to_message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else (message.reply_to_message.from_user.mention_html() if message.reply_to_message else None) if " " in rawtext: - if rawtext.split(" ")[0].replace('/','',1).isascii(): + parts = rawtext.split(" ") + if parts[0].replace('/','',1).isascii(): return - await message.reply(f"{from_user} {rawtext.split(" ")[0].replace('/','')}了 {replied_user if message.reply_to_message and replied_user != from_user else '自己' } {''.join(rawtext.split(" ")[1:])}!",disable_web_page_preview=True) + verb = parts[0].replace('/', '') + args = parts[1:] + to_remove, replace_with, punctuation, content = _parse_action(args) + verb = verb.removesuffix(to_remove) + replace_with + punc = "!" if punctuation is None else punctuation + if content: + await message.reply(f"{from_user} {verb} {replied_user if message.reply_to_message and replied_user != from_user else '自己'} {content}{punc}", disable_web_page_preview=True) + else: + await message.reply(f"{from_user} {verb} {replied_user if message.reply_to_message and replied_user != from_user else '自己'}{punc}", disable_web_page_preview=True) else: await message.reply(f"{from_user} {message.text.replace('/','')}了 {replied_user if message.reply_to_message and replied_user != from_user else '自己'}!",disable_web_page_preview=True) @@ -37,4 +94,17 @@ async def handle_reverse_actions(message: Message) -> None: logging.debug(f"收到了命中 \\ 开头的的消息,但是 actions 功能未启用,跳过处理") return logging.debug(f"收到了命中 \\ 开头的消息: {message.text}") - await message.reply(f"{from_user} 被 {replied_user if message.reply_to_message and replied_user != from_user else '自己'} {message.text.replace('\\','')}了!",disable_web_page_preview=True) \ No newline at end of file + rawtext = message.text + if " " in rawtext: + parts = rawtext.split(" ") + verb = parts[0].replace('\\', '') + args = parts[1:] + to_remove, replace_with, punctuation, content = _parse_action(args) + verb = verb.removesuffix(to_remove) + replace_with + punc = "!" if punctuation is None else punctuation + if content: + await message.reply(f"{from_user} 被 {replied_user if message.reply_to_message and replied_user != from_user else '自己'} {verb} {content}{punc}", disable_web_page_preview=True) + else: + await message.reply(f"{from_user} 被 {replied_user if message.reply_to_message and replied_user != from_user else '自己'} {verb}{punc}", disable_web_page_preview=True) + else: + await message.reply(f"{from_user} 被 {replied_user if message.reply_to_message and replied_user != from_user else '自己'} {message.text.replace('\\','')}了!",disable_web_page_preview=True) \ No newline at end of file From e30775d008b9dc4bf9b631775a5b3984594a53cb Mon Sep 17 00:00:00 2001 From: R1Yue Date: Fri, 29 May 2026 02:10:21 +0000 Subject: [PATCH 2/2] fix: actions/reverse_actions not working when replying to anonymous users --- core/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/actions.py b/core/actions.py index 490e126..ef1f131 100644 --- a/core/actions.py +++ b/core/actions.py @@ -70,7 +70,7 @@ async def handle_actions(message: Message) -> None: return from_user = message.from_user.mention_html(message.sender_chat.title) if message.sender_chat else message.from_user.mention_html() - replied_user = message.reply_to_message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else (message.reply_to_message.from_user.mention_html() if message.reply_to_message else None) + replied_user = message.reply_to_message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else (message.reply_to_message.from_user.mention_html() if message.reply_to_message and message.reply_to_message.from_user else None) if " " in rawtext: parts = rawtext.split(" ") if parts[0].replace('/','',1).isascii(): @@ -89,7 +89,7 @@ async def handle_actions(message: Message) -> None: async def handle_reverse_actions(message: Message) -> None: from_user = message.from_user.mention_html(message.sender_chat.title) if message.sender_chat else message.from_user.mention_html() - replied_user = message.reply_to_message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else message.reply_to_message.from_user.mention_html() + replied_user = message.reply_to_message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else (message.reply_to_message.from_user.mention_html() if message.reply_to_message and message.reply_to_message.from_user else None) if not await config.is_feature_enabled('actions', message.chat.id): logging.debug(f"收到了命中 \\ 开头的的消息,但是 actions 功能未启用,跳过处理") return