diff --git a/astrbot/core/star/command_management.py b/astrbot/core/star/command_management.py index 190ef2000..3801932b0 100644 --- a/astrbot/core/star/command_management.py +++ b/astrbot/core/star/command_management.py @@ -90,6 +90,7 @@ async def toggle_command(handler_full_name: str, enabled: bool) -> CommandDescri async def rename_command( handler_full_name: str, new_fragment: str, + aliases: list[str] | None = None, ) -> CommandDescriptor: descriptor = _build_descriptor_by_full_name(handler_full_name) if not descriptor: @@ -99,9 +100,24 @@ async def rename_command( if not new_fragment: raise ValueError("指令名不能为空。") + # 校验主指令名 candidate_full = _compose_command(descriptor.parent_signature, new_fragment) if _is_command_in_use(handler_full_name, candidate_full): - raise ValueError("新的指令名已被其他指令占用,请换一个名称。") + raise ValueError(f"指令名 '{candidate_full}' 已被其他指令占用。") + + # 校验别名 + if aliases: + for alias in aliases: + alias = alias.strip() + if not alias: + continue + alias_full = _compose_command(descriptor.parent_signature, alias) + if _is_command_in_use(handler_full_name, alias_full): + raise ValueError(f"别名 '{alias_full}' 已被其他指令占用。") + + existing_cfg = await db_helper.get_command_config(handler_full_name) + merged_extra = dict(existing_cfg.extra_data or {}) if existing_cfg else {} + merged_extra["resolved_aliases"] = aliases or [] config = await db_helper.upsert_command_config( handler_full_name=handler_full_name, @@ -114,7 +130,7 @@ async def rename_command( conflict_key=descriptor.original_command, resolution_strategy="manual_rename", note=None, - extra_data=None, + extra_data=merged_extra, auto_managed=False, ) _bind_descriptor_with_config(descriptor, config) @@ -363,14 +379,27 @@ def _apply_config_to_descriptor( new_fragment, ) + extra = config.extra_data or {} + resolved_aliases = extra.get("resolved_aliases") + if isinstance(resolved_aliases, list): + descriptor.aliases = [str(x) for x in resolved_aliases if str(x).strip()] + def _apply_config_to_runtime( descriptor: CommandDescriptor, config: CommandConfig, ) -> None: descriptor.handler.enabled = config.enabled - if descriptor.filter_ref and descriptor.current_fragment: - _set_filter_fragment(descriptor.filter_ref, descriptor.current_fragment) + if descriptor.filter_ref: + if descriptor.current_fragment: + _set_filter_fragment(descriptor.filter_ref, descriptor.current_fragment) + extra = config.extra_data or {} + resolved_aliases = extra.get("resolved_aliases") + if isinstance(resolved_aliases, list): + _set_filter_aliases( + descriptor.filter_ref, + [str(x) for x in resolved_aliases if str(x).strip()], + ) def _bind_configs_to_descriptors( @@ -409,6 +438,18 @@ def _set_filter_fragment( filter_ref._cmpl_cmd_names = None +def _set_filter_aliases( + filter_ref: CommandFilter | CommandGroupFilter, + aliases: list[str], +) -> None: + current_aliases = getattr(filter_ref, "alias", set()) + if set(aliases) == current_aliases: + return + setattr(filter_ref, "alias", set(aliases)) + if hasattr(filter_ref, "_cmpl_cmd_names"): + filter_ref._cmpl_cmd_names = None + + def _is_command_in_use( target_handler_full_name: str, candidate_full_command: str, diff --git a/astrbot/dashboard/routes/command.py b/astrbot/dashboard/routes/command.py index 5cb267169..abd38d886 100644 --- a/astrbot/dashboard/routes/command.py +++ b/astrbot/dashboard/routes/command.py @@ -61,12 +61,13 @@ async def rename_command(self): data = await request.get_json() handler_full_name = data.get("handler_full_name") new_name = data.get("new_name") + aliases = data.get("aliases") if not handler_full_name or not new_name: return Response().error("handler_full_name 与 new_name 均为必填。").__dict__ try: - await rename_command_service(handler_full_name, new_name) + await rename_command_service(handler_full_name, new_name, aliases=aliases) except ValueError as exc: return Response().error(str(exc)).__dict__ diff --git a/dashboard/src/components/extension/componentPanel/components/RenameDialog.vue b/dashboard/src/components/extension/componentPanel/components/RenameDialog.vue index ffdc5a826..bd88c02e4 100644 --- a/dashboard/src/components/extension/componentPanel/components/RenameDialog.vue +++ b/dashboard/src/components/extension/componentPanel/components/RenameDialog.vue @@ -1,14 +1,16 @@ @@ -32,7 +68,49 @@ const emit = defineEmits<{ variant="outlined" density="compact" autofocus + class="mb-2" /> + + + + {{ tm('dialogs.rename.aliases') }} + {{ showAliasEditor ? 'mdi-chevron-up' : 'mdi-chevron-down' }} + + + + + + + + + + {{ tm('dialogs.rename.addAlias') }} + + + + diff --git a/dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts b/dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts index a285c473f..ef900dc87 100644 --- a/dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts +++ b/dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts @@ -14,6 +14,7 @@ export function useCommandActions( show: false, command: null, newName: '', + aliases: [], loading: false }); @@ -53,6 +54,7 @@ export function useCommandActions( const openRenameDialog = (cmd: CommandItem) => { renameDialog.command = cmd; renameDialog.newName = cmd.current_fragment || ''; + renameDialog.aliases = [...(cmd.aliases || [])]; renameDialog.show = true; }; @@ -66,7 +68,8 @@ export function useCommandActions( try { const res = await axios.post('/api/commands/rename', { handler_full_name: renameDialog.command.handler_full_name, - new_name: renameDialog.newName.trim() + new_name: renameDialog.newName.trim(), + aliases: renameDialog.aliases.filter(a => a.trim()) }); if (res.data.status === 'ok') { toast(successMessage, 'success'); diff --git a/dashboard/src/components/extension/componentPanel/index.vue b/dashboard/src/components/extension/componentPanel/index.vue index 912af9156..66efe147d 100644 --- a/dashboard/src/components/extension/componentPanel/index.vue +++ b/dashboard/src/components/extension/componentPanel/index.vue @@ -288,6 +288,8 @@ watch(viewMode, async (mode) => { @update:show="renameDialog.show = $event" :new-name="renameDialog.newName" @update:new-name="renameDialog.newName = $event" + :aliases="renameDialog.aliases" + @update:aliases="renameDialog.aliases = $event" :command="renameDialog.command" :loading="renameDialog.loading" @confirm="handleConfirmRename" diff --git a/dashboard/src/components/extension/componentPanel/types.ts b/dashboard/src/components/extension/componentPanel/types.ts index d2b388ec9..e798dec71 100644 --- a/dashboard/src/components/extension/componentPanel/types.ts +++ b/dashboard/src/components/extension/componentPanel/types.ts @@ -52,6 +52,7 @@ export interface RenameDialogState { show: boolean; command: CommandItem | null; newName: string; + aliases: string[]; loading: boolean; } diff --git a/dashboard/src/i18n/locales/en-US/features/command.json b/dashboard/src/i18n/locales/en-US/features/command.json index ab17d7bb0..4c445402d 100644 --- a/dashboard/src/i18n/locales/en-US/features/command.json +++ b/dashboard/src/i18n/locales/en-US/features/command.json @@ -45,6 +45,8 @@ "rename": { "title": "Rename Command", "newName": "New command name", + "aliases": "Manage aliases", + "addAlias": "Add alias", "cancel": "Cancel", "confirm": "Confirm" }, diff --git a/dashboard/src/i18n/locales/zh-CN/features/command.json b/dashboard/src/i18n/locales/zh-CN/features/command.json index 514a9837a..321888ebb 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/command.json +++ b/dashboard/src/i18n/locales/zh-CN/features/command.json @@ -45,6 +45,8 @@ "rename": { "title": "重命名指令", "newName": "新指令名", + "aliases": "管理别名", + "addAlias": "添加别名", "cancel": "取消", "confirm": "确认" },