From e190bbeeed29a4217d48f6f6c552f95d49f65832 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 13:00:59 +0800 Subject: [PATCH] Optimize string concatenation in loops: replace += with list.join() (#3246) * Initial plan * Fix string concatenation performance issues in loops Co-authored-by: LIghtJUNction <106986785+LIghtJUNction@users.noreply.github.com> * Address code review feedback: Fix plugin list logic and add comment Co-authored-by: LIghtJUNction <106986785+LIghtJUNction@users.noreply.github.com> * Improve comment clarity for at_parts accumulation Co-authored-by: LIghtJUNction <106986785+LIghtJUNction@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: LIghtJUNction <106986785+LIghtJUNction@users.noreply.github.com> --- astrbot/core/db/migration/migra_3_to_4.py | 7 ++- .../strategies/baidu_aip.py | 7 ++- .../core/pipeline/result_decorate/stage.py | 5 +- astrbot/core/platform/astr_message_event.py | 27 ++++----- .../aiocqhttp/aiocqhttp_platform_adapter.py | 6 +- .../sources/discord/discord_platform_event.py | 7 ++- .../platform/sources/slack/slack_adapter.py | 16 +++--- .../platform/sources/slack/slack_event.py | 9 +-- .../core/provider/sources/dashscope_source.py | 5 +- astrbot/core/star/filter/command.py | 10 ++-- astrbot/core/star/filter/command_group.py | 29 +++++----- astrbot/dashboard/server.py | 9 +-- packages/astrbot/commands/conversation.py | 27 ++++++--- packages/astrbot/commands/persona.py | 7 ++- packages/astrbot/commands/plugin.py | 25 +++++---- packages/astrbot/commands/provider.py | 55 ++++++++++--------- packages/astrbot/long_term_memory.py | 10 ++-- packages/reminder/main.py | 7 ++- 18 files changed, 151 insertions(+), 117 deletions(-) diff --git a/astrbot/core/db/migration/migra_3_to_4.py b/astrbot/core/db/migration/migra_3_to_4.py index 13a14c327..a75c60a1b 100644 --- a/astrbot/core/db/migration/migra_3_to_4.py +++ b/astrbot/core/db/migration/migra_3_to_4.py @@ -250,14 +250,15 @@ async def migration_persona_data( try: begin_dialogs = persona.get("begin_dialogs", []) mood_imitation_dialogs = persona.get("mood_imitation_dialogs", []) - mood_prompt = "" + parts = [] user_turn = True for mood_dialog in mood_imitation_dialogs: if user_turn: - mood_prompt += f"A: {mood_dialog}\n" + parts.append(f"A: {mood_dialog}\n") else: - mood_prompt += f"B: {mood_dialog}\n" + parts.append(f"B: {mood_dialog}\n") user_turn = not user_turn + mood_prompt = "".join(parts) system_prompt = persona.get("prompt", "") if mood_prompt: system_prompt += f"Here are few shots of dialogs, you need to imitate the tone of 'B' in the following dialogs to respond:\n {mood_prompt}" diff --git a/astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py b/astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py index c11822896..bfa82de0e 100644 --- a/astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +++ b/astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py @@ -21,8 +21,9 @@ class BaiduAipStrategy(ContentSafetyStrategy): if "data" not in res: return False, "" count = len(res["data"]) - info = f"百度审核服务发现 {count} 处违规:\n" + parts = [f"百度审核服务发现 {count} 处违规:\n"] for i in res["data"]: - info += f"{i['msg']};\n" - info += "\n判断结果:" + res["conclusion"] + parts.append(f"{i['msg']};\n") + parts.append("\n判断结果:" + res["conclusion"]) + info = "".join(parts) return False, info diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 08661a367..5dfb52f6f 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -246,12 +246,13 @@ class ResultDecorateStage(Stage): elif ( result.use_t2i_ is None and self.ctx.astrbot_config["t2i"] ) or result.use_t2i_: - plain_str = "" + parts = [] for comp in result.chain: if isinstance(comp, Plain): - plain_str += "\n\n" + comp.text + parts.append("\n\n" + comp.text) else: break + plain_str = "".join(parts) if plain_str and len(plain_str) > self.t2i_word_threshold: render_start = time.time() try: diff --git a/astrbot/core/platform/astr_message_event.py b/astrbot/core/platform/astr_message_event.py index 583f6ae8b..1c37f6a69 100644 --- a/astrbot/core/platform/astr_message_event.py +++ b/astrbot/core/platform/astr_message_event.py @@ -91,33 +91,34 @@ class AstrMessageEvent(abc.ABC): return self.message_str def _outline_chain(self, chain: list[BaseMessageComponent] | None) -> str: - outline = "" if not chain: - return outline + return "" + + parts = [] for i in chain: if isinstance(i, Plain): - outline += i.text + parts.append(i.text) elif isinstance(i, Image): - outline += "[图片]" + parts.append("[图片]") elif isinstance(i, Face): - outline += f"[表情:{i.id}]" + parts.append(f"[表情:{i.id}]") elif isinstance(i, At): - outline += f"[At:{i.qq}]" + parts.append(f"[At:{i.qq}]") elif isinstance(i, AtAll): - outline += "[At:全体成员]" + parts.append("[At:全体成员]") elif isinstance(i, Forward): # 转发消息 - outline += "[转发消息]" + parts.append("[转发消息]") elif isinstance(i, Reply): # 引用回复 if i.message_str: - outline += f"[引用消息({i.sender_nickname}: {i.message_str})]" + parts.append(f"[引用消息({i.sender_nickname}: {i.message_str})]") else: - outline += "[引用消息]" + parts.append("[引用消息]") else: - outline += f"[{i.type}]" - outline += " " - return outline + parts.append(f"[{i.type}]") + parts.append(" ") + return "".join(parts) def get_message_outline(self) -> str: """获取消息概要。 diff --git a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py index bb9c4474a..85bb2fead 100644 --- a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +++ b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py @@ -315,6 +315,8 @@ class AiocqhttpAdapter(Platform): abm.message.append(a) elif t == "at": first_at_self_processed = False + # Accumulate @ mention text for efficient concatenation + at_parts = [] for m in m_group: try: @@ -354,13 +356,15 @@ class AiocqhttpAdapter(Platform): first_at_self_processed = True else: # 非第一个@机器人或@其他用户,添加到message_str - message_str += f" @{nickname}({m['data']['qq']}) " + at_parts.append(f" @{nickname}({m['data']['qq']}) ") else: abm.message.append(At(qq=str(m["data"]["qq"]), name="")) except ActionFailed as e: logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。") except BaseException as e: logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。") + + message_str += "".join(at_parts) else: for m in m_group: a = ComponentTypes[t](**m["data"]) diff --git a/astrbot/core/platform/sources/discord/discord_platform_event.py b/astrbot/core/platform/sources/discord/discord_platform_event.py index 3c701c4ce..06f921bc4 100644 --- a/astrbot/core/platform/sources/discord/discord_platform_event.py +++ b/astrbot/core/platform/sources/discord/discord_platform_event.py @@ -113,18 +113,18 @@ class DiscordPlatformEvent(AstrMessageEvent): message: MessageChain, ) -> tuple[str, list[discord.File], discord.ui.View | None, list[discord.Embed]]: """将 MessageChain 解析为 Discord 发送所需的内容""" - content = "" + content_parts = [] files = [] view = None embeds = [] reference_message_id = None for i in message.chain: # 遍历消息链 if isinstance(i, Plain): # 如果是文字类型的 - content += i.text + content_parts.append(i.text) elif isinstance(i, Reply): reference_message_id = i.id elif isinstance(i, At): - content += f"<@{i.qq}>" + content_parts.append(f"<@{i.qq}>") elif isinstance(i, Image): logger.debug(f"[Discord] 开始处理 Image 组件: {i}") try: @@ -238,6 +238,7 @@ class DiscordPlatformEvent(AstrMessageEvent): else: logger.debug(f"[Discord] 忽略了不支持的消息组件: {i.type}") + content = "".join(content_parts) if len(content) > 2000: logger.warning("[Discord] 消息内容超过2000字符,将被截断。") content = content[:2000] diff --git a/astrbot/core/platform/sources/slack/slack_adapter.py b/astrbot/core/platform/sources/slack/slack_adapter.py index 9f21656ed..6c74a4713 100644 --- a/astrbot/core/platform/sources/slack/slack_adapter.py +++ b/astrbot/core/platform/sources/slack/slack_adapter.py @@ -222,39 +222,41 @@ class SlackAdapter(Platform): if element.get("type") == "rich_text_section": # 处理富文本段落 section_elements = element.get("elements", []) - text_content = "" - + text_parts = [] for section_element in section_elements: element_type = section_element.get("type", "") if element_type == "text": # 普通文本 - text_content += section_element.get("text", "") + text_parts.append(section_element.get("text", "")) elif element_type == "user": # @用户提及 user_id = section_element.get("user_id", "") if user_id: # 将之前的文本内容先添加到组件中 + text_content = "".join(text_parts) if text_content.strip(): message_components.append( Plain(text=text_content), ) - text_content = "" + text_parts = [] # 添加@提及组件 message_components.append(At(qq=user_id, name="")) elif element_type == "channel": # #频道提及 channel_id = section_element.get("channel_id", "") - text_content += f"#{channel_id}" + text_parts.append(f"#{channel_id}") elif element_type == "link": # 链接 url = section_element.get("url", "") link_text = section_element.get("text", url) - text_content += f"[{link_text}]({url})" + text_parts.append(f"[{link_text}]({url})") elif element_type == "emoji": # 表情符号 emoji_name = section_element.get("name", "") - text_content += f":{emoji_name}:" + text_parts.append(f":{emoji_name}:") + + text_content = "".join(text_parts) if text_content.strip(): message_components.append(Plain(text=text_content)) diff --git a/astrbot/core/platform/sources/slack/slack_event.py b/astrbot/core/platform/sources/slack/slack_event.py index 21c1b0fed..c918abbac 100644 --- a/astrbot/core/platform/sources/slack/slack_event.py +++ b/astrbot/core/platform/sources/slack/slack_event.py @@ -148,14 +148,15 @@ class SlackMessageEvent(AstrMessageEvent): ) except Exception: # 如果块发送失败,尝试只发送文本 - fallback_text = "" + parts = [] for segment in message.chain: if isinstance(segment, Plain): - fallback_text += segment.text + parts.append(segment.text) elif isinstance(segment, File): - fallback_text += f" [文件: {segment.name}] " + parts.append(f" [文件: {segment.name}] ") elif isinstance(segment, Image): - fallback_text += " [图片] " + parts.append(" [图片] ") + fallback_text = "".join(parts) if self.get_group_id(): await self.web_client.chat_postMessage( diff --git a/astrbot/core/provider/sources/dashscope_source.py b/astrbot/core/provider/sources/dashscope_source.py index 7afa06e73..9b262c001 100644 --- a/astrbot/core/provider/sources/dashscope_source.py +++ b/astrbot/core/provider/sources/dashscope_source.py @@ -146,14 +146,15 @@ class ProviderDashscope(ProviderOpenAIOfficial): # RAG 引用脚标格式化 output_text = re.sub(r"\[(\d+)\]", r"[\1]", output_text) if self.output_reference and response.output.get("doc_references", None): - ref_str = "" + ref_parts = [] for ref in response.output.get("doc_references", []) or []: ref_title = ( ref.get("title", "") if ref.get("title") else ref.get("doc_name", "") ) - ref_str += f"{ref['index_id']}. {ref_title}\n" + ref_parts.append(f"{ref['index_id']}. {ref_title}\n") + ref_str = "".join(ref_parts) output_text += f"\n\n回答来源:\n{ref_str}" llm_response = LLMResponse("assistant") diff --git a/astrbot/core/star/filter/command.py b/astrbot/core/star/filter/command.py index 8508f173f..2a9868fdc 100755 --- a/astrbot/core/star/filter/command.py +++ b/astrbot/core/star/filter/command.py @@ -51,15 +51,15 @@ class CommandFilter(HandlerFilter): self._cmpl_cmd_names: list | None = None def print_types(self): - result = "" + parts = [] for k, v in self.handler_params.items(): if isinstance(v, type): - result += f"{k}({v.__name__})," + parts.append(f"{k}({v.__name__}),") elif isinstance(v, types.UnionType) or typing.get_origin(v) is typing.Union: - result += f"{k}({v})," + parts.append(f"{k}({v}),") else: - result += f"{k}({type(v).__name__})={v}," - result = result.rstrip(",") + parts.append(f"{k}({type(v).__name__})={v},") + result = "".join(parts).rstrip(",") return result def init_handler_md(self, handle_md: StarHandlerMetadata): diff --git a/astrbot/core/star/filter/command_group.py b/astrbot/core/star/filter/command_group.py index 0f5c19ec5..e1c2efb22 100755 --- a/astrbot/core/star/filter/command_group.py +++ b/astrbot/core/star/filter/command_group.py @@ -66,7 +66,7 @@ class CommandGroupFilter(HandlerFilter): event: AstrMessageEvent | None = None, cfg: AstrBotConfig | None = None, ) -> str: - result = "" + parts = [] for sub_filter in sub_command_filters: if isinstance(sub_filter, CommandFilter): custom_filter_pass = True @@ -74,31 +74,32 @@ class CommandGroupFilter(HandlerFilter): custom_filter_pass = sub_filter.custom_filter_ok(event, cfg) if custom_filter_pass: cmd_th = sub_filter.print_types() - result += f"{prefix}├── {sub_filter.command_name}" + line = f"{prefix}├── {sub_filter.command_name}" if cmd_th: - result += f" ({cmd_th})" + line += f" ({cmd_th})" else: - result += " (无参数指令)" + line += " (无参数指令)" if sub_filter.handler_md and sub_filter.handler_md.desc: - result += f": {sub_filter.handler_md.desc}" + line += f": {sub_filter.handler_md.desc}" - result += "\n" + parts.append(line + "\n") elif isinstance(sub_filter, CommandGroupFilter): custom_filter_pass = True if event and cfg: custom_filter_pass = sub_filter.custom_filter_ok(event, cfg) if custom_filter_pass: - result += f"{prefix}├── {sub_filter.group_name}" - result += "\n" - result += sub_filter.print_cmd_tree( - sub_filter.sub_command_filters, - prefix + "│ ", - event=event, - cfg=cfg, + parts.append(f"{prefix}├── {sub_filter.group_name}\n") + parts.append( + sub_filter.print_cmd_tree( + sub_filter.sub_command_filters, + prefix + "│ ", + event=event, + cfg=cfg, + ) ) - return result + return "".join(parts) def custom_filter_ok(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: for custom_filter in self.custom_filter_list: diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 3fb988a6c..84976f2ba 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -213,11 +213,12 @@ class AstrBotDashboard: raise Exception(f"端口 {port} 已被占用") - display = f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n" - display += f" ➜ 本地: http://localhost:{port}\n" + parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"] + parts.append(f" ➜ 本地: http://localhost:{port}\n") for ip in ip_addr: - display += f" ➜ 网络: http://{ip}:{port}\n" - display += " ➜ 默认用户名和密码: astrbot\n ✨✨✨\n" + parts.append(f" ➜ 网络: http://{ip}:{port}\n") + parts.append(" ➜ 默认用户名和密码: astrbot\n ✨✨✨\n") + display = "".join(parts) if not ip_addr: display += ( diff --git a/packages/astrbot/commands/conversation.py b/packages/astrbot/commands/conversation.py index 82b661773..9538d8f53 100644 --- a/packages/astrbot/commands/conversation.py +++ b/packages/astrbot/commands/conversation.py @@ -134,12 +134,13 @@ class ConversationCommands: size_per_page, ) - history = "" + parts = [] for context in contexts: if len(context) > 150: context = context[:150] + "..." - history += f"{context}\n" + parts.append(f"{context}\n") + history = "".join(parts) ret = ( f"当前对话历史记录:" f"{history or '无历史记录'}\n\n" @@ -154,7 +155,7 @@ class ConversationCommands: provider = self.context.get_using_provider(message.unified_msg_origin) if provider and provider.meta().type == "dify": """原有的Dify处理逻辑保持不变""" - ret = "Dify 对话列表:\n" + parts = ["Dify 对话列表:\n"] assert isinstance(provider, ProviderDify) data = await provider.api_client.get_chat_convs(message.unified_msg_origin) idx = 1 @@ -162,12 +163,17 @@ class ConversationCommands: 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" + parts.append( + f"{idx}. {conv['name']}({conv['id'][:4]})\n 上次更新:{ts_h}\n" + ) idx += 1 if idx == 1: - ret += "没有找到任何对话。" + parts.append("没有找到任何对话。") dify_cid = provider.conversation_ids.get(message.unified_msg_origin, None) - ret += f"\n\n用户: {message.unified_msg_origin}\n当前对话: {dify_cid}\n使用 /switch <序号> 切换对话。" + parts.append( + f"\n\n用户: {message.unified_msg_origin}\n当前对话: {dify_cid}\n使用 /switch <序号> 切换对话。" + ) + ret = "".join(parts) message.set_result(MessageEventResult().message(ret)) return @@ -185,7 +191,7 @@ class ConversationCommands: end_idx = start_idx + size_per_page conversations_paged = conversations_all[start_idx:end_idx] - ret = "对话列表:\n---\n" + parts = ["对话列表:\n---\n"] """全局序号从当前页的第一个开始""" global_index = start_idx + 1 @@ -204,10 +210,13 @@ class ConversationCommands: ) 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" + parts.append( + 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" + parts.append("---\n") + ret = "".join(parts) curr_cid = await self.context.conversation_manager.get_curr_conversation_id( message.unified_msg_origin, ) diff --git a/packages/astrbot/commands/persona.py b/packages/astrbot/commands/persona.py index 53582ce8e..1289cb569 100644 --- a/packages/astrbot/commands/persona.py +++ b/packages/astrbot/commands/persona.py @@ -59,10 +59,11 @@ class PersonaCommands: .use_t2i(False), ) elif l[1] == "list": - msg = "人格列表:\n" + parts = ["人格列表:\n"] for persona in self.context.provider_manager.personas: - msg += f"- {persona['name']}\n" - msg += "\n\n*输入 `/persona view 人格名` 查看人格详细信息" + parts.append(f"- {persona['name']}\n") + parts.append("\n\n*输入 `/persona view 人格名` 查看人格详细信息") + msg = "".join(parts) message.set_result(MessageEventResult().message(msg)) elif l[1] == "view": if len(l) == 2: diff --git a/packages/astrbot/commands/plugin.py b/packages/astrbot/commands/plugin.py index f9092ff97..ab45efc11 100644 --- a/packages/astrbot/commands/plugin.py +++ b/packages/astrbot/commands/plugin.py @@ -13,14 +13,17 @@ class PluginCommands: async def plugin_ls(self, event: AstrMessageEvent): """获取已经安装的插件列表。""" - plugin_list_info = "已加载的插件:\n" + parts = ["已加载的插件:\n"] for plugin in self.context.get_all_stars(): - plugin_list_info += f"- `{plugin.name}` By {plugin.author}: {plugin.desc}" + line = f"- `{plugin.name}` By {plugin.author}: {plugin.desc}" if not plugin.activated: - plugin_list_info += " (未启用)" - plugin_list_info += "\n" - if plugin_list_info.strip() == "": + line += " (未启用)" + parts.append(line + "\n") + + if len(parts) == 1: plugin_list_info = "没有加载任何插件。" + else: + plugin_list_info = "".join(parts) plugin_list_info += "\n使用 /plugin help <插件名> 查看插件帮助和加载的指令。\n使用 /plugin on/off <插件名> 启用或者禁用插件。" event.set_result( @@ -103,14 +106,14 @@ class PluginCommands: command_names.append(filter_.group_name) if len(command_handlers) > 0: - help_msg += "\n\n🔧 指令列表:\n" + parts = ["\n\n🔧 指令列表:\n"] for i in range(len(command_handlers)): - help_msg += f"- {command_names[i]}" + line = f"- {command_names[i]}" if command_handlers[i].desc: - help_msg += f": {command_handlers[i].desc}" - help_msg += "\n" - - help_msg += "\nTip: 指令的触发需要添加唤醒前缀,默认为 /。" + line += f": {command_handlers[i].desc}" + parts.append(line + "\n") + parts.append("\nTip: 指令的触发需要添加唤醒前缀,默认为 /。") + help_msg += "".join(parts) ret = f"🧩 插件 {plugin_name} 帮助信息:\n" + help_msg ret += "更多帮助信息请查看插件仓库 README。" diff --git a/packages/astrbot/commands/provider.py b/packages/astrbot/commands/provider.py index 750e9de5a..8db7324e4 100644 --- a/packages/astrbot/commands/provider.py +++ b/packages/astrbot/commands/provider.py @@ -19,38 +19,39 @@ class ProviderCommands: umo = event.unified_msg_origin if idx is None: - ret = "## 载入的 LLM 提供商\n" + parts = ["## 载入的 LLM 提供商\n"] for idx, llm in enumerate(self.context.get_all_providers()): id_ = llm.meta().id - ret += f"{idx + 1}. {id_} ({llm.meta().model})" + line = 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" + line += " (当前使用)" + parts.append(line + "\n") tts_providers = self.context.get_all_tts_providers() if tts_providers: - ret += "\n## 载入的 TTS 提供商\n" + parts.append("\n## 载入的 TTS 提供商\n") for idx, tts in enumerate(tts_providers): id_ = tts.meta().id - ret += f"{idx + 1}. {id_}" + line = 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" + line += " (当前使用)" + parts.append(line + "\n") stt_providers = self.context.get_all_stt_providers() if stt_providers: - ret += "\n## 载入的 STT 提供商\n" + parts.append("\n## 载入的 STT 提供商\n") for idx, stt in enumerate(stt_providers): id_ = stt.meta().id - ret += f"{idx + 1}. {id_}" + line = 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" + line += " (当前使用)" + parts.append(line + "\n") - ret += "\n使用 /provider <序号> 切换 LLM 提供商。" + parts.append("\n使用 /provider <序号> 切换 LLM 提供商。") + ret = "".join(parts) if tts_providers: ret += "\n使用 /provider tts <序号> 切换 TTS 提供商。" @@ -128,16 +129,17 @@ class ProviderCommands: .use_t2i(False), ) return - i = 1 - ret = "下面列出了此模型提供商可用模型:" - for model in models: - ret += f"\n{i}. {model}" - i += 1 + parts = ["下面列出了此模型提供商可用模型:"] + for i, model in enumerate(models, 1): + parts.append(f"\n{i}. {model}") curr_model = prov.get_model() or "无" - ret += f"\n当前模型: [{curr_model}]" + parts.append(f"\n当前模型: [{curr_model}]") + parts.append( + "\nTips: 使用 /model <模型名/编号>,即可实时更换模型。如目标模型不存在于上表,请输入模型名。" + ) - ret += "\nTips: 使用 /model <模型名/编号>,即可实时更换模型。如目标模型不存在于上表,请输入模型名。" + ret = "".join(parts) message.set_result(MessageEventResult().message(ret).use_t2i(False)) elif isinstance(idx_or_name, int): models = [] @@ -180,14 +182,15 @@ class ProviderCommands: 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]}" + parts = ["Key:"] + for i, k in enumerate(keys_data, 1): + parts.append(f"\n{i}. {k[:8]}") - ret += f"\n当前 Key: {curr_key[:8]}" - ret += "\n当前模型: " + prov.get_model() - ret += "\n使用 /key 切换 Key。" + parts.append(f"\n当前 Key: {curr_key[:8]}") + parts.append("\n当前模型: " + prov.get_model()) + parts.append("\n使用 /key 切换 Key。") + ret = "".join(parts) message.set_result(MessageEventResult().message(ret).use_t2i(False)) else: keys_data = prov.get_keys() diff --git a/packages/astrbot/long_term_memory.py b/packages/astrbot/long_term_memory.py index a686d35b2..ceca60ef7 100644 --- a/packages/astrbot/long_term_memory.py +++ b/packages/astrbot/long_term_memory.py @@ -119,13 +119,13 @@ class LongTermMemory: if event.get_message_type() == MessageType.GROUP_MESSAGE: datetime_str = datetime.datetime.now().strftime("%H:%M:%S") - final_message = f"[{event.message_obj.sender.nickname}/{datetime_str}]: " + parts = [f"[{event.message_obj.sender.nickname}/{datetime_str}]: "] cfg = self.cfg(event) for comp in event.get_messages(): if isinstance(comp, Plain): - final_message += f" {comp.text}" + parts.append(f" {comp.text}") elif isinstance(comp, Image): if cfg["image_caption"]: try: @@ -137,11 +137,13 @@ class LongTermMemory: cfg["image_caption_provider_id"], cfg["image_caption_prompt"], ) - final_message += f" [Image: {caption}]" + parts.append(f" [Image: {caption}]") except Exception as e: logger.error(f"获取图片描述失败: {e}") else: - final_message += " [Image]" + parts.append(" [Image]") + + final_message = "".join(parts) logger.debug(f"ltm | {event.unified_msg_origin} | {final_message}") self.session_chats[event.unified_msg_origin].append(final_message) if len(self.session_chats[event.unified_msg_origin]) > cfg["max_cnt"]: diff --git a/packages/reminder/main.py b/packages/reminder/main.py index 0349b9eb4..eaeec8d73 100644 --- a/packages/reminder/main.py +++ b/packages/reminder/main.py @@ -203,14 +203,15 @@ class Main(star.Star): if not reminders: yield event.plain_result("没有正在进行的待办事项。") else: - reminder_str = "正在进行的待办事项:\n" + parts = ["正在进行的待办事项:\n"] for i, reminder in enumerate(reminders): time_ = reminder.get("datetime", "") if not time_: cron_expr = reminder.get("cron", "") time_ = reminder.get("cron_h", "") + f"(Cron: {cron_expr})" - reminder_str += f"{i + 1}. {reminder['text']} - {time_}\n" - reminder_str += "\n使用 /reminder rm 删除待办事项。\n" + parts.append(f"{i + 1}. {reminder['text']} - {time_}\n") + parts.append("\n使用 /reminder rm 删除待办事项。\n") + reminder_str = "".join(parts) yield event.plain_result(reminder_str) @reminder.command("rm")