-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor: 优化 packages/astrbot 内置插件的代码结构以提高可维护性和可读性 #2924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Contributor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `packages/astrbot/main.py:33` </location>
<code_context>
+)
class Main(star.Star):
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring command handler delegation by using mix-in inheritance for command classes instead of per-method delegation.
```markdown
Instead of composing and then writing one‐line delegators for every command, you can turn each `*Commands` class into a mix-in and let `Main` inherit them directly. All decorated methods live in the mix-ins, so you:
1. Remove all the `self.xxx_c = XxxCommand(...)` lines from `Main.__init__`.
2. Drop every `async def help/llm/tool_ls/…` wrapper in `Main`.
3. Change your class signature to inherit the command mix-ins:
```python
from .commands import (
HelpCommand, LLMCommands, ToolCommands, PluginCommands,
AdminCommands, ConversationCommands, ProviderCommands,
PersonaCommands, AlterCmdCommands, SetUnsetCommands,
T2ICommand, TTSCommand, SIDCommand,
)
class Main(
star.Star,
HelpCommand, LLMCommands, ToolCommands, PluginCommands,
AdminCommands, ConversationCommands, ProviderCommands,
PersonaCommands, AlterCmdCommands, SetUnsetCommands,
T2ICommand, TTSCommand, SIDCommand,
):
def __init__(self, context: star.Context) -> None:
super().__init__(context)
self.context = context
cfg = context.get_config()
self.timezone = cfg.get("timezone") or None
if self.timezone:
logger.info(f"Timezone set to: {self.timezone}")
try:
self.ltm = LongTermMemory(self.context.astrbot_config_mgr, self.context)
except Exception as e:
logger.error(f"聊天增强 err: {e}")
```
4. In each mix-in, refer to `self.context` (from `Main`) instead of storing it again.
This removes a dozen boilerplate attributes and one‐liner methods, while still keeping every decorated handler in place.
```
</issue_to_address>
### Comment 2
<location> `packages/astrbot/commands/alter_cmd.py:12` </location>
<code_context>
+from enum import Enum
+
+
+class RstScene(Enum):
+ GROUP_UNIQUE_ON = ("group_unique_on", "群聊+会话隔离开启")
+ GROUP_UNIQUE_OFF = ("group_unique_off", "群聊+会话隔离关闭")
</code_context>
<issue_to_address>
**issue (complexity):** Consider moving RstScene to a shared module and refactoring alter_cmd into smaller private methods for clarity.
```suggestion
// 1) Move RstScene into a shared module instead of re-defining it:
– class RstScene(Enum): …
+ // in astrbot/core/star/rst_scene.py
+ class RstScene(Enum):
+ GROUP_UNIQUE_ON = ("group_unique_on", "群聊会话隔离开启")
+ GROUP_UNIQUE_OFF = ("group_unique_off", "群聊会话隔离关闭")
+ PRIVATE = ("private", "私聊")
+
+ @property
+ def key(self): return self.value[0]
+ @property
+ def name(self): return self.value[1]
+ @classmethod
+ def from_index(cls, i): return {1:cls.GROUP_UNIQUE_ON,2:cls.GROUP_UNIQUE_OFF,3:cls.PRIVATE}[i]
// 2) Extract alter_cmd’s three branches into small private methods:
– async def alter_cmd(self, event: AstrMessageEvent):
– … all the nested ifs …
+ async def alter_cmd(self, event: AstrMessageEvent):
+ tokens = self.parse_commands(event.message_str)
+ if self._is_reset_config(tokens):
+ return await self._handle_reset_config(event)
+ if self._is_reset_scene(tokens):
+ return await self._handle_reset_scene(event, tokens)
+ return await self._handle_permission_update(event, tokens)
+
+ def _is_reset_config(self, t): return t.len==3 and t.tokens[1]=="reset" and t.tokens[2]=="config"
+ def _is_reset_scene(self, t): return t.len>=4 and t.tokens[1]=="reset" and t.tokens[2]=="scene"
+
+ async def _handle_reset_config(self, event):
+ from astrbot.api import sp
+ cfg = await sp.global_get("alter_cmd", {})
+ reset = cfg.get("astrbot",{}).get("reset",{})
+ menu = (
+ f"reset命令权限配置\n"
+ f"1. 群聊会话隔离开: {reset.get('group_unique_on','admin')}\n"
+ f"2. 群聊会话隔离关: {reset.get('group_unique_off','admin')}\n"
+ f"3. 私聊: {reset.get('private','member')}\n"
+ "/alter_cmd reset scene <场景编号> <admin/member>"
+ )
+ await event.send(MessageChain().message(menu))
+
+ async def _handle_reset_scene(self, event, tokens):
+ num, perm = tokens.get(3), tokens.get(4)
+ if not num or not perm or not num.isdigit() or int(num)<1 or int(num)>3 or perm not in {"admin","member"}:
+ return await event.send(MessageChain().message("场景编号必须1-3且权限为admin/member"))
+ scene = RstScene.from_index(int(num))
+ await self._update_reset_permission(scene.key, perm)
+ await event.send(MessageChain().message(f"已将 reset 在{scene.name}设为{perm}"))
+
+ async def _handle_permission_update(self, event, tokens):
+ name, typ = " ".join(tokens.tokens[1:-1]), tokens.get(-1)
+ if typ not in {"admin","member"}:
+ return await event.send(MessageChain().message("指令类型错误,可选 admin, member"))
+ handler = self._find_handler(name)
+ if not handler:
+ return await event.send(MessageChain().message("未找到该指令"))
+ await self._save_permission(handler, typ)
+ await event.send(MessageChain().message(f"已将「{name}」权限调整为{typ}"))
+
+ def _find_handler(self, name):
+ for md in star_handlers_registry:
+ for f in md.event_filters:
+ if isinstance(f,(CommandFilter,CommandGroupFilter)) and f.equals(name):
+ return md
+ return None
+
+ async def _save_permission(self, md, typ):
+ from astrbot.api import sp, event as evt_mod
+ cfg = await sp.global_get("alter_cmd",{})
+ pl = cfg.setdefault(star_map[md.handler_module_path].name,{})
+ pl.setdefault(md.handler_name,{})["permission"]=typ
+ await sp.global_put("alter_cmd",cfg)
+ # inject or update PermissionTypeFilter
+ for f in md.event_filters:
+ if isinstance(f,PermissionTypeFilter):
+ f.permission_type = evt_mod.filter.PermissionType.ADMIN if typ=="admin" else evt_mod.filter.PermissionType.MEMBER
+ return
+ md.event_filters.insert(0, PermissionTypeFilter(
+ evt_mod.filter.PermissionType.ADMIN if typ=="admin" else evt_mod.filter.PermissionType.MEMBER
+ ))
```
</issue_to_address>
### Comment 3
<location> `packages/astrbot/commands/provider.py:12` </location>
<code_context>
+ def __init__(self, context: star.Context):
+ self.context = context
+
+ async def provider(
+ self, event: AstrMessageEvent, idx: Union[str, int, None] = None, idx2: Union[int, None] = None
+ ):
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring the provider method by splitting listing and switching logic into separate handlers to reduce long conditional branches.
```markdown
拆分 `provider` 方法,把“列出”和“切换”逻辑抽成独立 handler,避免长 if/elif。比如:
1. 在类里增加一个映射表和两个私有方法:
```python
PROVIDER_MAP = {
None: (ProviderType.CHAT_COMPLETION, "LLM 提供商", "get_all_providers", "get_using_provider"),
"tts": (ProviderType.TEXT_TO_SPEECH, "TTS 提供商", "get_all_tts_providers", "get_using_tts_provider"),
"stt": (ProviderType.SPEECH_TO_TEXT, "STT 提供商", "get_all_stt_providers", "get_using_stt_provider"),
}
async def _list_providers(self, event: AstrMessageEvent):
ret = []
umo = event.unified_msg_origin
for key,(ptype, title, all_fn, use_fn) in self.PROVIDER_MAP.items():
providers = getattr(self.context, all_fn)()
if not providers: continue
ret.append(f"## 载入的 {title}")
using = getattr(self.context, use_fn)(umo)
for i, prov in enumerate(providers, 1):
line = f"{i}. {prov.meta().id}"
if using and using.meta().id == prov.meta().id:
line += " (当前使用)"
ret.append(line)
ret.append("使用 /provider <序号> 切换 LLM,/provider tts/stt <序号> 切换 TTS/STT。")
event.set_result(MessageEventResult().message("\n".join(ret)))
```
2. 切换逻辑统一到 `_switch_provider`:
```python
async def _switch_provider(
self, event: AstrMessageEvent, key: Union[str,int], idx: int
):
if key not in self.PROVIDER_MAP:
return event.set_result(MessageEventResult().message("无效参数。"))
ptype, _, all_fn, _ = self.PROVIDER_MAP[key]
providers = getattr(self.context, all_fn)()
if not idx or idx < 1 or idx > len(providers):
return event.set_result(MessageEventResult().message("无效的序号。"))
pid = providers[idx - 1].meta().id
await self.context.provider_manager.set_provider(
provider_id=pid,
provider_type=ptype,
umo=event.unified_msg_origin,
)
event.set_result(MessageEventResult().message(f"成功切换到 {pid}。"))
```
3. 最后把原 `provider` 简化为:
```python
async def provider(self, event, key=None, idx=None):
if key is None:
return await self._list_providers(event)
return await self._switch_provider(event, key, idx)
}
```
同理,`model_ls` 和 `key` 也能按“list/switch”拆成两小块,减少嵌套、提升可读性。
```
</issue_to_address>
### Comment 4
<location> `packages/astrbot/commands/conversation.py:132-137` </location>
<code_context>
session_curr_cid = await conv_mgr.get_curr_conversation_id(umo)
if not session_curr_cid:
session_curr_cid = await conv_mgr.new_conversation(
umo, message.get_platform_id()
)
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use `or` for providing a fallback value ([`use-or-for-fallback`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/use-or-for-fallback))
```suggestion
session_curr_cid = await conv_mgr.get_curr_conversation_id(umo) or await conv_mgr.new_conversation(
umo, message.get_platform_id()
)
```
<br/><details><summary>Explanation</summary>Thanks to the flexibility of Python's `or` operator, you can use a single
assignment statement, even if a variable can retrieve its value from various
sources. This is shorter and easier to read than using multiple assignments with
`if not` conditions.
</details>
</issue_to_address>
### Comment 5
<location> `packages/astrbot/commands/conversation.py:151` </location>
<code_context>
f"{history if history else '无历史记录'}\n\n"
</code_context>
<issue_to_address>
**suggestion (code-quality):** Replace if-expression with `or` ([`or-if-exp-identity`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/or-if-exp-identity))
```suggestion
f"{history or '无历史记录'}\n\n"
```
<br/><details><summary>Explanation</summary>Here we find ourselves setting a value if it evaluates to `True`, and otherwise
using a default.
The 'After' case is a bit easier to read and avoids the duplication of
`input_currency`.
It works because the left-hand side is evaluated first. If it evaluates to
true then `currency` will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
`currency` will be set to `DEFAULT_CURRENCY`.
</details>
</issue_to_address>
### Comment 6
<location> `packages/astrbot/commands/conversation.py:202` </location>
<code_context>
title = conv.title if conv.title else "新对话"
</code_context>
<issue_to_address>
**suggestion (code-quality):** Replace if-expression with `or` ([`or-if-exp-identity`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/or-if-exp-identity))
```suggestion
title = conv.title or "新对话"
```
<br/><details><summary>Explanation</summary>Here we find ourselves setting a value if it evaluates to `True`, and otherwise
using a default.
The 'After' case is a bit easier to read and avoids the duplication of
`input_currency`.
It works because the left-hand side is evaluated first. If it evaluates to
true then `currency` will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
`currency` will be set to `DEFAULT_CURRENCY`.
</details>
</issue_to_address>
### Comment 7
<location> `packages/astrbot/commands/conversation.py:357` </location>
<code_context>
title = conversation.title if conversation.title else "新对话"
</code_context>
<issue_to_address>
**suggestion (code-quality):** Replace if-expression with `or` ([`or-if-exp-identity`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/or-if-exp-identity))
```suggestion
title = conversation.title or "新对话"
```
<br/><details><summary>Explanation</summary>Here we find ourselves setting a value if it evaluates to `True`, and otherwise
using a default.
The 'After' case is a bit easier to read and avoids the duplication of
`input_currency`.
It works because the left-hand side is evaluated first. If it evaluates to
true then `currency` will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
`currency` will be set to `DEFAULT_CURRENCY`.
</details>
</issue_to_address>
### Comment 8
<location> `packages/astrbot/commands/persona.py:38` </location>
<code_context>
curr_cid_title = conv.title if conv.title else "新对话"
</code_context>
<issue_to_address>
**suggestion (code-quality):** Replace if-expression with `or` ([`or-if-exp-identity`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/or-if-exp-identity))
```suggestion
curr_cid_title = conv.title or "新对话"
```
<br/><details><summary>Explanation</summary>Here we find ourselves setting a value if it evaluates to `True`, and otherwise
using a default.
The 'After' case is a bit easier to read and avoids the duplication of
`input_currency`.
It works because the left-hand side is evaluated first. If it evaluates to
true then `currency` will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
`currency` will be set to `DEFAULT_CURRENCY`.
</details>
</issue_to_address>
### Comment 9
<location> `packages/astrbot/commands/admin.py:13-20` </location>
<code_context>
async def op(self, event: AstrMessageEvent, admin_id: str = ""):
"""授权管理员。op <admin_id>"""
if admin_id == "":
event.set_result(
MessageEventResult().message(
"使用方法: /op <id> 授权管理员;/deop <id> 取消管理员。可通过 /sid 获取 ID。"
)
)
return
self.context.get_config()["admins_id"].append(str(admin_id))
self.context.get_config().save_config()
event.set_result(MessageEventResult().message("授权成功。"))
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Replaces an empty collection equality with a boolean operation ([`simplify-empty-collection-comparison`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/simplify-empty-collection-comparison/))
- Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
</issue_to_address>
### Comment 10
<location> `packages/astrbot/commands/admin.py:26-34` </location>
<code_context>
async def deop(self, event: AstrMessageEvent, admin_id: str = ""):
"""取消授权管理员。deop <admin_id>"""
if admin_id == "":
event.set_result(
MessageEventResult().message(
"使用方法: /deop <id> 取消管理员。可通过 /sid 获取 ID。"
)
)
return
try:
self.context.get_config()["admins_id"].remove(str(admin_id))
self.context.get_config().save_config()
event.set_result(MessageEventResult().message("取消授权成功。"))
except ValueError:
event.set_result(
MessageEventResult().message("此用户 ID 不在管理员名单内。")
)
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Replaces an empty collection equality with a boolean operation ([`simplify-empty-collection-comparison`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/simplify-empty-collection-comparison/))
- Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
</issue_to_address>
### Comment 11
<location> `packages/astrbot/commands/admin.py:44-52` </location>
<code_context>
async def wl(self, event: AstrMessageEvent, sid: str = ""):
"""添加白名单。wl <sid>"""
if sid == "":
event.set_result(
MessageEventResult().message(
"使用方法: /wl <id> 添加白名单;/dwl <id> 删除白名单。可通过 /sid 获取 ID。"
)
)
return
cfg = self.context.get_config(umo=event.unified_msg_origin)
cfg["platform_settings"]["id_whitelist"].append(str(sid))
cfg.save_config()
event.set_result(MessageEventResult().message("添加白名单成功。"))
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Replaces an empty collection equality with a boolean operation ([`simplify-empty-collection-comparison`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/simplify-empty-collection-comparison/))
- Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
</issue_to_address>
### Comment 12
<location> `packages/astrbot/commands/admin.py:58-67` </location>
<code_context>
async def dwl(self, event: AstrMessageEvent, sid: str = ""):
"""删除白名单。dwl <sid>"""
if sid == "":
event.set_result(
MessageEventResult().message(
"使用方法: /dwl <id> 删除白名单。可通过 /sid 获取 ID。"
)
)
return
try:
cfg = self.context.get_config(umo=event.unified_msg_origin)
cfg["platform_settings"]["id_whitelist"].remove(str(sid))
cfg.save_config()
event.set_result(MessageEventResult().message("删除白名单成功。"))
except ValueError:
event.set_result(MessageEventResult().message("此 SID 不在白名单内。"))
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Replaces an empty collection equality with a boolean operation ([`simplify-empty-collection-comparison`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/simplify-empty-collection-comparison/))
- Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))
</issue_to_address>
### Comment 13
<location> `packages/astrbot/commands/alter_cmd.py:47` </location>
<code_context>
async def alter_cmd(self, event: AstrMessageEvent):
token = self.parse_commands(event.message_str)
if token.len < 3:
await event.send(
MessageChain().message(
"该指令用于设置指令或指令组的权限。\n"
"格式: /alter_cmd <cmd_name> <admin/member>\n"
"例1: /alter_cmd c1 admin 将 c1 设为管理员指令\n"
"例2: /alter_cmd g1 c1 admin 将 g1 指令组的 c1 子指令设为管理员指令\n"
"/alter_cmd reset config 打开 reset 权限配置"
)
)
return
cmd_name = " ".join(token.tokens[1:-1])
cmd_type = token.get(-1)
if cmd_name == "reset" and cmd_type == "config":
from astrbot.api import sp
alter_cmd_cfg = await sp.global_get("alter_cmd", {})
plugin_ = alter_cmd_cfg.get("astrbot", {})
reset_cfg = plugin_.get("reset", {})
group_unique_on = reset_cfg.get("group_unique_on", "admin")
group_unique_off = reset_cfg.get("group_unique_off", "admin")
private = reset_cfg.get("private", "member")
config_menu = f"""reset命令权限细粒度配置
当前配置:
1. 群聊+会话隔离开: {group_unique_on}
2. 群聊+会话隔离关: {group_unique_off}
3. 私聊: {private}
修改指令格式:
/alter_cmd reset scene <场景编号> <admin/member>
例如: /alter_cmd reset scene 2 member"""
await event.send(MessageChain().message(config_menu))
return
if cmd_name == "reset" and cmd_type == "scene" and token.len >= 4:
scene_num = token.get(3)
perm_type = token.get(4)
if scene_num is None or perm_type is None:
await event.send(MessageChain().message("场景编号和权限类型不能为空"))
return
if not scene_num.isdigit() or int(scene_num) < 1 or int(scene_num) > 3:
await event.send(
MessageChain().message("场景编号必须是 1-3 之间的数字")
)
return
if perm_type not in ["admin", "member"]:
await event.send(
MessageChain().message("权限类型错误,只能是 admin 或 member")
)
return
scene_num = int(scene_num)
scene = RstScene.from_index(scene_num)
scene_key = scene.key
await self.update_reset_permission(scene_key, perm_type)
await event.send(
MessageChain().message(
f"已将 reset 命令在{scene.name}场景下的权限设为{perm_type}"
)
)
return
if cmd_type not in ["admin", "member"]:
await event.send(
MessageChain().message("指令类型错误,可选类型有 admin, member")
)
return
# 查找指令
found_command = None
cmd_group = False
for handler in star_handlers_registry:
assert isinstance(handler, StarHandlerMetadata)
for filter_ in handler.event_filters:
if isinstance(filter_, CommandFilter):
if filter_.equals(cmd_name):
found_command = handler
break
elif isinstance(filter_, CommandGroupFilter):
if filter_.equals(cmd_name):
found_command = handler
cmd_group = True
break
if not found_command:
await event.send(MessageChain().message("未找到该指令"))
return
found_plugin = star_map[found_command.handler_module_path]
from astrbot.api import sp
alter_cmd_cfg = await sp.global_get("alter_cmd", {})
plugin_ = alter_cmd_cfg.get(found_plugin.name, {})
cfg = plugin_.get(found_command.handler_name, {})
cfg["permission"] = cmd_type
plugin_[found_command.handler_name] = cfg
alter_cmd_cfg[found_plugin.name] = plugin_
await sp.global_put("alter_cmd", alter_cmd_cfg)
# 注入权限过滤器
found_permission_filter = False
for filter_ in found_command.event_filters:
if isinstance(filter_, PermissionTypeFilter):
if cmd_type == "admin":
import astrbot.api.event.filter as filter
filter_.permission_type = filter.PermissionType.ADMIN
else:
import astrbot.api.event.filter as filter
filter_.permission_type = filter.PermissionType.MEMBER
found_permission_filter = True
break
if not found_permission_filter:
import astrbot.api.event.filter as filter
found_command.event_filters.insert(
0,
PermissionTypeFilter(
filter.PermissionType.ADMIN
if cmd_type == "admin"
else filter.PermissionType.MEMBER
),
)
cmd_group_str = "指令组" if cmd_group else "指令"
await event.send(
MessageChain().message(
f"已将「{cmd_name}」{cmd_group_str} 的权限级别调整为 {cmd_type}。"
)
)
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Hoist repeated code outside conditional statement ([`hoist-statement-from-if`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/hoist-statement-from-if/))
- Low code quality found in AlterCmdCommands.alter\_cmd - 10% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
<br/><details><summary>Explanation</summary>
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 14
<location> `packages/astrbot/commands/conversation.py:146` </location>
<code_context>
async def his(self, message: AstrMessageEvent, page: int = 1):
"""查看对话记录"""
if not self.context.get_using_provider(message.unified_msg_origin):
message.set_result(
MessageEventResult().message("未找到任何 LLM 提供商。请先配置。")
)
return
size_per_page = 6
conv_mgr = self.context.conversation_manager
umo = message.unified_msg_origin
session_curr_cid = await conv_mgr.get_curr_conversation_id(umo)
if not session_curr_cid:
session_curr_cid = await conv_mgr.new_conversation(
umo, message.get_platform_id()
)
contexts, total_pages = await conv_mgr.get_human_readable_context(
umo, session_curr_cid, page, size_per_page
)
history = ""
for context in contexts:
if len(context) > 150:
context = context[:150] + "..."
history += f"{context}\n"
ret = (
f"当前对话历史记录:"
f"{history if history else '无历史记录'}\n\n"
f"第 {page} 页 | 共 {total_pages} 页\n"
f"*输入 /history 2 跳转到第 2 页"
)
message.set_result(MessageEventResult().message(ret).use_t2i(False))
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use f-string instead of string concatenation ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))
```suggestion
context = f"{context[:150]}..."
```
</issue_to_address>
### Comment 15
<location> `packages/astrbot/commands/conversation.py:158` </location>
<code_context>
async def convs(self, message: AstrMessageEvent, page: int = 1):
"""查看对话列表"""
provider = self.context.get_using_provider(message.unified_msg_origin)
if provider and provider.meta().type == "dify":
"""原有的Dify处理逻辑保持不变"""
ret = "Dify 对话列表:\n"
assert isinstance(provider, ProviderDify)
data = await provider.api_client.get_chat_convs(message.unified_msg_origin)
idx = 1
for conv in data["data"]:
ts_h = datetime.datetime.fromtimestamp(conv["updated_at"]).strftime(
"%m-%d %H:%M"
)
ret += f"{idx}. {conv['name']}({conv['id'][:4]})\n 上次更新:{ts_h}\n"
idx += 1
if idx == 1:
ret += "没有找到任何对话。"
dify_cid = provider.conversation_ids.get(message.unified_msg_origin, None)
ret += f"\n\n用户: {message.unified_msg_origin}\n当前对话: {dify_cid}\n使用 /switch <序号> 切换对话。"
message.set_result(MessageEventResult().message(ret))
return
size_per_page = 6
"""获取所有对话列表"""
conversations_all = await self.context.conversation_manager.get_conversations(
message.unified_msg_origin
)
"""计算总页数"""
total_pages = (len(conversations_all) + size_per_page - 1) // size_per_page
"""确保页码有效"""
page = max(1, min(page, total_pages))
"""分页处理"""
start_idx = (page - 1) * size_per_page
end_idx = start_idx + size_per_page
conversations_paged = conversations_all[start_idx:end_idx]
ret = "对话列表:\n---\n"
"""全局序号从当前页的第一个开始"""
global_index = start_idx + 1
"""生成所有对话的标题字典"""
_titles = {}
for conv in conversations_all:
title = conv.title if conv.title else "新对话"
_titles[conv.cid] = title
"""遍历分页后的对话生成列表显示"""
for conv in conversations_paged:
persona_id = conv.persona_id
if not persona_id or persona_id == "[%None]":
persona = await self.context.persona_manager.get_default_persona_v3(
umo=message.unified_msg_origin
)
persona_id = persona["name"]
title = _titles.get(conv.cid, "新对话")
ret += f"{global_index}. {title}({conv.cid[:4]})\n 人格情景: {persona_id}\n 上次更新: {datetime.datetime.fromtimestamp(conv.updated_at).strftime('%m-%d %H:%M')}\n"
global_index += 1
ret += "---\n"
curr_cid = await self.context.conversation_manager.get_curr_conversation_id(
message.unified_msg_origin
)
if curr_cid:
"""从所有对话的标题字典中获取标题"""
title = _titles.get(curr_cid, "新对话")
ret += f"\n当前对话: {title}({curr_cid[:4]})"
else:
ret += "\n当前对话: 无"
unique_session = self.context.get_config()["platform_settings"][
"unique_session"
]
if unique_session:
ret += "\n会话隔离粒度: 个人"
else:
ret += "\n会话隔离粒度: 群聊"
ret += f"\n第 {page} 页 | 共 {total_pages} 页"
ret += "\n*输入 /ls 2 跳转到第 2 页"
message.set_result(MessageEventResult().message(ret).use_t2i(False))
return
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
- Low code quality found in ConversationCommands.convs - 23% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
<br/><details><summary>Explanation</summary>
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 16
<location> `packages/astrbot/commands/conversation.py:407-408` </location>
<code_context>
async def del_conv(self, message: AstrMessageEvent):
"""删除当前对话"""
is_unique_session = self.context.get_config()["platform_settings"][
"unique_session"
]
if message.get_group_id() and not is_unique_session and message.role != "admin":
# 群聊,没开独立会话,发送人不是管理员
message.set_result(
MessageEventResult().message(
f"会话处于群聊,并且未开启独立会话,并且您 (ID {message.get_sender_id()}) 不是管理员,因此没有权限删除当前对话。"
)
)
return
provider = self.context.get_using_provider(message.unified_msg_origin)
if provider and provider.meta().type == "dify":
assert isinstance(provider, ProviderDify)
dify_cid = provider.conversation_ids.pop(message.unified_msg_origin, None)
if dify_cid:
await provider.api_client.delete_chat_conv(
message.unified_msg_origin, dify_cid
)
message.set_result(
MessageEventResult().message(
"删除当前对话成功。不再处于对话状态,使用 /switch 序号 切换到其他对话或 /new 创建。"
)
)
return
session_curr_cid = (
await self.context.conversation_manager.get_curr_conversation_id(
message.unified_msg_origin
)
)
if not session_curr_cid:
message.set_result(
MessageEventResult().message(
"当前未处于对话状态,请 /switch 序号 切换或 /new 创建。"
)
)
return
await self.context.conversation_manager.delete_conversation(
message.unified_msg_origin, session_curr_cid
)
message.set_result(
MessageEventResult().message(
"删除当前对话成功。不再处于对话状态,使用 /switch 序号 切换到其他对话或 /new 创建。"
)
)
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if dify_cid := provider.conversation_ids.pop(
message.unified_msg_origin, None
):
```
</issue_to_address>
### Comment 17
<location> `packages/astrbot/commands/llm.py:12-13` </location>
<code_context>
async def llm(self, event: AstrMessageEvent):
"""开启/关闭 LLM"""
cfg = self.context.get_config(umo=event.unified_msg_origin)
enable = cfg["provider_settings"]["enable"]
if enable:
cfg["provider_settings"]["enable"] = False
status = "关闭"
else:
cfg["provider_settings"]["enable"] = True
status = "开启"
cfg.save_config()
await event.send(MessageChain().message(f"{status} LLM 聊天功能。"))
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if enable := cfg["provider_settings"]["enable"]:
```
</issue_to_address>
### Comment 18
<location> `packages/astrbot/commands/persona.py:33` </location>
<code_context>
async def persona(self, message: AstrMessageEvent):
l = message.message_str.split(" ") # noqa: E741
umo = message.unified_msg_origin
curr_persona_name = "无"
cid = await self.context.conversation_manager.get_curr_conversation_id(umo)
default_persona = await self.context.persona_manager.get_default_persona_v3(
umo=umo
)
curr_cid_title = "无"
if cid:
conv = await self.context.conversation_manager.get_conversation(
unified_msg_origin=umo,
conversation_id=cid,
create_if_not_exists=True,
)
if conv is None:
message.set_result(
MessageEventResult().message(
"当前对话不存在,请先使用 /new 新建一个对话。"
)
)
return
if not conv.persona_id and not conv.persona_id == "[%None]":
curr_persona_name = default_persona["name"]
else:
curr_persona_name = conv.persona_id
curr_cid_title = conv.title if conv.title else "新对话"
curr_cid_title += f"({cid[:4]})"
if len(l) == 1:
message.set_result(
MessageEventResult()
.message(
f"""[Persona]
- 人格情景列表: `/persona list`
- 设置人格情景: `/persona 人格`
- 人格情景详细信息: `/persona view 人格`
- 取消人格: `/persona unset`
默认人格情景: {default_persona["name"]}
当前对话 {curr_cid_title} 的人格情景: {curr_persona_name}
配置人格情景请前往管理面板-配置页
"""
)
.use_t2i(False)
)
elif l[1] == "list":
msg = "人格列表:\n"
for persona in self.context.provider_manager.personas:
msg += f"- {persona['name']}\n"
msg += "\n\n*输入 `/persona view 人格名` 查看人格详细信息"
message.set_result(MessageEventResult().message(msg))
elif l[1] == "view":
if len(l) == 2:
message.set_result(MessageEventResult().message("请输入人格情景名"))
return
ps = l[2].strip()
if persona := next(
builtins.filter(
lambda persona: persona["name"] == ps,
self.context.provider_manager.personas,
),
None,
):
msg = f"人格{ps}的详细信息:\n"
msg += f"{persona['prompt']}\n"
else:
msg = f"人格{ps}不存在"
message.set_result(MessageEventResult().message(msg))
elif l[1] == "unset":
if not cid:
message.set_result(
MessageEventResult().message("当前没有对话,无法取消人格。")
)
return
await self.context.conversation_manager.update_conversation_persona_id(
message.unified_msg_origin, "[%None]"
)
message.set_result(MessageEventResult().message("取消人格成功。"))
else:
ps = "".join(l[1:]).strip()
if not cid:
message.set_result(
MessageEventResult().message(
"当前没有对话,请先开始对话或使用 /new 创建一个对话。"
)
)
return
if persona := next(
builtins.filter(
lambda persona: persona["name"] == ps,
self.context.provider_manager.personas,
),
None,
):
await self.context.conversation_manager.update_conversation_persona_id(
message.unified_msg_origin, ps
)
message.set_result(
MessageEventResult().message(
"设置成功。如果您正在切换到不同的人格,请注意使用 /reset 来清空上下文,防止原人格对话影响现人格。"
)
)
else:
message.set_result(
MessageEventResult().message(
"不存在该人格情景。使用 /persona list 查看所有。"
)
)
</code_context>
<issue_to_address>
**suggestion (code-quality):** We've found these issues:
- Simplify logical expression using De Morgan identities ([`de-morgan`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/de-morgan/))
- Low code quality found in PersonaCommands.persona - 19% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
```suggestion
if not conv.persona_id and conv.persona_id != "[%None]":
```
<br/><details><summary>Explanation</summary>
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 19
<location> `packages/astrbot/commands/plugin.py:105-115` </location>
<code_context>
async def plugin_help(self, event: AstrMessageEvent, plugin_name: str = ""):
"""获取插件帮助"""
if not plugin_name:
event.set_result(
MessageEventResult().message("/plugin help <插件名> 查看插件信息。")
)
return
plugin = self.context.get_registered_star(plugin_name)
if plugin is None:
event.set_result(MessageEventResult().message("未找到此插件。"))
return
help_msg = ""
help_msg += f"\n\n✨ 作者: {plugin.author}\n✨ 版本: {plugin.version}"
command_handlers = []
command_names = []
for handler in star_handlers_registry:
assert isinstance(handler, StarHandlerMetadata)
if handler.handler_module_path != plugin.module_path:
continue
for filter_ in handler.event_filters:
if isinstance(filter_, CommandFilter):
command_handlers.append(handler)
command_names.append(filter_.command_name)
break
elif isinstance(filter_, CommandGroupFilter):
command_handlers.append(handler)
command_names.append(filter_.group_name)
if len(command_handlers) > 0:
help_msg += "\n\n🔧 指令列表:\n"
for i in range(len(command_handlers)):
help_msg += f"- {command_names[i]}"
if command_handlers[i].desc:
help_msg += f": {command_handlers[i].desc}"
help_msg += "\n"
help_msg += "\nTip: 指令的触发需要添加唤醒前缀,默认为 /。"
ret = f"🧩 插件 {plugin_name} 帮助信息:\n" + help_msg
ret += "更多帮助信息请查看插件仓库 README。"
event.set_result(MessageEventResult().message(ret).use_t2i(False))
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Simplify sequence length comparison ([`simplify-len-comparison`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/simplify-len-comparison/))
- Use f-string instead of string concatenation ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))
</issue_to_address>
### Comment 20
<location> `packages/astrbot/commands/provider.py:12` </location>
<code_context>
async def provider(
self, event: AstrMessageEvent, idx: Union[str, int, None] = None, idx2: Union[int, None] = None
):
"""查看或者切换 LLM Provider"""
umo = event.unified_msg_origin
if idx is None:
ret = "## 载入的 LLM 提供商\n"
for idx, llm in enumerate(self.context.get_all_providers()):
id_ = llm.meta().id
ret += f"{idx + 1}. {id_} ({llm.meta().model})"
provider_using = self.context.get_using_provider(umo=umo)
if provider_using and provider_using.meta().id == id_:
ret += " (当前使用)"
ret += "\n"
tts_providers = self.context.get_all_tts_providers()
if tts_providers:
ret += "\n## 载入的 TTS 提供商\n"
for idx, tts in enumerate(tts_providers):
id_ = tts.meta().id
ret += f"{idx + 1}. {id_}"
tts_using = self.context.get_using_tts_provider(umo=umo)
if tts_using and tts_using.meta().id == id_:
ret += " (当前使用)"
ret += "\n"
stt_providers = self.context.get_all_stt_providers()
if stt_providers:
ret += "\n## 载入的 STT 提供商\n"
for idx, stt in enumerate(stt_providers):
id_ = stt.meta().id
ret += f"{idx + 1}. {id_}"
stt_using = self.context.get_using_stt_provider(umo=umo)
if stt_using and stt_using.meta().id == id_:
ret += " (当前使用)"
ret += "\n"
ret += "\n使用 /provider <序号> 切换 LLM 提供商。"
if tts_providers:
ret += "\n使用 /provider tts <序号> 切换 TTS 提供商。"
if stt_providers:
ret += "\n使用 /provider stt <切换> STT 提供商。"
event.set_result(MessageEventResult().message(ret))
elif idx == "tts":
if idx2 is None:
event.set_result(MessageEventResult().message("请输入序号。"))
return
else:
if idx2 > len(self.context.get_all_tts_providers()) or idx2 < 1:
event.set_result(MessageEventResult().message("无效的序号。"))
provider = self.context.get_all_tts_providers()[idx2 - 1]
id_ = provider.meta().id
await self.context.provider_manager.set_provider(
provider_id=id_,
provider_type=ProviderType.TEXT_TO_SPEECH,
umo=umo,
)
event.set_result(MessageEventResult().message(f"成功切换到 {id_}。"))
elif idx == "stt":
if idx2 is None:
event.set_result(MessageEventResult().message("请输入序号。"))
return
else:
if idx2 > len(self.context.get_all_stt_providers()) or idx2 < 1:
event.set_result(MessageEventResult().message("无效的序号。"))
provider = self.context.get_all_stt_providers()[idx2 - 1]
id_ = provider.meta().id
await self.context.provider_manager.set_provider(
provider_id=id_,
provider_type=ProviderType.SPEECH_TO_TEXT,
umo=umo,
)
event.set_result(MessageEventResult().message(f"成功切换到 {id_}。"))
elif isinstance(idx, int):
if idx > len(self.context.get_all_providers()) or idx < 1:
event.set_result(MessageEventResult().message("无效的序号。"))
provider = self.context.get_all_providers()[idx - 1]
id_ = provider.meta().id
await self.context.provider_manager.set_provider(
provider_id=id_,
provider_type=ProviderType.CHAT_COMPLETION,
umo=umo,
)
event.set_result(MessageEventResult().message(f"成功切换到 {id_}。"))
else:
event.set_result(MessageEventResult().message("无效的参数。"))
</code_context>
<issue_to_address>
**issue (code-quality):** Low code quality found in ProviderCommands.provider - 11% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
<br/><details><summary>Explanation</summary>The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 21
<location> `packages/astrbot/commands/provider.py:117` </location>
<code_context>
async def model_ls(
self, message: AstrMessageEvent, idx_or_name: Union[int, str, None] = None
):
"""查看或者切换模型"""
prov = self.context.get_using_provider(message.unified_msg_origin)
if not prov:
message.set_result(
MessageEventResult().message("未找到任何 LLM 提供商。请先配置。")
)
return
# 定义正则表达式匹配 API 密钥
api_key_pattern = re.compile(r"key=[^&'\" ]+")
if idx_or_name is None:
models = []
try:
models = await prov.get_models()
except BaseException as e:
err_msg = api_key_pattern.sub("key=***", str(e))
message.set_result(
MessageEventResult()
.message("获取模型列表失败: " + err_msg)
.use_t2i(False)
)
return
i = 1
ret = "下面列出了此服务提供商可用模型:"
for model in models:
ret += f"\n{i}. {model}"
i += 1
curr_model = prov.get_model() or "无"
ret += f"\n当前模型: [{curr_model}]"
ret += "\nTips: 使用 /model <模型名/编号>,即可实时更换模型。如目标模型不存在于上表,请输入模型名。"
message.set_result(MessageEventResult().message(ret).use_t2i(False))
else:
if isinstance(idx_or_name, int):
models = []
try:
models = await prov.get_models()
except BaseException as e:
message.set_result(
MessageEventResult().message("获取模型列表失败: " + str(e))
)
return
if idx_or_name > len(models) or idx_or_name < 1:
message.set_result(MessageEventResult().message("模型序号错误。"))
else:
try:
new_model = models[idx_or_name - 1]
prov.set_model(new_model)
except BaseException as e:
message.set_result(
MessageEventResult().message("切换模型未知错误: " + str(e))
)
message.set_result(MessageEventResult().message("切换模型成功。"))
else:
prov.set_model(idx_or_name)
message.set_result(
MessageEventResult().message(f"切换模型到 {prov.get_model()}。")
)
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Use f-string instead of string concatenation [×3] ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))
- Move assignment closer to its usage within a block ([`move-assign-in-block`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/move-assign-in-block/))
- Replace manual loop counter with call to enumerate ([`convert-to-enumerate`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/convert-to-enumerate/))
</issue_to_address>
### Comment 22
<location> `packages/astrbot/commands/provider.py:195-197` </location>
<code_context>
async def key(self, message: AstrMessageEvent, index: Union[int, None] = None):
prov = self.context.get_using_provider(message.unified_msg_origin)
if not prov:
message.set_result(
MessageEventResult().message("未找到任何 LLM 提供商。请先配置。")
)
return
if index is None:
keys_data = prov.get_keys()
curr_key = prov.get_current_key()
ret = "Key:"
for i, k in enumerate(keys_data):
ret += f"\n{i + 1}. {k[:8]}"
ret += f"\n当前 Key: {curr_key[:8]}"
ret += "\n当前模型: " + prov.get_model()
ret += "\n使用 /key <idx> 切换 Key。"
message.set_result(MessageEventResult().message(ret).use_t2i(False))
else:
keys_data = prov.get_keys()
if index > len(keys_data) or index < 1:
message.set_result(MessageEventResult().message("Key 序号错误。"))
else:
try:
new_key = keys_data[index - 1]
prov.set_key(new_key)
except BaseException as e:
message.set_result(
MessageEventResult().message("切换 Key 未知错误: " + str(e))
)
message.set_result(MessageEventResult().message("切换 Key 成功。"))
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use f-string instead of string concatenation ([`use-fstring-for-concatenation`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-fstring-for-concatenation/))
```suggestion
message.set_result(MessageEventResult().message(f"切换 Key 未知错误: {str(e)}"))
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
… persona injection
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
fixes #XYZ
Motivation / 动机
优化 astrbot 内置插件的文件结构,提高可读性和可维护性。
Modifications / 改动点
将 main.py 的指令逻辑都移动到独立的文件中。
Verification Steps / 验证步骤
Screenshots or Test Results / 运行截图或测试结果
Compatibility & Breaking Changes / 兼容性与破坏性变更
Checklist / 检查清单
requirements.txt和pyproject.toml文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations inrequirements.txtandpyproject.toml.Sourcery 总结
重构
packages/astrbot中的内部插件代码,通过将main.py中所有的命令实现提取到专门的命令模块中,并更新接口以提高模块化和可读性。增强功能:
packages/astrbot/commands下的独立类中AbstractProvider.get_models转换为异步方法以匹配异步使用CommandParser.get添加返回类型注解以改进类型检查Original summary in English
Summary by Sourcery
Refactor the internal plugin code in packages/astrbot by extracting all command implementations from main.py into dedicated command modules and updating interfaces for better modularity and readability.
Enhancements: