From 889cfc799bc805a79b98ceb7e49f133d53927b6e Mon Sep 17 00:00:00 2001 From: "Mr.Xia" <1424473282@qq.com> Date: Mon, 6 Apr 2026 23:45:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=A7=AF=E5=88=86?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8F=92=E4=BB=B6=EF=BC=8C=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=A7=AF=E5=88=86=E6=9F=A5=E8=AF=A2=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增积分查询插件,包含我的积分、积分查询、积分排行和积分历史查询命令 - 支持群组和私聊场景,排行榜功能仅限群组使用 - 实现用户显示名称优先级(群昵称 > 昵称 > 用户ID) - 添加详细的帮助文档和使用说明 --- .../plugins/danding_points_query/README.md | 73 ++++++++ .../plugins/danding_points_query/__init__.py | 15 ++ .../plugins/danding_points_query/commands.py | 163 ++++++++++++++++++ .../plugins/danding_points_query/config.py | 6 + 4 files changed, 257 insertions(+) create mode 100644 danding_bot/plugins/danding_points_query/README.md create mode 100644 danding_bot/plugins/danding_points_query/__init__.py create mode 100644 danding_bot/plugins/danding_points_query/commands.py create mode 100644 danding_bot/plugins/danding_points_query/config.py diff --git a/danding_bot/plugins/danding_points_query/README.md b/danding_bot/plugins/danding_points_query/README.md new file mode 100644 index 0000000..9c7fffe --- /dev/null +++ b/danding_bot/plugins/danding_points_query/README.md @@ -0,0 +1,73 @@ +# Danding Points Query 插件 + +用户交互层,提供积分查询相关的命令。 + +## 命令列表 + +### 我的积分 +查询当前用户的积分余额。 + +``` +我的积分 +``` + +**示例输出:** +``` +张三 的积分余额:1500 +``` + +### 积分查询 +查询指定用户的积分余额。支持 @用户 或输入用户ID。 + +``` +积分查询 @用户 +积分查询 123456789 +``` + +**示例输出:** +``` +李四 的积分余额:2000 +``` + +### 积分排行 +显示积分排行榜前10名,按积分从高到低排序。仅在群组中可用。 + +``` +积分排行 +``` + +**示例输出:** +``` +🏆 积分排行榜 TOP 10 + +#1 张三 5000 分 +#2 李四 4500 分 +#3 王五 4000 分 +... +``` + +### 积分历史查询 +查询用户最近5条积分变动记录。支持 @用户、输入用户ID,或不输入参数查询自己的记录。 + +``` +积分历史查询 +积分历史查询 @用户 +积分历史查询 123456789 +``` + +**示例输出:** +``` +📊 张三 的积分变动记录(最近5条) + +2026-04-06 10:30:45 +100 余额: 5000 赛马冠军奖励 +2026-04-06 10:25:30 +50 余额: 4900 赛马参赛奖励 +2026-04-06 10:20:15 -100 余额: 4850 赛马下注 +... +``` + +## 实现细节 + +- 所有命令都支持群组和私聊 +- 排行榜命令仅在群组中可用 +- 用户显示优先级:群昵称 > 昵称 > 用户ID +- 积分变动记录按时间倒序显示(最新的在前) diff --git a/danding_bot/plugins/danding_points_query/__init__.py b/danding_bot/plugins/danding_points_query/__init__.py new file mode 100644 index 0000000..498d359 --- /dev/null +++ b/danding_bot/plugins/danding_points_query/__init__.py @@ -0,0 +1,15 @@ +from nonebot.plugin import PluginMetadata +from .config import Config + +__plugin_meta__ = PluginMetadata( + name="Danding Points Query", + description="User-facing commands for querying points/积分", + usage="Commands: 我的积分, 积分查询, 积分排行, 积分历史查询", + type="application", + config=Config, + extra={ + "required_plugins": ["danding_bot.plugins.danding_points"], + }, +) + +from . import commands # noqa: F401, E402 diff --git a/danding_bot/plugins/danding_points_query/commands.py b/danding_bot/plugins/danding_points_query/commands.py new file mode 100644 index 0000000..9cf077e --- /dev/null +++ b/danding_bot/plugins/danding_points_query/commands.py @@ -0,0 +1,163 @@ +from nonebot import on_command, require +from nonebot.adapters.onebot.v11 import Bot, Event, GroupMessageEvent, Message, MessageSegment +from nonebot.params import CommandArg + +require("danding_bot.plugins.danding_points") +from danding_bot.plugins.danding_points import points_api + +# Command handlers +help_cmd = on_command("积分帮助", priority=5) +my_points_cmd = on_command("我的积分", priority=5) +query_points_cmd = on_command("积分查询", priority=5) +ranking_cmd = on_command("积分排行", priority=5) +history_cmd = on_command("积分历史查询", priority=5) + + +async def _get_user_name(bot: Bot, event: Event, user_id: str) -> str: + """Get user display name (group card > nickname > user_id).""" + try: + if isinstance(event, GroupMessageEvent): + info = await bot.get_group_member_info( + group_id=event.group_id, user_id=int(user_id) + ) + return info.get("card") or info.get("nickname") or user_id + except Exception: + pass + return user_id + + +def _parse_at_user(message: Message) -> str | None: + """Extract user_id from @mention in message.""" + for segment in message: + if segment.type == "at": + return str(segment.data.get("qq")) + return None + + +@help_cmd.handle() +async def handle_help(): + """Show points system help.""" + help_text = """📚 积分系统帮助 + +【查询命令】 +• 我的积分 + 查询你的积分余额 + +• 积分查询 @用户 / 积分查询 用户ID + 查询指定用户的积分余额 + 例:积分查询 @张三 或 积分查询 123456789 + +• 积分排行 + 查看积分排行榜前10名(仅群组可用) + +• 积分历史查询 [@用户 / 用户ID] + 查询最近5条积分变动记录 + 例:积分历史查询(查自己) + 积分历史查询 @李四 + 积分历史查询 987654321 + +【积分来源】 +• 赛马参赛:获得参赛奖励 +• 赛马冠军:获得冠军奖励 +• 赛马下注:下注获胜可获得奖励 + +【积分用途】 +• 赛马下注:消费积分进行下注 + +【其他】 +• 积分帮助 + 显示此帮助信息""" + await help_cmd.finish(help_text) + + +@my_points_cmd.handle() +async def handle_my_points(bot: Bot, event: Event): + """Query current user's points.""" + user_id = str(event.user_id) + balance = await points_api.get_balance(user_id) + user_name = await _get_user_name(bot, event, user_id) + await my_points_cmd.finish(f"{user_name} 的积分余额:{balance}") + + +@query_points_cmd.handle() +async def handle_query_points(bot: Bot, event: Event, arg: Message = CommandArg()): + """Query specific user's points.""" + # Try to parse @mention first + user_id = _parse_at_user(arg) + + # If no @mention, try to parse user_id from text + if not user_id: + text = arg.extract_plain_text().strip() + if text.isdigit(): + user_id = text + else: + await query_points_cmd.finish("请输入用户ID或@用户") + return + + balance = await points_api.get_balance(user_id) + user_name = await _get_user_name(bot, event, user_id) + await query_points_cmd.finish(f"{user_name} 的积分余额:{balance}") + + +@ranking_cmd.handle() +async def handle_ranking(bot: Bot, event: Event): + """Query top 10 points ranking.""" + if not isinstance(event, GroupMessageEvent): + await ranking_cmd.finish("此命令仅在群组中可用") + return + + ranking = await points_api.get_ranking(limit=10, order_by="points") + + if not ranking: + await ranking_cmd.finish("暂无排行数据") + return + + lines = ["🏆 积分排行榜 TOP 10\n"] + for entry in ranking: + user_id = entry["user_id"] + user_name = await _get_user_name(bot, event, user_id) + points = entry["points"] + rank = entry["rank"] + lines.append(f"#{rank:2d} {user_name} {points} 分") + + await ranking_cmd.finish("\n".join(lines)) + + +@history_cmd.handle() +async def handle_history(bot: Bot, event: Event, arg: Message = CommandArg()): + """Query user's recent 5 point transactions.""" + # Try to parse @mention first + user_id = _parse_at_user(arg) + + # If no @mention, try to parse user_id from text or use current user + if not user_id: + text = arg.extract_plain_text().strip() + if text.isdigit(): + user_id = text + else: + user_id = str(event.user_id) + + transactions = await points_api.get_transactions(user_id, limit=5, offset=0) + + if not transactions: + user_name = await _get_user_name(bot, event, user_id) + await history_cmd.finish(f"{user_name} 暂无积分变动记录") + return + + user_name = await _get_user_name(bot, event, user_id) + lines = [f"📊 {user_name} 的积分变动记录(最近5条)\n"] + + for tx in transactions: + amount = tx["amount"] + balance_after = tx["balance_after"] + source = tx["source"] + reason = tx["reason"] or source + created_at = tx["created_at"] + + # Format amount with sign + amount_str = f"{amount:+d}" + lines.append( + f"{created_at} {amount_str:>6s} 余额: {balance_after} {reason}" + ) + + await history_cmd.finish("\n".join(lines)) diff --git a/danding_bot/plugins/danding_points_query/config.py b/danding_bot/plugins/danding_points_query/config.py new file mode 100644 index 0000000..20fc9f4 --- /dev/null +++ b/danding_bot/plugins/danding_points_query/config.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class Config(BaseModel): + """Configuration for danding_points_query plugin.""" + pass