From 618f8aa7d20314449e767c681039b7e6deb78971 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 25 Jul 2024 10:44:17 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/command/internal_handler.py | 18 ++++----- model/command/openai_official_handler.py | 48 +++++++++++++++++++++--- model/platform/qq_official.py | 19 +++++++--- model/provider/openai_official.py | 17 +++++---- util/config_utils.py | 4 +- util/t2i/strategies/local_strategy.py | 2 +- util/t2i/strategies/network_strategy.py | 4 +- util/updator/zip_updator.py | 5 +++ 8 files changed, 85 insertions(+), 32 deletions(-) diff --git a/model/command/internal_handler.py b/model/command/internal_handler.py index 5f0c58cc..50f05527 100644 --- a/model/command/internal_handler.py +++ b/model/command/internal_handler.py @@ -19,7 +19,7 @@ class InternalCommandHandler: self.plugin_manager = plugin_manager self.manager.register("help", "查看帮助", 10, self.help) - self.manager.register("nick", "设置机器人昵称", 10, self.set_nick) + self.manager.register("wake", "设置机器人唤醒词", 10, self.set_nick) self.manager.register("update", "更新 AstrBot", 10, self.update) self.manager.register("plugin", "插件管理", 10, self.plugin) self.manager.register("reboot", "重启 AstrBot", 10, self.reboot) @@ -33,14 +33,14 @@ class InternalCommandHandler: return CommandResult().message("你没有权限使用该指令。") l = message_str.split(" ") if len(l) == 1: - return CommandResult().message("【设置机器人昵称】示例:\n支持多昵称\nnick 昵称1 昵称2 昵称3") + return CommandResult().message("设置机器人唤醒词,支持多唤醒词。以唤醒词开头的消息会唤醒机器人处理,起到 @ 的效果。\n示例:wake 昵称1 昵称2 昵称3") nick = l[1:] context.config_helper.put("nick_qq", nick) context.nick = tuple(nick) return CommandResult( hit=True, success=True, - message_chain=f"已经成功将昵称设定为 {nick}", + message_chain=f"已经成功将唤醒词设定为 {nick}", ) def update(self, message: AstrMessageEvent, context: Context): @@ -51,13 +51,13 @@ class InternalCommandHandler: success=False, message_chain="你没有权限使用该指令", ) + update_info = context.updator.check_update(None, None) if tokens.len == 1: - update_info = context.updator.check_update(None, None) ret = "" if not update_info: ret = f"当前已经是最新版本 v{VERSION}。" else: - ret = f"发现新版本 v{update_info['version']}。\n- 使用 /update latest 更新到最新版本。\n- 使用 /update vX.X.X 更新到指定版本。" + ret = f"发现新版本 {update_info.version},更新内容如下:\n---\n{update_info.body}\n---\n- 使用 /update latest 更新到最新版本。\n- 使用 /update vX.X.X 更新到指定版本。" return CommandResult( hit=True, success=False, @@ -67,7 +67,7 @@ class InternalCommandHandler: if tokens.get(1) == "latest": try: context.updator.update() - return CommandResult().message(f"已经成功更新到最新版本 v{update_info['version']}。要应用更新,请重启 AstrBot。输入 /reboot 即可重启") + return CommandResult().message(f"已经成功更新到最新版本 v{update_info.version}。要应用更新,请重启 AstrBot。输入 /reboot 即可重启") except BaseException as e: return CommandResult().message(f"更新失败。原因:{str(e)}") elif tokens.get(1).startswith("v"): @@ -157,13 +157,13 @@ class InternalCommandHandler: msg = "# Help Center\n## 指令列表\n" for key, value in self.manager.commands_handler.items(): if value.plugin_metadata: - msg += f"`{key}` ({value.plugin_metadata.plugin_name}) - {value.description}\n" - else: msg += f"`{key}` - {value.description}\n" + msg += f"- `{key}` ({value.plugin_metadata.plugin_name}): {value.description}\n" + else: msg += f"- `{key}`: {value.description}\n" # plugins if context.cached_plugins != None: plugin_list_info = "" for plugin in context.cached_plugins: - plugin_list_info += f"`{plugin.metadata.plugin_name}` {plugin.metadata.desc}\n" + plugin_list_info += f"- `{plugin.metadata.plugin_name}` {plugin.metadata.desc}\n" if plugin_list_info.strip() != "": msg += "\n## 插件列表\n> 使用plugin v 插件名 查看插件帮助\n" msg += plugin_list_info diff --git a/model/command/openai_official_handler.py b/model/command/openai_official_handler.py index 49b645ab..1f783610 100644 --- a/model/command/openai_official_handler.py +++ b/model/command/openai_official_handler.py @@ -25,6 +25,7 @@ class OpenAIOfficialCommandHandler(): self.manager.register("unset", "清除个性化人格设置", 10, self.unset) self.manager.register("set", "设置个性化人格", 10, self.set) self.manager.register("draw", "调用 DallE 模型画图", 10, self.draw) + self.manager.register("model", "切换模型", 10, self.model) self.manager.register("画", "调用 DallE 模型画图", 10, self.draw) def set_provider(self, provider): @@ -37,6 +38,41 @@ class OpenAIOfficialCommandHandler(): return CommandResult().message("重置成功") elif tokens.get(1) == 'p': await self.provider.forget(message.session_id) + + async def model(self, message: AstrMessageEvent, context: Context): + tokens = self.manager.command_parser.parse(message.message_str) + if tokens.len == 1: + ret = await self._print_models() + return CommandResult().message(ret) + model = tokens.get(1) + if model.isdigit(): + try: + models = await self.provider.get_models() + except BaseException as e: + logger.error(f"获取模型列表失败: {str(e)}") + return CommandResult().message("获取模型列表失败,无法使用编号切换模型。可以尝试直接输入模型名来切换,如 gpt-4o。") + models = list(models) + if int(model) <= len(models) and int(model) >= 1: + model = models[int(model)-1] + self.provider.set_model(model.id) + return CommandResult().message(f"模型已设置为 {model.id}") + else: + self.provider.set_model(model) + return CommandResult().message(f"模型已设置为 {model} (自定义)") + + async def _print_models(self): + try: + models = await self.provider.get_models() + except BaseException as e: + return "获取模型列表失败: " + str(e) + i = 1 + ret = "OpenAI GPT 类可用模型" + for model in models: + ret += f"\n{i}. {model.id}" + i += 1 + ret += "\nTips: 使用 /model 模型名/编号,即可实时更换模型。如目标模型不存在于上表,请输入模型名。" + logger.debug(ret) + return ret def his(self, message: AstrMessageEvent, context: Context): tokens = self.manager.command_parser.parse(message.message_str) @@ -108,12 +144,12 @@ class OpenAIOfficialCommandHandler(): def set(self, message: AstrMessageEvent, context: Context): l = message.message_str.split(" ") if len(l) == 1: - return CommandResult().message("【人格文本由PlexPt开源项目awesome-chatgpt-prompts-zh提供】\n设置人格: \n/set 人格名。例如/set 编剧\n人格列表: /set list\n人格详细信息: /set view 人格名\n自定义人格: /set 人格文本\n重置会话(清除人格): /reset\n重置会话(保留人格): /reset p\n【当前人格】: " + str(self.provider.curr_personality)) + return CommandResult().message("- 设置人格: \nset 人格名。例如 set 编剧\n- 人格列表: set list\n- 人格详细信息: set view 人格名\n- 自定义人格: set 人格文本\n- 重置会话(清除人格): reset\n- 重置会话(保留人格): reset p\n\n【当前人格】: " + str(self.provider.curr_personality['prompt'])) elif l[1] == "list": msg = "人格列表:\n" for key in personalities.keys(): - msg += f" |-{key}\n" - msg += '\n\n*输入/set view 人格名查看人格详细信息' + msg += f"- {key}\n" + msg += '\n\n*输入 set view 人格名 查看人格详细信息' return CommandResult().message(msg) elif l[1] == "view": if len(l) == 2: @@ -126,20 +162,20 @@ class OpenAIOfficialCommandHandler(): msg = f"人格{ps}不存在" return CommandResult().message(msg) else: - ps = l[1].strip() + ps = "".join(l[1:]).strip() if ps in personalities: self.provider.curr_personality = { 'name': ps, 'prompt': personalities[ps] } - self.provider.personality_set(ps, message.session_id) + self.provider.personality_set(self.provider.curr_personality, message.session_id) return CommandResult().message(f"人格已设置。 \n人格信息: {ps}") else: self.provider.curr_personality = { 'name': '自定义人格', 'prompt': ps } - self.provider.personality_set(ps, message.session_id) + self.provider.personality_set(self.provider.curr_personality, message.session_id) return CommandResult().message(f"人格已设置。 \n人格信息: {ps}") async def draw(self, message: AstrMessageEvent, context: Context): diff --git a/model/platform/qq_official.py b/model/platform/qq_official.py index f2d60ea9..8c7fc2fe 100644 --- a/model/platform/qq_official.py +++ b/model/platform/qq_official.py @@ -9,7 +9,7 @@ import botpy.types.message from botpy.types.message import Reference, Media from botpy import Client -from util.io import save_temp_img +from util.io import save_temp_img, download_image_by_url from . import Platform from type.astrbot_message import * from type.message_event import * @@ -80,7 +80,7 @@ class QQOfficial(Platform): self.client.set_platform(self) - def _parse_to_qqofficial(self, message: List[BaseMessageComponent]): + async def _parse_to_qqofficial(self, message: List[BaseMessageComponent], is_group: bool = False): plain_text = "" image_path = None # only one img supported for i in message: @@ -92,8 +92,9 @@ class QQOfficial(Platform): elif i.file and i.file.startswith("base64://"): img_data = base64.b64decode(i.file[9:]) image_path = save_temp_img(img_data) - else: - image_path = save_temp_img(i.file) + elif i.file and i.file.startswith("http"): + # 如果是群消息,不需要下载 + image_path = await download_image_by_url(i.file) if not is_group else i.file return plain_text, image_path def _parse_from_qqofficial(self, message: Union[botpy.message.Message, botpy.message.GroupMessage], @@ -237,7 +238,7 @@ class QQOfficial(Platform): rendered_images = await self.convert_to_t2i_chain(result_message) if isinstance(result_message, list): - plain_text, image_path = self._parse_to_qqofficial(result_message) + plain_text, image_path = await self._parse_to_qqofficial(result_message, message.type == MessageType.GROUP_MESSAGE) else: plain_text = result_message @@ -319,17 +320,23 @@ class QQOfficial(Platform): media = await self.client.api.post_group_file(kwargs['group_openid'], 1, image_url) del kwargs['file_image'] kwargs['media'] = media + logger.debug(f"发送群图片: {media}") kwargs['msg_type'] = 7 # 富媒体 await self.client.api.post_group_message(**kwargs) elif 'channel_id' in kwargs: # 频道消息 if 'file_image' in kwargs: kwargs['file_image'] = kwargs['file_image'].replace("file:///", "") + # 频道消息发图只支持本地 + if kwargs['file_image'].startswith("http"): + kwargs['file_image'] = await download_image_by_url(kwargs['file_image']) await self.client.api.post_message(**kwargs) else: # 频道私聊消息 if 'file_image' in kwargs: kwargs['file_image'] = kwargs['file_image'].replace("file:///", "") + if kwargs['file_image'].startswith("http"): + kwargs['file_image'] = await download_image_by_url(kwargs['file_image']) await self.client.api.post_dms(**kwargs) async def send_msg(self, target: Dict[str, str], result_message: Union[List[BaseMessageComponent], str]): @@ -343,7 +350,7 @@ class QQOfficial(Platform): - 如果目标是 频道私聊,请添加 key `guild_id`。 ''' if isinstance(result_message, list): - plain_text, image_path = self._parse_to_qqofficial(result_message) + plain_text, image_path = await self._parse_to_qqofficial(result_message) else: plain_text = result_message diff --git a/model/provider/openai_official.py b/model/provider/openai_official.py index ab7516f3..73e5baaf 100644 --- a/model/provider/openai_official.py +++ b/model/provider/openai_official.py @@ -188,12 +188,14 @@ class ProviderOpenAIOfficial(Provider): return self.model_configs['model'].startswith("gpt-4") async def get_models(self): - ''' - 获取所有模型 - ''' - models = await self.client.models.list() - logger.info(f"OpenAI 模型列表:{models}") - return models + try: + models = await self.client.models.list() + except NotFoundError as e: + bu = str(self.client.base_url) + self.client.base_url = bu + "/v1" + models = await self.client.models.list() + finally: + return filter(lambda x: x.id.startswith("gpt"), models.data) async def assemble_context(self, session_id: str, prompt: str, image_url: str = None): ''' @@ -376,7 +378,8 @@ class ProviderOpenAIOfficial(Provider): if "maximum context length" in str(e): logger.warn(f"OpenAI 请求失败:{e}。上下文长度超过限制。尝试弹出最早的记录然后重试。") self.pop_record(session_id) - + + logger.warning(traceback.format_exc()) logger.warning(f"OpenAI 请求失败:{e}。重试第 {retry} 次。") time.sleep(1) diff --git a/util/config_utils.py b/util/config_utils.py index 688bd74b..935793c9 100644 --- a/util/config_utils.py +++ b/util/config_utils.py @@ -11,7 +11,7 @@ def init_configs(): cc.init_attributes("qq_forward_threshold", 200) cc.init_attributes("qq_welcome", "") - cc.init_attributes("qq_pic_mode", False) + cc.init_attributes("qq_pic_mode", True) cc.init_attributes("gocq_host", "127.0.0.1") cc.init_attributes("gocq_http_port", 5700) cc.init_attributes("gocq_websocket_port", 6700) @@ -104,6 +104,6 @@ def inject_to_context(context: Context): if isinstance(nick_qq, list): nick_qq = tuple(nick_qq) context.nick = nick_qq - context.t2i_mode = cc.get("qq_pic_mode", False) + context.t2i_mode = cc.get("qq_pic_mode", True) return cfg \ No newline at end of file diff --git a/util/t2i/strategies/local_strategy.py b/util/t2i/strategies/local_strategy.py index 5711a483..0a27601d 100644 --- a/util/t2i/strategies/local_strategy.py +++ b/util/t2i/strategies/local_strategy.py @@ -17,7 +17,7 @@ class LocalRenderStrategy(RenderStrategy): except Exception as e: pass - async def render(self, text: str, **kwargs): + async def render(self, text: str, return_url: bool=False) -> str: font_size = 26 image_width = 800 image_height = 600 diff --git a/util/t2i/strategies/network_strategy.py b/util/t2i/strategies/network_strategy.py index cd59ce36..67f406d6 100644 --- a/util/t2i/strategies/network_strategy.py +++ b/util/t2i/strategies/network_strategy.py @@ -30,7 +30,9 @@ class NetworkRenderStrategy(RenderStrategy): "version": f"v{VERSION}", }, "options": { - "full_page": True + "full_page": True, + "type": "jpeg", + "quality": 25, } } diff --git a/util/updator/zip_updator.py b/util/updator/zip_updator.py index a49feee4..102c9ee5 100644 --- a/util/updator/zip_updator.py +++ b/util/updator/zip_updator.py @@ -10,6 +10,11 @@ class ReleaseInfo(): published_at: str body: str + def __init__(self, version: str = '', published_at: str = '', body: str = '') -> None: + self.version = version + self.published_at = published_at + self.body = body + def __str__(self) -> str: return f"新版本: {self.version}, 发布于: {self.published_at}, 详细内容: {self.body}"