mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
版本更新 (#1666)
* ✨ 父级插件加载 * ✅ 添加测试:更新与添加插件 (#1594) * ✅ 测试更新与添加插件 * ✅ Sourcery建议 * 👷 添加pytest * 🎨 优化代码 * 🐛 bug修复 * 🐛修复添加插件返回403的问题 (#1595) * 完善测试方法 * vscode测试配置 * 重构插件安装过程 * 🎨 修改readme * Update README.md * 🐛 修改bug与版本锁定 * 🐛 修复超级用户对群组功能开关 * 🐛 修复插件商店检查插件更新问题 (#1597) * 🐛 修复插件商店检查插件更新问题 * 🐛 恶意命令检测问题 * 🐛 增加插件状态检查 (#1598) * ✅ 优化测试用例 * 🐛 更改插件更新与安装逻辑 * 🐛 修复更新群组成员信息 * 🎨 代码优化 * 🚀 更新Dockerfile (#1599) * 🎨 更新requirements * ➕ 添加依赖aiocache * ⚡ 添加github镜像 * ✨ 添加仓库目录多获取渠道 * 🐛 修复测试用例 * ✨ 添加API缓存 * 🎨 采取Sourcery建议 * 🐛 文件下载逻辑修改 * 🎨 优化代码 * 🐛 修复插件开关有时出现错误 * ✨ 重构自检ui * 🐛 自检html修正 * 修复签到逻辑bug,并使代码更灵活以适应签到好感度等级配置 (#1606) * 修复签到功能已知问题 * 修复签到功能已知问题 * 修改参数名称 * 修改uid判断 --------- Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com> * 🎨 代码结构优化 * 🐛 私聊时修改插件时删除私聊帮助 * 🐛 过滤父插件 * 🐛 修复自检在ARM上的问题 (#1607) * 🐛 修复自检在ARM上的问题 * ✅ 优化测试 * ✨ 支持mysql,psql,sqlite随机函数 * 🔧 VSCode配置修改 * 🔧 VSCode配置修改 * ✨ 添加金币排行 Co-Authored-By: HibiKier <45528451+HibiKier@users.noreply.github.com> * 📝 修改README Co-Authored-By: HibiKier <45528451+HibiKier@users.noreply.github.com> * 🔨 提取GitHub相关操作 (#1609) * 🔨 提取GitHub相关操作 * 🔨 重构API策略 * ✨ 签到/金币排行限制最大数量 (#1616) * ✨ 签到/金币排行限制最大数量 * 🐛 修复超级用户id获取问题 * 🐛 修复路径解压与挂载 (#1619) * 🐛 修复功能少时zhenxun帮助图片排序问题 (#1620) * 🐛 签到文本适应 (#1622) * 🐛 好感度排行提供默认值 (#1624) * 🎈 优先使用github api (#1625) * ✨ 重构帮助,限制普通用户查询管理插件 (#1626) * 🐛 修复群权限与插件等级匹配 (#1627) * ✨ 当管理员尝试ban真寻时将被反杀 (#1628) * ✨ 群组发言时间检测提供开关配置 (#1630) * 🐳 chore: 支持自动修改版本号 (#1629) * 🎈 perf(github_utils): 支持github url下载遍历 (#1632) * 🎈 perf(github_utils): 支持github url下载遍历 * 🐞 fix(http_utils): 修复一些下载问题 * 🦄 refactor(http_utils): 部分重构 * chore(version): Update version to v0.2.2-e6f17c4 --------- Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com> * 🧪 test(auto_update): 修复测试用例 (#1633) * 🐛 修复商店商品为空时报错 (#1634) * 🐛 修复群权限与插件等级匹配 (#1635) * ✨ message_build支持AtAll (#1639) * 🎈 perf: 使用commit号下载插件 (#1641) * 🎈 perf: 使用commit号下载插件 * chore(version): Update version to v0.2.2-f9c7360 --------- Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com> * 🐳 chore: 修改运行检查触发路径 (#1642) * 🐳 chore: 修改运行检查触发路径 * 🐳 chore: 添加tests目录 * ✨ 重构qq群事件处理 (#1643) * 🐛 签到名称自适应 (#1644) * 🎨 更新README (#1645) * 🐛 fix(http_utils): 流式下载Content-Length错误 (#1647) * 🐛 修复群组中帮助功能状态显示问题 (#1650) * 🐛 修复群欢迎消息设置 (#1651) * 🐛 修复webui下载后首次启动错误 (#1652) * 🐛 修复webui下载后首次启动错误 * chore(version): Update version to v0.2.2-4a8ef85 --------- Co-authored-by: HibiKier <HibiKier@users.noreply.github.com> * ✨ 移除默认图片文件夹:爬 (#1653) * ✨ 安装/移除插件提供插件安装/卸载方法用于插件初始化 (#1654) * ✨ 新增超级用户与管理员帮助模板 (#1655) * ✨ 新增个人信息命令 (#1657) * ✨ 修改个人信息菜单名称 (#1658) * ✨ 新增插件商店api (#1659) * ✨ 新增插件商店api * chore(version): Update version to v0.2.2-7e15f20 --------- Co-authored-by: HibiKier <HibiKier@users.noreply.github.com> * ✨ 将cd,block,count限制复原配置文件 (#1662) * 🎨 修改README (#1663) * 🎨 修改版本号 (#1664) * 🎨 修改requirements (#1665) --------- Co-authored-by: AkashiCoin <l1040186796@gmail.com> Co-authored-by: fanyinrumeng <42991257+fanyinrumeng@users.noreply.github.com> Co-authored-by: AkashiCoin <i@loli.vet> Co-authored-by: Elaga <1728903318@qq.com> Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com> Co-authored-by: HibiKier <HibiKier@users.noreply.github.com>
This commit is contained in:
co-authored by
AkashiCoin
HibiKier
AkashiCoin
fanyinrumeng
AkashiCoin
Elaga
parent
daa40ae8b8
commit
4e33bf3a50
@@ -0,0 +1,529 @@
|
||||
import io
|
||||
import os
|
||||
import tarfile
|
||||
import zipfile
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.utils import get_response_json as _get_response_json
|
||||
from tests.utils import _v11_group_message_event, _v11_private_message_send
|
||||
|
||||
|
||||
def get_response_json(file: str) -> dict:
|
||||
return _get_response_json(Path() / "auto_update", file)
|
||||
|
||||
|
||||
def init_mocked_api(mocked_api: MockRouter) -> None:
|
||||
mocked_api.get(
|
||||
url="https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest",
|
||||
name="release_latest",
|
||||
).respond(json=get_response_json("release_latest.json"))
|
||||
|
||||
mocked_api.head(
|
||||
url="https://raw.githubusercontent.com/",
|
||||
name="head_raw",
|
||||
).respond(text="")
|
||||
mocked_api.head(
|
||||
url="https://github.com/",
|
||||
name="head_github",
|
||||
).respond(text="")
|
||||
mocked_api.head(
|
||||
url="https://codeload.github.com/",
|
||||
name="head_codeload",
|
||||
).respond(text="")
|
||||
|
||||
mocked_api.get(
|
||||
url="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/dev/__version__",
|
||||
name="dev_branch_version",
|
||||
).respond(text="__version__: v0.2.2-e6f17c4")
|
||||
mocked_api.get(
|
||||
url="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/__version__",
|
||||
name="main_branch_version",
|
||||
).respond(text="__version__: v0.2.2-e6f17c4")
|
||||
mocked_api.get(
|
||||
url="https://api.github.com/repos/HibiKier/zhenxun_bot/tarball/v0.2.2",
|
||||
name="release_download_url",
|
||||
).respond(
|
||||
status_code=302,
|
||||
headers={
|
||||
"Location": "https://codeload.github.com/HibiKier/zhenxun_bot/legacy.tar.gz/refs/tags/v0.2.2"
|
||||
},
|
||||
)
|
||||
|
||||
tar_buffer = io.BytesIO()
|
||||
zip_bytes = io.BytesIO()
|
||||
|
||||
from zhenxun.builtin_plugins.auto_update.config import (
|
||||
REPLACE_FOLDERS,
|
||||
REQ_TXT_FILE_STRING,
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
|
||||
# 指定要添加到压缩文件中的文件路径列表
|
||||
file_paths: list[str] = [
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
REQ_TXT_FILE_STRING,
|
||||
]
|
||||
|
||||
# 打开一个tarfile对象,写入到上面创建的BytesIO对象中
|
||||
with tarfile.open(mode="w:gz", fileobj=tar_buffer) as tar:
|
||||
add_files_and_folders_to_tar(tar, file_paths, folders=REPLACE_FOLDERS)
|
||||
|
||||
with zipfile.ZipFile(zip_bytes, mode="w", compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
add_files_and_folders_to_zip(zipf, file_paths, folders=REPLACE_FOLDERS)
|
||||
|
||||
mocked_api.get(
|
||||
url="https://codeload.github.com/HibiKier/zhenxun_bot/legacy.tar.gz/refs/tags/v0.2.2",
|
||||
name="release_download_url_redirect",
|
||||
).respond(
|
||||
content=tar_buffer.getvalue(),
|
||||
)
|
||||
mocked_api.get(
|
||||
url="https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/dev.zip",
|
||||
name="dev_download_url",
|
||||
).respond(
|
||||
content=zip_bytes.getvalue(),
|
||||
)
|
||||
mocked_api.get(
|
||||
url="https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/main.zip",
|
||||
name="main_download_url",
|
||||
).respond(
|
||||
content=zip_bytes.getvalue(),
|
||||
)
|
||||
|
||||
|
||||
# TODO Rename this here and in `init_mocked_api`
|
||||
def add_files_and_folders_to_zip(
|
||||
zipf: zipfile.ZipFile, file_paths: list[str], folders: list[str] = []
|
||||
):
|
||||
"""Add files and folders to a zip archive.
|
||||
|
||||
This function creates a directory structure within the specified zip
|
||||
archive and adds the provided files to it. It also creates additional
|
||||
subdirectories as specified in the folders list.
|
||||
|
||||
Args:
|
||||
zipf: The zip archive to which files and folders will be added.
|
||||
file_paths: A list of file names to be added to the zip archive.
|
||||
folders: An optional list of subdirectory names to be created
|
||||
within the base folder.
|
||||
"""
|
||||
|
||||
# 假设有一个文件夹名为 folder_name
|
||||
folder_name = "my_folder/"
|
||||
|
||||
# 添加文件夹到 ZIP 中,注意 ZIP 中文件夹路径应以 '/' 结尾
|
||||
zipf.writestr(folder_name, "") # 空内容表示这是一个文件夹
|
||||
|
||||
for file_path in file_paths:
|
||||
# 将文件添加到 ZIP 中,路径为 folder_name + file_name
|
||||
zipf.writestr(f"{folder_name}{os.path.basename(file_path)}", b"new")
|
||||
base_folder = f"{folder_name}zhenxun/"
|
||||
zipf.writestr(base_folder, "")
|
||||
|
||||
for folder in folders:
|
||||
zipf.writestr(f"{base_folder}{folder}/", "")
|
||||
|
||||
|
||||
# TODO Rename this here and in `init_mocked_api`
|
||||
def add_files_and_folders_to_tar(
|
||||
tar: tarfile.TarFile, file_paths: list[str], folders: list[str] = []
|
||||
):
|
||||
"""Add files and folders to a tar archive.
|
||||
|
||||
This function creates a directory structure within the specified tar
|
||||
archive and adds the provided files to it. It also creates additional
|
||||
subdirectories as specified in the folders list.
|
||||
|
||||
Args:
|
||||
tar: The tar archive to which files and folders will be added.
|
||||
file_paths: A list of file names to be added to the tar archive.
|
||||
folders: An optional list of subdirectory names to be created
|
||||
within the base folder.
|
||||
"""
|
||||
|
||||
folder_name = "my_folder"
|
||||
tarinfo = tarfile.TarInfo(folder_name)
|
||||
add_directory_to_tar(tarinfo, tar)
|
||||
# 读取并添加指定的文件
|
||||
for file_path in file_paths:
|
||||
# 创建TarInfo对象
|
||||
tar_buffer = io.BytesIO(b"new")
|
||||
tarinfo = tarfile.TarInfo(
|
||||
f"{folder_name}/{file_path}"
|
||||
) # 使用文件名作为tar中的名字
|
||||
tarinfo.mode = 0o644 # 设置文件夹权限
|
||||
tarinfo.size = len(tar_buffer.getvalue()) # 设置文件大小
|
||||
|
||||
# 添加文件
|
||||
tar.addfile(tarinfo, fileobj=tar_buffer)
|
||||
|
||||
base_folder = f"{folder_name}/zhenxun"
|
||||
tarinfo = tarfile.TarInfo(base_folder)
|
||||
add_directory_to_tar(tarinfo, tar)
|
||||
for folder in folders:
|
||||
tarinfo = tarfile.TarInfo(f"{base_folder}{folder}")
|
||||
add_directory_to_tar(tarinfo, tar)
|
||||
|
||||
|
||||
# TODO Rename this here and in `_extracted_from_init_mocked_api_43`
|
||||
def add_directory_to_tar(tarinfo, tar):
|
||||
"""Add a directory entry to a tar archive.
|
||||
|
||||
This function modifies the provided tarinfo object to set its type
|
||||
as a directory and assigns the appropriate permissions before adding
|
||||
it to the specified tar archive.
|
||||
|
||||
Args:
|
||||
tarinfo: The tarinfo object representing the directory.
|
||||
tar: The tar archive to which the directory will be added.
|
||||
"""
|
||||
|
||||
tarinfo.type = tarfile.DIRTYPE
|
||||
tarinfo.mode = 0o755
|
||||
tar.addfile(tarinfo)
|
||||
|
||||
|
||||
def init_mocker_path(mocker: MockerFixture, tmp_path: Path):
|
||||
from zhenxun.builtin_plugins.auto_update.config import (
|
||||
REQ_TXT_FILE_STRING,
|
||||
VERSION_FILE_STRING,
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.install_requirement",
|
||||
return_value=None,
|
||||
)
|
||||
mock_tmp_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.TMP_PATH",
|
||||
new=tmp_path / "auto_update",
|
||||
)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
mock_backup_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.BACKUP_PATH",
|
||||
new=tmp_path / "backup",
|
||||
)
|
||||
mock_download_gz_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.DOWNLOAD_GZ_FILE",
|
||||
new=mock_tmp_path / "download_latest_file.tar.gz",
|
||||
)
|
||||
mock_download_zip_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.DOWNLOAD_ZIP_FILE",
|
||||
new=mock_tmp_path / "download_latest_file.zip",
|
||||
)
|
||||
mock_pyproject_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.PYPROJECT_FILE",
|
||||
new=tmp_path / PYPROJECT_FILE_STRING,
|
||||
)
|
||||
mock_pyproject_lock_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.PYPROJECT_LOCK_FILE",
|
||||
new=tmp_path / PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
mock_req_txt_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.REQ_TXT_FILE",
|
||||
new=tmp_path / REQ_TXT_FILE_STRING,
|
||||
)
|
||||
mock_version_file = mocker.patch(
|
||||
"zhenxun.builtin_plugins.auto_update._data_source.VERSION_FILE",
|
||||
new=tmp_path / VERSION_FILE_STRING,
|
||||
)
|
||||
open(mock_version_file, "w").write("__version__: v0.2.2")
|
||||
return (
|
||||
mock_tmp_path,
|
||||
mock_base_path,
|
||||
mock_backup_path,
|
||||
mock_download_gz_file,
|
||||
mock_download_zip_file,
|
||||
mock_pyproject_file,
|
||||
mock_pyproject_lock_file,
|
||||
mock_req_txt_file,
|
||||
mock_version_file,
|
||||
)
|
||||
|
||||
|
||||
async def test_check_update_release(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试检查更新(release)
|
||||
"""
|
||||
from zhenxun.builtin_plugins.auto_update import _matcher
|
||||
from zhenxun.builtin_plugins.auto_update.config import (
|
||||
REPLACE_FOLDERS,
|
||||
REQ_TXT_FILE_STRING,
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
(
|
||||
mock_tmp_path,
|
||||
mock_base_path,
|
||||
mock_backup_path,
|
||||
mock_download_gz_file,
|
||||
mock_download_zip_file,
|
||||
mock_pyproject_file,
|
||||
mock_pyproject_lock_file,
|
||||
mock_req_txt_file,
|
||||
mock_version_file,
|
||||
) = init_mocker_path(mocker, tmp_path)
|
||||
|
||||
# 确保目录下有一个子目录,以便 os.listdir() 能返回一个目录名
|
||||
mock_tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for folder in REPLACE_FOLDERS:
|
||||
(mock_base_path / folder).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mock_pyproject_file.write_bytes(b"")
|
||||
mock_pyproject_lock_file.write_bytes(b"")
|
||||
mock_req_txt_file.write_bytes(b"")
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot = cast(Bot, bot)
|
||||
raw_message = "检查更新 release"
|
||||
event = _v11_group_message_event(
|
||||
raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_api(
|
||||
"send_msg",
|
||||
_v11_private_message_send(
|
||||
message="检测真寻已更新,版本更新:v0.2.2 -> v0.2.2\n" "开始更新...",
|
||||
user_id=UserId.SUPERUSER,
|
||||
),
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(
|
||||
"版本更新完成\n" "版本: v0.2.2 -> v0.2.2\n" "请重新启动真寻以完成更新!"
|
||||
),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_finished(_matcher)
|
||||
assert mocked_api["release_latest"].called
|
||||
assert mocked_api["release_download_url_redirect"].called
|
||||
|
||||
assert (mock_backup_path / PYPROJECT_FILE_STRING).exists()
|
||||
assert (mock_backup_path / PYPROJECT_LOCK_FILE_STRING).exists()
|
||||
assert (mock_backup_path / REQ_TXT_FILE_STRING).exists()
|
||||
|
||||
assert not mock_download_gz_file.exists()
|
||||
assert not mock_download_zip_file.exists()
|
||||
|
||||
assert mock_pyproject_file.read_bytes() == b"new"
|
||||
assert mock_pyproject_lock_file.read_bytes() == b"new"
|
||||
assert mock_req_txt_file.read_bytes() == b"new"
|
||||
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert not (mock_base_path / folder).exists()
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert (mock_backup_path / folder).exists()
|
||||
|
||||
|
||||
async def test_check_update_dev(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试检查更新(开发环境)
|
||||
"""
|
||||
from zhenxun.builtin_plugins.auto_update import _matcher
|
||||
from zhenxun.builtin_plugins.auto_update.config import (
|
||||
REPLACE_FOLDERS,
|
||||
REQ_TXT_FILE_STRING,
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
(
|
||||
mock_tmp_path,
|
||||
mock_base_path,
|
||||
mock_backup_path,
|
||||
mock_download_gz_file,
|
||||
mock_download_zip_file,
|
||||
mock_pyproject_file,
|
||||
mock_pyproject_lock_file,
|
||||
mock_req_txt_file,
|
||||
mock_version_file,
|
||||
) = init_mocker_path(mocker, tmp_path)
|
||||
|
||||
# 确保目录下有一个子目录,以便 os.listdir() 能返回一个目录名
|
||||
mock_tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
for folder in REPLACE_FOLDERS:
|
||||
(mock_base_path / folder).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mock_pyproject_file.write_bytes(b"")
|
||||
mock_pyproject_lock_file.write_bytes(b"")
|
||||
mock_req_txt_file.write_bytes(b"")
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot = cast(Bot, bot)
|
||||
raw_message = "检查更新 dev"
|
||||
event = _v11_group_message_event(
|
||||
raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_api(
|
||||
"send_msg",
|
||||
_v11_private_message_send(
|
||||
message="检测真寻已更新,版本更新:v0.2.2 -> v0.2.2-e6f17c4\n"
|
||||
"开始更新...",
|
||||
user_id=UserId.SUPERUSER,
|
||||
),
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(
|
||||
"版本更新完成\n"
|
||||
"版本: v0.2.2 -> v0.2.2-e6f17c4\n"
|
||||
"请重新启动真寻以完成更新!"
|
||||
),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_finished(_matcher)
|
||||
assert mocked_api["dev_download_url"].called
|
||||
assert (mock_backup_path / PYPROJECT_FILE_STRING).exists()
|
||||
assert (mock_backup_path / PYPROJECT_LOCK_FILE_STRING).exists()
|
||||
assert (mock_backup_path / REQ_TXT_FILE_STRING).exists()
|
||||
|
||||
assert not mock_download_gz_file.exists()
|
||||
assert not mock_download_zip_file.exists()
|
||||
|
||||
assert mock_pyproject_file.read_bytes() == b"new"
|
||||
assert mock_pyproject_lock_file.read_bytes() == b"new"
|
||||
assert mock_req_txt_file.read_bytes() == b"new"
|
||||
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert (mock_base_path / folder).exists()
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert (mock_backup_path / folder).exists()
|
||||
|
||||
|
||||
async def test_check_update_main(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试检查更新(正式环境)
|
||||
"""
|
||||
from zhenxun.builtin_plugins.auto_update import _matcher
|
||||
from zhenxun.builtin_plugins.auto_update.config import (
|
||||
REPLACE_FOLDERS,
|
||||
REQ_TXT_FILE_STRING,
|
||||
PYPROJECT_FILE_STRING,
|
||||
PYPROJECT_LOCK_FILE_STRING,
|
||||
)
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
(
|
||||
mock_tmp_path,
|
||||
mock_base_path,
|
||||
mock_backup_path,
|
||||
mock_download_gz_file,
|
||||
mock_download_zip_file,
|
||||
mock_pyproject_file,
|
||||
mock_pyproject_lock_file,
|
||||
mock_req_txt_file,
|
||||
mock_version_file,
|
||||
) = init_mocker_path(mocker, tmp_path)
|
||||
|
||||
# 确保目录下有一个子目录,以便 os.listdir() 能返回一个目录名
|
||||
mock_tmp_path.mkdir(parents=True, exist_ok=True)
|
||||
for folder in REPLACE_FOLDERS:
|
||||
(mock_base_path / folder).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mock_pyproject_file.write_bytes(b"")
|
||||
mock_pyproject_lock_file.write_bytes(b"")
|
||||
mock_req_txt_file.write_bytes(b"")
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot = cast(Bot, bot)
|
||||
raw_message = "检查更新 main"
|
||||
event = _v11_group_message_event(
|
||||
raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_api(
|
||||
"send_msg",
|
||||
_v11_private_message_send(
|
||||
message="检测真寻已更新,版本更新:v0.2.2 -> v0.2.2-e6f17c4\n"
|
||||
"开始更新...",
|
||||
user_id=UserId.SUPERUSER,
|
||||
),
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(
|
||||
"版本更新完成\n"
|
||||
"版本: v0.2.2 -> v0.2.2-e6f17c4\n"
|
||||
"请重新启动真寻以完成更新!"
|
||||
),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_finished(_matcher)
|
||||
assert mocked_api["main_download_url"].called
|
||||
assert (mock_backup_path / PYPROJECT_FILE_STRING).exists()
|
||||
assert (mock_backup_path / PYPROJECT_LOCK_FILE_STRING).exists()
|
||||
assert (mock_backup_path / REQ_TXT_FILE_STRING).exists()
|
||||
|
||||
assert not mock_download_gz_file.exists()
|
||||
assert not mock_download_zip_file.exists()
|
||||
|
||||
assert mock_pyproject_file.read_bytes() == b"new"
|
||||
assert mock_pyproject_lock_file.read_bytes() == b"new"
|
||||
assert mock_req_txt_file.read_bytes() == b"new"
|
||||
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert (mock_base_path / folder).exists()
|
||||
for folder in REPLACE_FOLDERS:
|
||||
assert (mock_backup_path / folder).exists()
|
||||
@@ -0,0 +1,231 @@
|
||||
import platform
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
import nonebot
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
|
||||
platform_uname = platform.uname_result(
|
||||
system="Linux",
|
||||
node="zhenxun",
|
||||
release="5.15.0-1027-azure",
|
||||
version="#1 SMP Debian 5.15.0-1027-azure",
|
||||
machine="x86_64",
|
||||
) # type: ignore
|
||||
cpuinfo_get_cpu_info = {"brand_raw": "Intel(R) Core(TM) i7-10700K"}
|
||||
|
||||
|
||||
def init_mocker(mocker: MockerFixture, tmp_path: Path):
|
||||
mock_psutil = mocker.patch("zhenxun.builtin_plugins.check.data_source.psutil")
|
||||
mock_cpuinfo = mocker.patch("zhenxun.builtin_plugins.check.data_source.cpuinfo")
|
||||
mock_cpuinfo.get_cpu_info.return_value = cpuinfo_get_cpu_info
|
||||
|
||||
mock_platform = mocker.patch("zhenxun.builtin_plugins.check.data_source.platform")
|
||||
mock_platform.uname.return_value = platform_uname
|
||||
|
||||
mock_template_to_pic = mocker.patch("zhenxun.builtin_plugins.check.template_to_pic")
|
||||
mock_template_to_pic_return = mocker.AsyncMock()
|
||||
mock_template_to_pic.return_value = mock_template_to_pic_return
|
||||
|
||||
mock_build_message = mocker.patch(
|
||||
"zhenxun.builtin_plugins.check.MessageUtils.build_message"
|
||||
)
|
||||
mock_build_message_return = mocker.AsyncMock()
|
||||
mock_build_message.return_value = mock_build_message_return
|
||||
|
||||
mock_template_path_new = tmp_path / "resources" / "template"
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.check.TEMPLATE_PATH", new=mock_template_path_new
|
||||
)
|
||||
return (
|
||||
mock_psutil,
|
||||
mock_cpuinfo,
|
||||
mock_platform,
|
||||
mock_template_to_pic,
|
||||
mock_template_to_pic_return,
|
||||
mock_build_message,
|
||||
mock_build_message_return,
|
||||
mock_template_path_new,
|
||||
)
|
||||
|
||||
|
||||
async def test_check(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试自检
|
||||
"""
|
||||
from zhenxun.configs.config import BotConfig
|
||||
from zhenxun.builtin_plugins.check import _matcher
|
||||
from zhenxun.builtin_plugins.check.data_source import __get_version
|
||||
|
||||
(
|
||||
mock_psutil,
|
||||
mock_cpuinfo,
|
||||
mock_platform,
|
||||
mock_template_to_pic,
|
||||
mock_template_to_pic_return,
|
||||
mock_build_message,
|
||||
mock_build_message_return,
|
||||
mock_template_path_new,
|
||||
) = init_mocker(mocker, tmp_path)
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = "自检"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
mock_template_to_pic.assert_awaited_once_with(
|
||||
template_path=str((mock_template_path_new / "check").absolute()),
|
||||
template_name="main.html",
|
||||
templates={
|
||||
"data": {
|
||||
"cpu_info": "1.0% - 1.0Ghz [1 core]",
|
||||
"cpu_process": 1.0,
|
||||
"ram_info": "1.0 / 1.0 GB",
|
||||
"ram_process": 100.0,
|
||||
"swap_info": "1.0 / 1.0 GB",
|
||||
"swap_process": 100.0,
|
||||
"disk_info": "1.0 / 1.0 GB",
|
||||
"disk_process": 100.0,
|
||||
"brand_raw": cpuinfo_get_cpu_info["brand_raw"],
|
||||
"baidu": "red",
|
||||
"google": "red",
|
||||
"system": f"{platform_uname.system} " f"{platform_uname.release}",
|
||||
"version": __get_version(),
|
||||
"plugin_count": len(nonebot.get_loaded_plugins()),
|
||||
"nickname": BotConfig.self_nickname,
|
||||
}
|
||||
},
|
||||
pages={
|
||||
"viewport": {"width": 195, "height": 750},
|
||||
"base_url": f"file://{mock_template_path_new.absolute()}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
mock_template_to_pic.assert_awaited_once()
|
||||
mock_build_message.assert_called_once_with(mock_template_to_pic_return)
|
||||
mock_build_message_return.send.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_check_arm(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试自检(arm)
|
||||
"""
|
||||
from zhenxun.configs.config import BotConfig
|
||||
from zhenxun.builtin_plugins.check import _matcher
|
||||
from zhenxun.builtin_plugins.check.data_source import __get_version
|
||||
|
||||
platform_uname_arm = platform.uname_result(
|
||||
system="Linux",
|
||||
node="zhenxun",
|
||||
release="5.15.0-1017-oracle",
|
||||
version="#22~20.04.1-Ubuntu SMP Wed Aug 24 11:13:15 UTC 2022",
|
||||
machine="aarch64",
|
||||
) # type: ignore
|
||||
mock_subprocess_check_output = mocker.patch(
|
||||
"zhenxun.builtin_plugins.check.data_source.subprocess.check_output"
|
||||
)
|
||||
mock_environ_copy = mocker.patch(
|
||||
"zhenxun.builtin_plugins.check.data_source.os.environ.copy"
|
||||
)
|
||||
mock_environ_copy_return = mocker.MagicMock()
|
||||
mock_environ_copy.return_value = mock_environ_copy_return
|
||||
(
|
||||
mock_psutil,
|
||||
mock_cpuinfo,
|
||||
mock_platform,
|
||||
mock_template_to_pic,
|
||||
mock_template_to_pic_return,
|
||||
mock_build_message,
|
||||
mock_build_message_return,
|
||||
mock_template_path_new,
|
||||
) = init_mocker(mocker, tmp_path)
|
||||
|
||||
mock_platform.uname.return_value = platform_uname_arm
|
||||
mock_cpuinfo.get_cpu_info.return_value = {}
|
||||
mock_psutil.cpu_freq.return_value = {}
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = "自检"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
mock_template_to_pic.assert_awaited_once_with(
|
||||
template_path=str((mock_template_path_new / "check").absolute()),
|
||||
template_name="main.html",
|
||||
templates={
|
||||
"data": {
|
||||
"cpu_info": "1.0% - 0.0Ghz [1 core]",
|
||||
"cpu_process": 1.0,
|
||||
"ram_info": "1.0 / 1.0 GB",
|
||||
"ram_process": 100.0,
|
||||
"swap_info": "1.0 / 1.0 GB",
|
||||
"swap_process": 100.0,
|
||||
"disk_info": "1.0 / 1.0 GB",
|
||||
"disk_process": 100.0,
|
||||
"brand_raw": "",
|
||||
"baidu": "red",
|
||||
"google": "red",
|
||||
"system": f"{platform_uname_arm.system} "
|
||||
f"{platform_uname_arm.release}",
|
||||
"version": __get_version(),
|
||||
"plugin_count": len(nonebot.get_loaded_plugins()),
|
||||
"nickname": BotConfig.self_nickname,
|
||||
}
|
||||
},
|
||||
pages={
|
||||
"viewport": {"width": 195, "height": 750},
|
||||
"base_url": f"file://{mock_template_path_new.absolute()}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
mock_subprocess_check_output.assert_has_calls(
|
||||
[
|
||||
mocker.call(["lscpu"], env=mock_environ_copy_return),
|
||||
mocker.call().decode(),
|
||||
mocker.call().decode().splitlines(),
|
||||
mocker.call().decode().splitlines().__iter__(),
|
||||
mocker.call(["dmidecode", "-s", "processor-frequency"]),
|
||||
mocker.call().decode(),
|
||||
mocker.call().decode().split(),
|
||||
mocker.call().decode().split().__getitem__(0),
|
||||
mocker.call().decode().split().__getitem__().__float__(),
|
||||
] # type: ignore
|
||||
)
|
||||
mock_template_to_pic.assert_awaited_once()
|
||||
mock_build_message.assert_called_once_with(mock_template_to_pic_return)
|
||||
mock_build_message_return.send.assert_awaited_once()
|
||||
@@ -0,0 +1,339 @@
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.builtin_plugins.plugin_store.utils import init_mocked_api
|
||||
|
||||
|
||||
@pytest.mark.parametrize("package_api", ["jsd", "gh"])
|
||||
async def test_add_plugin_basic(
|
||||
package_api: str,
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试添加基础插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
|
||||
if package_api != "jsd":
|
||||
mocked_api["zhenxun_bot_plugins_metadata"].respond(404)
|
||||
if package_api != "gh":
|
||||
mocked_api["zhenxun_bot_plugins_tree"].respond(404)
|
||||
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 安装成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
assert mocked_api["search_image_plugin_file_init"].called
|
||||
assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("package_api", ["jsd", "gh"])
|
||||
async def test_add_plugin_basic_commit_version(
|
||||
package_api: str,
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试添加基础插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
|
||||
if package_api != "jsd":
|
||||
mocked_api["zhenxun_bot_plugins_metadata_commit"].respond(404)
|
||||
if package_api != "gh":
|
||||
mocked_api["zhenxun_bot_plugins_tree_commit"].respond(404)
|
||||
|
||||
plugin_id = 3
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 B站订阅 安装成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
if package_api == "jsd":
|
||||
assert mocked_api["zhenxun_bot_plugins_metadata_commit"].called
|
||||
if package_api == "gh":
|
||||
assert mocked_api["zhenxun_bot_plugins_tree_commit"].called
|
||||
assert mocked_api["bilibili_sub_plugin_file_init"].called
|
||||
assert (mock_base_path / "plugins" / "bilibili_sub" / "__init__.py").is_file()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("package_api", ["jsd", "gh"])
|
||||
async def test_add_plugin_basic_is_not_dir(
|
||||
package_api: str,
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试添加基础插件,插件不是目录
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
|
||||
if package_api != "jsd":
|
||||
mocked_api["zhenxun_bot_plugins_metadata"].respond(404)
|
||||
if package_api != "gh":
|
||||
mocked_api["zhenxun_bot_plugins_tree"].respond(404)
|
||||
|
||||
plugin_id = 0
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 鸡汤 安装成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
assert mocked_api["jitang_plugin_file"].called
|
||||
assert (mock_base_path / "plugins" / "alapi" / "jitang.py").is_file()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("package_api", ["jsd", "gh"])
|
||||
async def test_add_plugin_extra(
|
||||
package_api: str,
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试添加额外插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
|
||||
if package_api != "jsd":
|
||||
mocked_api["zhenxun_github_sub_metadata"].respond(404)
|
||||
if package_api != "gh":
|
||||
mocked_api["zhenxun_github_sub_tree"].respond(404)
|
||||
|
||||
plugin_id = 4
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message: str = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 github订阅 安装成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
assert mocked_api["github_sub_plugin_file_init"].called
|
||||
assert (mock_base_path / "plugins" / "github_sub" / "__init__.py").is_file()
|
||||
|
||||
|
||||
async def test_plugin_not_exist_add(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件不存在,添加插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
plugin_id = -1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件ID不存在..."),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
|
||||
|
||||
async def test_add_plugin_exist(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件已经存在,添加插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins",
|
||||
return_value=[("search_image", "0.1")],
|
||||
)
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"添加插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 已安装,无需重复安装"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
@@ -0,0 +1,140 @@
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.builtin_plugins.plugin_store.utils import init_mocked_api
|
||||
|
||||
|
||||
async def test_plugin_store(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件商店
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
from zhenxun.builtin_plugins.plugin_store.data_source import row_style
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
mock_table_page = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ImageTemplate.table_page"
|
||||
)
|
||||
mock_table_page_return = mocker.AsyncMock()
|
||||
mock_table_page.return_value = mock_table_page_return
|
||||
|
||||
mock_build_message = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.MessageUtils.build_message"
|
||||
)
|
||||
mock_build_message_return = mocker.AsyncMock()
|
||||
mock_build_message.return_value = mock_build_message_return
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = "插件商店"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
mock_table_page.assert_awaited_once_with(
|
||||
"插件列表",
|
||||
"通过添加/移除插件 ID 来管理插件",
|
||||
["-", "ID", "名称", "简介", "作者", "版本", "类型"],
|
||||
[
|
||||
["", 0, "鸡汤", "喏,亲手为你煮的鸡汤", "HibiKier", "0.1", "普通插件"],
|
||||
["", 1, "识图", "以图搜图,看破本源", "HibiKier", "0.1", "普通插件"],
|
||||
["", 2, "网易云热评", "生了个人,我很抱歉", "HibiKier", "0.1", "普通插件"],
|
||||
[
|
||||
"",
|
||||
3,
|
||||
"B站订阅",
|
||||
"非常便利的B站订阅通知",
|
||||
"HibiKier",
|
||||
"0.3-b101fbc",
|
||||
"普通插件",
|
||||
],
|
||||
[
|
||||
"",
|
||||
4,
|
||||
"github订阅",
|
||||
"订阅github用户或仓库",
|
||||
"xuanerwa",
|
||||
"0.7",
|
||||
"普通插件",
|
||||
],
|
||||
[
|
||||
"",
|
||||
5,
|
||||
"Minecraft查服",
|
||||
"Minecraft服务器状态查询,支持IPv6",
|
||||
"molanp",
|
||||
"1.13",
|
||||
"普通插件",
|
||||
],
|
||||
],
|
||||
text_style=row_style,
|
||||
)
|
||||
mock_build_message.assert_called_once_with(mock_table_page_return)
|
||||
mock_build_message_return.send.assert_awaited_once()
|
||||
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
|
||||
|
||||
async def test_plugin_store_fail(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件商店
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins.json",
|
||||
name="basic_plugins",
|
||||
).respond(404)
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = "插件商店"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message("获取插件列表失败..."),
|
||||
result=None,
|
||||
exception=None,
|
||||
bot=bot,
|
||||
)
|
||||
|
||||
assert mocked_api["basic_plugins"].called
|
||||
@@ -0,0 +1,142 @@
|
||||
# ruff: noqa: ASYNC230
|
||||
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.builtin_plugins.plugin_store.utils import init_mocked_api, get_content_bytes
|
||||
|
||||
|
||||
async def test_remove_plugin(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试删除插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
|
||||
plugin_path = mock_base_path / "plugins" / "search_image"
|
||||
plugin_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(plugin_path / "__init__.py", "wb") as f:
|
||||
f.write(get_content_bytes("search_image.py"))
|
||||
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"移除插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 移除成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
assert not (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file()
|
||||
|
||||
|
||||
async def test_plugin_not_exist_remove(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件不存在,移除插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
plugin_id = -1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"移除插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_2,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件ID不存在..."),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
|
||||
|
||||
async def test_remove_plugin_not_install(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件未安装,移除插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
_ = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"移除插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_2,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 不存在..."),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
@@ -0,0 +1,182 @@
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.builtin_plugins.plugin_store.utils import init_mocked_api
|
||||
|
||||
|
||||
async def test_search_plugin_name(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试搜索插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
from zhenxun.builtin_plugins.plugin_store.data_source import row_style
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
mock_table_page = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ImageTemplate.table_page"
|
||||
)
|
||||
mock_table_page_return = mocker.AsyncMock()
|
||||
mock_table_page.return_value = mock_table_page_return
|
||||
|
||||
mock_build_message = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.MessageUtils.build_message"
|
||||
)
|
||||
mock_build_message_return = mocker.AsyncMock()
|
||||
mock_build_message.return_value = mock_build_message_return
|
||||
|
||||
plugin_name = "github订阅"
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"搜索插件 {plugin_name}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
mock_table_page.assert_awaited_once_with(
|
||||
"插件列表",
|
||||
"通过添加/移除插件 ID 来管理插件",
|
||||
["-", "ID", "名称", "简介", "作者", "版本", "类型"],
|
||||
[
|
||||
[
|
||||
"",
|
||||
4,
|
||||
"github订阅",
|
||||
"订阅github用户或仓库",
|
||||
"xuanerwa",
|
||||
"0.7",
|
||||
"普通插件",
|
||||
]
|
||||
],
|
||||
text_style=row_style,
|
||||
)
|
||||
mock_build_message.assert_called_once_with(mock_table_page_return)
|
||||
mock_build_message_return.send.assert_awaited_once()
|
||||
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
|
||||
|
||||
async def test_search_plugin_author(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试搜索插件,作者
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
from zhenxun.builtin_plugins.plugin_store.data_source import row_style
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
|
||||
mock_table_page = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ImageTemplate.table_page"
|
||||
)
|
||||
mock_table_page_return = mocker.AsyncMock()
|
||||
mock_table_page.return_value = mock_table_page_return
|
||||
|
||||
mock_build_message = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.MessageUtils.build_message"
|
||||
)
|
||||
mock_build_message_return = mocker.AsyncMock()
|
||||
mock_build_message.return_value = mock_build_message_return
|
||||
|
||||
author_name = "xuanerwa"
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"搜索插件 {author_name}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
mock_table_page.assert_awaited_once_with(
|
||||
"插件列表",
|
||||
"通过添加/移除插件 ID 来管理插件",
|
||||
["-", "ID", "名称", "简介", "作者", "版本", "类型"],
|
||||
[
|
||||
[
|
||||
"",
|
||||
4,
|
||||
"github订阅",
|
||||
"订阅github用户或仓库",
|
||||
"xuanerwa",
|
||||
"0.7",
|
||||
"普通插件",
|
||||
]
|
||||
],
|
||||
text_style=row_style,
|
||||
)
|
||||
mock_build_message.assert_called_once_with(mock_table_page_return)
|
||||
mock_build_message_return.send.assert_awaited_once()
|
||||
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
|
||||
|
||||
async def test_plugin_not_exist_search(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件不存在,搜索插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
plugin_name = "not_exist_plugin_name"
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"搜索插件 {plugin_name}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_3,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="未找到相关插件..."),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
@@ -0,0 +1,206 @@
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
from nonebug import App
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
|
||||
from tests.utils import _v11_group_message_event
|
||||
from tests.config import BotId, UserId, GroupId, MessageId
|
||||
from tests.builtin_plugins.plugin_store.utils import init_mocked_api
|
||||
|
||||
|
||||
async def test_update_plugin_basic_need_update(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试更新基础插件,插件需要更新
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mock_base_path = mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins",
|
||||
return_value=[("search_image", "0.0")],
|
||||
)
|
||||
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"更新插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 更新成功! 重启后生效"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
assert mocked_api["search_image_plugin_file_init"].called
|
||||
assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file()
|
||||
|
||||
|
||||
async def test_update_plugin_basic_is_new(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试更新基础插件,插件是最新版
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
|
||||
new=tmp_path / "zhenxun",
|
||||
)
|
||||
mocker.patch(
|
||||
"zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins",
|
||||
return_value=[("search_image", "0.1")],
|
||||
)
|
||||
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"更新插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 已是最新版本"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
assert mocked_api["basic_plugins"].called
|
||||
assert mocked_api["extra_plugins"].called
|
||||
|
||||
|
||||
async def test_plugin_not_exist_update(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件不存在,更新插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
plugin_id = -1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"更新插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_2,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件ID不存在..."),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_plugin_not_install(
|
||||
app: App,
|
||||
mocker: MockerFixture,
|
||||
mocked_api: MockRouter,
|
||||
create_bot: Callable,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""
|
||||
测试插件不存在,更新插件
|
||||
"""
|
||||
from zhenxun.builtin_plugins.plugin_store import _matcher
|
||||
|
||||
init_mocked_api(mocked_api=mocked_api)
|
||||
plugin_id = 1
|
||||
|
||||
async with app.test_matcher(_matcher) as ctx:
|
||||
bot = create_bot(ctx)
|
||||
bot: Bot = cast(Bot, bot)
|
||||
raw_message = f"更新插件 {plugin_id}"
|
||||
event: GroupMessageEvent = _v11_group_message_event(
|
||||
message=raw_message,
|
||||
self_id=BotId.QQ_BOT,
|
||||
user_id=UserId.SUPERUSER,
|
||||
group_id=GroupId.GROUP_ID_LEVEL_5,
|
||||
message_id=MessageId.MESSAGE_ID_2,
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot=bot, event=event)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event=event,
|
||||
message=Message(message="插件 识图 未安装,无法更新"),
|
||||
result=None,
|
||||
bot=bot,
|
||||
)
|
||||
@@ -0,0 +1,86 @@
|
||||
# ruff: noqa: ASYNC230
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from respx import MockRouter
|
||||
|
||||
from tests.utils import get_content_bytes as _get_content_bytes
|
||||
from tests.utils import get_response_json as _get_response_json
|
||||
|
||||
|
||||
def get_response_json(file: str) -> dict:
|
||||
return _get_response_json(Path() / "plugin_store", file=file)
|
||||
|
||||
|
||||
def get_content_bytes(file: str) -> bytes:
|
||||
return _get_content_bytes(Path() / "plugin_store", file)
|
||||
|
||||
|
||||
def init_mocked_api(mocked_api: MockRouter) -> None:
|
||||
# metadata
|
||||
mocked_api.get(
|
||||
"https://data.jsdelivr.com/v1/packages/gh/zhenxun-org/zhenxun_bot_plugins@main",
|
||||
name="zhenxun_bot_plugins_metadata",
|
||||
).respond(json=get_response_json("zhenxun_bot_plugins_metadata.json"))
|
||||
mocked_api.get(
|
||||
"https://data.jsdelivr.com/v1/packages/gh/xuanerwa/zhenxun_github_sub@main",
|
||||
name="zhenxun_github_sub_metadata",
|
||||
).respond(json=get_response_json("zhenxun_github_sub_metadata.json"))
|
||||
mocked_api.get(
|
||||
"https://data.jsdelivr.com/v1/packages/gh/zhenxun-org/zhenxun_bot_plugins@b101fbc",
|
||||
name="zhenxun_bot_plugins_metadata_commit",
|
||||
).respond(json=get_response_json("zhenxun_bot_plugins_metadata.json"))
|
||||
|
||||
# tree
|
||||
mocked_api.get(
|
||||
"https://api.github.com/repos/zhenxun-org/zhenxun_bot_plugins/git/trees/main?recursive=1",
|
||||
name="zhenxun_bot_plugins_tree",
|
||||
).respond(json=get_response_json("zhenxun_bot_plugins_tree.json"))
|
||||
mocked_api.get(
|
||||
"https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/trees/main?recursive=1",
|
||||
name="zhenxun_github_sub_tree",
|
||||
).respond(json=get_response_json("zhenxun_github_sub_tree.json"))
|
||||
mocked_api.get(
|
||||
"https://api.github.com/repos/zhenxun-org/zhenxun_bot_plugins/git/trees/b101fbc?recursive=1",
|
||||
name="zhenxun_bot_plugins_tree_commit",
|
||||
).respond(json=get_response_json("zhenxun_bot_plugins_tree.json"))
|
||||
|
||||
mocked_api.head(
|
||||
"https://raw.githubusercontent.com/",
|
||||
name="head_raw",
|
||||
).respond(200, text="")
|
||||
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins.json",
|
||||
name="basic_plugins",
|
||||
).respond(json=get_response_json("basic_plugins.json"))
|
||||
mocked_api.get(
|
||||
"https://cdn.jsdelivr.net/gh/zhenxun-org/zhenxun_bot_plugins@main/plugins.json",
|
||||
name="basic_plugins_jsdelivr",
|
||||
).respond(200, json=get_response_json("basic_plugins.json"))
|
||||
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins_index/index/plugins.json",
|
||||
name="extra_plugins",
|
||||
).respond(200, json=get_response_json("extra_plugins.json"))
|
||||
mocked_api.get(
|
||||
"https://cdn.jsdelivr.net/gh/zhenxun-org/zhenxun_bot_plugins_index@index/plugins.json",
|
||||
name="extra_plugins_jsdelivr",
|
||||
).respond(200, json=get_response_json("extra_plugins.json"))
|
||||
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/search_image/__init__.py",
|
||||
name="search_image_plugin_file_init",
|
||||
).respond(content=get_content_bytes("search_image.py"))
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/alapi/jitang.py",
|
||||
name="jitang_plugin_file",
|
||||
).respond(content=get_content_bytes("jitang.py"))
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/xuanerwa/zhenxun_github_sub/main/github_sub/__init__.py",
|
||||
name="github_sub_plugin_file_init",
|
||||
).respond(content=get_content_bytes("github_sub.py"))
|
||||
mocked_api.get(
|
||||
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/b101fbc/plugins/bilibili_sub/__init__.py",
|
||||
name="bilibili_sub_plugin_file_init",
|
||||
).respond(content=get_content_bytes("bilibili_sub.py"))
|
||||
@@ -0,0 +1,22 @@
|
||||
class BotId:
|
||||
QQ_BOT = 12345
|
||||
|
||||
|
||||
class UserId:
|
||||
SUPERUSER = 10000
|
||||
SUPERUSER_QQ = 11000
|
||||
SUPERUSER_DODO = 12000
|
||||
USER_LEVEL_0 = 10010
|
||||
USER_LEVEL_5 = 10005
|
||||
|
||||
|
||||
class GroupId:
|
||||
GROUP_ID_LEVEL_0 = 20000
|
||||
GROUP_ID_LEVEL_5 = 20005
|
||||
|
||||
|
||||
class MessageId:
|
||||
MESSAGE_ID = 30001
|
||||
MESSAGE_ID_2 = 30002
|
||||
MESSAGE_ID_3 = 30003
|
||||
MESSAGE_ID_4 = 30004
|
||||
+80
-11
@@ -1,37 +1,106 @@
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
from collections.abc import Callable
|
||||
|
||||
import nonebot
|
||||
import pytest
|
||||
from nonebot.plugin import Plugin
|
||||
from nonebug import NONEBOT_INIT_KWARGS
|
||||
import nonebot
|
||||
from nonebug.app import App
|
||||
from pytest_mock import MockerFixture
|
||||
from respx import MockRouter
|
||||
from pytest_mock import MockerFixture
|
||||
from nonebug import NONEBOT_INIT_KWARGS
|
||||
from nonebug.mixin.process import MatcherContext
|
||||
|
||||
from tests.config import BotId, UserId
|
||||
|
||||
nonebot.load_plugin("nonebot_plugin_session")
|
||||
|
||||
|
||||
def get_response_json(path: str) -> dict:
|
||||
return json.loads(
|
||||
(Path(__file__).parent / "response" / path).read_text(encoding="utf8")
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config: pytest.Config) -> None:
|
||||
config.stash[NONEBOT_INIT_KWARGS] = {
|
||||
"driver": "~fastapi+~httpx+~websockets",
|
||||
"superusers": ["AkashiCoin"],
|
||||
"command_start": "",
|
||||
"superusers": [UserId.SUPERUSER.__str__()],
|
||||
"command_start": [""],
|
||||
"session_running_expression": "别急呀,小真寻要宕机了!QAQ",
|
||||
"image_to_bytes": False,
|
||||
"nickname": ["真寻", "小真寻", "绪山真寻", "小寻子"],
|
||||
"session_expire_timeout": 30,
|
||||
"self_nickname": "小真寻",
|
||||
"db_url": "sqlite://:memory:",
|
||||
"platform_superusers": {"qq": ["qq_su"], "dodo": ["dodo_su"]},
|
||||
"platform_superusers": {
|
||||
"qq": [UserId.SUPERUSER_QQ.__str__()],
|
||||
"dodo": [UserId.SUPERUSER_DODO.__str__()],
|
||||
},
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
"log_level": "DEBUG",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def load_plugin(nonebug_init: None) -> set[Plugin]:
|
||||
return nonebot.load_plugins("zhenxun.plugins")
|
||||
def _init_bot(nonebug_init: None):
|
||||
from nonebot.adapters.onebot.v11 import Adapter as OneBotV11Adapter
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(OneBotV11Adapter)
|
||||
|
||||
nonebot.load_plugin("nonebot_plugin_alconna")
|
||||
nonebot.load_plugin("nonebot_plugin_apscheduler")
|
||||
nonebot.load_plugin("nonebot_plugin_userinfo")
|
||||
nonebot.load_plugin("nonebot_plugin_htmlrender")
|
||||
|
||||
nonebot.load_plugins("zhenxun/builtin_plugins")
|
||||
nonebot.load_plugins("zhenxun/plugins")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def app(app: App, tmp_path: Path, mocker: MockerFixture):
|
||||
mocker.patch("nonebot.drivers.websockets.connect", return_value=MockRouter())
|
||||
return app
|
||||
from zhenxun.services.db_context import init, disconnect
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
# 清除连接钩子,现在 NoneBug 会自动触发 on_bot_connect
|
||||
driver._bot_connection_hook.clear()
|
||||
mock_config_path = mocker.MagicMock()
|
||||
mock_config_path.LOG_PATH = tmp_path / "log"
|
||||
# mock_config_path.LOG_PATH.mkdir(parents=True, exist_ok=True)
|
||||
mock_config_path.DATA_PATH = tmp_path / "data"
|
||||
# mock_config_path.DATA_PATH.mkdir(parents=True, exist_ok=True)
|
||||
mock_config_path.TEMP_PATH = tmp_path / "resources" / "temp"
|
||||
# mock_config_path.TEMP_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mocker.patch("zhenxun.configs.path_config", new=mock_config_path)
|
||||
|
||||
await init()
|
||||
# await driver._lifespan.startup()
|
||||
os.environ["AIOCACHE_DISABLE"] = "1"
|
||||
|
||||
yield app
|
||||
|
||||
del os.environ["AIOCACHE_DISABLE"]
|
||||
# await driver._lifespan.shutdown()
|
||||
await disconnect()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_bot() -> Callable:
|
||||
from nonebot.adapters.onebot.v11 import Bot, Adapter
|
||||
|
||||
def _create_bot(context: MatcherContext) -> Bot:
|
||||
return context.create_bot(
|
||||
base=Bot,
|
||||
adapter=nonebot.get_adapter(Adapter),
|
||||
self_id=BotId.QQ_BOT.__str__(),
|
||||
)
|
||||
|
||||
return _create_bot
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_api(respx_mock: MockRouter):
|
||||
return respx_mock
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="B站订阅",
|
||||
description="非常便利的B站订阅通知",
|
||||
usage="""
|
||||
usage:
|
||||
B站直播,番剧,UP动态开播等提醒
|
||||
主播订阅相当于 直播间订阅 + UP订阅
|
||||
指令:
|
||||
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
|
||||
删除订阅 ['主播'/'UP'/'id'] [id]
|
||||
查看订阅
|
||||
示例:
|
||||
添加订阅主播 2345344 <-(直播房间id)
|
||||
添加订阅UP 2355543 <-(个人主页id)
|
||||
添加订阅番剧 史莱姆 <-(支持模糊搜索)
|
||||
添加订阅番剧 125344 <-(番剧id)
|
||||
删除订阅id 2324344 <-(任意id,通过查看订阅获取)
|
||||
""".strip(),
|
||||
extra=PluginExtraData(
|
||||
author="HibiKier",
|
||||
version="0.3-b101fbc",
|
||||
superuser_help="""
|
||||
登录b站获取cookie防止风控:
|
||||
bil_check/检测b站
|
||||
bil_login/登录b站
|
||||
bil_logout/退出b站 uid
|
||||
示例:
|
||||
登录b站
|
||||
检测b站
|
||||
bil_logout 12345<-(退出登录的b站uid,通过检测b站获取)
|
||||
""",
|
||||
).dict(),
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="github订阅",
|
||||
description="订阅github用户或仓库",
|
||||
usage="""
|
||||
usage:
|
||||
github新Comment,PR,Issue等提醒
|
||||
指令:
|
||||
添加github ['用户'/'仓库'] [用户名/{owner/repo}]
|
||||
删除github [用户名/{owner/repo}]
|
||||
查看github
|
||||
示例:添加github订阅 用户 HibiKier
|
||||
示例:添加gb订阅 仓库 HibiKier/zhenxun_bot
|
||||
示例:添加github 用户 HibiKier
|
||||
示例:删除gb订阅 HibiKier
|
||||
""".strip(),
|
||||
extra=PluginExtraData(
|
||||
author="xuanerwa",
|
||||
version="0.7",
|
||||
).dict(),
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="鸡汤",
|
||||
description="喏,亲手为你煮的鸡汤",
|
||||
usage="""
|
||||
不喝点什么感觉有点不舒服
|
||||
指令:
|
||||
鸡汤
|
||||
""".strip(),
|
||||
extra=PluginExtraData(
|
||||
author="HibiKier",
|
||||
version="0.1",
|
||||
).dict(),
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from zhenxun.configs.utils import PluginExtraData
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="识图",
|
||||
description="以图搜图,看破本源",
|
||||
usage="""
|
||||
识别图片 [二次元图片]
|
||||
指令:
|
||||
识图 [图片]
|
||||
""".strip(),
|
||||
extra=PluginExtraData(
|
||||
author="HibiKier",
|
||||
version="0.1",
|
||||
menu_type="一些工具",
|
||||
).dict(),
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"url": "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/172632135",
|
||||
"assets_url": "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/172632135/assets",
|
||||
"upload_url": "https://uploads.github.com/repos/HibiKier/zhenxun_bot/releases/172632135/assets{?name,label}",
|
||||
"html_url": "https://github.com/HibiKier/zhenxun_bot/releases/tag/v0.2.2",
|
||||
"id": 172632135,
|
||||
"author": {
|
||||
"login": "HibiKier",
|
||||
"id": 45528451,
|
||||
"node_id": "MDQ6VXNlcjQ1NTI4NDUx",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/45528451?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/HibiKier",
|
||||
"html_url": "https://github.com/HibiKier",
|
||||
"followers_url": "https://api.github.com/users/HibiKier/followers",
|
||||
"following_url": "https://api.github.com/users/HibiKier/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/HibiKier/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/HibiKier/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/HibiKier/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/HibiKier/orgs",
|
||||
"repos_url": "https://api.github.com/users/HibiKier/repos",
|
||||
"events_url": "https://api.github.com/users/HibiKier/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/HibiKier/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"node_id": "RE_kwDOFe9cjs4KSihH",
|
||||
"tag_name": "v0.2.2",
|
||||
"target_commitish": "main",
|
||||
"name": "v0.2.2",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"created_at": "2024-08-29T18:48:53Z",
|
||||
"published_at": "2024-08-29T18:49:34Z",
|
||||
"assets": [],
|
||||
"tarball_url": "https://api.github.com/repos/HibiKier/zhenxun_bot/tarball/v0.2.2",
|
||||
"zipball_url": "https://api.github.com/repos/HibiKier/zhenxun_bot/zipball/v0.2.2",
|
||||
"body": "* 集成webui\r\n* 移除plugins\r\n* 签到和帮助ui改版\r\n* 修改bug"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"鸡汤": {
|
||||
"module": "jitang",
|
||||
"module_path": "plugins.alapi.jitang",
|
||||
"description": "喏,亲手为你煮的鸡汤",
|
||||
"usage": "不喝点什么感觉有点不舒服\n 指令:\n 鸡汤",
|
||||
"author": "HibiKier",
|
||||
"version": "0.1",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": false
|
||||
},
|
||||
"识图": {
|
||||
"module": "search_image",
|
||||
"module_path": "plugins.search_image",
|
||||
"description": "以图搜图,看破本源",
|
||||
"usage": "识别图片 [二次元图片]\n 指令:\n 识图 [图片]",
|
||||
"author": "HibiKier",
|
||||
"version": "0.1",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": true
|
||||
},
|
||||
"网易云热评": {
|
||||
"module": "comments_163",
|
||||
"module_path": "plugins.alapi.comments_163",
|
||||
"description": "生了个人,我很抱歉",
|
||||
"usage": "到点了,还是防不了下塔\n 指令:\n 网易云热评/到点了/12点了",
|
||||
"author": "HibiKier",
|
||||
"version": "0.1",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": false
|
||||
},
|
||||
"B站订阅": {
|
||||
"module": "bilibili_sub",
|
||||
"module_path": "plugins.bilibili_sub",
|
||||
"description": "非常便利的B站订阅通知",
|
||||
"usage": "B站直播,番剧,UP动态开播等提醒",
|
||||
"author": "HibiKier",
|
||||
"version": "0.3-b101fbc",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"github订阅": {
|
||||
"module": "github_sub",
|
||||
"module_path": "github_sub",
|
||||
"description": "订阅github用户或仓库",
|
||||
"usage": "usage:\n github新Comment,PR,Issue等提醒\n 指令:\n 添加github ['用户'/'仓库'] [用户名/{owner/repo}]\n 删除github [用户名/{owner/repo}]\n 查看github\n 示例:添加github订阅 用户 HibiKier\n 示例:添加gb订阅 仓库 HibiKier/zhenxun_bot\n 示例:添加github 用户 HibiKier\n 示例:删除gb订阅 HibiKier",
|
||||
"author": "xuanerwa",
|
||||
"version": "0.7",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": true,
|
||||
"github_url": "https://github.com/xuanerwa/zhenxun_github_sub"
|
||||
},
|
||||
"Minecraft查服": {
|
||||
"module": "mc_check",
|
||||
"module_path": "mc_check",
|
||||
"description": "Minecraft服务器状态查询,支持IPv6",
|
||||
"usage": "Minecraft服务器状态查询,支持IPv6\n用法:\n\t查服 [ip]:[端口] / 查服 [ip]\n\t设置语言 zh-cn\n\t当前语言\n\t语言列表\neg:\t\nmcheck ip:port / mcheck ip\n\tset_lang en\n\tlang_now\n\tlang_list",
|
||||
"author": "molanp",
|
||||
"version": "1.13",
|
||||
"plugin_type": "NORMAL",
|
||||
"is_dir": true,
|
||||
"github_url": "https://github.com/molanp/zhenxun_check_Minecraft"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"type": "gh",
|
||||
"name": "zhenxun-org/zhenxun_bot_plugins",
|
||||
"version": "main",
|
||||
"default": null,
|
||||
"files": [
|
||||
{
|
||||
"type": "directory",
|
||||
"name": "plugins",
|
||||
"files": [
|
||||
{
|
||||
"type": "directory",
|
||||
"name": "search_image",
|
||||
"files": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "__init__.py",
|
||||
"hash": "a4Yp9HPoBzMwvnQDT495u0yYqTQWofkOyHxEi1FdVb0=",
|
||||
"size": 3010
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "directory",
|
||||
"name": "alapi",
|
||||
"files": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "__init__.py",
|
||||
"hash": "ndDxtO0pAq3ZTb4RdqW7FTDgOGC/RjS1dnwdaQfT0uQ=",
|
||||
"size": 284
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "_data_source.py",
|
||||
"hash": "KOLqtj4TQWWQco5bA4tWFc7A0z1ruMyDk1RiKeqJHRA=",
|
||||
"size": 919
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "comments_163.py",
|
||||
"hash": "Q5pZsj1Pj+EJMdKYcPtLqejcXAWUQIoXVQG49PZPaSI=",
|
||||
"size": 1593
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "cover.py",
|
||||
"hash": "QSjtcy0oVrjaRiAWZKmUJlp0L4DQqEcdYNmExNo9mgc=",
|
||||
"size": 1438
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "jitang.py",
|
||||
"hash": "xh43Osxt0xogTH448gUMC+/DaSGmCFme8DWUqC25IbU=",
|
||||
"size": 1411
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "poetry.py",
|
||||
"hash": "Aj2unoNQboj3/0LhIrYU+dCa5jvMdpjMYXYUayhjuz4=",
|
||||
"size": 1530
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "directory",
|
||||
"name": "bilibili_sub",
|
||||
"files": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "__init__.py",
|
||||
"hash": "407DCgNFcZnuEK+d716j8EWrFQc4Nlxa35V3yemy3WQ=",
|
||||
"size": 14293
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"stats": "https://data.jsdelivr.com/v1/stats/packages/gh/zhenxun-org/zhenxun_bot_plugins@main"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"type": "gh",
|
||||
"name": "xuanerwa/zhenxun_github_sub",
|
||||
"version": "main",
|
||||
"default": null,
|
||||
"files": [
|
||||
{
|
||||
"type": "directory",
|
||||
"name": "github_sub",
|
||||
"files": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "__init__.py",
|
||||
"hash": "z1C5BBK0+atbDghbyRlF2xIDwk0HQdHM1yXQZkF7/t8=",
|
||||
"size": 7551
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"stats": "https://data.jsdelivr.com/v1/stats/packages/gh/xuanerwa/zhenxun_github_sub@main"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"sha": "438298b9e88f9dafa7020e99d7c7b4c98f93aea6",
|
||||
"url": "https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/trees/438298b9e88f9dafa7020e99d7c7b4c98f93aea6",
|
||||
"tree": [
|
||||
{
|
||||
"path": "LICENSE",
|
||||
"mode": "100644",
|
||||
"type": "blob",
|
||||
"sha": "f288702d2fa16d3cdf0035b15a9fcbc552cd88e7",
|
||||
"size": 35149,
|
||||
"url": "https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/blobs/f288702d2fa16d3cdf0035b15a9fcbc552cd88e7"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"mode": "100644",
|
||||
"type": "blob",
|
||||
"sha": "e974cfc9b973d4a041f03e693ea20563a933b7ca",
|
||||
"size": 955,
|
||||
"url": "https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/blobs/e974cfc9b973d4a041f03e693ea20563a933b7ca"
|
||||
},
|
||||
{
|
||||
"path": "github_sub",
|
||||
"mode": "040000",
|
||||
"type": "tree",
|
||||
"sha": "0f7d76bcf472e2ab0610fa542b067633d6e3ae7e",
|
||||
"url": "https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/trees/0f7d76bcf472e2ab0610fa542b067633d6e3ae7e"
|
||||
},
|
||||
{
|
||||
"path": "github_sub/__init__.py",
|
||||
"mode": "100644",
|
||||
"type": "blob",
|
||||
"sha": "7d17fd49fe82fa3897afcef61b2c694ed93a4ba3",
|
||||
"size": 7551,
|
||||
"url": "https://api.github.com/repos/xuanerwa/zhenxun_github_sub/git/blobs/7d17fd49fe82fa3897afcef61b2c694ed93a4ba3"
|
||||
}
|
||||
],
|
||||
"truncated": false
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot.adapters.onebot.v11.event import Sender
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment, GroupMessageEvent
|
||||
|
||||
|
||||
def get_response_json(base_path: Path, file: str) -> dict:
|
||||
try:
|
||||
return json.loads(
|
||||
(Path(__file__).parent / "response" / base_path / file).read_text(
|
||||
encoding="utf8"
|
||||
)
|
||||
)
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
raise ValueError(f"Error reading or parsing JSON file: {e}") from e
|
||||
|
||||
|
||||
def get_content_bytes(base_path: Path, path: str) -> bytes:
|
||||
try:
|
||||
return (Path(__file__).parent / "content" / base_path / path).read_bytes()
|
||||
except FileNotFoundError as e:
|
||||
raise ValueError(f"Error reading file: {e}") from e
|
||||
|
||||
|
||||
def _v11_group_message_event(
|
||||
message: str,
|
||||
self_id: int,
|
||||
user_id: int,
|
||||
group_id: int,
|
||||
message_id: int,
|
||||
to_me: bool = True,
|
||||
) -> GroupMessageEvent:
|
||||
return GroupMessageEvent(
|
||||
time=1122,
|
||||
self_id=self_id,
|
||||
post_type="message",
|
||||
sub_type="",
|
||||
user_id=user_id,
|
||||
message_id=message_id,
|
||||
message=Message(message),
|
||||
original_message=Message(message),
|
||||
message_type="group",
|
||||
raw_message=message,
|
||||
font=1,
|
||||
sender=Sender(user_id=user_id),
|
||||
to_me=to_me,
|
||||
group_id=group_id,
|
||||
)
|
||||
|
||||
|
||||
def _v11_private_message_send(
|
||||
message: str,
|
||||
user_id: int,
|
||||
):
|
||||
return {
|
||||
"message_type": "private",
|
||||
"user_id": user_id,
|
||||
"message": [
|
||||
MessageSegment(
|
||||
type="text",
|
||||
data={
|
||||
"text": message,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user