diff --git a/nb_cli/cli/commands/adapter.py b/nb_cli/cli/commands/adapter.py index 42fefecb..7af4a266 100644 --- a/nb_cli/cli/commands/adapter.py +++ b/nb_cli/cli/commands/adapter.py @@ -6,6 +6,7 @@ from nb_cli import _ from nb_cli.config import GLOBAL_CONFIG +from nb_cli.exceptions import NoSelectablePackageError from nb_cli.cli.utils import find_exact_package, format_package_results from nb_cli.cli import ( CLI_DEFAULT_STYLE, @@ -21,6 +22,7 @@ call_pip_update, call_pip_install, call_pip_uninstall, + list_installed_adapters, ) @@ -80,6 +82,13 @@ async def store(): @adapter.command( name="list", help=_("List nonebot adapters published on nonebot homepage.") ) +@click.option( + "--installed", + is_flag=True, + default=False, + flag_value=True, + help=_("Whether to list installed adapters only in current project."), +) @click.option( "--include-unpublished", is_flag=True, @@ -88,8 +97,12 @@ async def store(): help=_("Whether to include unpublished adapters."), ) @run_async -async def list_(include_unpublished: bool = False): - adapters = await list_adapters(include_unpublished=include_unpublished) +async def list_(installed: bool = False, include_unpublished: bool = False): + adapters = ( + await list_installed_adapters() + if installed + else await list_adapters(include_unpublished=include_unpublished) + ) if include_unpublished: click.secho(_("WARNING: Unpublished adapters may be included."), fg="yellow") click.echo(format_package_results(adapters)) @@ -143,13 +156,23 @@ async def install( include_unpublished: bool = False, ): try: + _installed = { + (a.project_link, a.module_name) for a in await list_installed_adapters() + } adapter = await find_exact_package( _("Adapter name to install:"), name, - await list_adapters(include_unpublished=include_unpublished), + [ + a + for a in await list_adapters(include_unpublished=include_unpublished) + if (a.project_link, a.module_name) not in _installed + ], ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No available adapter found to install.")) + return if include_unpublished: click.secho( @@ -216,10 +239,13 @@ async def update( adapter = await find_exact_package( _("Adapter name to update:"), name, - await list_adapters(include_unpublished=include_unpublished), + await list_installed_adapters(), ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No installed adapter found to update.")) + return if include_unpublished: click.secho( @@ -263,12 +289,13 @@ async def uninstall(name: str | None, pip_args: list[str] | None): adapter = await find_exact_package( _("Adapter name to uninstall:"), name, - await list_adapters( - include_unpublished=True # unpublished modules are always removable - ), + await list_installed_adapters(), ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No installed adapter found to uninstall.")) + return try: can_uninstall = GLOBAL_CONFIG.remove_adapter(adapter) diff --git a/nb_cli/cli/commands/plugin.py b/nb_cli/cli/commands/plugin.py index b1fd4644..1260b6fe 100644 --- a/nb_cli/cli/commands/plugin.py +++ b/nb_cli/cli/commands/plugin.py @@ -6,6 +6,7 @@ from nb_cli import _ from nb_cli.config import GLOBAL_CONFIG +from nb_cli.exceptions import NoSelectablePackageError from nb_cli.cli.utils import find_exact_package, format_package_results from nb_cli.cli import ( CLI_DEFAULT_STYLE, @@ -21,6 +22,7 @@ call_pip_update, call_pip_install, call_pip_uninstall, + list_installed_plugins, ) @@ -80,6 +82,13 @@ async def store(): @plugin.command( name="list", help=_("List nonebot plugins published on nonebot homepage.") ) +@click.option( + "--installed", + is_flag=True, + default=False, + flag_value=True, + help=_("Whether to list installed plugins only in current project."), +) @click.option( "--include-unpublished", is_flag=True, @@ -88,8 +97,12 @@ async def store(): help=_("Whether to include unpublished plugins."), ) @run_async -async def list_(include_unpublished: bool = False): - plugins = await list_plugins(include_unpublished=include_unpublished) +async def list_(installed: bool = False, include_unpublished: bool = False): + plugins = ( + await list_installed_plugins() + if installed + else await list_plugins(include_unpublished=include_unpublished) + ) if include_unpublished: click.secho(_("WARNING: Unpublished plugins may be included."), fg="yellow") click.echo(format_package_results(plugins)) @@ -143,13 +156,23 @@ async def install( include_unpublished: bool = False, ): try: + _installed = { + (p.project_link, p.module_name) for p in await list_installed_plugins() + } plugin = await find_exact_package( _("Plugin name to install:"), name, - await list_plugins(include_unpublished=include_unpublished), + [ + p + for p in await list_plugins(include_unpublished=include_unpublished) + if (p.project_link, p.module_name) not in _installed + ], ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No available plugin found to install.")) + return if include_unpublished: click.secho( @@ -216,10 +239,13 @@ async def update( plugin = await find_exact_package( _("Plugin name to update:"), name, - await list_plugins(include_unpublished=include_unpublished), + await list_installed_plugins(), ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No installed plugin found to update.")) + return if include_unpublished: click.secho( @@ -263,12 +289,13 @@ async def uninstall(name: str | None, pip_args: list[str] | None): plugin = await find_exact_package( _("Plugin name to uninstall:"), name, - await list_plugins( - include_unpublished=True # unpublished modules are always removable - ), + await list_installed_plugins(), ) except CancelledError: return + except NoSelectablePackageError: + click.echo(_("No installed plugin found to uninstall.")) + return try: can_uninstall = GLOBAL_CONFIG.remove_plugin(plugin) diff --git a/nb_cli/cli/utils.py b/nb_cli/cli/utils.py index 02b235c5..31412e9d 100644 --- a/nb_cli/cli/utils.py +++ b/nb_cli/cli/utils.py @@ -14,6 +14,7 @@ from nb_cli import _ from nb_cli.config import Driver, Plugin, Adapter +from nb_cli.exceptions import NoSelectablePackageError T = TypeVar("T", Adapter, Plugin, Driver) P = ParamSpec("P") @@ -59,6 +60,8 @@ def __call__(self, x: Adapter | Plugin | Driver, *, value: str) -> bool: ... async def find_exact_package(question: str, name: str | None, packages: list[T]) -> T: if name is None: + if not packages: + raise NoSelectablePackageError("No packages available to select.") return ( await ListPrompt( question, diff --git a/nb_cli/exceptions.py b/nb_cli/exceptions.py index e15c4ed9..bf9a434d 100644 --- a/nb_cli/exceptions.py +++ b/nb_cli/exceptions.py @@ -24,3 +24,7 @@ class ProjectInvalidError(RuntimeError): class LocalCacheExpired(RuntimeError): """Raised when local metadata cache is outdated.""" + + +class NoSelectablePackageError(RuntimeError): + """Raised when there is no selectable package.""" diff --git a/nb_cli/handlers/__init__.py b/nb_cli/handlers/__init__.py index f5ecf823..e638b56b 100644 --- a/nb_cli/handlers/__init__.py +++ b/nb_cli/handlers/__init__.py @@ -77,12 +77,14 @@ from .plugin import list_plugins as list_plugins from .plugin import create_plugin as create_plugin from .plugin import list_builtin_plugins as list_builtin_plugins +from .plugin import list_installed_plugins as list_installed_plugins # isort: split # adapter from .adapter import list_adapters as list_adapters from .adapter import create_adapter as create_adapter +from .adapter import list_installed_adapters as list_installed_adapters # isort: split diff --git a/nb_cli/handlers/adapter.py b/nb_cli/handlers/adapter.py index fb6837fc..4f212005 100644 --- a/nb_cli/handlers/adapter.py +++ b/nb_cli/handlers/adapter.py @@ -2,9 +2,11 @@ from cookiecutter.main import cookiecutter -from nb_cli.config import Adapter from nb_cli.compat import model_dump +from nb_cli.exceptions import ProjectInvalidError +from nb_cli.config import Adapter, NoneBotConfig, LegacyNoneBotConfig +from .meta import get_nonebot_config, requires_project_root from .store import load_module_data, load_unpublished_modules TEMPLATE_ROOT = Path(__file__).parent.parent / "template" / "adapter" @@ -42,3 +44,34 @@ async def list_adapters( ).values() ) ] + + +@requires_project_root +async def list_installed_adapters(*, cwd: Path | None = None) -> list[Adapter]: + config_data = get_nonebot_config(cwd) + adapters = await load_module_data("adapter") + await load_unpublished_modules( + Adapter + ) + + result: list[Adapter] = [] + + if isinstance(config_data, NoneBotConfig): + adapter_info = config_data.adapters + allowed_pairs = { + (pkg_name, m.module_name) + for pkg_name, modules in adapter_info.items() + for m in modules + } + for adapter in adapters: + if (adapter.project_link, adapter.module_name) in allowed_pairs: + result.append(adapter) + elif isinstance(config_data, LegacyNoneBotConfig): + adapter_info = config_data.adapters + allowed_pairs = {m.module_name for m in adapter_info} + for adapter in adapters: + if adapter.module_name in allowed_pairs: + result.append(adapter) + else: + raise ProjectInvalidError("Invalid config data type") + + return result diff --git a/nb_cli/handlers/plugin.py b/nb_cli/handlers/plugin.py index 49aed3e2..462e5997 100644 --- a/nb_cli/handlers/plugin.py +++ b/nb_cli/handlers/plugin.py @@ -4,13 +4,19 @@ from cookiecutter.main import cookiecutter -from nb_cli.config import Plugin from nb_cli.compat import model_dump +from nb_cli.exceptions import ProjectInvalidError +from nb_cli.config import Plugin, NoneBotConfig, LegacyNoneBotConfig from . import templates from .process import create_process -from .meta import requires_nonebot, get_default_python from .store import load_module_data, load_unpublished_modules +from .meta import ( + requires_nonebot, + get_default_python, + get_nonebot_config, + requires_project_root, +) TEMPLATE_ROOT = Path(__file__).parent.parent / "template" / "plugin" @@ -67,3 +73,32 @@ async def list_plugins( ).values() ) ] + + +@requires_project_root +async def list_installed_plugins(*, cwd: Path | None = None) -> list[Plugin]: + config_data = get_nonebot_config(cwd) + plugins = await load_module_data("plugin") + await load_unpublished_modules(Plugin) + + result: list[Plugin] = [] + + if isinstance(config_data, NoneBotConfig): + plugin_info = config_data.plugins + allowed_plugins = { + (pkg_name, module_name) + for pkg_name, module_names in plugin_info.items() + for module_name in module_names + } + for plugin in plugins: + if (plugin.project_link, plugin.module_name) in allowed_plugins: + result.append(plugin) + elif isinstance(config_data, LegacyNoneBotConfig): + plugin_info = config_data.plugins + allowed_plugins = set(plugin_info) + for plugin in plugins: + if plugin.module_name in allowed_plugins: + result.append(plugin) + else: + raise ProjectInvalidError("Invalid config data type") + + return result diff --git a/nb_cli/locale/zh_CN/LC_MESSAGES/nb-cli.po b/nb_cli/locale/zh_CN/LC_MESSAGES/nb-cli.po index 48702802..ef061ee4 100644 --- a/nb_cli/locale/zh_CN/LC_MESSAGES/nb-cli.po +++ b/nb_cli/locale/zh_CN/LC_MESSAGES/nb-cli.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: nb-cli 1.0.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-10-07 17:05+0000\n" +"POT-Creation-Date: 2025-12-01 15:03+0000\n" "PO-Revision-Date: 2023-01-11 08:56+0000\n" "Last-Translator: FULL NAME \n" "Language: zh_Hans_CN\n" @@ -22,109 +22,117 @@ msgstr "" msgid "Are you sure to exit NB CLI?" msgstr "你确定要退出 NB CLI 吗?" -#: nb_cli/cli/__init__.py:67 +#: nb_cli/cli/__init__.py:65 msgid "The working directory." msgstr "工作目录." -#: nb_cli/cli/__init__.py:77 +#: nb_cli/cli/__init__.py:75 msgid "Python executable path." msgstr "Python 可执行文件路径." -#: nb_cli/cli/__init__.py:85 +#: nb_cli/cli/__init__.py:83 msgid "Auto detect virtual environment." msgstr "自动检测虚拟环境." -#: nb_cli/cli/__init__.py:108 nb_cli/cli/commands/adapter.py:44 +#: nb_cli/cli/__init__.py:106 nb_cli/cli/commands/adapter.py:46 #: nb_cli/cli/commands/cache.py:42 nb_cli/cli/commands/driver.py:42 -#: nb_cli/cli/commands/plugin.py:44 nb_cli/cli/commands/self.py:42 +#: nb_cli/cli/commands/plugin.py:46 nb_cli/cli/commands/self.py:42 #, python-brace-format msgid "Run subcommand {sub_cmd.name!r}" msgstr "运行子命令 {sub_cmd.name!r}" -#: nb_cli/cli/__init__.py:112 nb_cli/cli/commands/adapter.py:51 +#: nb_cli/cli/__init__.py:110 nb_cli/cli/commands/adapter.py:53 #: nb_cli/cli/commands/cache.py:49 nb_cli/cli/commands/driver.py:49 -#: nb_cli/cli/commands/plugin.py:51 nb_cli/cli/commands/self.py:49 +#: nb_cli/cli/commands/plugin.py:53 nb_cli/cli/commands/self.py:49 msgid "Exit NB CLI." msgstr "退出 NB CLI." -#: nb_cli/cli/__init__.py:117 +#: nb_cli/cli/__init__.py:115 msgid "Welcome to NoneBot CLI!" msgstr "欢迎使用 NoneBot CLI!" -#: nb_cli/cli/__init__.py:123 nb_cli/cli/commands/adapter.py:57 +#: nb_cli/cli/__init__.py:121 nb_cli/cli/commands/adapter.py:59 #: nb_cli/cli/commands/cache.py:55 nb_cli/cli/commands/driver.py:55 -#: nb_cli/cli/commands/plugin.py:57 nb_cli/cli/commands/self.py:55 +#: nb_cli/cli/commands/plugin.py:59 nb_cli/cli/commands/self.py:55 msgid "What do you want to do?" msgstr "你想要进行什么操作?" -#: nb_cli/cli/customize.py:109 +#: nb_cli/cli/customize.py:112 #, python-brace-format msgid "Run script {script_name!r}" msgstr "运行脚本 {script_name!r}" -#: nb_cli/cli/utils.py:95 +#: nb_cli/cli/utils.py:96 #, python-brace-format msgid "Package {name} not found." msgstr "包 {name} 未找到." -#: nb_cli/cli/utils.py:97 +#: nb_cli/cli/utils.py:98 msgid "*** You may check with `--include-unpublished` option if supported." msgstr "*** 可以对支持的命令使用 `--include-unpublished` 选项来确认." -#: nb_cli/cli/commands/adapter.py:28 +#: nb_cli/cli/commands/adapter.py:30 msgid "Manage bot adapters." msgstr "管理 bot 适配器." -#: nb_cli/cli/commands/adapter.py:49 nb_cli/cli/commands/cache.py:47 -#: nb_cli/cli/commands/driver.py:47 nb_cli/cli/commands/plugin.py:49 +#: nb_cli/cli/commands/adapter.py:51 nb_cli/cli/commands/cache.py:47 +#: nb_cli/cli/commands/driver.py:47 nb_cli/cli/commands/plugin.py:51 #: nb_cli/cli/commands/self.py:47 msgid "Back to top level." msgstr "返回上一级." -#: nb_cli/cli/commands/adapter.py:69 +#: nb_cli/cli/commands/adapter.py:71 msgid "Open nonebot adapter store." msgstr "打开 NoneBot 适配器商店." -#: nb_cli/cli/commands/adapter.py:76 +#: nb_cli/cli/commands/adapter.py:78 msgid "NB-CLI - NoneBot Adapter Store" msgstr "NB-CLI - NoneBot 适配器商店" -#: nb_cli/cli/commands/adapter.py:81 +#: nb_cli/cli/commands/adapter.py:83 msgid "List nonebot adapters published on nonebot homepage." msgstr "列出 NoneBot 官网上发布的适配器." -#: nb_cli/cli/commands/adapter.py:88 nb_cli/cli/commands/adapter.py:104 -#: nb_cli/cli/commands/adapter.py:132 nb_cli/cli/commands/adapter.py:205 +#: nb_cli/cli/commands/adapter.py:90 +msgid "Whether to list installed adapters only in current project." +msgstr "是否只列出安装到当前项目的适配器." + +#: nb_cli/cli/commands/adapter.py:97 nb_cli/cli/commands/adapter.py:117 +#: nb_cli/cli/commands/adapter.py:145 nb_cli/cli/commands/adapter.py:228 msgid "Whether to include unpublished adapters." msgstr "是否要包含已下架的适配器." -#: nb_cli/cli/commands/adapter.py:94 nb_cli/cli/commands/adapter.py:115 +#: nb_cli/cli/commands/adapter.py:107 nb_cli/cli/commands/adapter.py:128 msgid "WARNING: Unpublished adapters may be included." msgstr "警告: 结果中可能含有已下架的适配器." -#: nb_cli/cli/commands/adapter.py:98 +#: nb_cli/cli/commands/adapter.py:111 msgid "Search for nonebot adapters published on nonebot homepage." msgstr "搜索 NoneBot 官网上发布的适配器." -#: nb_cli/cli/commands/adapter.py:110 +#: nb_cli/cli/commands/adapter.py:123 msgid "Adapter name to search:" msgstr "想要搜索的适配器名称:" -#: nb_cli/cli/commands/adapter.py:122 +#: nb_cli/cli/commands/adapter.py:135 msgid "Install nonebot adapter to current project." msgstr "安装适配器到当前项目." -#: nb_cli/cli/commands/adapter.py:147 +#: nb_cli/cli/commands/adapter.py:163 msgid "Adapter name to install:" msgstr "想要安装的适配器名称:" -#: nb_cli/cli/commands/adapter.py:157 nb_cli/cli/commands/adapter.py:227 +#: nb_cli/cli/commands/adapter.py:174 +msgid "No available adapter found to install." +msgstr "没有可供安装的适配器." + +#: nb_cli/cli/commands/adapter.py:180 nb_cli/cli/commands/adapter.py:253 msgid "" "WARNING: Unpublished adapters may be installed. These adapters may be " "unmaintained or unusable." msgstr "警告: 有可能安装已下架的适配器. 其可能缺少维护或不可用." -#: nb_cli/cli/commands/adapter.py:169 +#: nb_cli/cli/commands/adapter.py:192 #, python-brace-format msgid "" "Errors occurred in installing adapter {adapter.name}\n" @@ -135,79 +143,87 @@ msgstr "" "*** 尝试使用 `--no-restrict-version` 选项执行 `nb adapter install` " "命令在宽松的版本约束下解析依赖可能可以解决此问题." -#: nb_cli/cli/commands/adapter.py:182 +#: nb_cli/cli/commands/adapter.py:205 #, python-brace-format msgid "Failed to add adapter {adapter.name} to config: {e}" msgstr "添加适配器 {adapter.name} 到配置文件失败: {e}" -#: nb_cli/cli/commands/adapter.py:191 +#: nb_cli/cli/commands/adapter.py:214 #, python-brace-format msgid "Failed to add adapter {adapter.name} to dependencies: {e}" msgstr "添加适配器 {adapter.name} 到依赖项失败: {e}" -#: nb_cli/cli/commands/adapter.py:198 +#: nb_cli/cli/commands/adapter.py:221 msgid "Update nonebot adapter." msgstr "更新适配器." -#: nb_cli/cli/commands/adapter.py:217 +#: nb_cli/cli/commands/adapter.py:240 msgid "Adapter name to update:" msgstr "想要更新的适配器名称:" -#: nb_cli/cli/commands/adapter.py:236 +#: nb_cli/cli/commands/adapter.py:247 +msgid "No installed adapter found to update." +msgstr "没有可供更新的适配器:" + +#: nb_cli/cli/commands/adapter.py:262 #, python-brace-format msgid "Errors occurred in updating adapter {adapter.name}. Aborted." msgstr "更新适配器 {adapter.name} 时发生错误. 已中止." -#: nb_cli/cli/commands/adapter.py:247 +#: nb_cli/cli/commands/adapter.py:273 #, python-brace-format msgid "Failed to update adapter {adapter.name} to dependencies: {e}" msgstr "更新适配器 {adapter.name} 的依赖项失败: {e}" -#: nb_cli/cli/commands/adapter.py:256 +#: nb_cli/cli/commands/adapter.py:282 msgid "Uninstall nonebot adapter from current project." msgstr "移除当前项目中的适配器." -#: nb_cli/cli/commands/adapter.py:264 +#: nb_cli/cli/commands/adapter.py:290 msgid "Adapter name to uninstall:" msgstr "想要移除的适配器名称:" -#: nb_cli/cli/commands/adapter.py:279 +#: nb_cli/cli/commands/adapter.py:297 +msgid "No installed adapter found to uninstall." +msgstr "没有可供移除的适配器." + +#: nb_cli/cli/commands/adapter.py:306 #, python-brace-format msgid "Failed to remove adapter {adapter.name} from config: {e}" msgstr "从配置文件移除适配器 {adapter.name} 失败: {e}" -#: nb_cli/cli/commands/adapter.py:290 +#: nb_cli/cli/commands/adapter.py:317 msgid "Create a new nonebot adapter." msgstr "新建适配器" -#: nb_cli/cli/commands/adapter.py:298 +#: nb_cli/cli/commands/adapter.py:325 msgid "The adapter template to use." msgstr "使用的适配器模板." -#: nb_cli/cli/commands/adapter.py:309 +#: nb_cli/cli/commands/adapter.py:336 msgid "Adapter name:" msgstr "适配器名称:" -#: nb_cli/cli/commands/adapter.py:330 +#: nb_cli/cli/commands/adapter.py:357 msgid "Where to store the adapter?" msgstr "请输入适配器存储的位置:" -#: nb_cli/cli/commands/adapter.py:331 nb_cli/cli/commands/adapter.py:334 -#: nb_cli/cli/commands/plugin.py:337 nb_cli/cli/commands/plugin.py:340 +#: nb_cli/cli/commands/adapter.py:358 nb_cli/cli/commands/adapter.py:361 +#: nb_cli/cli/commands/plugin.py:364 nb_cli/cli/commands/plugin.py:367 msgid "Other" msgstr "其他" -#: nb_cli/cli/commands/adapter.py:336 nb_cli/cli/commands/adapter.py:345 -#: nb_cli/cli/commands/plugin.py:342 nb_cli/cli/commands/plugin.py:352 +#: nb_cli/cli/commands/adapter.py:363 nb_cli/cli/commands/adapter.py:372 +#: nb_cli/cli/commands/plugin.py:369 nb_cli/cli/commands/plugin.py:379 msgid "Output Dir:" msgstr "输出目录:" -#: nb_cli/cli/commands/adapter.py:342 nb_cli/cli/commands/plugin.py:349 +#: nb_cli/cli/commands/adapter.py:369 nb_cli/cli/commands/plugin.py:376 msgid "Output dir is not a directory!" msgstr "输出目录不是一个文件夹!" -#: nb_cli/cli/commands/adapter.py:347 nb_cli/cli/commands/plugin.py:344 -#: nb_cli/cli/commands/plugin.py:354 +#: nb_cli/cli/commands/adapter.py:374 nb_cli/cli/commands/plugin.py:371 +#: nb_cli/cli/commands/plugin.py:381 msgid "Invalid output dir!" msgstr "无效的输出目录!" @@ -415,54 +431,62 @@ msgstr "想要移除的驱动器名称:" msgid "Failed to remove driver {driver.name} from dependencies: {e}" msgstr "从依赖项中移除驱动器 {adapter.name} 失败: {e}" -#: nb_cli/cli/commands/plugin.py:28 +#: nb_cli/cli/commands/plugin.py:30 msgid "Manage bot plugins." msgstr "管理 bot 插件." -#: nb_cli/cli/commands/plugin.py:69 +#: nb_cli/cli/commands/plugin.py:71 msgid "Open nonebot plugin store." msgstr "打开 NoneBot 插件商店." -#: nb_cli/cli/commands/plugin.py:76 +#: nb_cli/cli/commands/plugin.py:78 msgid "NB-CLI - NoneBot Plugin Store" msgstr "NB-CLI - NoneBot 插件商店" -#: nb_cli/cli/commands/plugin.py:81 +#: nb_cli/cli/commands/plugin.py:83 msgid "List nonebot plugins published on nonebot homepage." msgstr "列出 NoneBot 官网上发布的插件." -#: nb_cli/cli/commands/plugin.py:88 nb_cli/cli/commands/plugin.py:104 -#: nb_cli/cli/commands/plugin.py:132 nb_cli/cli/commands/plugin.py:205 +#: nb_cli/cli/commands/plugin.py:90 +msgid "Whether to list installed plugins only in current project." +msgstr "是否只列出安装到当前项目的插件." + +#: nb_cli/cli/commands/plugin.py:97 nb_cli/cli/commands/plugin.py:117 +#: nb_cli/cli/commands/plugin.py:145 nb_cli/cli/commands/plugin.py:228 msgid "Whether to include unpublished plugins." msgstr "是否要包含已下架的插件." -#: nb_cli/cli/commands/plugin.py:94 nb_cli/cli/commands/plugin.py:115 +#: nb_cli/cli/commands/plugin.py:107 nb_cli/cli/commands/plugin.py:128 msgid "WARNING: Unpublished plugins may be included." msgstr "警告: 结果中可能含有已下架的插件." -#: nb_cli/cli/commands/plugin.py:98 +#: nb_cli/cli/commands/plugin.py:111 msgid "Search for nonebot plugins published on nonebot homepage." msgstr "搜索 NoneBot 官网上发布的插件." -#: nb_cli/cli/commands/plugin.py:110 +#: nb_cli/cli/commands/plugin.py:123 msgid "Plugin name to search:" msgstr "想要搜索的插件名称:" -#: nb_cli/cli/commands/plugin.py:122 +#: nb_cli/cli/commands/plugin.py:135 msgid "Install nonebot plugin to current project." msgstr "安装插件到当前项目." -#: nb_cli/cli/commands/plugin.py:147 +#: nb_cli/cli/commands/plugin.py:163 msgid "Plugin name to install:" msgstr "想要安装的插件名称:" -#: nb_cli/cli/commands/plugin.py:157 nb_cli/cli/commands/plugin.py:227 +#: nb_cli/cli/commands/plugin.py:174 +msgid "No available plugin found to install." +msgstr "没有可供安装的插件." + +#: nb_cli/cli/commands/plugin.py:180 nb_cli/cli/commands/plugin.py:253 msgid "" "WARNING: Unpublished plugins may be installed. These plugins may be " "unmaintained or unusable." msgstr "警告: 有可能安装已下架的插件. 其可能缺少维护或不可用." -#: nb_cli/cli/commands/plugin.py:169 +#: nb_cli/cli/commands/plugin.py:192 #, python-brace-format msgid "" "Errors occurred in installing plugin {plugin.name}\n" @@ -473,64 +497,72 @@ msgstr "" "*** 尝试使用 `--no-restrict-version` 选项执行 `nb plugin install` " "命令在宽松的版本约束下解析依赖可能可以解决此问题." -#: nb_cli/cli/commands/plugin.py:182 +#: nb_cli/cli/commands/plugin.py:205 #, python-brace-format msgid "Failed to add plugin {plugin.name} to config: {e}" msgstr "添加插件 {plugin.name} 到配置文件失败: {e}" -#: nb_cli/cli/commands/plugin.py:191 +#: nb_cli/cli/commands/plugin.py:214 #, python-brace-format msgid "Failed to add plugin {plugin.name} to dependencies: {e}" msgstr "添加插件 {plugin.name} 到依赖项失败: {e}" -#: nb_cli/cli/commands/plugin.py:198 +#: nb_cli/cli/commands/plugin.py:221 msgid "Update nonebot plugin." msgstr "更新插件." -#: nb_cli/cli/commands/plugin.py:217 +#: nb_cli/cli/commands/plugin.py:240 msgid "Plugin name to update:" msgstr "想要更新的插件名称:" -#: nb_cli/cli/commands/plugin.py:236 +#: nb_cli/cli/commands/plugin.py:247 +msgid "No installed plugin found to update." +msgstr "没有可供更新的插件:" + +#: nb_cli/cli/commands/plugin.py:262 #, python-brace-format msgid "Errors occurred in updating plugin {plugin.name}. Aborted." msgstr "更新插件 {plugin.name} 时发生错误. 已中止." -#: nb_cli/cli/commands/plugin.py:247 +#: nb_cli/cli/commands/plugin.py:273 #, python-brace-format msgid "Failed to update plugin {plugin.name} to dependencies: {e}" msgstr "更新插件 {plugin.name} 的依赖项失败: {e}" -#: nb_cli/cli/commands/plugin.py:256 +#: nb_cli/cli/commands/plugin.py:282 msgid "Uninstall nonebot plugin from current project." msgstr "移除当前项目中的插件." -#: nb_cli/cli/commands/plugin.py:264 +#: nb_cli/cli/commands/plugin.py:290 msgid "Plugin name to uninstall:" msgstr "想要移除的插件名称:" -#: nb_cli/cli/commands/plugin.py:279 +#: nb_cli/cli/commands/plugin.py:297 +msgid "No installed plugin found to uninstall." +msgstr "没有可供移除的插件." + +#: nb_cli/cli/commands/plugin.py:306 #, python-brace-format msgid "Failed to remove plugin {plugin.name} from config: {e}" msgstr "从配置文件中移除插件 {plugin.name} 失败: {e}" -#: nb_cli/cli/commands/plugin.py:290 +#: nb_cli/cli/commands/plugin.py:317 msgid "Create a new nonebot plugin." msgstr "创建一个新的插件." -#: nb_cli/cli/commands/plugin.py:299 +#: nb_cli/cli/commands/plugin.py:326 msgid "The plugin template to use." msgstr "使用的插件模板." -#: nb_cli/cli/commands/plugin.py:311 +#: nb_cli/cli/commands/plugin.py:338 msgid "Plugin name:" msgstr "插件名称:" -#: nb_cli/cli/commands/plugin.py:319 +#: nb_cli/cli/commands/plugin.py:346 msgid "Use nested plugin?" msgstr "使用嵌套插件?" -#: nb_cli/cli/commands/plugin.py:336 nb_cli/cli/commands/project.py:207 +#: nb_cli/cli/commands/plugin.py:363 nb_cli/cli/commands/project.py:206 msgid "Where to store the plugin?" msgstr "请输入插件存储位置:" @@ -542,212 +574,212 @@ msgstr "bootstrap (初学者或用户)" msgid "simple (for plugin developer)" msgstr "simple (插件开发者)" -#: nb_cli/cli/commands/project.py:84 +#: nb_cli/cli/commands/project.py:83 msgid "Loading adapters..." msgstr "正在加载适配器..." -#: nb_cli/cli/commands/project.py:86 +#: nb_cli/cli/commands/project.py:85 msgid "Loading drivers..." msgstr "正在加载驱动器..." -#: nb_cli/cli/commands/project.py:91 +#: nb_cli/cli/commands/project.py:90 msgid "Project Name:" msgstr "项目名称:" -#: nb_cli/cli/commands/project.py:93 +#: nb_cli/cli/commands/project.py:92 msgid "Invalid project name!" msgstr "无效的项目名称!" -#: nb_cli/cli/commands/project.py:101 +#: nb_cli/cli/commands/project.py:100 msgid "Which adapter(s) would you like to use?" msgstr "要使用哪些适配器?" -#: nb_cli/cli/commands/project.py:111 +#: nb_cli/cli/commands/project.py:110 msgid "You haven't chosen any adapter! Please confirm." msgstr "你没有选择任何适配器! 请确认." -#: nb_cli/cli/commands/project.py:125 +#: nb_cli/cli/commands/project.py:124 msgid "Which driver(s) would you like to use?" msgstr "要使用哪些驱动器?" -#: nb_cli/cli/commands/project.py:133 +#: nb_cli/cli/commands/project.py:132 msgid "Chosen drivers is not valid!" msgstr "选择的驱动器不合法!" -#: nb_cli/cli/commands/project.py:147 +#: nb_cli/cli/commands/project.py:146 msgid "User global (default, suitable for single instance in single user)" msgstr "用户全局 (默认, 适用于单用户下单实例)" -#: nb_cli/cli/commands/project.py:148 +#: nb_cli/cli/commands/project.py:147 msgid "Current project (suitable for multiple/portable instances)" msgstr "当前项目 (适用于多实例/便携实例)" -#: nb_cli/cli/commands/project.py:150 +#: nb_cli/cli/commands/project.py:149 msgid "" "User global (isolate by project name, suitable for multiple instances in " "single user)" msgstr "用户全局 (按项目名称隔离, 适用于单用户下多实例)" -#: nb_cli/cli/commands/project.py:153 +#: nb_cli/cli/commands/project.py:152 msgid "Custom storage location (for advanced users)" msgstr "自定义存储位置 (高级用户)" -#: nb_cli/cli/commands/project.py:158 +#: nb_cli/cli/commands/project.py:157 msgid "Which strategy of local storage would you like to use?" msgstr "要使用什么本地存储策略?" -#: nb_cli/cli/commands/project.py:181 +#: nb_cli/cli/commands/project.py:180 msgid "Cache directory to use:" msgstr "要使用的缓存目录:" -#: nb_cli/cli/commands/project.py:186 +#: nb_cli/cli/commands/project.py:185 msgid "Data directory to use:" msgstr "要使用的数据目录:" -#: nb_cli/cli/commands/project.py:191 +#: nb_cli/cli/commands/project.py:190 msgid "Config directory to use:" msgstr "要使用的配置目录:" -#: nb_cli/cli/commands/project.py:203 +#: nb_cli/cli/commands/project.py:202 #, python-brace-format msgid "1) In a \"{dir_name}\" folder" msgstr "1) 在 \"{dir_name}\" 文件夹中" -#: nb_cli/cli/commands/project.py:204 +#: nb_cli/cli/commands/project.py:203 msgid "2) In a \"src\" folder" msgstr "2) 在 \"src\" 文件夹中" -#: nb_cli/cli/commands/project.py:214 +#: nb_cli/cli/commands/project.py:213 msgid "Which developer tool(s) would you like to use?" msgstr "要使用哪些开发工具?" -#: nb_cli/cli/commands/project.py:216 nb_cli/cli/commands/project.py:217 +#: nb_cli/cli/commands/project.py:215 nb_cli/cli/commands/project.py:216 msgid " (Recommended)" msgstr " (推荐)" -#: nb_cli/cli/commands/project.py:219 +#: nb_cli/cli/commands/project.py:218 msgid " (Advanced user)" msgstr " (高级用户)" -#: nb_cli/cli/commands/project.py:224 +#: nb_cli/cli/commands/project.py:223 msgid "Cannot choose 'Pylance/Pyright' and 'BasedPyright' at the same time." msgstr "不能同时选择 'Pylance/Pyright' 和 'BasedPyright'." -#: nb_cli/cli/commands/project.py:241 +#: nb_cli/cli/commands/project.py:240 msgid "Create a NoneBot project." msgstr "创建一个 NoneBot 项目." -#: nb_cli/cli/commands/project.py:249 +#: nb_cli/cli/commands/project.py:248 msgid "The project template to use." msgstr "使用的项目模板." -#: nb_cli/cli/commands/project.py:254 +#: nb_cli/cli/commands/project.py:253 msgid "The python interpreter virtualenv is installed into." msgstr "虚拟环境使用的 Python 解释器." -#: nb_cli/cli/commands/project.py:271 +#: nb_cli/cli/commands/project.py:270 msgid "Select a template to use:" msgstr "选择一个要使用的模板:" -#: nb_cli/cli/commands/project.py:293 +#: nb_cli/cli/commands/project.py:292 msgid "Install dependencies now?" msgstr "立即安装依赖?" -#: nb_cli/cli/commands/project.py:306 +#: nb_cli/cli/commands/project.py:305 msgid "Create virtual environment?" msgstr "创建虚拟环境?" -#: nb_cli/cli/commands/project.py:313 +#: nb_cli/cli/commands/project.py:312 #, python-brace-format msgid "Creating virtual environment in {venv_dir} ..." msgstr "在 {venv_dir} 中创建虚拟环境..." -#: nb_cli/cli/commands/project.py:339 +#: nb_cli/cli/commands/project.py:338 msgid "Which builtin plugin(s) would you like to use?" msgstr "要使用哪些内置插件?" -#: nb_cli/cli/commands/project.py:352 +#: nb_cli/cli/commands/project.py:351 #, python-brace-format msgid "Failed to add builtin plugins {builtin_plugins} to config: {e}" msgstr "添加内置插件 {builtin_plugins} 到配置文件失败: {e}" -#: nb_cli/cli/commands/project.py:360 +#: nb_cli/cli/commands/project.py:359 msgid "" "Failed to install dependencies! You should install the dependencies " "manually." msgstr "安装依赖失败! 请手动安装依赖." -#: nb_cli/cli/commands/project.py:366 +#: nb_cli/cli/commands/project.py:365 msgid "Done!" msgstr "完成!" -#: nb_cli/cli/commands/project.py:369 +#: nb_cli/cli/commands/project.py:368 msgid "" "Add following packages to your project using dependency manager like " "poetry or pdm:" msgstr "使用 poetry 或 pdm 等依赖管理工具添加以下包:" -#: nb_cli/cli/commands/project.py:375 +#: nb_cli/cli/commands/project.py:374 msgid "Run the following command to start your bot:" msgstr "运行以下命令来启动你的机器人:" -#: nb_cli/cli/commands/project.py:381 +#: nb_cli/cli/commands/project.py:380 msgid "Generate entry file of your bot." msgstr "生成机器人的入口文件." -#: nb_cli/cli/commands/project.py:387 +#: nb_cli/cli/commands/project.py:386 msgid "The file script saved to." msgstr "脚本文件保存路径." -#: nb_cli/cli/commands/project.py:396 +#: nb_cli/cli/commands/project.py:395 msgid "Run the bot in current folder." msgstr "在当前文件夹中运行机器人." -#: nb_cli/cli/commands/project.py:403 +#: nb_cli/cli/commands/project.py:402 msgid "Exist entry file of your bot." msgstr "存在的机器人入口文件." -#: nb_cli/cli/commands/project.py:410 +#: nb_cli/cli/commands/project.py:409 msgid "Reload the bot when file changed." msgstr "当文件发生变化时重新加载机器人." -#: nb_cli/cli/commands/project.py:416 +#: nb_cli/cli/commands/project.py:415 msgid "Paths to watch for changes." msgstr "要监视变化的路径." -#: nb_cli/cli/commands/project.py:422 +#: nb_cli/cli/commands/project.py:421 msgid "Files to watch for changes." msgstr "要监视变化的文件." -#: nb_cli/cli/commands/project.py:428 +#: nb_cli/cli/commands/project.py:427 msgid "Files to ignore for changes." msgstr "要忽略变化的文件." -#: nb_cli/cli/commands/project.py:435 +#: nb_cli/cli/commands/project.py:434 msgid "Delay time for reloading in seconds." msgstr "重新加载的延迟时间(秒)." -#: nb_cli/cli/commands/project.py:468 +#: nb_cli/cli/commands/project.py:467 msgid "Upgrade the project format of your bot." msgstr "升级机器人的项目格式." -#: nb_cli/cli/commands/project.py:473 +#: nb_cli/cli/commands/project.py:472 msgid "Are you sure to upgrade the project format?" msgstr "你确定要升级项目格式吗?" -#: nb_cli/cli/commands/project.py:476 +#: nb_cli/cli/commands/project.py:475 msgid "Successfully upgraded project format." msgstr "成功升级项目格式." -#: nb_cli/cli/commands/project.py:480 +#: nb_cli/cli/commands/project.py:479 msgid "Downgrade the project format of your bot." msgstr "降级机器人的项目格式." -#: nb_cli/cli/commands/project.py:485 +#: nb_cli/cli/commands/project.py:484 msgid "Are you sure to downgrade the project format?" msgstr "你确定要降级项目格式吗?" -#: nb_cli/cli/commands/project.py:488 +#: nb_cli/cli/commands/project.py:487 msgid "Successfully downgraded project format." msgstr "成功降级项目格式." @@ -793,7 +825,7 @@ msgstr "无法找到项目根目录! {config_file} 文件不存在." msgid "Using python: {python_path}" msgstr "使用 Python: {python_path}" -#: nb_cli/config/parser.py:457 +#: nb_cli/config/parser.py:454 msgid "" "WARNING: Legacy configuration format detected.\n" "*** Use `nb upgrade-format` to upgrade to the new format." @@ -801,38 +833,38 @@ msgstr "" "警告: 检测到旧的项目格式.\n" "*** 使用 `nb upgrade-format` 升级至新格式." -#: nb_cli/handlers/meta.py:86 +#: nb_cli/handlers/meta.py:91 msgid "Cannot find a valid Python interpreter." msgstr "无法找到可用的 Python 解释器." -#: nb_cli/handlers/meta.py:127 nb_cli/handlers/meta.py:136 +#: nb_cli/handlers/meta.py:132 nb_cli/handlers/meta.py:141 msgid "Failed to get Python version." msgstr "获取 Python 版本失败." -#: nb_cli/handlers/meta.py:128 nb_cli/handlers/meta.py:191 -#: nb_cli/handlers/meta.py:250 +#: nb_cli/handlers/meta.py:133 nb_cli/handlers/meta.py:196 +#: nb_cli/handlers/meta.py:255 #, python-brace-format msgid "Exit code {code}" msgstr "退出码 {code}" -#: nb_cli/handlers/meta.py:155 +#: nb_cli/handlers/meta.py:160 #, python-brace-format msgid "Python {major}.{minor} is not supported." msgstr "Python {major}.{minor} 不受支持." -#: nb_cli/handlers/meta.py:190 nb_cli/handlers/meta.py:199 +#: nb_cli/handlers/meta.py:195 nb_cli/handlers/meta.py:204 msgid "Failed to get NoneBot version." msgstr "获取 NoneBot 版本失败." -#: nb_cli/handlers/meta.py:217 +#: nb_cli/handlers/meta.py:222 msgid "NoneBot is not installed." msgstr "NoneBot 未安装." -#: nb_cli/handlers/meta.py:249 nb_cli/handlers/meta.py:258 +#: nb_cli/handlers/meta.py:254 nb_cli/handlers/meta.py:263 msgid "Failed to get pip version." msgstr "获取 pip 版本失败." -#: nb_cli/handlers/meta.py:276 +#: nb_cli/handlers/meta.py:281 msgid "pip is not installed." msgstr "pip 未安装." @@ -845,7 +877,7 @@ msgstr "当前格式已为新格式." msgid "WARNING: Inconsistent adapter name info: {old!r} -> {new!r}" msgstr "警告: 适配器名称信息不一致: {old!r} -> {new!r}" -#: nb_cli/handlers/project.py:189 +#: nb_cli/handlers/project.py:190 msgid "Current format is already the old format." msgstr "当前格式已为旧格式." @@ -877,32 +909,32 @@ msgstr "停止重载监视" msgid "WARNING: Cache directory is unavailable." msgstr "警告: 缓存目录不可用." -#: nb_cli/handlers/store.py:121 nb_cli/handlers/store.py:212 +#: nb_cli/handlers/store.py:123 nb_cli/handlers/store.py:214 #, python-brace-format msgid "Invalid module type: {module_type}" msgstr "无效的模块类型: {module_type}" -#: nb_cli/handlers/store.py:157 +#: nb_cli/handlers/store.py:159 #, python-brace-format msgid "WARNING: Failed to cache data for module {module_type}." msgstr "警告: {module_type} 模块类型数据缓存失败." -#: nb_cli/handlers/store.py:166 +#: nb_cli/handlers/store.py:168 #, python-brace-format msgid "WARNING: Failed to update unpublished data for module {module_type}." msgstr "警告: {module_type} 模块类型下架数据更新失败." -#: nb_cli/handlers/store.py:176 +#: nb_cli/handlers/store.py:178 #, python-brace-format msgid "Failed to get {module_type} list." msgstr "获取 {module_type} 列表失败." -#: nb_cli/handlers/store.py:227 +#: nb_cli/handlers/store.py:229 #, python-brace-format msgid "Invalid local cache of module type: {module_type}" msgstr "无效的模块类型缓存: {module_type}" -#: nb_cli/handlers/store.py:263 +#: nb_cli/handlers/store.py:265 #, python-brace-format msgid "" "WARNING: Failed to download latest data of module {module_type}. Expired "