Skip to content

Conversation

@AstralSolipsism
Copy link

@AstralSolipsism AstralSolipsism commented Jan 27, 2026

Motivation / 动机

本次改动旨在为后续“插件排序/执行顺序可配置”功能做前置铺垫:先在后端补齐“优先级覆盖”数据层与管理接口,并在前端提供最低限度的优先级展示,便于验证与后续扩展。

具体解决的问题:

  • 当前插件处理器(handler)优先级仅存在于代码/元数据中,缺少统一的查询与配置入口。在插件生态日益复杂的未来,我认为有必要引入插件优先级管理,同时考虑兼容支持后续的优先级排序 UI 与持久化配置。
  • 在不引入排序交互的前提下,先提供可观测性(展示与接口)降低后续迭代成本。

Modifications / 改动点

  • 新增后端优先级管理API:
    • GET /api/plugin/priority: 获取所有插件及其处理器的优先级信息(包含原始值与覆盖后的有效值)
    • POST /api/plugin/priority: 更新处理器优先级(持久化覆盖配置)
    • POST /api/plugin/priority/reset: 重置优先级覆盖(支持指定 handler 或全部重置)
  • 通过 shared_preferences 持久化优先级覆盖配置(handler_priority_overrides)
  • 在核心生命周期初始化时加载优先级覆盖,使重启后仍能生效
  • StarHandlerRegistry 支持优先级覆盖并按“有效优先级”排序(优先级高的先执行)
  • 为 CommandDescriptor 与指令相关 API 响应补充 priority 字段,便于前端展示
  • 在 dashboard CommandTable 组件中新增“优先级”列,仅用于展示(暂不提供排序/拖拽编辑功能)
  • 添加优先级列的 i18n 翻译(中文/英文)

涉及核心文件:

  • 后端:

    • astrbot/dashboard/routes/plugin.py
    • astrbot/core/star/star_handler.py
    • astrbot/core/core_lifecycle.py
    • astrbot/core/star/command_management.py
  • 前端:

    • dashboard/src/components/extension/componentPanel/components/CommandTable.vue
    • dashboard/src/components/extension/componentPanel/types.ts
    • dashboard/src/i18n/locales/zh-CN/features/command.json
    • dashboard/src/i18n/locales/en-US/features/command.json
  • This is NOT a breaking change. / 这不是一个破坏性变更。

显示效果
image


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

引入后端插件处理器优先级管理与持久化机制,并通过新的 API 对外暴露,同时在仪表盘 UI 中展示处理器/命令优先级。

新功能:

  • 新增 REST API,用于查询、更新和重置插件处理器优先级,包括每个处理器的覆写状态以及过滤器汇总信息。
  • 将处理器优先级覆写信息持久化存储,并在核心生命周期初始化时加载,使被覆写的优先级在重启后仍然生效。
  • 通过命令描述符和 API 响应对前端暴露处理器优先级值,便于前端使用。
  • 在仪表盘命令列表中新增优先级列,并提供中英文本地化。

改进:

  • 扩展星号处理器注册表以支持运行时优先级覆写,并按“有效优先级”而不仅仅是静态元数据对处理器进行排序。
Original summary in English

Summary by Sourcery

Introduce backend plugin handler priority management with persistence and expose it via new APIs, and surface handler/command priorities in the dashboard UI.

New Features:

  • Add REST APIs to query, update, and reset plugin handler priorities, including per-handler override status and filter summaries.
  • Persist handler priority overrides and load them during core lifecycle initialization so overridden priorities survive restarts.
  • Expose handler priority values through command descriptors and API responses for frontend consumption.
  • Display a priority column in the dashboard command table, localized in Chinese and English.

Enhancements:

  • Extend the star handler registry to support runtime priority overrides and sort handlers by effective priority rather than only static metadata.

- 新增后端优先级管理API:

  - GET /api/plugin/priority: 获取所有处理器优先级信息

  - POST /api/plugin/priority: 更新处理器优先级

  - POST /api/plugin/priority/reset: 重置优先级为原始值

- 通过 shared_preferences 持久化优先级覆盖配置

- 在核心生命周期初始化时加载优先级覆盖

- 为 CommandDescriptor 和指令 API 响应添加 priority 字段

- 在 dashboard CommandTable 组件中添加优先级列展示

- 添加优先级列的 i18n 翻译 (中文/英文)
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jan 27, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并给出了一些总体反馈:

  • get_priority 中,备用描述字符串 '无描述' 以及 filters_summary 的取值('auto', 'event_listener' 等)是硬编码且未本地化的,这会导致 API 使用方收到混合语言的内容;建议要么保持这些字段与语言无关(例如使用代码/枚举),要么把它们接入现有的 i18n 机制。
  • get_priorityupdate_priorityreset_priority 中宽泛的 except Exception 捕获会让调试变得更困难,也可能掩盖编程错误;更好的做法是显式捕获并处理预期的错误类型,让非预期异常继续抛出(或者至少在重新记录日志时加入更多结构化上下文)。
面向 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- In `get_priority`, the fallback description string `'无描述'` and the `filters_summary` values (`'auto'`, `'event_listener'`, etc.) are hardcoded and not localized, which means API consumers will get mixed-language content; consider either keeping these fields language-agnostic (e.g., codes) or wiring them through the existing i18n mechanism.
- The broad `except Exception` blocks in `get_priority`, `update_priority`, and `reset_priority` make debugging harder and can hide programming errors; it would be better to catch and handle expected error types explicitly and let unexpected exceptions surface (or at least re-log them with more structured context).

## Individual Comments

### Comment 1
<location> `astrbot/core/star/command_management.py:292` </location>
<code_context>
         is_group=isinstance(filter_ref, CommandGroupFilter),
         is_sub_command=is_sub_command,
         reserved=plugin_meta.reserved if plugin_meta else False,
+        priority=handler.extras_configs.get("priority", 0),
     )
     return descriptor
</code_context>

<issue_to_address>
**question:** Consider exposing effective priority (respecting overrides) rather than only the static extras_configs priority.

Since `CommandDescriptor.priority` comes directly from `handler.extras_configs.get("priority", 0)`, it won’t reflect any overrides applied by `StarHandlerRegistry.load_priority_overrides`. If this priority is meant to match actual execution order (e.g., in the command table UI), consider passing the effective priority instead—either via a `StarHandlerRegistry.get_effective_priority(handler)` helper or by injecting the effective value when building the descriptor—so the displayed priority matches the one used for ordering.
</issue_to_address>

### Comment 2
<location> `astrbot/dashboard/routes/plugin.py:421` </location>
<code_context>
+
+        return ", ".join(parts) if parts else "event_listener"
+
+    async def get_priority(self):
+        try:
+            stored = await sp.global_get("handler_priority_overrides", {})
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting shared storage/priority handling, validation, and filter-summary logic into small helpers to make these endpoints shorter and easier to follow.

You can reduce duplication and make the flow easier to follow by extracting the repeated storage / map handling and the error wrapper into small helpers, and by centralizing some validation. Here are a few focused refactors that keep behavior intact:

### 1. Extract storage + priority map handling

You build `stored` and `priority_map` in all three endpoints. Pull that into helpers:

```python
async def _load_priority_overrides(self) -> tuple[dict, dict[str, int]]:
    stored = await sp.global_get("handler_priority_overrides", {})
    if not isinstance(stored, dict):
        stored = {}
    priority_map: dict[str, int] = {}
    for handler_full_name, v in stored.items():
        if isinstance(v, dict) and "priority" in v:
            priority_map[handler_full_name] = int(v.get("priority", 0))
    return stored, priority_map

async def _save_priority_overrides(
    self,
    stored: dict,
) -> dict[str, int]:
    await sp.global_put("handler_priority_overrides", stored)
    # rebuild priority_map from stored
    priority_map = {
        k: v.get("priority", 0)
        for k, v in stored.items()
        if isinstance(v, dict)
    }
    star_handlers_registry.load_priority_overrides(priority_map)
    return priority_map
```

Then your endpoints shrink:

```python
async def get_priority(self):
    try:
        stored, priority_map = await self._load_priority_overrides()
        star_handlers_registry.load_priority_overrides(priority_map)
        # existing plugin/handler iteration logic unchanged...
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

```python
async def update_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        updates = data.get("updates", []) if isinstance(data, dict) else []
        # validation (see below)...
        stored, priority_map = await self._load_priority_overrides()
        # apply updates to stored + priority_map...
        await self._save_priority_overrides(stored)
        return Response().ok(None, "优先级更新成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

```python
async def reset_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        # validation (see below)...
        stored, _ = await self._load_priority_overrides()
        if reset_all:
            stored = {}
        else:
            for h in handler_full_names:
                if isinstance(h, str):
                    stored.pop(h, None)
        await self._save_priority_overrides(stored)
        return Response().ok(None, "重置成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority/reset: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

This keeps all behavior but makes the main handlers read as “parse/validate → apply → save”.

### 2. Centralize `update_priority` validation

You can pull the inline checks into a helper that returns a normalized, validated list of updates or an error response:

```python
def _validate_priority_updates(self, data: Any) -> tuple[list[dict] | None, dict | None]:
    updates = data.get("updates", []) if isinstance(data, dict) else []
    if not isinstance(updates, list) or not updates:
        return None, Response().error("updates fields must be a non-empty list").__dict__

    normalized: list[dict] = []
    for item in updates:
        if not isinstance(item, dict):
            continue
        handler_full_name = item.get("handler_full_name")
        if not isinstance(handler_full_name, str) or not handler_full_name:
            continue
        if "priority" not in item:
            continue
        try:
            new_priority = int(item["priority"])
        except (TypeError, ValueError):
            return None, Response().error(
                f"invalid priority for handler: {handler_full_name}"
            ).__dict__
        handler = star_handlers_registry.get_handler_by_full_name(handler_full_name)
        if handler is None:
            return None, Response().error(
                f"handler not found: {handler_full_name}"
            ).__dict__
        normalized.append(
            {
                "handler": handler,
                "handler_full_name": handler_full_name,
                "priority": new_priority,
            }
        )

    if not normalized:
        return None, Response().error(
            "updates fields must contain at least one valid entry"
        ).__dict__

    return normalized, None
```

Use it inside `update_priority`:

```python
async def update_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        updates, error = self._validate_priority_updates(data)
        if error:
            return error

        stored, priority_map = await self._load_priority_overrides()
        for item in updates:
            handler = item["handler"]
            handler_full_name = item["handler_full_name"]
            new_priority = item["priority"]
            original_priority = handler.extras_configs.get("priority", 0)
            stored[handler_full_name] = {
                "priority": new_priority,
                "original_priority": original_priority,
            }
            priority_map[handler_full_name] = new_priority

        await self._save_priority_overrides(stored)
        return Response().ok(None, "优先级更新成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

This removes most of the scattered inline checks and early returns from the main method.

### 3. Normalize `reset_priority` request parsing

Similarly, you can pull `reset_all` / `handler_full_names` parsing into a helper to avoid nested conditionals:

```python
def _parse_reset_request(self, data: Any) -> tuple[bool, list[str] | None, dict | None]:
    if not isinstance(data, dict):
        return False, None, Response().error("invalid request body").__dict__

    reset_all = data.get("reset_all", False) is True
    handler_full_names = data.get("handler_full_names", [])

    if reset_all:
        return True, [], None

    if not isinstance(handler_full_names, list) or not handler_full_names:
        return False, None, Response().error(
            "handler_full_names must be a non-empty list or reset_all=true"
        ).__dict__

    valid_names = [h for h in handler_full_names if isinstance(h, str)]
    if not valid_names:
        return False, None, Response().error(
            "handler_full_names must contain at least one valid string"
        ).__dict__

    return False, valid_names, None
```

Usage:

```python
async def reset_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        reset_all, handler_full_names, error = self._parse_reset_request(data)
        if error:
            return error

        stored, _ = await self._load_priority_overrides()
        if reset_all:
            stored = {}
        else:
            for h in handler_full_names:
                stored.pop(h, None)

        await self._save_priority_overrides(stored)
        return Response().ok(None, "重置成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority/reset: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

This keeps the logic identical while making it easier to extend with new filter types.

### 4. Optional: Simplify `_build_filters_summary`

You can separate type → text mapping to avoid growing `if/elif` chains:

```python
def _filter_to_summary(self, f: Any) -> str | None:
    if isinstance(f, CommandFilter):
        base = getattr(f, "parent_command_names", None)
        base = base[0] if base else ""
        cmd = f"{base} {f.command_name}".strip()
        return f"command: {cmd}"
    if isinstance(f, CommandGroupFilter):
        try:
            cmd = f.get_complete_command_names()[0].strip()
        except Exception:
            cmd = "command_group"
        return f"command_group: {cmd}"
    if isinstance(f, RegexFilter):
        return f"regex: {f.regex_str}"
    if isinstance(f, PermissionTypeFilter):
        return "permission"
    return None

def _build_filters_summary(self, handler: StarHandlerMetadata) -> str:
    if handler.event_type != EventType.AdapterMessageEvent:
        return "auto"

    parts = []
    for f in handler.event_filters:
        summary = self._filter_to_summary(f)
        if summary:
            parts.append(summary)
    return ", ".join(parts) if parts else "event_listener"
```

This keeps the logic identical while making it easier to extend with new filter types.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这些 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续的 Review。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • In get_priority, the fallback description string '无描述' and the filters_summary values ('auto', 'event_listener', etc.) are hardcoded and not localized, which means API consumers will get mixed-language content; consider either keeping these fields language-agnostic (e.g., codes) or wiring them through the existing i18n mechanism.
  • The broad except Exception blocks in get_priority, update_priority, and reset_priority make debugging harder and can hide programming errors; it would be better to catch and handle expected error types explicitly and let unexpected exceptions surface (or at least re-log them with more structured context).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `get_priority`, the fallback description string `'无描述'` and the `filters_summary` values (`'auto'`, `'event_listener'`, etc.) are hardcoded and not localized, which means API consumers will get mixed-language content; consider either keeping these fields language-agnostic (e.g., codes) or wiring them through the existing i18n mechanism.
- The broad `except Exception` blocks in `get_priority`, `update_priority`, and `reset_priority` make debugging harder and can hide programming errors; it would be better to catch and handle expected error types explicitly and let unexpected exceptions surface (or at least re-log them with more structured context).

## Individual Comments

### Comment 1
<location> `astrbot/core/star/command_management.py:292` </location>
<code_context>
         is_group=isinstance(filter_ref, CommandGroupFilter),
         is_sub_command=is_sub_command,
         reserved=plugin_meta.reserved if plugin_meta else False,
+        priority=handler.extras_configs.get("priority", 0),
     )
     return descriptor
</code_context>

<issue_to_address>
**question:** Consider exposing effective priority (respecting overrides) rather than only the static extras_configs priority.

Since `CommandDescriptor.priority` comes directly from `handler.extras_configs.get("priority", 0)`, it won’t reflect any overrides applied by `StarHandlerRegistry.load_priority_overrides`. If this priority is meant to match actual execution order (e.g., in the command table UI), consider passing the effective priority instead—either via a `StarHandlerRegistry.get_effective_priority(handler)` helper or by injecting the effective value when building the descriptor—so the displayed priority matches the one used for ordering.
</issue_to_address>

### Comment 2
<location> `astrbot/dashboard/routes/plugin.py:421` </location>
<code_context>
+
+        return ", ".join(parts) if parts else "event_listener"
+
+    async def get_priority(self):
+        try:
+            stored = await sp.global_get("handler_priority_overrides", {})
</code_context>

<issue_to_address>
**issue (complexity):** Consider extracting shared storage/priority handling, validation, and filter-summary logic into small helpers to make these endpoints shorter and easier to follow.

You can reduce duplication and make the flow easier to follow by extracting the repeated storage / map handling and the error wrapper into small helpers, and by centralizing some validation. Here are a few focused refactors that keep behavior intact:

### 1. Extract storage + priority map handling

You build `stored` and `priority_map` in all three endpoints. Pull that into helpers:

```python
async def _load_priority_overrides(self) -> tuple[dict, dict[str, int]]:
    stored = await sp.global_get("handler_priority_overrides", {})
    if not isinstance(stored, dict):
        stored = {}
    priority_map: dict[str, int] = {}
    for handler_full_name, v in stored.items():
        if isinstance(v, dict) and "priority" in v:
            priority_map[handler_full_name] = int(v.get("priority", 0))
    return stored, priority_map

async def _save_priority_overrides(
    self,
    stored: dict,
) -> dict[str, int]:
    await sp.global_put("handler_priority_overrides", stored)
    # rebuild priority_map from stored
    priority_map = {
        k: v.get("priority", 0)
        for k, v in stored.items()
        if isinstance(v, dict)
    }
    star_handlers_registry.load_priority_overrides(priority_map)
    return priority_map
```

Then your endpoints shrink:

```python
async def get_priority(self):
    try:
        stored, priority_map = await self._load_priority_overrides()
        star_handlers_registry.load_priority_overrides(priority_map)
        # existing plugin/handler iteration logic unchanged...
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

```python
async def update_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        updates = data.get("updates", []) if isinstance(data, dict) else []
        # validation (see below)...
        stored, priority_map = await self._load_priority_overrides()
        # apply updates to stored + priority_map...
        await self._save_priority_overrides(stored)
        return Response().ok(None, "优先级更新成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

```python
async def reset_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        # validation (see below)...
        stored, _ = await self._load_priority_overrides()
        if reset_all:
            stored = {}
        else:
            for h in handler_full_names:
                if isinstance(h, str):
                    stored.pop(h, None)
        await self._save_priority_overrides(stored)
        return Response().ok(None, "重置成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority/reset: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

This keeps all behavior but makes the main handlers read as “parse/validate → apply → save”.

### 2. Centralize `update_priority` validation

You can pull the inline checks into a helper that returns a normalized, validated list of updates or an error response:

```python
def _validate_priority_updates(self, data: Any) -> tuple[list[dict] | None, dict | None]:
    updates = data.get("updates", []) if isinstance(data, dict) else []
    if not isinstance(updates, list) or not updates:
        return None, Response().error("updates fields must be a non-empty list").__dict__

    normalized: list[dict] = []
    for item in updates:
        if not isinstance(item, dict):
            continue
        handler_full_name = item.get("handler_full_name")
        if not isinstance(handler_full_name, str) or not handler_full_name:
            continue
        if "priority" not in item:
            continue
        try:
            new_priority = int(item["priority"])
        except (TypeError, ValueError):
            return None, Response().error(
                f"invalid priority for handler: {handler_full_name}"
            ).__dict__
        handler = star_handlers_registry.get_handler_by_full_name(handler_full_name)
        if handler is None:
            return None, Response().error(
                f"handler not found: {handler_full_name}"
            ).__dict__
        normalized.append(
            {
                "handler": handler,
                "handler_full_name": handler_full_name,
                "priority": new_priority,
            }
        )

    if not normalized:
        return None, Response().error(
            "updates fields must contain at least one valid entry"
        ).__dict__

    return normalized, None
```

Use it inside `update_priority`:

```python
async def update_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        updates, error = self._validate_priority_updates(data)
        if error:
            return error

        stored, priority_map = await self._load_priority_overrides()
        for item in updates:
            handler = item["handler"]
            handler_full_name = item["handler_full_name"]
            new_priority = item["priority"]
            original_priority = handler.extras_configs.get("priority", 0)
            stored[handler_full_name] = {
                "priority": new_priority,
                "original_priority": original_priority,
            }
            priority_map[handler_full_name] = new_priority

        await self._save_priority_overrides(stored)
        return Response().ok(None, "优先级更新成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

This removes most of the scattered inline checks and early returns from the main method.

### 3. Normalize `reset_priority` request parsing

Similarly, you can pull `reset_all` / `handler_full_names` parsing into a helper to avoid nested conditionals:

```python
def _parse_reset_request(self, data: Any) -> tuple[bool, list[str] | None, dict | None]:
    if not isinstance(data, dict):
        return False, None, Response().error("invalid request body").__dict__

    reset_all = data.get("reset_all", False) is True
    handler_full_names = data.get("handler_full_names", [])

    if reset_all:
        return True, [], None

    if not isinstance(handler_full_names, list) or not handler_full_names:
        return False, None, Response().error(
            "handler_full_names must be a non-empty list or reset_all=true"
        ).__dict__

    valid_names = [h for h in handler_full_names if isinstance(h, str)]
    if not valid_names:
        return False, None, Response().error(
            "handler_full_names must contain at least one valid string"
        ).__dict__

    return False, valid_names, None
```

Usage:

```python
async def reset_priority(self):
    if DEMO_MODE:
        return Response().error(
            "You are not permitted to do this operation in demo mode"
        ).__dict__

    try:
        data = await request.get_json()
        reset_all, handler_full_names, error = self._parse_reset_request(data)
        if error:
            return error

        stored, _ = await self._load_priority_overrides()
        if reset_all:
            stored = {}
        else:
            for h in handler_full_names:
                stored.pop(h, None)

        await self._save_priority_overrides(stored)
        return Response().ok(None, "重置成功").__dict__
    except Exception as e:
        logger.error(f"/api/plugin/priority/reset: {traceback.format_exc()}")
        return Response().error(str(e)).__dict__
```

### 4. Optional: Simplify `_build_filters_summary`

You can separate type → text mapping to avoid growing `if/elif` chains:

```python
def _filter_to_summary(self, f: Any) -> str | None:
    if isinstance(f, CommandFilter):
        base = getattr(f, "parent_command_names", None)
        base = base[0] if base else ""
        cmd = f"{base} {f.command_name}".strip()
        return f"command: {cmd}"
    if isinstance(f, CommandGroupFilter):
        try:
            cmd = f.get_complete_command_names()[0].strip()
        except Exception:
            cmd = "command_group"
        return f"command_group: {cmd}"
    if isinstance(f, RegexFilter):
        return f"regex: {f.regex_str}"
    if isinstance(f, PermissionTypeFilter):
        return "permission"
    return None

def _build_filters_summary(self, handler: StarHandlerMetadata) -> str:
    if handler.event_type != EventType.AdapterMessageEvent:
        return "auto"

    parts = []
    for f in handler.event_filters:
        summary = self._filter_to_summary(f)
        if summary:
            parts.append(summary)
    return ", ".join(parts) if parts else "event_listener"
```

This keeps the logic identical while making it easier to extend with new filter types.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. labels Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend area:webui The bug / feature is about webui(dashboard) of astrbot. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant