From 292a3a43ba320bf8894601ee05ae0c5c3d521c0d Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 10 Sep 2024 03:31:17 -0400 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E7=8E=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/coverage_test.yml | 6 ++- .gitignore | 3 +- astrbot/message/handler.py | 5 +- model/command/internal_handler.py | 11 ++-- model/platform/qq_aiocqhttp.py | 13 +++-- model/provider/openai_official.py | 2 +- tests/mocks/onebot.py | 7 ++- tests/mocks/qq_official.py | 23 +++++--- tests/test_message.py | 82 ++++++++++++++++++++++++++++- 9 files changed, 125 insertions(+), 27 deletions(-) diff --git a/.github/workflows/coverage_test.yml b/.github/workflows/coverage_test.yml index a021daa7c..941084087 100644 --- a/.github/workflows/coverage_test.yml +++ b/.github/workflows/coverage_test.yml @@ -26,7 +26,11 @@ jobs: mkdir temp - name: Run tests - run: PYTHONPATH=./ pytest --cov=. tests/ -v + run: | + export LLM_MODEL=${{ secrets.LLM_MODEL }} + export OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE }} + export OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} + PYTHONPATH=./ pytest --cov=. tests/ -v - name: Upload results to Codecov uses: codecov/codecov-action@v4 diff --git a/.gitignore b/.gitignore index 7815d9b88..91514b8d7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ cmd_config.json data/* cookies.json logs/ -addons/plugins \ No newline at end of file +addons/plugins +.coverage \ No newline at end of file diff --git a/astrbot/message/handler.py b/astrbot/message/handler.py index 501960825..b8bce1f99 100644 --- a/astrbot/message/handler.py +++ b/astrbot/message/handler.py @@ -110,8 +110,7 @@ class MessageHandler(): self.llm_wake_prefix = self.context.config_helper.llm_settings.wake_prefix if self.llm_wake_prefix: self.llm_wake_prefix = self.llm_wake_prefix.strip() - self.nicks = self.context.config_helper.wake_prefix - self.provider = self.context.llms[0] if len(self.context.llms) > 0 else None + self.provider = self.context.llms[0].llm_instance if len(self.context.llms) > 0 else None self.reply_prefix = str(self.context.config_helper.platform_settings.reply_prefix) self.llm_tools = FuncCall(self.provider) @@ -140,7 +139,7 @@ class MessageHandler(): return # remove the nick prefix - for nick in self.nicks: + for nick in self.context.config_helper.wake_prefix: if msg_plain.startswith(nick): msg_plain = msg_plain.removeprefix(nick) break diff --git a/model/command/internal_handler.py b/model/command/internal_handler.py index e4e120f79..058987e4b 100644 --- a/model/command/internal_handler.py +++ b/model/command/internal_handler.py @@ -62,11 +62,12 @@ class InternalCommandHandler: return CommandResult().message("你没有权限使用该指令。") l = message_str.split(" ") if len(l) == 1: - return CommandResult().message(f"设置机器人唤醒词。以唤醒词开头的消息会唤醒机器人处理,起到 @ 的效果。\n示例:wake 昵称。当前唤醒词有:{context.config_helper.wake_prefix}") + return CommandResult().message(f"设置机器人唤醒词。以唤醒词开头的消息会唤醒机器人处理,起到 @ 的效果。\n示例:wake 昵称。当前唤醒词是:{context.config_helper.wake_prefix[0]}") nick = l[1].strip() if not nick: return CommandResult().message("wake: 请指定唤醒词。") context.config_helper.wake_prefix = [nick] + context.config_helper.save_config() return CommandResult( hit=True, success=True, @@ -88,11 +89,7 @@ class InternalCommandHandler: ret = f"当前已经是最新版本 v{VERSION}。" else: 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, - message_chain=ret, - ) + return CommandResult().message(ret) else: if tokens.get(1) == "latest": try: @@ -182,7 +179,7 @@ class InternalCommandHandler: async with session.get("https://soulter.top/channelbot/notice.json") as resp: notice = (await resp.json())["notice"] except BaseException as e: - logger.warn("An error occurred while fetching astrbot notice. Never mind, it's not important.") + logger.warning("An error occurred while fetching astrbot notice. Never mind, it's not important.") msg = "# Help Center\n## 指令列表\n" for key, value in self.manager.commands_handler.items(): diff --git a/model/platform/qq_aiocqhttp.py b/model/platform/qq_aiocqhttp.py index c6f4164f8..e97e5baa6 100644 --- a/model/platform/qq_aiocqhttp.py +++ b/model/platform/qq_aiocqhttp.py @@ -112,7 +112,7 @@ class AIOCQHTTP(Platform): while self.context.running: await asyncio.sleep(1) - def pre_check(self, message: AstrBotMessage) -> bool: + async def pre_check(self, message: AstrBotMessage) -> bool: # if message chain contains Plain components or # At components which points to self_id, return True if message.type == MessageType.FRIEND_MESSAGE: @@ -121,7 +121,7 @@ class AIOCQHTTP(Platform): if isinstance(comp, At) and str(comp.qq) == message.self_id: return True, "at" # check commands which ignore prefix - if self.context.command_manager.check_command_ignore_prefix(message.message_str): + if await self.context.command_manager.check_command_ignore_prefix(message.message_str): return True, "command" # check nicks if self.check_nick(message.message_str): @@ -132,7 +132,7 @@ class AIOCQHTTP(Platform): logger.info( f"{message.sender.nickname}/{message.sender.user_id} -> {self.parse_message_outline(message)}") - ok, reason = self.pre_check(message) + ok, reason = await self.pre_check(message) if not ok: return @@ -173,6 +173,8 @@ class AIOCQHTTP(Platform): # 如果是等待回复的消息 if message.session_id in self.waiting and self.waiting[message.session_id] == '': self.waiting[message.session_id] = message + + return message_result async def reply_msg(self, @@ -188,17 +190,18 @@ class AIOCQHTTP(Platform): res = [Plain(text=res), ] # if image mode, put all Plain texts into a new picture. - if use_t2i or (use_t2i == None and self.context.config_helper.t2i) and isinstance(result_message, list): + if (use_t2i or (use_t2i == None and self.context.config_helper.t2i)) and isinstance(result_message, list): rendered_images = await self.convert_to_t2i_chain(res) if rendered_images: try: await self._reply(message, rendered_images) - return + return rendered_images except BaseException as e: logger.warn(traceback.format_exc()) logger.warn(f"以文本转图片的形式回复消息时发生错误: {e},将尝试默认方式。") await self._reply(message, res) + return res async def _reply(self, message: Union[AstrBotMessage, Dict], message_chain: List[BaseMessageComponent]): await self.record_metrics() diff --git a/model/provider/openai_official.py b/model/provider/openai_official.py index e1bf13ab0..2b6e08544 100644 --- a/model/provider/openai_official.py +++ b/model/provider/openai_official.py @@ -355,10 +355,10 @@ class ProviderOpenAIOfficial(Provider): if ok: continue else: raise Exception("所有 OpenAI API Key 目前都不可用。") except BadRequestError as e: + retry += 1 logger.warn(f"OpenAI 请求异常:{e}。") if "image_url is only supported by certain models." in str(e): raise Exception(f"当前模型 { self.get_curr_model() } 不支持图片输入,请更换模型。") - retry += 1 except RateLimitError as e: if "You exceeded your current quota" in str(e): self.keys_data[self.chosen_api_key] = False diff --git a/tests/mocks/onebot.py b/tests/mocks/onebot.py index 66df3d1ee..1b204c507 100644 --- a/tests/mocks/onebot.py +++ b/tests/mocks/onebot.py @@ -1,3 +1,4 @@ +import copy from aiocqhttp import Event class MockOneBotMessage(): @@ -10,4 +11,8 @@ class MockOneBotMessage(): return self.group_event_sample def create_random_direct_message(self): - return self.friend_event_sample \ No newline at end of file + return self.friend_event_sample + + def create_msg(self, text: str): + self.group_event_sample.message = [{'data': {'qq': '3430871669'}, 'type': 'at'}, {'data': {'text': text}, 'type': 'text'}] + return self.group_event_sample \ No newline at end of file diff --git a/tests/mocks/qq_official.py b/tests/mocks/qq_official.py index 0d665d289..0978502aa 100644 --- a/tests/mocks/qq_official.py +++ b/tests/mocks/qq_official.py @@ -3,19 +3,19 @@ import botpy.message class MockQQOfficialMessage(): def __init__(self): # 这些数据已经经过去敏处理 - self.group_plain_text_sample = {'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': 'just reply me `ok`', 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_sS6HqVPgtqV99eGliL-B-s7tOAbAq.IwuxikQF99Zo0ZBTGwimNMI9tHdSVqDwLokBtxf6ZR0.wT2ZicHpFjKstG81ovPjw88HwjHppK6Gc!', 'timestamp': '2024-07-27T19:58:52+08:00'} - self.group_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'size': 1440173, 'url': 'https://multimedia.nt.qq.com.cn/download?appid=1407&fileid=Cgk5MDU2MTc5OTISFBvbdDR6nYEHsqWEfYauN9wphLxlGK3zVyD_Cii9ibiql8eHA1CAvaMB&rkey=CAESKE4_cASDm1t162vI7q9gitU2u0SUciVRg1fbyn3zYe9f_XHL2vhiB0s&spec=0', 'width': 1186}], 'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': ' ', 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_sS6HqVPgtqV99eGliL-B-gPHZcYCXwRupoe8vE-ZOTrTxu7SAaxnZZpw5EcmZ2njqYIyLrdKiL0AQzPPUtGntMtG81ovPjw88HwjHppK6Gc!', 'timestamp': '2024-07-27T20:06:32+08:00'} - self.group_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'size': 1440173, 'url': 'https://multimedia.nt.qq.com.cn/download?appid=1407&fileid=Cgk5MDU2MTc5OTISFBvbdDR6nYEHsqWEfYauN9wphLxlGK3zVyD_CiiMytyomceHA1CAvaMB&rkey=CAQSKDOc_jvbthUjVk7zSzPCqflD2XWA0OWzO5qCNsiRFY4RfQMuHYt8KDU&spec=0', 'width': 1186}], 'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': " What's this", 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_sS6HqVPgtqV99eGliL-B-sxsf5-CTemxnIrv6O3G6ZYZ6EVI3I2Z4wNye7dUiKuyvRiHM9aM.-tTLCT.qsJy1stG81ovPjw88HwjHppK6Gc!', 'timestamp': '2024-07-27T20:15:24+08:00'} + self.group_plain_text_sample = {'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': 'just reply me `ok`', 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_test', 'timestamp': '2024-07-27T19:58:52+08:00'} + self.group_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'size': 1440173, 'url': 'https://multimedia.nt.qq.com.cn/download?appid=1407&fileid=Cgk5MDU2MTc5OTISFBvbdDR6nYEHsqWEfYauN9wphLxlGK3zVyD_Cii9ibiql8eHA1CAvaMB&rkey=CAESKE4_cASDm1t162vI7q9gitU2u0SUciVRg1fbyn3zYe9f_XHL2vhiB0s&spec=0', 'width': 1186}], 'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': ' ', 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_test', 'timestamp': '2024-07-27T20:06:32+08:00'} + self.group_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'size': 1440173, 'url': 'https://multimedia.nt.qq.com.cn/download?appid=1407&fileid=Cgk5MDU2MTc5OTISFBvbdDR6nYEHsqWEfYauN9wphLxlGK3zVyD_CiiMytyomceHA1CAvaMB&rkey=CAQSKDOc_jvbthUjVk7zSzPCqflD2XWA0OWzO5qCNsiRFY4RfQMuHYt8KDU&spec=0', 'width': 1186}], 'author': {'id': '3E47ABD92415AFEF02DAD74FFAB592D1', 'member_openid': '3E47ABD92415AFEF02DAD74FFAB592D1'}, 'content': " What's this", 'group_id': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'group_openid': 'BF5D5CA67932FFC4AFD18D4309DB759D', 'id': 'ROBOT1.0_test', 'timestamp': '2024-07-27T20:15:24+08:00'} self.group_event_id_sample = "GROUP_AT_MESSAGE_CREATE:ss6hqvpgtqv99eglilbjpsdzvudsjev64th8srgofxqkgxwpynhysl6q6ws849" self.guild_plain_text_sample = {'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'bot': False, 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '9941389', 'content': '<@!2519660939131724751> just reply me `ok`', 'guild_id': '7969749791337194879', 'id': '08ffca96ebdaa68fcd6e108de3de0438ef0e48a6c793b506', 'member': {'joined_at': '2022-08-13T13:13:56+08:00', 'nick': 'Soulter', 'roles': ['4', '23']}, 'mentions': [{'avatar': 'http://thirdqq.qlogo.cn/g?b=oidb&k=OUbv2LTECcjQt48ibDS4OcA&kti=ZqTjpgAAAAI&s=0&t=1708501824', 'bot': True, 'id': '2519660939131724751', 'username': '浅橙Bot'}], 'seq': 1903, 'seq_in_channel': '1903', 'timestamp': '2024-07-27T20:10:14+08:00'} - self.guild_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2665728996', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/75802001660367636/9941389-2665728996-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'bot': False, 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '9941389', 'content': '<@!2519660939131724751> ', 'guild_id': '7969749791337194879', 'id': '08ffca96ebdaa68fcd6e108de3de0438f10e48dbc793b506', 'member': {'joined_at': '2022-08-13T13:13:56+08:00', 'nick': 'Soulter', 'roles': ['4', '23']}, 'mentions': [{'avatar': 'http://thirdqq.qlogo.cn/g?b=oidb&k=mZ2Hn0BN5MLlBJTve0WIoA&kti=ZqTjnwAAAAA&s=0&t=1708501824', 'bot': True, 'id': '2519660939131724751', 'username': '浅橙Bot'}], 'seq': 1905, 'seq_in_channel': '1905', 'timestamp': '2024-07-27T20:11:07+08:00'} - self.guild_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2501183002', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/75802001660367636/9941389-2501183002-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'bot': False, 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '9941389', 'content': "<@!2519660939131724751> What's this", 'guild_id': '7969749791337194879', 'id': '08ffca96ebdaa68fcd6e108de3de0438f30e48a2c993b506', 'member': {'joined_at': '2022-08-13T13:13:56+08:00', 'nick': 'Soulter', 'roles': ['4', '23']}, 'mentions': [{'avatar': 'http://thirdqq.qlogo.cn/g?b=oidb&k=mZ2Hn0BN5MLlBJTve0WIoA&kti=ZqTjnwAAAAA&s=0&t=1708501824', 'bot': True, 'id': '2519660939131724751', 'username': '浅橙Bot'}], 'seq': 1907, 'seq_in_channel': '1907', 'timestamp': '2024-07-27T20:14:26+08:00'} + self.guild_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2665728996', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/75802001660367636/9941389-2665728996-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'bot': False, 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '9941389', 'content': '<@!2519660939131724751> ', 'guild_id': '7969749791337194879', 'id': 'testid', 'member': {'joined_at': '2022-08-13T13:13:56+08:00', 'nick': 'Soulter', 'roles': ['4', '23']}, 'mentions': [{'avatar': 'http://thirdqq.qlogo.cn/g?b=oidb&k=mZ2Hn0BN5MLlBJTve0WIoA&kti=ZqTjnwAAAAA&s=0&t=1708501824', 'bot': True, 'id': '2519660939131724751', 'username': '浅橙Bot'}], 'seq': 1905, 'seq_in_channel': '1905', 'timestamp': '2024-07-27T20:11:07+08:00'} + self.guild_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2501183002', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/75802001660367636/9941389-2501183002-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'bot': False, 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '9941389', 'content': "<@!2519660939131724751> What's this", 'guild_id': '7969749791337194879', 'id': 'testid', 'member': {'joined_at': '2022-08-13T13:13:56+08:00', 'nick': 'Soulter', 'roles': ['4', '23']}, 'mentions': [{'avatar': 'http://thirdqq.qlogo.cn/g?b=oidb&k=mZ2Hn0BN5MLlBJTve0WIoA&kti=ZqTjnwAAAAA&s=0&t=1708501824', 'bot': True, 'id': '2519660939131724751', 'username': '浅橙Bot'}], 'seq': 1907, 'seq_in_channel': '1907', 'timestamp': '2024-07-27T20:14:26+08:00'} self.guild_event_id_sample = "AT_MESSAGE_CREATE:e4c09708-781d-44d0-b8cf-34bf3d4e2e64" self.direct_plain_text_sample = {'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '33342831678707631', 'content': 'just reply me `ok`', 'direct_message': True, 'guild_id': '3398240095091349322', 'id': '08caaea38bcaabbe942f10afaf8fb08fa49d3b38a5014898c893b506', 'member': {'joined_at': '2023-03-13T19:40:31+08:00'}, 'seq': 165, 'seq_in_channel': '165', 'src_guild_id': '7969749791337194879', 'timestamp': '2024-07-27T20:12:08+08:00'} - self.direct_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2658044992', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/92265551678707631/33342831678707631-2658044992-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '33342831678707631', 'direct_message': True, 'guild_id': '3398240095091349322', 'id': '08caaea38bcaabbe942f10afaf8fb08fa49d3b38a70148adc893b506', 'member': {'joined_at': '2023-03-13T19:40:31+08:00'}, 'seq': 167, 'seq_in_channel': '167', 'src_guild_id': '7969749791337194879', 'timestamp': '2024-07-27T20:12:29+08:00'} - self.direct_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2526212938', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/92265551678707631/33342831678707631-2526212938-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '33342831678707631', 'content': "What's this", 'direct_message': True, 'guild_id': '3398240095091349322', 'id': '08caaea38bcaabbe942f10afaf8fb08fa49d3b38a80148f2c893b506', 'member': {'joined_at': '2023-03-13T19:40:31+08:00'}, 'seq': 168, 'seq_in_channel': '168', 'src_guild_id': '7969749791337194879', 'timestamp': '2024-07-27T20:13:38+08:00'} + self.direct_plain_image_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2658044992', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/92265551678707631/33342831678707631-2658044992-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '33342831678707631', 'direct_message': True, 'guild_id': '3398240095091349322', 'id': 'testid', 'member': {'joined_at': '2023-03-13T19:40:31+08:00'}, 'seq': 167, 'seq_in_channel': '167', 'src_guild_id': '7969749791337194879', 'timestamp': '2024-07-27T20:12:29+08:00'} + self.direct_multimedia_sample = {'attachments': [{'content_type': 'image/png', 'filename': '165FCBF8BD6F42496B58A6C66C5D4255.png', 'height': 1034, 'id': '2526212938', 'size': 1440173, 'url': 'gchat.qpic.cn/qmeetpic/92265551678707631/33342831678707631-2526212938-165FCBF8BD6F42496B58A6C66C5D4255/0', 'width': 1186}], 'author': {'avatar': 'https://qqchannel-profile-1251316161.file.myqcloud.com/168087977775f0eae70da8e512?t=1680879777', 'id': '6946931796791550499', 'username': 'Soulter'}, 'channel_id': '33342831678707631', 'content': "What's this", 'direct_message': True, 'guild_id': '3398240095091349322', 'id': 'testid', 'member': {'joined_at': '2023-03-13T19:40:31+08:00'}, 'seq': 168, 'seq_in_channel': '168', 'src_guild_id': '7969749791337194879', 'timestamp': '2024-07-27T20:13:38+08:00'} self.direct_event_id_sample = "DIRECT_MESSAGE_CREATE:e4c09708-781d-44d0-b8cf-34bf3d4e2e64" def create_random_group_message(self): @@ -42,4 +42,13 @@ class MockQQOfficialMessage(): ) return mocked + def create_msg(self, text: str): + sample = self.group_plain_text_sample.copy() + sample['content'] = text + mocked = botpy.message.Message( + api=None, + event_id=self.group_event_id_sample, + data=sample + ) + return mocked diff --git a/tests/test_message.py b/tests/test_message.py index 809b87164..7015785e1 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -8,6 +8,7 @@ from tests.mocks.onebot import MockOneBotMessage from astrbot.bootstrap import AstrBotBootstrap from model.platform.qq_official import QQOfficial from model.platform.qq_aiocqhttp import AIOCQHTTP +from model.provider.openai_official import ProviderOpenAIOfficial from type.astrbot_message import * from type.message_event import * from SparkleLogging.utils.core import LogManager @@ -24,7 +25,17 @@ pytest_plugins = ('pytest_asyncio',) os.environ['TEST_MODE'] = 'on' bootstrap = AstrBotBootstrap() + +llm_config = bootstrap.context.config_helper.llm[0] +llm_config.api_base = os.environ['OPENAI_API_BASE'] +llm_config.key = [os.environ['OPENAI_API_KEY']] +llm_config.model_config.model = os.environ['LLM_MODEL'] +llm_config.model_config.max_tokens = 1000 +llm_provider = ProviderOpenAIOfficial(llm_config) asyncio.run(bootstrap.run()) +bootstrap.message_handler.provider = llm_provider +bootstrap.config_helper.wake_prefix = ["/"] +bootstrap.config_helper.admins_id = ["905617992"] for p_config in bootstrap.context.config_helper.platform: if isinstance(p_config, QQOfficialPlatformConfig): @@ -67,4 +78,73 @@ class TestBasicMessageHandle(): event = MockOneBotMessage().create_random_direct_message() abm = aiocqhttp.convert_message(event) ret = await aiocqhttp.handle_msg(abm) - print(ret) \ No newline at end of file + print(ret) + +class TestInteralCommandHsandle(): + def create(self, text: str): + event = MockOneBotMessage().create_msg(text) + abm = aiocqhttp.convert_message(event) + return abm + + async def fast_test(self, text: str): + abm = self.create(text) + ret = await aiocqhttp.handle_msg(abm) + print(f"Command: {text}, Result: {ret.result_message}") + return ret + + @pytest.mark.asyncio + async def test_config_save(self): + abm = self.create("/websearch on") + ret = await aiocqhttp.handle_msg(abm) + assert bootstrap.context.config_helper.llm_settings.web_search \ + == bootstrap.config_helper.get("llm_settings")['web_search'] + + @pytest.mark.asyncio + async def test_websearch(self): + await self.fast_test("/websearch") + await self.fast_test("/websearch on") + await self.fast_test("/websearch off") + + @pytest.mark.asyncio + async def test_help(self): + await self.fast_test("/help") + + @pytest.mark.asyncio + async def test_myid(self): + await self.fast_test("/myid") + + @pytest.mark.asyncio + async def test_wake(self): + await self.fast_test("/wake") + await self.fast_test("/wake #") + assert "#" in bootstrap.context.config_helper.wake_prefix + assert "#" in bootstrap.context.config_helper.get("wake_prefix") + await self.fast_test("#wake /") + + @pytest.mark.asyncio + async def test_sleep(self): + await self.fast_test("/provider") + + @pytest.mark.asyncio + async def test_update(self): + await self.fast_test("/update") + + @pytest.mark.asyncio + async def test_t2i(self): + if not bootstrap.context.config_helper.t2i: + abm = self.create("/t2i") + await aiocqhttp.handle_msg(abm) + await self.fast_test("/help") + +class TestLLMChat(): + @pytest.mark.asyncio + async def test_llm_chat(self): + os.environ["TEST_LLM"] = "on" + ret = await llm_provider.text_chat("Just reply `ok`", "test") + print(ret) + event = MockOneBotMessage().create_msg("Just reply `ok`") + abm = aiocqhttp.convert_message(event) + ret = await aiocqhttp.handle_msg(abm) + print(ret) + os.environ["TEST_LLM"] = "off" + \ No newline at end of file