From 822a4c4c2e3ca8bd80b048a761d94ede9873f6f2 Mon Sep 17 00:00:00 2001 From: This-is-XiaoDeng <1744793737@qq.com> Date: Fri, 24 Jul 2026 22:14:14 +0800 Subject: [PATCH 1/4] feat: add subaccount admin command for superusers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an admin-only command 'subaccount' (alias '子账号') to the larkuser plugin, restricted to superusers, for managing sub-account bindings: - view: Check the binding status of a specified user ID - bind: Bind one account as a sub-account of another (with confirmation) - unbind: Unbind a sub-account from its parent (with confirmation) - unbind --all: Unbind all sub-accounts of a specified main account (with confirmation) Bind/unbind operations use larkuser's prompt tool to require explicit confirmation, listing the IDs and nicknames of involved accounts. Also adds helper functions to larkutils/subaccount.py: - get_sub_accounts(main_account) -> list[str] - remove_main_account(user_id) -> None - remove_all_sub_accounts(main_account) -> list[str] Closes # (pending PR) --- src/lang/en_us/larkuser.yaml | 35 +++ src/lang/zh_hans/larkuser.yaml | 35 +++ src/lang/zh_tw/larkuser.yaml | 35 +++ .../nonebot_plugin_larkuser/__init__.py | 1 + .../matchers/subaccount_admin.py | 280 ++++++++++++++++++ .../nonebot_plugin_larkutils/__init__.py | 2 +- .../nonebot_plugin_larkutils/subaccount.py | 32 ++ 7 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py diff --git a/src/lang/en_us/larkuser.yaml b/src/lang/en_us/larkuser.yaml index a2a8eb1d7..26e36143e 100644 --- a/src/lang/en_us/larkuser.yaml +++ b/src/lang/en_us/larkuser.yaml @@ -100,6 +100,41 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +subaccount_admin: + permission_denied: "Permission denied. Only superusers can use this command." + usage: | + Usage: + subaccount view - View binding status of the specified ID + subaccount bind - Bind a sub-account to a main account + subaccount unbind - Unbind a sub-account from its main account + subaccount unbind --all - Unbind all sub-accounts of a main account + view_is_sub: | + User {} ({}) is currently a sub-account + Main account: {} ({}) + view_has_subs: | + User {} ({}) is a main account with {} sub-account(s): + {} + view_none: "User {} ({}) has no sub-account bindings and is not a sub-account." + bind_already_bound: | + Sub-account {} ({}) is already bound to main account {} ({})! + Please unbind first if you want to change. + bind_target_not_found: "Target main account {} does not exist in the system." + bind_confirm: | + Confirm binding sub-account {} ({}) to main account {} ({})? + confirm_prompt: "Please enter y/yes to confirm, or send q to cancel:" + bind_success: "Sub-account {} ({}) has been successfully bound to main account {} ({})." + unbind_not_bound: "User {} ({}) is not a sub-account." + unbind_confirm: | + Confirm unbinding sub-account {} ({}) from its main account {} ({})? + After unbinding, the sub-account will become independent. + unbind_success: "Sub-account {} ({}) has been successfully unbound from main account {} ({})." + unbind_all_no_subs: "User {} ({}) has no sub-accounts." + unbind_all_confirm: | + Confirm unbinding all {} sub-account(s) of main account {} ({})? + {} + After unbinding, all sub-accounts will become independent. + unbind_all_success: "Successfully unbound all {} sub-account(s) of main account {} ({})." + input: user_nickname: | Moonlark 没有办法读取您的展示名称(昵称)。请发送您的展示名称,也可以发送“q”留空。 diff --git a/src/lang/zh_hans/larkuser.yaml b/src/lang/zh_hans/larkuser.yaml index ffba43218..b2a3fd60a 100644 --- a/src/lang/zh_hans/larkuser.yaml +++ b/src/lang/zh_hans/larkuser.yaml @@ -104,6 +104,41 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +subaccount_admin: + permission_denied: "权限不足,只有超级管理员可以使用此命令。" + usage: | + 用法: + subaccount view <用户ID> - 查看指定 ID 的绑定情况 + subaccount bind <子账号ID> <主账号ID> - 将子账号绑定到主账号 + subaccount unbind <子账号ID> - 解绑指定子账号的主账号 + subaccount unbind <主账号ID> --all - 解绑指定主账号的所有子账号 + view_is_sub: | + 用户 {} ({}) 当前是子账号状态 + 主账号: {} ({}) + view_has_subs: | + 用户 {} ({}) 是主账号,有 {} 个子账号: + {} + view_none: "用户 {} ({}) 没有绑定任何子账号,也不是子账号。" + bind_already_bound: | + 子账号 {} ({}) 已经绑定到主账号 {} ({}),无法重复绑定! + 如需更改请先解绑。 + bind_target_not_found: "目标主账号 {} 在系统中不存在,无法绑定。" + bind_confirm: | + 确认将子账号 {} ({}) 绑定到主账号 {} ({}) 吗? + confirm_prompt: "请输入 y/是 确认,或发送 q 取消:" + bind_success: "子账号 {} ({}) 已成功绑定到主账号 {} ({})。" + unbind_not_bound: "用户 {} ({}) 不是子账号,无法解绑。" + unbind_confirm: | + 确认将子账号 {} ({}) 从其主账号 {} ({}) 解绑吗? + 解绑后,该子账号将恢复独立身份。 + unbind_success: "子账号 {} ({}) 已成功从其主账号 {} ({}) 解绑。" + unbind_all_no_subs: "用户 {} ({}) 没有子账号,无需解绑。" + unbind_all_confirm: | + 确认解绑主账号 {} ({}) 的所有 {} 个子账号吗? + {} + 解绑后,这些子账号将全部恢复独立身份。 + unbind_all_success: "已成功将主账号 {} ({}) 的 {} 个子账号全部解绑。" + input: user_nickname: | Moonlark 没有办法读取您的展示名称(昵称)。请发送您的展示名称,也可以发送“q”留空。 diff --git a/src/lang/zh_tw/larkuser.yaml b/src/lang/zh_tw/larkuser.yaml index dae0f4bec..8134997c6 100644 --- a/src/lang/zh_tw/larkuser.yaml +++ b/src/lang/zh_tw/larkuser.yaml @@ -100,6 +100,41 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +subaccount_admin: + permission_denied: "權限不足,只有超級管理員可以使用此命令。" + usage: | + 用法: + subaccount view <用戶ID> - 查看指定 ID 的綁定情況 + subaccount bind <子帳號ID> <主帳號ID> - 將子帳號綁定到主帳號 + subaccount unbind <子帳號ID> - 解綁指定子帳號的主帳號 + subaccount unbind <主帳號ID> --all - 解綁指定主帳號的所有子帳號 + view_is_sub: | + 用戶 {} ({}) 當前是子帳號狀態 + 主帳號: {} ({}) + view_has_subs: | + 用戶 {} ({}) 是主帳號,有 {} 個子帳號: + {} + view_none: "用戶 {} ({}) 沒有綁定任何子帳號,也不是子帳號。" + bind_already_bound: | + 子帳號 {} ({}) 已經綁定到主帳號 {} ({}),無法重複綁定! + 如需更改請先解綁。 + bind_target_not_found: "目標主帳號 {} 在系統中不存在,無法綁定。" + bind_confirm: | + 確認將子帳號 {} ({}) 綁定到主帳號 {} ({}) 嗎? + confirm_prompt: "請輸入 y/是 確認,或發送 q 取消:" + bind_success: "子帳號 {} ({}) 已成功綁定到主帳號 {} ({})。" + unbind_not_bound: "用戶 {} ({}) 不是子帳號,無法解綁。" + unbind_confirm: | + 確認將子帳號 {} ({}) 從其主帳號 {} ({}) 解綁嗎? + 解綁後,該子帳號將恢復獨立身份。 + unbind_success: "子帳號 {} ({}) 已成功從其主帳號 {} ({}) 解綁。" + unbind_all_no_subs: "用戶 {} ({}) 沒有子帳號,無需解綁。" + unbind_all_confirm: | + 確認解綁主帳號 {} ({}) 的所有 {} 個子帳號嗎? + {} + 解綁後,這些子帳號將全部恢復獨立身份。 + unbind_all_success: "已成功將主帳號 {} ({}) 的 {} 個子帳號全部解綁。" + input: user_nickname: | Moonlark 没有办法读取您的展示名称(昵称)。请发送您的展示名称,也可以发送“q”留空。 diff --git a/src/plugins/nonebot_plugin_larkuser/__init__.py b/src/plugins/nonebot_plugin_larkuser/__init__.py index fa3bfeb0f..23568ceb9 100644 --- a/src/plugins/nonebot_plugin_larkuser/__init__.py +++ b/src/plugins/nonebot_plugin_larkuser/__init__.py @@ -18,6 +18,7 @@ from .matchers import recorder, panel, whoami, register, setnick +from .matchers import subaccount_admin from .utils.matcher import patch_matcher from .utils.level import get_level_by_experience from .utils.user import get_user, MoonlarkUser, get_registered_users, get_registered_user_list, get_registered_user_ids diff --git a/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py b/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py new file mode 100644 index 000000000..4fe8b9863 --- /dev/null +++ b/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py @@ -0,0 +1,280 @@ +# Moonlark - A new ChatBot +# Copyright (C) 2026 Moonlark Development Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# ############################################################################## + +from nonebot import on_command +from nonebot.adapters import Message +from nonebot.params import CommandArg +from nonebot_plugin_larkutils import ( + get_user_id, + is_user_superuser, + set_main_account, + get_main_account, + get_sub_accounts, + remove_main_account, + remove_all_sub_accounts, +) +from nonebot_plugin_orm import get_session + +from ..lang import lang +from ..models import UserData, GuestUser +from ..utils.waiter import prompt as get_confirmation + + +subaccount_admin = on_command( + "subaccount", + aliases={"子账号"}, + permission=None, +) + + +async def _get_user_nickname(user_id: str) -> str: + """尝试获取用户昵称,用于展示""" + async with get_session() as session: + user_data = await session.get(UserData, {"user_id": user_id}) + if user_data is not None and user_data.nickname: + return user_data.nickname + guest_data = await session.get(GuestUser, {"user_id": user_id}) + if guest_data is not None and guest_data.nickname: + return guest_data.nickname + return f"用户-{user_id[-4:]}" + + +@subaccount_admin.handle() +async def _( + args: Message = CommandArg(), + user_id: str = get_user_id(), + is_superuser: bool = is_user_superuser(), +) -> None: + if not is_superuser: + await lang.finish("subaccount_admin.permission_denied", user_id) + + text = args.extract_plain_text().strip() + if not text: + await lang.finish("subaccount_admin.usage", user_id) + + parts = text.split() + action = parts[0].lower() + + if action == "view" or action == "状态": + if len(parts) < 2: + await lang.finish("subaccount_admin.usage", user_id) + target_id = parts[1] + await _handle_view(target_id, user_id) + + elif action == "bind" or action == "绑定": + if len(parts) < 3: + await lang.finish("subaccount_admin.usage", user_id) + sub_id = parts[1] + main_id = parts[2] + await _handle_bind(sub_id, main_id, user_id) + + elif action == "unbind" or action == "解绑": + if len(parts) >= 3 and (parts[2] == "--all" or parts[2] == "全部"): + await _handle_unbind_all(parts[1], user_id) + elif len(parts) >= 2: + await _handle_unbind(parts[1], user_id) + else: + await lang.finish("subaccount_admin.usage", user_id) + + else: + await lang.finish("subaccount_admin.usage", user_id) + + +async def _handle_view(target_id: str, user_id: str) -> None: + """查看指定 ID 的绑定情况""" + main_account = await get_main_account(target_id) + sub_accounts = await get_sub_accounts(target_id) + nickname = await _get_user_nickname(target_id) + + if main_account != target_id: + # 该账号是子账号 + main_nickname = await _get_user_nickname(main_account) + await lang.finish( + "subaccount_admin.view_is_sub", + user_id, + target_id, + nickname, + main_account, + main_nickname, + ) + elif sub_accounts: + # 该账号是主账号且有子账号 + sub_info = [] + for sub_id in sub_accounts: + sub_nick = await _get_user_nickname(sub_id) + sub_info.append(f"{sub_id} ({sub_nick})") + sub_list = "\n".join(f" - {s}" for s in sub_info) + await lang.finish( + "subaccount_admin.view_has_subs", + user_id, + target_id, + nickname, + len(sub_accounts), + sub_list, + ) + else: + # 既不是子账号也没有子账号 + await lang.finish( + "subaccount_admin.view_none", + user_id, + target_id, + nickname, + ) + + +async def _handle_bind(sub_id: str, main_id: str, user_id: str) -> None: + """将 sub_id 绑定为 main_id 的子账号""" + # 检查 sub_id 是否已经有主账号 + existing_main = await get_main_account(sub_id) + if existing_main != sub_id: + existing_main_nick = await _get_user_nickname(existing_main) + sub_nick = await _get_user_nickname(sub_id) + await lang.finish( + "subaccount_admin.bind_already_bound", + user_id, + sub_id, + sub_nick, + existing_main, + existing_main_nick, + ) + + # 检查 main_id 是否存在(通过能否查到 UserData 或 GuestUser 来判断) + # 但也可以对任意 ID 进行绑定,不过为了合理,还是检查一下 + async with get_session() as session: + main_data = await session.get(UserData, {"user_id": main_id}) + if main_data is None: + guest_data = await session.get(GuestUser, {"user_id": main_id}) + if guest_data is None: + await lang.finish( + "subaccount_admin.bind_target_not_found", + user_id, + main_id, + ) + + sub_nick = await _get_user_nickname(sub_id) + main_nick = await _get_user_nickname(main_id) + + # 发送确认消息 + await lang.send( + "subaccount_admin.bind_confirm", + user_id, + sub_id, + sub_nick, + main_id, + main_nick, + ) + await get_confirmation( + await lang.text("subaccount_admin.confirm_prompt", user_id), + user_id, + checker=lambda msg: msg.strip().lower() in ("y", "yes", "是", "确认"), + retry=2, + ) + + await set_main_account(sub_id, main_id) + await lang.finish( + "subaccount_admin.bind_success", + user_id, + sub_id, + sub_nick, + main_id, + main_nick, + ) + + +async def _handle_unbind(sub_id: str, user_id: str) -> None: + """解绑指定子账号的主账号""" + existing_main = await get_main_account(sub_id) + if existing_main == sub_id: + sub_nick = await _get_user_nickname(sub_id) + await lang.finish( + "subaccount_admin.unbind_not_bound", + user_id, + sub_id, + sub_nick, + ) + + sub_nick = await _get_user_nickname(sub_id) + main_nick = await _get_user_nickname(existing_main) + + await lang.send( + "subaccount_admin.unbind_confirm", + user_id, + sub_id, + sub_nick, + existing_main, + main_nick, + ) + await get_confirmation( + await lang.text("subaccount_admin.confirm_prompt", user_id), + user_id, + checker=lambda msg: msg.strip().lower() in ("y", "yes", "是", "确认"), + retry=2, + ) + + await remove_main_account(sub_id) + await lang.finish( + "subaccount_admin.unbind_success", + user_id, + sub_id, + sub_nick, + existing_main, + main_nick, + ) + + +async def _handle_unbind_all(main_id: str, user_id: str) -> None: + """解绑指定主账号的所有子账号""" + sub_accounts = await get_sub_accounts(main_id) + if not sub_accounts: + main_nick = await _get_user_nickname(main_id) + await lang.finish( + "subaccount_admin.unbind_all_no_subs", + user_id, + main_id, + main_nick, + ) + + main_nick = await _get_user_nickname(main_id) + sub_info = [] + for sub_id in sub_accounts: + sub_nick = await _get_user_nickname(sub_id) + sub_info.append(f" - {sub_id} ({sub_nick})") + sub_list = "\n".join(sub_info) + + await lang.send( + "subaccount_admin.unbind_all_confirm", + user_id, + main_id, + main_nick, + len(sub_accounts), + sub_list, + ) + await get_confirmation( + await lang.text("subaccount_admin.confirm_prompt", user_id), + user_id, + checker=lambda msg: msg.strip().lower() in ("y", "yes", "是", "确认"), + retry=2, + ) + + removed = await remove_all_sub_accounts(main_id) + await lang.finish( + "subaccount_admin.unbind_all_success", + user_id, + main_id, + main_nick, + len(removed), + ) diff --git a/src/plugins/nonebot_plugin_larkutils/__init__.py b/src/plugins/nonebot_plugin_larkutils/__init__.py index 2d0016524..4a5307f60 100644 --- a/src/plugins/nonebot_plugin_larkutils/__init__.py +++ b/src/plugins/nonebot_plugin_larkutils/__init__.py @@ -30,7 +30,7 @@ from .superuser import is_user_superuser, is_superuser from .user import get_user_id, is_private_message from .bot import is_public_qq_bot -from .subaccount import get_main_account, set_main_account +from .subaccount import get_main_account, set_main_account, get_sub_accounts, remove_main_account, remove_all_sub_accounts from .user_id import parse_special_user_id from .file import open_file, FileManager, FileType from .jrrp import get_luck_value diff --git a/src/plugins/nonebot_plugin_larkutils/subaccount.py b/src/plugins/nonebot_plugin_larkutils/subaccount.py index 06213e09e..fe872dd55 100644 --- a/src/plugins/nonebot_plugin_larkutils/subaccount.py +++ b/src/plugins/nonebot_plugin_larkutils/subaccount.py @@ -25,6 +25,8 @@ from nonebot_plugin_localstore import get_data_dir from nonebot_plugin_orm import get_session +from sqlalchemy import select, delete + from .models import MainAccountMapping logger = getLogger(__name__) @@ -58,6 +60,36 @@ async def get_main_account(user_id: str) -> str: return user_id +async def get_sub_accounts(main_account: str) -> list[str]: + """获取指定主账号的所有子账号 user_id 列表""" + await _ensure_migrated() + async with get_session() as session: + stmt = select(MainAccountMapping.user_id).where(MainAccountMapping.main_account == main_account) + return list(await session.scalars(stmt)) + + +async def remove_main_account(user_id: str) -> None: + """移除指定子账号的主账号绑定(解绑子账号)""" + async with get_session() as session: + mapping = await session.get(MainAccountMapping, {"user_id": user_id}) + if mapping is not None: + await session.delete(mapping) + await session.commit() + + +async def remove_all_sub_accounts(main_account: str) -> list[str]: + """移除指定主账号的所有子账号绑定,返回被移除的子账号 ID 列表""" + await _ensure_migrated() + async with get_session() as session: + stmt = select(MainAccountMapping.user_id).where(MainAccountMapping.main_account == main_account) + sub_ids = list(await session.scalars(stmt)) + if sub_ids: + delete_stmt = delete(MainAccountMapping).where(MainAccountMapping.main_account == main_account) + await session.execute(delete_stmt) + await session.commit() + return sub_ids + + async def _migrate_from_files() -> None: """将旧的文件存储的主账号映射数据迁移到数据库""" global _migration_done From 69bb87224a52549350df6355e3f3b8bb0fc576fc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:15:00 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot_plugin_larkuser/matchers/subaccount_admin.py | 1 - src/plugins/nonebot_plugin_larkutils/__init__.py | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py b/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py index 4fe8b9863..c5d46b642 100644 --- a/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py +++ b/src/plugins/nonebot_plugin_larkuser/matchers/subaccount_admin.py @@ -33,7 +33,6 @@ from ..models import UserData, GuestUser from ..utils.waiter import prompt as get_confirmation - subaccount_admin = on_command( "subaccount", aliases={"子账号"}, diff --git a/src/plugins/nonebot_plugin_larkutils/__init__.py b/src/plugins/nonebot_plugin_larkutils/__init__.py index 4a5307f60..555ffc76b 100644 --- a/src/plugins/nonebot_plugin_larkutils/__init__.py +++ b/src/plugins/nonebot_plugin_larkutils/__init__.py @@ -30,7 +30,13 @@ from .superuser import is_user_superuser, is_superuser from .user import get_user_id, is_private_message from .bot import is_public_qq_bot -from .subaccount import get_main_account, set_main_account, get_sub_accounts, remove_main_account, remove_all_sub_accounts +from .subaccount import ( + get_main_account, + set_main_account, + get_sub_accounts, + remove_main_account, + remove_all_sub_accounts, +) from .user_id import parse_special_user_id from .file import open_file, FileManager, FileType from .jrrp import get_luck_value From 06c67e421c9956ef0ff1a496e0c9cbe80781d745 Mon Sep 17 00:00:00 2001 From: This-is-XiaoDeng <1744793737@qq.com> Date: Fri, 24 Jul 2026 22:24:26 +0800 Subject: [PATCH 3/4] feat: add help.yaml entry for subaccount command Add help metadata for the new subaccount admin command to larkhelp system, categorized as 'superuser' with usage instructions in all three languages (zh_hans, en_us, zh_tw). --- src/lang/en_us/larkuser.yaml | 8 ++++++++ src/lang/zh_hans/larkuser.yaml | 7 +++++++ src/lang/zh_tw/larkuser.yaml | 8 ++++++++ src/plugins/nonebot_plugin_larkuser/help.yaml | 9 +++++++++ 4 files changed, 32 insertions(+) diff --git a/src/lang/en_us/larkuser.yaml b/src/lang/en_us/larkuser.yaml index 26e36143e..c0d9716ee 100644 --- a/src/lang/en_us/larkuser.yaml +++ b/src/lang/en_us/larkuser.yaml @@ -100,6 +100,14 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +help_subaccount: + description: Manage Sub-Accounts + details: "[Admin] Manage sub-account bindings: view status, bind/unbind" + usage1: "subaccount view (View binding status)" + usage2: "subaccount bind (Bind sub-account)" + usage3: "subaccount unbind (Unbind sub-account)" + usage4: "subaccount unbind --all (Unbind all sub-accounts)" + subaccount_admin: permission_denied: "Permission denied. Only superusers can use this command." usage: | diff --git a/src/lang/zh_hans/larkuser.yaml b/src/lang/zh_hans/larkuser.yaml index b2a3fd60a..00ffba174 100644 --- a/src/lang/zh_hans/larkuser.yaml +++ b/src/lang/zh_hans/larkuser.yaml @@ -104,6 +104,13 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +help_subaccount: + description: 管理子账号 + details: "[管理员专用] 管理子账号绑定关系:查看绑定状态、绑定/解绑子账号" + usage1: "subaccount view <用户ID> (查看绑定状态)" + usage2: "subaccount bind <子账号ID> <主账号ID> (绑定子账号)" + usage3: "subaccount unbind <子账号ID> (解绑子账号)" + usage4: "subaccount unbind <主账号ID> --all (解绑全部子账号)" subaccount_admin: permission_denied: "权限不足,只有超级管理员可以使用此命令。" usage: | diff --git a/src/lang/zh_tw/larkuser.yaml b/src/lang/zh_tw/larkuser.yaml index 8134997c6..27acc0094 100644 --- a/src/lang/zh_tw/larkuser.yaml +++ b/src/lang/zh_tw/larkuser.yaml @@ -100,6 +100,14 @@ help_setnick: description: 修改昵称 details: "修改自己在 Moonlark 中的昵称,不带参数则解锁昵称锁定" usage: "setnick <昵称> (修改昵称,不带参数解锁锁定)" +help_subaccount: + description: 管理子帳號 + details: "[管理員專用] 管理子帳號綁定關係:查看綁定狀態、綁定/解綁子帳號" + usage1: "subaccount view <用戶ID> (查看綁定狀態)" + usage2: "subaccount bind <子帳號ID> <主帳號ID> (綁定子帳號)" + usage3: "subaccount unbind <子帳號ID> (解綁子帳號)" + usage4: "subaccount unbind <主帳號ID> --all (解綁全部子帳號)" + subaccount_admin: permission_denied: "權限不足,只有超級管理員可以使用此命令。" usage: | diff --git a/src/plugins/nonebot_plugin_larkuser/help.yaml b/src/plugins/nonebot_plugin_larkuser/help.yaml index c0bcc9076..d3d59344b 100644 --- a/src/plugins/nonebot_plugin_larkuser/help.yaml +++ b/src/plugins/nonebot_plugin_larkuser/help.yaml @@ -18,3 +18,12 @@ commands: usages: - help_setnick.usage category: setting + subaccount: + description: help_subaccount.description + details: help_subaccount.details + usages: + - help_subaccount.usage1 + - help_subaccount.usage2 + - help_subaccount.usage3 + - help_subaccount.usage4 + category: superuser From 59bf542ad9ee9348ef2f8fdcbedbe462c4741a00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:27:01 +0000 Subject: [PATCH 4/4] Auto update from GitHub Actions --- COMMANDS.md | 8 ++++++++ poetry.lock | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/COMMANDS.md b/COMMANDS.md index ff910020f..c8cee36a5 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -406,3 +406,11 @@ Moonlark 投票 - `/model <模型名> (更换默认模型)` - `/model <模型名> <应用标识> (设置应用专用模型)` - `/model :default: <应用标识> (删除应用配置)` +## `subaccount`: 管理子账号 + +[管理员专用] 管理子账号绑定关系:查看绑定状态、绑定/解绑子账号 +> 此指令仅 Moonlark 管理员可用。 +- `/subaccount view <用户ID> (查看绑定状态)` +- `/subaccount bind <子账号ID> <主账号ID> (绑定子账号)` +- `/subaccount unbind <子账号ID> (解绑子账号)` +- `/subaccount unbind <主账号ID> --all (解绑全部子账号)` diff --git a/poetry.lock b/poetry.lock index 25071f301..5539f1153 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6222,14 +6222,14 @@ files = [ [[package]] name = "tqdm" -version = "4.69.0" +version = "4.69.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tqdm-4.69.0-py3-none-any.whl", hash = "sha256:9979978912be667a6ef21fd5d8abf54e324e63d82f7f43c360792ebc2bc4e622"}, - {file = "tqdm-4.69.0.tar.gz", hash = "sha256:700c5e85dcd5f009dd6222588a29180a193a748247a5d855b4d67db93d79a53b"}, + {file = "tqdm-4.69.1-py3-none-any.whl", hash = "sha256:0a654b96f7a2660cceb615b56f307ec2bef96c515409014a429a561981ab52b4"}, + {file = "tqdm-4.69.1.tar.gz", hash = "sha256:2be21080a0ce17e902c2f1baeb6a74bf551b67bbdfa4bc0109fad471d0b4cb0d"}, ] [package.dependencies]