From 4ed1791b30389622e8591cb6e2c063f3ba68fdb3 Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:23:14 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E4=BE=9D=E8=B5=96=E6=9B=B4=E6=96=B0=20(#1837?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: 修复添加插件依赖更新 * :wrench: 修改插件依赖安装命令为使用poetry运行pip * :bug: 修复群组入群与退群提示 * :bug: 修复群组踢出用户提醒 * :art: 代码优化 * :art: 群欢迎迁移优化 * :adhesive_bandage: 精确webui调用统计 * :rotating_light: auto fix by pre-commit hooks * :bug: 修复测试 * :art: fix pre-commit.ci * :art: fix pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- poetry.lock | 28 +++++++-- pyproject.toml | 3 +- tests/conftest.py | 33 ++++++++++- .../admin/welcome_message/data_source.py | 2 + .../platform/qq/group_handle/__init__.py | 14 ++--- .../platform/qq/group_handle/data_source.py | 58 ++++++++++++------- .../builtin_plugins/plugin_store/__init__.py | 4 +- .../plugin_store/data_source.py | 46 +++++++++------ zhenxun/builtin_plugins/sign_in/utils.py | 5 +- .../web_ui/api/tabs/main/data_source.py | 26 +++++++-- zhenxun/configs/config.py | 2 + zhenxun/utils/decorator/retry.py | 16 +++++ zhenxun/utils/http_utils.py | 52 +++++++++-------- zhenxun/utils/message.py | 16 +++++ zhenxun/utils/platform.py | 50 ++++++++-------- 15 files changed, 247 insertions(+), 108 deletions(-) create mode 100644 zhenxun/utils/decorator/retry.py diff --git a/poetry.lock b/poetry.lock index 5a0b38fa..2bcba9fd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2226,13 +2226,13 @@ reference = "aliyun" [[package]] name = "nonebot-plugin-uninfo" -version = "0.4.1" +version = "0.6.8" description = "Universal Information Model for Nonebot2" optional = false python-versions = ">=3.9" files = [ - {file = "nonebot_plugin_uninfo-0.4.1-py3-none-any.whl", hash = "sha256:2075874a540f27cb650ff5e64482324121c39e4374b850df323482744910beac"}, - {file = "nonebot_plugin_uninfo-0.4.1.tar.gz", hash = "sha256:8c36d62684029d813dd01acd2cc759f07163923b8274935250dcd3e9293ef560"}, + {file = "nonebot_plugin_uninfo-0.6.8-py3-none-any.whl", hash = "sha256:0adc7e731885883bfcb873ec715c69cff75b878092884d28c7d6ff314940ad6b"}, + {file = "nonebot_plugin_uninfo-0.6.8.tar.gz", hash = "sha256:0a30b500b1172fa15cc175b370c7a5935eb2f0515d188a2c73bf8e8ed7ae81d1"}, ] [package.dependencies] @@ -4131,6 +4131,26 @@ type = "legacy" url = "https://mirrors.aliyun.com/pypi/simple" reference = "aliyun" +[[package]] +name = "tenacity" +version = "9.0.0" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + [[package]] name = "text-unidecode" version = "1.3" @@ -4883,4 +4903,4 @@ reference = "aliyun" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "69299e37ab69af7e3a020cc383fc2f2706300cf869a92dc7b76fe133288c405d" +content-hash = "2fc15734ee6edc0a9b2b2f025375e7f41204bc21970745b65d5bc445eed897c7" diff --git a/pyproject.toml b/pyproject.toml index 4c289fc4..3b449cff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,8 +41,9 @@ python-jose = { extras = ["cryptography"], version = "^3.3.0" } python-multipart = "^0.0.9" aiocache = "^0.12.2" py-cpuinfo = "^9.0.0" -nonebot-plugin-uninfo = "^0.4.1" nonebot-plugin-alconna = "^0.54.0" +tenacity = "^9.0.0" +nonebot-plugin-uninfo = ">0.4.1" [tool.poetry.group.dev.dependencies] nonebug = "^0.4" diff --git a/tests/conftest.py b/tests/conftest.py index 01841f2e..0fce1583 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ from pytest_asyncio import is_async_test from pytest_mock import MockerFixture from respx import MockRouter -from tests.config import BotId, UserId +from tests.config import BotId, GroupId, UserId nonebot.load_plugin("nonebot_plugin_session") @@ -64,6 +64,37 @@ def _init_bot(nonebug_init: None): nonebot.load_plugins("zhenxun/builtin_plugins") nonebot.load_plugins("zhenxun/plugins") + # 手动缓存 uninfo 所需信息 + from nonebot_plugin_uninfo import ( + Scene, + SceneType, + Session, + SupportAdapter, + SupportScope, + User, + ) + from nonebot_plugin_uninfo.adapters.onebot11.main import fetcher as onebot11_fetcher + from nonebot_plugin_uninfo.adapters.onebot12.main import fetcher as onebot12_fetcher + + onebot11_fetcher.session_cache = { + f"group_{GroupId.GROUP_ID_LEVEL_5}_{UserId.SUPERUSER}": Session( + self_id="test", + adapter=SupportAdapter.onebot11, + scope=SupportScope.qq_client, + scene=Scene(str(GroupId.GROUP_ID_LEVEL_0), SceneType.GROUP), + user=User(str(UserId.SUPERUSER)), + ), + } + onebot12_fetcher.session_cache = { + f"group_{GroupId.GROUP_ID_LEVEL_5}_{UserId.SUPERUSER}": Session( + self_id="test", + adapter=SupportAdapter.onebot12, + scope=SupportScope.qq_client, + scene=Scene(str(GroupId.GROUP_ID_LEVEL_0), SceneType.GROUP), + user=User(str(UserId.SUPERUSER)), + ), + } + @pytest.fixture async def app(app: App, tmp_path: Path, mocker: MockerFixture): diff --git a/zhenxun/builtin_plugins/admin/welcome_message/data_source.py b/zhenxun/builtin_plugins/admin/welcome_message/data_source.py index 5ba125ac..811a8dda 100644 --- a/zhenxun/builtin_plugins/admin/welcome_message/data_source.py +++ b/zhenxun/builtin_plugins/admin/welcome_message/data_source.py @@ -54,6 +54,8 @@ def migrate(path: Path): path: 路径 """ text_file = path / "text.json" + if not text_file.exists(): + return with text_file.open(encoding="utf8") as f: json_data = json.load(f) new_data = {} diff --git a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py index 2ca706c6..d621f087 100644 --- a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py +++ b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py @@ -103,7 +103,7 @@ group_increase_handle = on_notice( group_decrease_handle = on_notice( priority=1, block=False, - rule=notice_rule([GroupMemberDecreaseEvent, GroupMemberIncreaseEvent]), + rule=notice_rule([GroupMemberDecreaseEvent, GroupDecreaseNoticeEvent]), ) """群员减少处理""" add_group = on_request(priority=1, block=False) @@ -116,19 +116,19 @@ async def _( session: Uninfo, event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent, ): - user_id = str(event.user_id) - group_id = str(event.group_id) - if user_id == bot.self_id: + if session.user.id == bot.self_id: """新成员为bot本身""" group, _ = await GroupConsole.get_or_create( - group_id=group_id, channel_id__isnull=True + group_id=str(event.group_id), channel_id__isnull=True ) try: - await GroupManager.add_bot(bot, str(event.operator_id), group_id, group) + await GroupManager.add_bot( + bot, str(event.operator_id), str(event.group_id), group + ) except ForceAddGroupError as e: await PlatformUtils.send_superuser(bot, e.get_info()) else: - await GroupManager.add_user(session, bot, user_id, group_id) + await GroupManager.add_user(session, bot) @group_decrease_handle.handle() diff --git a/zhenxun/builtin_plugins/platform/qq/group_handle/data_source.py b/zhenxun/builtin_plugins/platform/qq/group_handle/data_source.py index fc188eb1..1190fb5e 100644 --- a/zhenxun/builtin_plugins/platform/qq/group_handle/data_source.py +++ b/zhenxun/builtin_plugins/platform/qq/group_handle/data_source.py @@ -4,6 +4,7 @@ from pathlib import Path import random from nonebot.adapters import Bot +from nonebot.exception import ActionFailed from nonebot_plugin_alconna import At, UniMessage from nonebot_plugin_uninfo import Uninfo import ujson as json @@ -54,7 +55,7 @@ class GroupManager: if plugin_list := await PluginInfo.filter(default_status=False).all(): for plugin in plugin_list: block_plugin += f"<{plugin.module}," - group_info = await bot.get_group_info(group_id=group_id) + group_info = await bot.get_group_info(group_id=group_id, no_cache=True) await GroupConsole.create( group_id=group_info["group_id"], group_name=group_info["group_name"], @@ -215,33 +216,45 @@ class GroupManager: msg_list.insert(0, At("user", user_id)) logger.info("发送群欢迎消息...", "入群检测", session=session) if msg_list: - await MessageUtils.build_message(msg_list).send() # type: ignore - else: - image = DEFAULT_IMAGE_PATH / random.choice( - os.listdir(DEFAULT_IMAGE_PATH) - ) - await MessageUtils.build_message( - [ - "新人快跑啊!!本群现状↓(快使用自定义群欢迎消息!)", - image, - ] - ).send() + await MessageUtils.build_message(msg_list).finish() # type: ignore + image = DEFAULT_IMAGE_PATH / random.choice(os.listdir(DEFAULT_IMAGE_PATH)) + await MessageUtils.build_message( + [ + "新人快跑啊!!本群现状↓(快使用自定义群欢迎消息!)", + image, + ] + ).send() @classmethod - async def add_user(cls, session: Uninfo, bot: Bot, user_id: str, group_id: str): + async def add_user(cls, session: Uninfo, bot: Bot): """拉入用户 参数: + session: Uninfo bot: Bot - user_id: 用户id - group_id: 群组id """ + user_id = session.user.id + group_id = "" + if session.group: + if session.group.parent: + group_id = session.group.parent.id + else: + group_id = session.group.id join_time = datetime.now() - user_info = await bot.get_group_member_info(group_id=group_id, user_id=user_id) + try: + user_info = await bot.get_group_member_info( + group_id=int(group_id), user_id=int(user_id), no_cache=True + ) + except ActionFailed as e: + logger.warning("获取用户信息识别...", e=e) + user_info = {"user_id": user_id, "group_id": group_id, "nickname": ""} await GroupInfoUser.update_or_create( user_id=str(user_info["user_id"]), group_id=str(user_info["group_id"]), - defaults={"user_name": user_info["nickname"], "user_join_time": join_time}, + defaults={ + "user_name": user_info["nickname"], + "user_join_time": join_time, + }, ) logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") if not await CommonUtils.task_is_block( @@ -310,10 +323,13 @@ class GroupManager: group_id=group_id, ) if sub_type == "kick": - operator = await bot.get_group_member_info( - user_id=int(operator_id), group_id=int(group_id) - ) - operator_name = operator["card"] or operator["nickname"] + if operator_id != "0": + operator = await bot.get_group_member_info( + user_id=int(operator_id), group_id=int(group_id) + ) + operator_name = operator["card"] or operator["nickname"] + else: + operator_name = "" return f"{user_name} 被 {operator_name} 送走了." elif sub_type == "leave": return f"{user_name}离开了我们..." diff --git a/zhenxun/builtin_plugins/plugin_store/__init__.py b/zhenxun/builtin_plugins/plugin_store/__init__.py index ff54cc1f..7e9f52a0 100644 --- a/zhenxun/builtin_plugins/plugin_store/__init__.py +++ b/zhenxun/builtin_plugins/plugin_store/__init__.py @@ -44,14 +44,14 @@ _matcher = on_alconna( ) _matcher.shortcut( - r"添加插件", + r"(添加|安装)插件", command="插件商店", arguments=["add", "{%0}"], prefix=True, ) _matcher.shortcut( - r"移除插件", + r"(移除|卸载)插件", command="插件商店", arguments=["remove", "{%0}"], prefix=True, diff --git a/zhenxun/builtin_plugins/plugin_store/data_source.py b/zhenxun/builtin_plugins/plugin_store/data_source.py index 0bc477b4..39c7d263 100644 --- a/zhenxun/builtin_plugins/plugin_store/data_source.py +++ b/zhenxun/builtin_plugins/plugin_store/data_source.py @@ -51,7 +51,7 @@ def install_requirement(plugin_path: Path): try: result = subprocess.run( - ["pip", "install", "-r", str(existing_requirements)], + ["poetry", "run", "pip", "install", "-r", str(existing_requirements)], check=True, capture_output=True, text=True, @@ -232,8 +232,9 @@ class ShopManage: raise ValueError("所有API获取插件文件失败,请检查网络连接") if module_path == ".": module_path = "" + replace_module_path = module_path.replace(".", "/") files = repo_api.get_files( - module_path=module_path.replace(".", "/") + ("" if is_dir else ".py"), + module_path=replace_module_path + ("" if is_dir else ".py"), is_dir=is_dir, ) download_urls = [await repo_info.get_raw_download_urls(file) for file in files] @@ -248,25 +249,32 @@ class ShopManage: else: # 安装依赖 plugin_path = base_path / "/".join(module_path.split(".")) - req_files = repo_api.get_files(REQ_TXT_FILE_STRING, False) - req_files.extend(repo_api.get_files("requirement.txt", False)) - logger.debug(f"获取插件依赖文件列表: {req_files}", "插件管理") - req_download_urls = [ - await repo_info.get_raw_download_urls(file) for file in req_files - ] - req_paths: list[Path | str] = [plugin_path / file for file in req_files] - logger.debug(f"插件依赖文件下载路径: {req_paths}", "插件管理") - if req_files: - result = await AsyncHttpx.gather_download_file( - req_download_urls, req_paths + try: + req_files = repo_api.get_files( + f"{replace_module_path}/{REQ_TXT_FILE_STRING}", False ) - for success in result: - if not success: - raise Exception("插件依赖文件下载失败") - logger.debug(f"插件依赖文件列表: {req_paths}", "插件管理") - install_requirement(plugin_path) + req_files.extend( + repo_api.get_files(f"{replace_module_path}/requirement.txt", False) + ) + logger.debug(f"获取插件依赖文件列表: {req_files}", "插件管理") + req_download_urls = [ + await repo_info.get_raw_download_urls(file) for file in req_files + ] + req_paths: list[Path | str] = [plugin_path / file for file in req_files] + logger.debug(f"插件依赖文件下载路径: {req_paths}", "插件管理") + if req_files: + result = await AsyncHttpx.gather_download_file( + req_download_urls, req_paths + ) + for success in result: + if not success: + raise Exception("插件依赖文件下载失败") + logger.debug(f"插件依赖文件列表: {req_paths}", "插件管理") + install_requirement(plugin_path) + except ValueError as e: + logger.warning("未获取到依赖文件路径...", e=e) return True - raise Exception("插件下载失败") + raise Exception("插件下载失败...") @classmethod async def remove_plugin(cls, plugin_id: str) -> str: diff --git a/zhenxun/builtin_plugins/sign_in/utils.py b/zhenxun/builtin_plugins/sign_in/utils.py index 3e224f60..9faf1120 100644 --- a/zhenxun/builtin_plugins/sign_in/utils.py +++ b/zhenxun/builtin_plugins/sign_in/utils.py @@ -16,6 +16,7 @@ from zhenxun.models.sign_log import SignLog from zhenxun.models.sign_user import SignUser from zhenxun.utils.http_utils import AsyncHttpx from zhenxun.utils.image_utils import BuildImage +from zhenxun.utils.platform import PlatformUtils from .config import ( SIGN_BACKGROUND_PATH, @@ -430,7 +431,9 @@ async def _generate_html_card( ) now = datetime.now() data = { - "ava_url": session.user.avatar, + "ava_url": PlatformUtils.get_user_avatar_url( + user.user_id, PlatformUtils.get_platform(session), session.self_id + ), "name": nickname, "uid": uid, "sign_count": f"{user.sign_count}", diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py index b652fb81..5526d516 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/main/data_source.py @@ -248,13 +248,31 @@ class ApiDataSource: if bot_id: query = query.filter(bot_id=bot_id) if date_type == QueryDateType.DAY: - query = query.filter(create_time__gte=now - timedelta(hours=now.hour)) + query = query.filter( + create_time__gte=now + - timedelta(hours=now.hour, minutes=now.minute, seconds=now.second) + ) if date_type == QueryDateType.WEEK: - query = query.filter(create_time__gte=now - timedelta(days=7)) + query = query.filter( + create_time__gte=now + - timedelta( + days=7, hours=now.hour, minutes=now.minute, seconds=now.second + ) + ) if date_type == QueryDateType.MONTH: - query = query.filter(create_time__gte=now - timedelta(days=30)) + query = query.filter( + create_time__gte=now + - timedelta( + days=30, hours=now.hour, minutes=now.minute, seconds=now.second + ) + ) if date_type == QueryDateType.YEAR: - query = query.filter(create_time__gte=now - timedelta(days=365)) + query = query.filter( + create_time__gte=now + - timedelta( + days=365, hours=now.hour, minutes=now.minute, seconds=now.second + ) + ) return query @classmethod diff --git a/zhenxun/configs/config.py b/zhenxun/configs/config.py index 9dc12ab6..c51818ab 100644 --- a/zhenxun/configs/config.py +++ b/zhenxun/configs/config.py @@ -6,6 +6,8 @@ from pydantic import BaseModel from .utils import ConfigsManager +__all__ = ["BotConfig", "Config"] + class BotSetting(BaseModel): self_nickname: str = "" diff --git a/zhenxun/utils/decorator/retry.py b/zhenxun/utils/decorator/retry.py new file mode 100644 index 00000000..2a6cc757 --- /dev/null +++ b/zhenxun/utils/decorator/retry.py @@ -0,0 +1,16 @@ +from httpx import ConnectError, HTTPStatusError, TimeoutException +from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed + + +class Retry: + @staticmethod + def api(): + """接口调用重试""" + return retry( + reraise=True, + stop=stop_after_attempt(3), + wait=wait_fixed(1), + retry=retry_if_exception_type( + (TimeoutException, ConnectError, HTTPStatusError) + ), + ) diff --git a/zhenxun/utils/http_utils.py b/zhenxun/utils/http_utils.py index 52bf4461..79149674 100644 --- a/zhenxun/utils/http_utils.py +++ b/zhenxun/utils/http_utils.py @@ -7,6 +7,7 @@ import time from typing import Any, ClassVar, Literal import aiofiles +from anyio import EndOfStream import httpx from httpx import ConnectTimeout, HTTPStatusError, Response from nonebot_plugin_alconna import UniMessage @@ -41,7 +42,7 @@ class AsyncHttpx: verify: bool = True, use_proxy: bool = True, proxy: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 **kwargs, ) -> Response: """Get @@ -96,7 +97,7 @@ class AsyncHttpx: verify: bool = True, use_proxy: bool = True, proxy: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 **kwargs, ) -> Response: if not headers: @@ -123,7 +124,7 @@ class AsyncHttpx: verify: bool = True, use_proxy: bool = True, proxy: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 **kwargs, ) -> Response: """Get @@ -166,7 +167,7 @@ class AsyncHttpx: params: dict[str, str] | None = None, headers: dict[str, str] | None = None, cookies: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 **kwargs, ) -> Response: """ @@ -219,7 +220,7 @@ class AsyncHttpx: proxy: dict[str, str] | None = None, headers: dict[str, str] | None = None, cookies: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 stream: bool = False, follow_redirects: bool = True, **kwargs, @@ -311,12 +312,17 @@ class AsyncHttpx: completed=response.num_bytes_downloaded, ) logger.info( - f"下载 {u} 成功.. " - f"Path:{path.absolute()}" + f"下载 {u} 成功.. Path:{path.absolute()}" ) return True except (TimeoutError, ConnectTimeout, HTTPStatusError): logger.warning(f"下载 {u} 失败.. 尝试下一个地址..") + except EndOfStream as e: + logger.warning( + f"下载 {url} EndOfStream 异常 Path:{path.absolute()}", e=e + ) + if path.exists(): + return True logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}") except Exception as e: logger.error(f"下载 {url} 错误 Path:{path.absolute()}", e=e) @@ -334,7 +340,7 @@ class AsyncHttpx: proxy: dict[str, str] | None = None, headers: dict[str, str] | None = None, cookies: dict[str, str] | None = None, - timeout: int = 30, + timeout: int = 30, # noqa: ASYNC109 **kwargs, ) -> list[bool]: """分组同时下载文件 @@ -374,22 +380,22 @@ class AsyncHttpx: tasks = [] result_ = [] for x, y in zip(_split_url_list, _split_path_list): - for url, path in zip(x, y): - tasks.append( - asyncio.create_task( - cls.download_file( - url, - path, - params=params, - headers=headers, - cookies=cookies, - use_proxy=use_proxy, - timeout=timeout, - proxy=proxy, - **kwargs, - ) + tasks.extend( + asyncio.create_task( + cls.download_file( + url, + path, + params=params, + headers=headers, + cookies=cookies, + use_proxy=use_proxy, + timeout=timeout, + proxy=proxy, + **kwargs, ) ) + for url, path in zip(x, y) + ) _x = await asyncio.gather(*tasks) result_ = result_ + list(_x) tasks.clear() @@ -465,7 +471,7 @@ class AsyncPlaywright: wait_until: ( Literal["domcontentloaded", "load", "networkidle"] | None ) = "networkidle", - timeout: float | None = None, + timeout: float | None = None, # noqa: ASYNC109 type_: Literal["jpeg", "png"] | None = None, user_agent: str | None = None, cookies: list[dict[str, Any]] | dict[str, Any] | None = None, diff --git a/zhenxun/utils/message.py b/zhenxun/utils/message.py index cc84e2c6..9cbdbbfa 100644 --- a/zhenxun/utils/message.py +++ b/zhenxun/utils/message.py @@ -17,6 +17,7 @@ from nonebot_plugin_alconna import ( Voice, ) from pydantic import BaseModel +import ujson as json from zhenxun.configs.config import BotConfig from zhenxun.services.log import logger @@ -141,6 +142,21 @@ class MessageUtils: ) return UniMessage(Reference(nodes=node_list)) + @classmethod + def markdown(cls, content: dict) -> Message: + """markdown格式消息 + + 参数: + content: 消息内容 + + 返回: + Message: 构造完成的消息 + """ + content_data = base64.b64encode(json.dumps(content).encode("utf-8")).decode( + "utf-8" + ) + return Message(f"[CQ:markdown,data=base64://{content_data}]") + @classmethod def custom_forward_msg( cls, diff --git a/zhenxun/utils/platform.py b/zhenxun/utils/platform.py index 0034dd14..fbf877e5 100644 --- a/zhenxun/utils/platform.py +++ b/zhenxun/utils/platform.py @@ -55,6 +55,8 @@ class PlatformUtils: """ if isinstance(session, Bot): return bool(BotConfig.get_qbot_uid(session.self_id)) + if BotConfig.get_qbot_uid(session.self_id): + return True return session.scope == SupportScope.qq_api @classmethod @@ -354,32 +356,30 @@ class PlatformUtils: 返回: tuple[list[GroupConsole], str]: 群组列表, 平台 """ - if interface := get_interface(bot): - platform = cls.get_platform(bot) - result_list = [] - scenes = await interface.get_scenes(SceneType.GROUP) - for scene in scenes: - group_id = scene.id - result_list.append( - GroupConsole( - group_id=scene.id, - group_name=scene.name, - ) + if not (interface := get_interface(bot)): + return [], "" + platform = cls.get_platform(bot) + result_list = [] + scenes = await interface.get_scenes(SceneType.GROUP) + for scene in scenes: + group_id = scene.id + result_list.append( + GroupConsole( + group_id=scene.id, + group_name=scene.name, ) - if not only_group and platform != "qq": - if channel_list := await interface.get_scenes( - parent_scene_id=group_id - ): - for channel in channel_list: - result_list.append( - GroupConsole( - group_id=scene.id, - group_name=channel.name, - channel_id=channel.id, - ) - ) - return result_list, platform - return [], "" + ) + if not only_group and platform != "qq": + if channel_list := await interface.get_scenes(parent_scene_id=group_id): + result_list.extend( + GroupConsole( + group_id=scene.id, + group_name=channel.name, + channel_id=channel.id, + ) + for channel in channel_list + ) + return result_list, platform @classmethod async def update_friend(cls, bot: Bot) -> int: