Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e33bf3a50 | ||
|
|
daa40ae8b8 | ||
|
|
0074483280 |
@@ -16,6 +16,8 @@ SELF_NICKNAME="小真寻"
|
||||
|
||||
# 数据库配置
|
||||
# 示例: "postgres://user:password@127.0.0.1:5432/database"
|
||||
# 示例: "mysql://user:password@127.0.0.1:3306/database"
|
||||
# 示例: "sqlite:data/db/zhenxun.db" 在data目录下建立db文件夹
|
||||
DB_URL = ""
|
||||
|
||||
# 系统代理
|
||||
|
||||
@@ -3,8 +3,16 @@ name: 检查bot是否运行正常
|
||||
on:
|
||||
push:
|
||||
branches: ["dev", "main"]
|
||||
paths:
|
||||
- zhenxun/**
|
||||
- tests/**
|
||||
- bot.py
|
||||
pull_request:
|
||||
branches: ["dev", "main"]
|
||||
paths:
|
||||
- zhenxun/**
|
||||
- tests/**
|
||||
- bot.py
|
||||
|
||||
jobs:
|
||||
bot-check:
|
||||
@@ -28,7 +36,14 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pypoetry
|
||||
key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}
|
||||
key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
|
||||
|
||||
- name: Cache playwright cache
|
||||
id: cache-playwright
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}
|
||||
|
||||
- name: Cache Data cache
|
||||
uses: actions/cache@v3
|
||||
@@ -42,7 +57,9 @@ jobs:
|
||||
rm -rf poetry.lock
|
||||
poetry source remove ali
|
||||
poetry install --no-root
|
||||
poetry run pip install pydantic==1.10
|
||||
|
||||
- name: Run tests
|
||||
run: poetry run pytest --cov=zhenxun --cov-report xml
|
||||
|
||||
- name: Check bot run
|
||||
id: bot_check_run
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Update Version
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- zhenxun/**
|
||||
- resources/**
|
||||
- bot.py
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
update-version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Read current version
|
||||
id: read_version
|
||||
run: |
|
||||
version_line=$(grep '__version__' __version__)
|
||||
version=$(echo $version_line | sed -E 's/__version__:\s*v([0-9]+\.[0-9]+\.[0-9]+)(-.+)?/\1/')
|
||||
echo "Current version: $version"
|
||||
echo "current_version=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check for version file changes
|
||||
id: check_diff
|
||||
run: |
|
||||
if git diff --name-only HEAD~1 HEAD | grep -q '__version__'; then
|
||||
echo "Version file has changes"
|
||||
echo "version_changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Version file has no changes"
|
||||
echo "version_changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Get commit hash
|
||||
id: get_commit_hash
|
||||
run: echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version file
|
||||
if: steps.check_diff.outputs.version_changed == 'false'
|
||||
run: |
|
||||
current_version="${{ steps.read_version.outputs.current_version }}"
|
||||
commit_hash="${{ steps.get_commit_hash.outputs.commit_hash }}"
|
||||
new_version="v${current_version}-${commit_hash}"
|
||||
echo "Updating version to: $new_version"
|
||||
echo "__version__: $new_version" > __version__
|
||||
git config --global user.name "${{ github.event.pull_request.user.login }}"
|
||||
git config --global user.email "${{ github.event.pull_request.user.login }}@users.noreply.github.com"
|
||||
git add __version__
|
||||
git remote set-url origin https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git
|
||||
git commit -m "chore(version): Update version to $new_version"
|
||||
git push origin HEAD:${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Check updated version
|
||||
if: steps.check_diff.outputs.version_changed == 'false'
|
||||
run: cat __version__
|
||||
@@ -174,11 +174,9 @@ data/
|
||||
/resources/image/prts/
|
||||
/configs/config.py
|
||||
configs/config.yaml
|
||||
./.env
|
||||
./.env.dev
|
||||
plugins/csgo_server/
|
||||
plugins/activity/
|
||||
!/resources/image/genshin/alc/back.png
|
||||
!/data/genshin_alc/
|
||||
.vscode/launch.json
|
||||
/resources/template/my_info
|
||||
plugins_/
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"charliermarsh.ruff",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.isort",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance"
|
||||
]
|
||||
}
|
||||
@@ -23,7 +23,37 @@
|
||||
"ujson",
|
||||
"unban",
|
||||
"userinfo",
|
||||
"zhenxun"
|
||||
"zhenxun",
|
||||
"jsdelivr"
|
||||
],
|
||||
"python.analysis.autoImportCompletions": true
|
||||
"python.analysis.autoImportCompletions": true,
|
||||
"python.testing.pytestArgs": ["tests"],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff", // 默认使用 Ruff 格式化
|
||||
"editor.wordBasedSuggestions": "allDocuments",
|
||||
"editor.formatOnType": true,
|
||||
"editor.formatOnSave": true, // 保存时自动格式化
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.ruff": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
}
|
||||
},
|
||||
"ruff.format.preview": false,
|
||||
"isort.check": true,
|
||||
"ruff.importStrategy": "useBundled",
|
||||
"ruff.organizeImports": false,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,13 @@ WORKDIR /app/zhenxun
|
||||
|
||||
COPY . /app/zhenxun
|
||||
|
||||
RUN apt update && \
|
||||
apt upgrade -y && \
|
||||
apt install -y --no-install-recommends \
|
||||
gcc \
|
||||
g++ && \
|
||||
apt clean
|
||||
|
||||
RUN pip install poetry -i https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
RUN poetry install
|
||||
@@ -14,6 +21,8 @@ VOLUME /app/zhenxun/data /app/zhenxun/data
|
||||
|
||||
VOLUME /app/zhenxun/resources /app/zhenxun/resources
|
||||
|
||||
VOLUME /app/zhenxun/.env.dev /app/zhenxun/.env.dev
|
||||
|
||||
RUN poetry run playwright install --with-deps chromium
|
||||
|
||||
CMD ["poetry", "run", "python", "bot.py"]
|
||||
CMD ["poetry", "run", "python", "bot.py"]
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<div align=center>
|
||||
|
||||
## 绪山真寻Bot
|
||||
## 绪山真寻 Bot
|
||||
|
||||
</div>
|
||||
|
||||
@@ -39,11 +39,12 @@
|
||||
:tada:喜欢真寻,于是真寻就来了!:tada:
|
||||
|
||||
本项目符合 [OneBot](https://github.com/howmanybots/onebot) 标准,可基于以下项目与机器人框架/平台进行交互
|
||||
| 项目地址 | 平台 | 核心作者 | 备注 |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| [LLOneBot](https://github.com/LLOneBot/LLOneBot) | NTQQ | linyuchen | 可用 |
|
||||
| [Napcat](https://github.com/NapNeko/NapCatQQ) | NTQQ | NapNeko | 可用 |
|
||||
| [Lagrange.Core](https://github.com/LagrangeDev/Lagrange.Core) | | LagrangeDev/Linwenxuan04 | 可用
|
||||
|
||||
| 项目地址 | 平台 | 核心作者 | 备注 |
|
||||
| :-----------------------------------------------------------: | :--: | :----------------------: | :--: |
|
||||
| [LLOneBot](https://github.com/LLOneBot/LLOneBot) | NTQQ | linyuchen | 可用 |
|
||||
| [Napcat](https://github.com/NapNeko/NapCatQQ) | NTQQ | NapNeko | 可用 |
|
||||
| [Lagrange.Core](https://github.com/LagrangeDev/Lagrange.Core) | | LagrangeDev/Linwenxuan04 | 可用 |
|
||||
|
||||
</div>
|
||||
|
||||
@@ -57,8 +58,9 @@
|
||||
|
||||
<div align=center>
|
||||
|
||||
<img width="400" height="400" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png"/>
|
||||
<img width="350" height="350" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png"/>
|
||||
<img width="250" height="500" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png"/>
|
||||
<img width="180" height="450" src="https://github.com/HibiKier/zhenxun_bot/blob/dev/docs_image/zhenxun_help.png"/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -66,7 +68,7 @@
|
||||
|
||||
### 1. 体验一下?
|
||||
|
||||
这是一个免费的,版本为dev的zhenxun,你可以通过napcat或拉格朗日等直接连接用于体验与测试
|
||||
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 napcat 或拉格朗日等直接连接用于体验与测试
|
||||
(球球了测试君!)
|
||||
|
||||
```
|
||||
@@ -82,16 +84,13 @@ AccessToken: PUBLIC_ZHENXUN_TEST
|
||||
|
||||
“不要害怕,你的背后还有千千万万的 <strong>伙伴</strong> 啊!”
|
||||
|
||||
| 项目名称 | 主要用途 | 仓库作者 | 备注 |
|
||||
| :-----------------------------------------------------------------------------------------------: | :------: | :-------------------------------------------: | :-------------------------------------------------------------: |
|
||||
| [插件库](https://github.com/zhenxun-org/zhenxun_bot_plugins) | 插件 | [zhenxun-org](https://github.com/zhenxun-org) | 原plugins文件夹插件 |
|
||||
| [插件索引库](https://github.com/zhenxun-org/zhenxun_bot_plugins_index) | 插件 | [zhenxun-org](https://github.com/zhenxun-org) | 扩展插件索引库 |
|
||||
| [WebUi](https://github.com/HibiKier/zhenxun_bot_webui) | 管理 | [hibikier](https://github.com/HibiKier) | 基于真寻WebApi的webui实现 |
|
||||
| [一键安装](https://github.com/zhenxun-org/zhenxun_bot-deploy) | 安装 | [AkashiCoin](https://github.com/AkashiCoin) | 新版本未测试 |
|
||||
| [Docker单机版](https://github.com/Sakuracio/zhenxun_bot_docker) | 安装 | [zhenxun-org](https://github.com/zhenxun-org) | 新版本未测试 |
|
||||
| [Docker全量版](https://shields.io/badge/GITHUB-SinKy--Yan-4476AF?logo=github&style=for-the-badge) | 安装 | [zhenxun-org](https://github.com/zhenxun-org) | 包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等(新版本未测试) |
|
||||
|
||||
PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
|
||||
| 项目名称 | 主要用途 | 仓库作者 | 备注 |
|
||||
| :--------------------------------------------------------------------: | :------: | :-------------------------------------------------: | :---------------------------: |
|
||||
| [插件库](https://github.com/zhenxun-org/zhenxun_bot_plugins) | 插件 | [zhenxun-org](https://github.com/zhenxun-org) | 原 plugins 文件夹插件 |
|
||||
| [插件索引库](https://github.com/zhenxun-org/zhenxun_bot_plugins_index) | 插件 | [zhenxun-org](https://github.com/zhenxun-org) | 扩展插件索引库 |
|
||||
| [一键安装](https://github.com/soloxiaoye2022/zhenxun_bot-deploy) | 安装 | [soloxiaoye2022](https://github.com/soloxiaoye2022) | 第三方 |
|
||||
| [WebUi](https://github.com/HibiKier/zhenxun_bot_webui) | 管理 | [hibikier](https://github.com/HibiKier) | 基于真寻 WebApi 的 webui 实现 |
|
||||
| [安卓 app(WebUi)](https://github.com/YuS1aN/zhenxun_bot_android_ui) | 安装 | [YuS1aN](https://github.com/YuS1aN) | 第三方 |
|
||||
|
||||
<details>
|
||||
<summary> <strong> WebUI </strong>后台示例图 </summary>
|
||||
@@ -113,10 +112,10 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可
|
||||
## ~~来点优点?~~ 可爱难道还不够吗
|
||||
|
||||
- 实现了许多功能,且提供了大量功能管理命令
|
||||
- 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
|
||||
- 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
|
||||
- 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`PluginCdBlock` 等
|
||||
- **..... 更多详细请通过`传送门`查看文档!**
|
||||
- 通过 Config 配置项将所有插件配置统计保存至 config.yaml,利于统一用户修改
|
||||
- 方便增删插件,原生 nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
|
||||
- 提供了 cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`PluginCdBlock` 等
|
||||
- **..... 更多详细请通过[[传送门](https://hibikier.github.io/zhenxun_bot/)]查看文档!**
|
||||
|
||||
## 简单部署
|
||||
|
||||
@@ -135,9 +134,6 @@ poetry install # 安装依赖
|
||||
poetry shell # 进入虚拟环境
|
||||
python bot.py
|
||||
|
||||
# 在Linux系统,你可能还需要运行此命令安装playwright依赖
|
||||
playwright install-deps
|
||||
|
||||
# 首次后会在data目录下生成config.yaml文件
|
||||
# config.yaml用户配置插件
|
||||
```
|
||||
@@ -159,6 +155,8 @@ playwright install-deps
|
||||
'
|
||||
# 此处填写你的数据库地址
|
||||
# 示例: "postgres://user:password@127.0.0.1:5432/database"
|
||||
# 示例: "mysql://user:password@127.0.0.1:5432/database"
|
||||
# 示例: "sqlite:data/db/zhenxun.db" 在data目录下建立db文件夹
|
||||
DB_URL = "" # 数据库地址
|
||||
|
||||
|
||||
@@ -167,7 +165,7 @@ playwright install-deps
|
||||
|
||||
```
|
||||
|
||||
## 功能列表 (旧版列表)
|
||||
<!-- ## 功能列表 (旧版列表)
|
||||
|
||||
<details>
|
||||
<summary>已实现的功能</summary>
|
||||
@@ -176,7 +174,7 @@ playwright install-deps
|
||||
|
||||
- [x] 昵称系统(群与群与私聊分开.)
|
||||
|
||||
- [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
|
||||
- [x] 图灵 AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
|
||||
- [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
|
||||
- [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
|
||||
- [x] 色图(这不是基础功能嘛喂)
|
||||
@@ -185,12 +183,12 @@ playwright install-deps
|
||||
- [x] 鸡汤/语录
|
||||
- [x] 骂我(钉宫语音)
|
||||
- [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
|
||||
- [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要session,可能需要代理,阿里云服务器等ip也许已经被ban了(我无代理访问失败),如果访问太多账号API调用可能被禁止访问api!)
|
||||
- [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置 cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要 session,可能需要代理,阿里云服务器等 ip 也许已经被 ban 了(我无代理访问失败),如果访问太多账号 API 调用可能被禁止访问 api!)
|
||||
- [x] 鲁迅说过
|
||||
- [x] 构造假消息(自定义的分享链接)
|
||||
- [x] 商店/我的金币/购买道具/使用道具
|
||||
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
|
||||
- [x] 我有一个朋友想问问..(借鉴pcrbot插件)
|
||||
- [x] 8 种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
|
||||
- [x] 我有一个朋友想问问..(借鉴 pcrbot 插件)
|
||||
- [x] 原神黄历
|
||||
- [x] 原神今日素材
|
||||
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
|
||||
@@ -200,37 +198,37 @@ playwright install-deps
|
||||
- [x] 原神签到/自动签到
|
||||
- [x] 金币红包
|
||||
- [x] 微博热搜
|
||||
- [x] B站主播/UP/番剧订阅
|
||||
- [x] B 站主播/UP/番剧订阅
|
||||
|
||||
- [x] pil对图片的一些操作
|
||||
- [x] BUFF饰品底价查询(需要session)
|
||||
- [x] pil 对图片的一些操作
|
||||
- [x] BUFF 饰品底价查询(需要 session)
|
||||
- [x] 天气查询
|
||||
- [x] 疫情查询
|
||||
- [x] bt磁力搜索(咳咳,这功能我想dddd)
|
||||
- [x] reimu搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
- [x] bt 磁力搜索(咳咳,这功能我想 dddd)
|
||||
- [x] reimu 搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
- [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
- [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
|
||||
- [x] 搜番
|
||||
- [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
|
||||
- [x] epic免费游戏
|
||||
- [x] p站排行榜
|
||||
- [x] p站搜图
|
||||
- [x] epic 免费游戏
|
||||
- [x] p 站排行榜
|
||||
- [x] p 站搜图
|
||||
- [x] 翻译(日英韩)
|
||||
- [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令)
|
||||
- [x] pix 图库(一个自己的图库,含有增删查改,黑名单等命令)
|
||||
|
||||
- [x] 查看当前群欢迎消息
|
||||
- [x] 查看该群自己的权限
|
||||
- [x] 我的信息(只是为了看看什么时候入群)
|
||||
- [x] 更新信息(如果继续更新的话)
|
||||
- [x] go-cqhttp最新版下载和上传(不需要请删除)
|
||||
- [x] go-cqhttp 最新版下载和上传(不需要请删除)
|
||||
- [x] 撤回
|
||||
- [x] 滴滴滴-(用户对超级用户发送消息)
|
||||
- [x] 金币红包/金币排行
|
||||
- [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
|
||||
- [x] 网易云热评
|
||||
- [x] 念首古诗
|
||||
- [x] 获取b站视频封面
|
||||
- [x] 通过PID获取图片
|
||||
- [x] 获取 b 站视频封面
|
||||
- [x] 通过 PID 获取图片
|
||||
- [x] 功能统计可视化
|
||||
- [x] 词云
|
||||
- [x] 关于
|
||||
@@ -242,12 +240,12 @@ playwright install-deps
|
||||
- [x] 95%的群功能开关
|
||||
- [x] 查看群内被动技能状态
|
||||
- [x] 自定义群欢迎消息(是真寻的不是管家的!)
|
||||
- [x] .ban/.unban(支持设置ban时长)= 黑白名单
|
||||
- [x] .ban/.unban(支持设置 ban 时长)= 黑白名单
|
||||
- [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数
|
||||
- [x] 上传图片/连续上传图片 (上传图片至指定图库)
|
||||
- [x] 移动图片 (同上)
|
||||
- [x] 删除图片 (同上)
|
||||
- [x] 群内B站订阅
|
||||
- [x] 群内 B 站订阅
|
||||
- [x] 词条设置
|
||||
- [x] 休息吧/醒来
|
||||
|
||||
@@ -267,17 +265,17 @@ playwright install-deps
|
||||
- [x] 修改群权限
|
||||
- [x] ban
|
||||
- [x] 更新色图
|
||||
- [x] 更新价格/更加图片(csgo开箱)
|
||||
- [x] 更新价格/更加图片(csgo 开箱)
|
||||
- [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
|
||||
- [x] 更新原神今日素材/更新原神资源信息
|
||||
- [x] PIX相关操作
|
||||
- [x] PIX 相关操作
|
||||
- [x] 检查更新真寻
|
||||
- [x] 重启
|
||||
- [x] 添加/删除/查看群白名单
|
||||
- [x] 功能开关(更多设置)
|
||||
- [x] 功能状态
|
||||
- [x] b了
|
||||
- [x] 执行sql
|
||||
- [x] b 了
|
||||
- [x] 执行 sql
|
||||
- [x] 重载配置
|
||||
- [x] 清理临时数据
|
||||
- [x] 增删群认证
|
||||
@@ -296,9 +294,9 @@ playwright install-deps
|
||||
|
||||
- [x] 群早晚安
|
||||
- [x] 每日开箱重置提醒
|
||||
- [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
|
||||
- [x] b 站转发解析(解析 b 站分享信息,支持 bv,bilibili 链接,b 站手机端转发卡片,cv,b23.tv),且 5 分钟内不解析相同 url
|
||||
- [x] 丢人爬(爬表情包)
|
||||
- [x] epic通知(每日发送epic免费游戏链接)
|
||||
- [x] epic 通知(每日发送 epic 免费游戏链接)
|
||||
- [x] 原神黄历提醒
|
||||
- [x] 复读
|
||||
|
||||
@@ -307,15 +305,15 @@ playwright install-deps
|
||||
- [x] 刷屏禁言检测
|
||||
|
||||
- [x] 功能调用统计
|
||||
- [x] 检测恶意触发命令(将被最高权限ban掉30分钟,只有最高权限(9级)可以进行unban)
|
||||
- [x] 检测恶意触发命令(将被最高权限 ban 掉 30 分钟,只有最高权限(9 级)可以进行 unban)
|
||||
- [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
|
||||
- [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能))
|
||||
- [x] 群聊时间检测(当群聊最后一人发言时间大于当前 36 小时后将关闭该群所有通知(即被动技能))
|
||||
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
|
||||
- [x] 群权限系统
|
||||
- [x] 定时更新权限
|
||||
- [x] 自动配置重载
|
||||
|
||||
</details>
|
||||
</details> -->
|
||||
|
||||
## [爱发电](https://afdian.net/@HibiKier)
|
||||
|
||||
@@ -325,7 +323,7 @@ playwright install-deps
|
||||
|
||||
### 感谢名单
|
||||
|
||||
(可以告诉我你的 **github** 地址,我偷偷换掉0v|)
|
||||
(可以告诉我你的 **github** 地址,我偷偷换掉 0v|)
|
||||
|
||||
[shenqi](https://afdian.net/u/fa923a8cfe3d11eba61752540025c377)
|
||||
[A_Kyuu](https://afdian.net/u/b83954fc2c1211eba9eb52540025c377)
|
||||
@@ -343,13 +341,13 @@ playwright install-deps
|
||||
[本喵无敌哒](https://afdian.net/u/dffaa9005bc911ebb69b52540025c377)
|
||||
[椎名冬羽](https://afdian.net/u/ca1ebd64395e11ed81b452540025c377)
|
||||
[kaito](https://afdian.net/u/a055e20a498811eab1f052540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[笑柒 XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[请问一份爱多少钱](https://afdian.net/u/f57ef6602dbd11ed977f52540025c377)
|
||||
[咸鱼鱼鱼鱼](https://afdian.net/u/8e39b9a400e011ed9f4a52540025c377)
|
||||
[Kafka](https://afdian.net/u/41d66798ef6911ecbc5952540025c377)
|
||||
[墨然](https://afdian.net/u/8aa5874a644d11eb8a6752540025c377)
|
||||
[爱发电用户\_T9e4](https://afdian.net/u/2ad1bb82f3a711eca22852540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[笑柒 XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[noahzark](https://afdian.net/a/noahzark)
|
||||
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
|
||||
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
|
||||
@@ -374,11 +372,11 @@ playwright install-deps
|
||||
|
||||
</details>
|
||||
|
||||
## 更新
|
||||
<!-- ## 更新
|
||||
|
||||
### 2024/8/11
|
||||
|
||||
- 更新dev
|
||||
- 更新 dev -->
|
||||
|
||||
<!-- ### 2024/1/25
|
||||
|
||||
@@ -820,20 +818,20 @@ playwright install-deps
|
||||
|
||||
## Todo
|
||||
|
||||
- [x] web管理
|
||||
- [x] web 管理
|
||||
|
||||
## 感谢
|
||||
|
||||
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
|
||||
[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台.
|
||||
[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架
|
||||
[Angel-Hair / XUN_Bot](https://github.com/Angel-Hair/XUN_Bot) :一个基于NoneBot和酷Q的功能性QQ机器人
|
||||
[pcrbot / cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins) :hoshino插件合集
|
||||
[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp 的 golang 实现,轻量、原生跨平台.
|
||||
[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台 Python 异步机器人框架
|
||||
[Angel-Hair / XUN_Bot](https://github.com/Angel-Hair/XUN_Bot) :一个基于 NoneBot 和酷 Q 的功能性 QQ 机器人
|
||||
[pcrbot / cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins) :hoshino 插件合集
|
||||
[MeetWq /nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) :A simple withdraw plugin for Nonebot2
|
||||
[maxesisn / nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) :适用于nonebot2的点歌插件
|
||||
[maxesisn / nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) :适用于 nonebot2 的点歌插件
|
||||
[nonepkg / nonebot-plugin-manager](https://github.com/nonepkg/nonebot-plugin-manager) :Nonebot Plugin Manager base on import hook
|
||||
[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件
|
||||
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
|
||||
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
|
||||
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
|
||||
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于HoshinoBot/NoneBot2的原神UID查询插件
|
||||
[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神 bot,这是一个基于 nonebot 和 HoshinoBot 的原神娱乐及信息查询插件
|
||||
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于 nonebot2 机器人框架的鲁迅说插件
|
||||
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何 bot 的词库
|
||||
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于 nonebot2 的 qq 机器人
|
||||
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于 HoshinoBot/NoneBot2 的原神 UID 查询插件
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__: v0.2.2
|
||||
__version__: v0.2.3
|
||||
|
||||
|
After Width: | Height: | Size: 917 KiB |
@@ -25,6 +25,7 @@ ujson = "^5.9.0"
|
||||
nonebot-adapter-kaiheila = "^0.3.0"
|
||||
nb-cli = "^1.3.0"
|
||||
nonebot2 = "^2.1.3"
|
||||
pydantic = "1.10.18"
|
||||
nonebot-adapter-discord = "^0.1.3"
|
||||
nonebot-adapter-dodo = "^0.1.4"
|
||||
pillow = "9.5"
|
||||
@@ -46,7 +47,10 @@ dateparser = "^1.2.0"
|
||||
bilireq = "0.2.3post0"
|
||||
python-jose = { extras = ["cryptography"], version = "^3.3.0" }
|
||||
python-multipart = "^0.0.9"
|
||||
nonebot-plugin-alconna = "^0.51.1"
|
||||
nonebot-plugin-alconna = "0.51.1"
|
||||
arclet-alconna = "1.8.23"
|
||||
aiocache = "^0.12.2"
|
||||
py-cpuinfo = "^9.0.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
nonebug = "^0.3.2"
|
||||
@@ -133,6 +137,9 @@ typeCheckingMode = "standard"
|
||||
reportShadowedImports = false
|
||||
disableBytesTypePromotions = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 456 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ECharts Example</title>
|
||||
@@ -7,14 +8,15 @@
|
||||
<script src="https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js"></script>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 图表容器 -->
|
||||
<div id="main" style="width: 1000px;height:500px;"></div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.getElementById('main'));
|
||||
|
||||
|
||||
// 指定图表的配置项和数据
|
||||
var option = {
|
||||
xAxis: {
|
||||
@@ -23,28 +25,35 @@
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: {{data.category_data|tojson}}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: {{data.data|tojson}},
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
// 为每个柱子设置随机颜色
|
||||
color: function () {
|
||||
// 生成一个随机颜色
|
||||
return 'rgb(' +
|
||||
Math.round(Math.random() * 255) + ',' +
|
||||
Math.round(Math.random() * 255) + ',' +
|
||||
Math.round(Math.random() * 255) + ')';
|
||||
}
|
||||
}
|
||||
data: {
|
||||
{
|
||||
data.category_data | tojson
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
},
|
||||
series: [{
|
||||
data: {
|
||||
{
|
||||
data.data | tojson
|
||||
}
|
||||
},
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
// 为每个柱子设置随机颜色
|
||||
color: function () {
|
||||
// 生成一个随机颜色
|
||||
return 'rgb(' +
|
||||
Math.round(Math.random() * 255) + ',' +
|
||||
Math.round(Math.random() * 255) + ',' +
|
||||
Math.round(Math.random() * 255) + ')';
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: fzrzFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("../../font/fzrzExtraBold.ttf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
height: 750px;
|
||||
width: 395px;
|
||||
background-color: antiquewhite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-image {
|
||||
height: 215px;
|
||||
width: 395px;
|
||||
}
|
||||
|
||||
.abs-image {
|
||||
height: 426px;
|
||||
width: 93px;
|
||||
position: absolute;
|
||||
right: 39px;
|
||||
top: 240px;
|
||||
}
|
||||
|
||||
.bot-text {
|
||||
height: 55px;
|
||||
width: 335px;
|
||||
position: relative;
|
||||
padding: 0 30px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 448px;
|
||||
width: 335px;
|
||||
padding: 0 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: #EB869D;
|
||||
height: 32px;
|
||||
font-family: 'fzrzFont';
|
||||
color: white;
|
||||
width: max-content;
|
||||
border-radius: 20px;
|
||||
padding: 0 10px;
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.title-n {
|
||||
height: 23px;
|
||||
width: 23px;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
left: 36px;
|
||||
top: 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-a {
|
||||
background-color: #ED859E;
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.network {
|
||||
display: flex;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.network-item {
|
||||
font-family: 'fzrzFont';
|
||||
color: #EC859F;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.network-status {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.network-text {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.network-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.data-status-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.data-status {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
/* background-color:blueviolet; */
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.data-status-item {
|
||||
display: flex;
|
||||
font-family: 'fzrzFont';
|
||||
font-size: 15px;
|
||||
line-height: 18.5px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.process {
|
||||
height: 28px;
|
||||
width: 105px;
|
||||
border: #ED859E 4px solid;
|
||||
border-radius: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.process-bar {
|
||||
height: 28px;
|
||||
width: 30px;
|
||||
background-color: #EB869D;
|
||||
border-top-right-radius: 30px;
|
||||
border-bottom-right-radius: 30px;
|
||||
}
|
||||
|
||||
.data-status-item-text {
|
||||
margin-left: 10px;
|
||||
color: #EC839D;
|
||||
}
|
||||
|
||||
.line {
|
||||
background-color: #EC859F;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-family: 'fzrzFont';
|
||||
color: #EC859F;
|
||||
height: 185px;
|
||||
}
|
||||
|
||||
.status-text-title {
|
||||
/* height: 25%; */
|
||||
display: flex;
|
||||
/* justify-content: center; */
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-family: 'fzrzFont';
|
||||
color: #ecbac7;
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 1px;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>test</title>
|
||||
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<img class="top-image" src="res/img/top.jpg"></img>
|
||||
<img class="abs-image" src="res/img/bk.png"></img>
|
||||
<div class="bot-text">
|
||||
<div class="title">
|
||||
<div class="title-n">
|
||||
<div class="title-a"></div>
|
||||
</div>
|
||||
<span style="margin-left: 22px;">{{data.nickname}}自检</span>
|
||||
</div>
|
||||
<div class="network">
|
||||
<div class="network-item">BaiDu<span class="network-status"
|
||||
style="background-color: {{data.baidu}}; border: 1px solid {{data.baidu}}"></span></div>
|
||||
<div class="network-item">Google<span class="network-status"
|
||||
style="background-color: {{data.google}}; border: 1px solid {{data.google}}"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="data-status">
|
||||
<div class="data-status-item">
|
||||
<div class="process">
|
||||
<div class="process-bar" style="width: {{data.cpu_process}}%;"></div>
|
||||
</div>
|
||||
<div class="data-status-item-text">
|
||||
<div>CPU</div>
|
||||
<!-- <div style="font-size: 13px;">0.8% - 3.5Ghz [14core]</div> -->
|
||||
<div style="font-size: 13px;">{{data.cpu_info}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-status-item">
|
||||
<div class="process">
|
||||
<div class="process-bar" style="width: {{data.ram_process}}%;"></div>
|
||||
</div>
|
||||
<div class="data-status-item-text">
|
||||
<div>RAM</div>
|
||||
<div>{{data.ram_info}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-status-item">
|
||||
<div class="process">
|
||||
<div class="process-bar" style="width: {{data.swap_process}}%;"></div>
|
||||
</div>
|
||||
<div class="data-status-item-text">
|
||||
<div>SWAP</div>
|
||||
<div>{{data.swap_info}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-status-item">
|
||||
<div class="process">
|
||||
<div class="process-bar" style="width: {{data.disk_process}}%;"></div>
|
||||
</div>
|
||||
<div class="data-status-item-text">
|
||||
<div>DISK</div>
|
||||
<div>{{data.disk_info}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="status-text">
|
||||
<p class="status-text-title">CPU <span
|
||||
style="font-size: 12px;line-height: 18.5px;">{{data.brand_raw}}</span></p>
|
||||
<p class="status-text-title">SYSTEM <span
|
||||
style="font-size: 13px;line-height: 18.5px;">{{data.system}}</span></p>
|
||||
<p class="status-text-title">VERSION <span>{{data.version}}</span></p>
|
||||
<p class="status-text-title">PLUGINS <span>{{data.plugin_count}} loaded</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip">Create By Zhenxun</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript" src="main.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 209 KiB |
@@ -0,0 +1,100 @@
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: fzrzFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("../../font/fzrzExtraBold.ttf");
|
||||
}
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
width: 1400px;
|
||||
position: relative;
|
||||
background-image: url('res/img/bk.jpg');
|
||||
background-size: cover;
|
||||
font-family: 'cr105Font';
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
font-size: 60px;
|
||||
font-family: 'fzrzFont';
|
||||
/* margin-left: 40px; */
|
||||
/* color: #F67186; */
|
||||
background: linear-gradient(to right, #F67186, #F7889C);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.main {
|
||||
background-image: url('res/img/main.png');
|
||||
background-size: 100% 100%;
|
||||
/* background-size: cover; */
|
||||
height: 100%;
|
||||
width: 1370px;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
/* box-shadow: 5px 5px 10px 0 rgba(0,0,0,0.5); */
|
||||
}
|
||||
|
||||
.items-border {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.items {
|
||||
border: #F67186 2px solid;
|
||||
border-radius: 20px;
|
||||
width: 675px;
|
||||
padding: 30px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
background-image: url('res/img/title.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
height: 55px;
|
||||
width: 350px;
|
||||
font-family: 'fzrzFont';
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: 35px;
|
||||
border-radius: 16px;
|
||||
letter-spacing:4px;
|
||||
}
|
||||
|
||||
.usage-title {
|
||||
font-size: 30px;
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
}
|
||||
|
||||
.item-des {
|
||||
color: #F78094;
|
||||
border-radius: 20px;
|
||||
font-family: 'fzrzFont';
|
||||
font-size: 30px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.item-usage {
|
||||
color: #F78094;
|
||||
border-radius: 20px;
|
||||
font-family: 'fzrzFont';
|
||||
font-size: 20px;
|
||||
border: #F67186 5px dotted;
|
||||
padding: 60px 10px 10px 10px;
|
||||
position: relative;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>test</title>
|
||||
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="main">
|
||||
<div class="title">
|
||||
{{data.nickname}}的{{data.help_name}}帮助
|
||||
</div>
|
||||
<div class="items-border">
|
||||
{% for plugin in data['plugin_list'] %}
|
||||
<div class="items">
|
||||
<div class="item-title">
|
||||
{{plugin.name}}
|
||||
</div>
|
||||
<div class="item-des">
|
||||
简介: {{plugin.description}}
|
||||
</div>
|
||||
<div class="item-usage">
|
||||
<p class="usage-title">用法:</p>
|
||||
{{plugin.usage}}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- <div class="items">
|
||||
<div class="item-title">
|
||||
刷屏禁言
|
||||
</div>
|
||||
<div class="item-des">
|
||||
简介: 刷屏禁言相关操作
|
||||
</div>
|
||||
<div class="item-usage">
|
||||
用法:<br>
|
||||
指令:<br>
|
||||
设置刷屏: 查看当前设置<br>
|
||||
-c [count]: 检测最大次数<br>
|
||||
-t [time]: 规定时间内<br>
|
||||
-d [duration]: 禁言时长<br>
|
||||
示例:<br>
|
||||
设置刷屏 -c 10: 设置最大次数为10<br>
|
||||
设置刷屏 -t 100 -d 20: 设置规定时间和禁言时长<br>
|
||||
设置刷屏 -d 10: 设置禁言时长为10<br>
|
||||
* 即 X 秒内发送同样消息 N 次,禁言 M 分钟 *
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript" src="main.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 706 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,235 @@
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: fzrzFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("../../font/HYWenHei-85W.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: systFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("../../font/syst.otf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: syhtFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("../../font/syht.otf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
width: 1754px;
|
||||
height: 1240px;
|
||||
position: relative;
|
||||
background-size: cover;
|
||||
font-family: 'fzrzFont';
|
||||
padding: 20px;
|
||||
background-color: #E08A9F;
|
||||
border-radius: 50px;
|
||||
padding: 40px;
|
||||
color: #E08A9F;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.main{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
background-color: white;
|
||||
height: 1123px;
|
||||
width: 682px;
|
||||
border-radius: 50px;
|
||||
padding: 56px 83px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.weather-img {
|
||||
position: absolute;
|
||||
height: 60px;
|
||||
left: 325px;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.top-date {
|
||||
display: flex;
|
||||
position: relative;
|
||||
font-family: "systFont";
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
margin-top: 65px;
|
||||
}
|
||||
|
||||
.user-info-ava {
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.ava-img {
|
||||
height: 250px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
border: 2px solid #E0899E;
|
||||
}
|
||||
|
||||
.user-info-data {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
position: absolute;
|
||||
width: 140px;
|
||||
left: 50%;
|
||||
bottom: -27px;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #E0899E;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-des {
|
||||
margin-top: 60px;
|
||||
font-size: 35px;
|
||||
position: relative;
|
||||
height: 710px;
|
||||
}
|
||||
|
||||
.des-img {
|
||||
height: 430px;
|
||||
position: absolute;
|
||||
bottom: -28px;
|
||||
left: 85px;
|
||||
}
|
||||
|
||||
.tag-img-content {
|
||||
height: 120px;
|
||||
width: 75px;
|
||||
background-color: #E8BB88;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 120px;
|
||||
}
|
||||
|
||||
.tag-img-test {
|
||||
border-left: 37px solid transparent;
|
||||
border-right: 38px solid transparent;
|
||||
border-bottom: 54px solid white;
|
||||
width: 0;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
height: 496px;
|
||||
top: 175px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
writing-mode: vertical-rl; /* 从右向左垂直排列 */
|
||||
color: #754E43;
|
||||
height: 190px;
|
||||
width: 65px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 30px;
|
||||
border-top-left-radius: 30px;
|
||||
border-bottom-left-radius: 30px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.main-img {
|
||||
position: absolute;
|
||||
height: 200px;
|
||||
right: 150px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.sign-data {
|
||||
margin-top: 105px;
|
||||
}
|
||||
|
||||
.base-title {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.sign-level {
|
||||
border: 2px solid #E08A9F;
|
||||
padding: 7px 3px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.line {
|
||||
border: 1px solid #E08A9F;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.text-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
||||
.test-icon {
|
||||
height: 50px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.test-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.test-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.text-chart {
|
||||
margin-top: 20px;
|
||||
background-color: #E0899E;
|
||||
color: white;
|
||||
padding: 30px;
|
||||
border-radius: 30px;
|
||||
height: 560px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 490px;
|
||||
width: 622px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.select {
|
||||
background-color: #E0899E;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.uname {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1; /* 显示的最大行数 */
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden; /* 隐藏溢出内容 */
|
||||
text-overflow: ellipsis; /* 溢出部分显示为省略号 */
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js"></script>
|
||||
<title>test</title>
|
||||
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
<div class="top-date">{{data.date}}......
|
||||
<img src="./res/img/{{data.weather}}.png" alt="sun" class="weather-img" />
|
||||
<span style="margin-left: 180px;">24℃</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-info-ava">
|
||||
<img src="{{data.ava_url}}" alt="test" class="ava-img" />
|
||||
<div class="nickname">{{data.title}}</div>
|
||||
</div>
|
||||
<div class="user-info-data">
|
||||
<div class="uname">
|
||||
<span style="font-size: 35px;">姓名:</span>
|
||||
<span>{{data.nickname}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">种族:</span>
|
||||
<span>{{data.race}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">性别:</span>
|
||||
<span>{{data.sex}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">职业:</span>
|
||||
<span>{{data.occ}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">UID:</span>
|
||||
<span>{{data.uid}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-des">
|
||||
<p>个人简介:</p>
|
||||
<div style="line-height: 60px;">
|
||||
{{data.description}}
|
||||
</div>
|
||||
<img src="./res/img/1.png" class="des-img" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 50px;"></div>
|
||||
<div class="main-content">
|
||||
<div class="menu">
|
||||
<div class="menu-item" style="background-color: #E8BC89;">我的信息</div>
|
||||
<div class="menu-item" style="background-color: #EDCCC3;">我的道具</div>
|
||||
<div class="menu-item" style="background-color: #F0DAC6;">我的卡组</div>
|
||||
<div class="menu-item" style="background-color: #F7E5E4;">我的设置</div>
|
||||
</div>
|
||||
<div class="tag-img">
|
||||
<div class="tag-img-content">
|
||||
<div class="tag-img-test"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img src="./res/img/2.png" class="main-img" />
|
||||
<div class="sign-data">
|
||||
<p class="base-title">好感度等级: {{data.sign_level}}级</p>
|
||||
<div>
|
||||
<span class="sign-level {{data.select_index[0]}}">路人</span>
|
||||
<span class="sign-level {{data.select_index[1]}}">陌生</span>
|
||||
<span class="sign-level {{data.select_index[2]}}">初识</span>
|
||||
<span class="sign-level {{data.select_index[3]}}">普通</span>
|
||||
<span class="sign-level {{data.select_index[4]}}">熟悉</span>
|
||||
<span class="sign-level {{data.select_index[5]}}">信赖</span>
|
||||
<span class="sign-level {{data.select_index[6]}}">相知</span>
|
||||
<span class="sign-level {{data.select_index[7]}}">厚谊</span>
|
||||
<span class="sign-level {{data.select_index[8]}}">亲密</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="test-content" style="margin-top: 30px">
|
||||
<img src="./res/img/level.png" class="test-icon" />
|
||||
权限等级: {{data.level}}
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-item">
|
||||
<div class="test-content" style="width: 50%;">
|
||||
<img src="./res/img/gold.png" class="test-icon" />
|
||||
金币数量: {{data.gold}}
|
||||
</div>
|
||||
<div class="test-content">
|
||||
<img src="./res/img/prop.png" class="test-icon" />
|
||||
道具数量: {{data.prop}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-item">
|
||||
<div class="test-content" style="width: 50%;">
|
||||
<img src="./res/img/call.png" class="test-icon" />
|
||||
调用次数: {{data.call}}
|
||||
</div>
|
||||
<div class="test-content">
|
||||
<img src="./res/img/say.png" class="test-icon" />
|
||||
发言统计: {{data.say}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="line"></div> -->
|
||||
<div class="text-chart">
|
||||
<div class="test-title">
|
||||
<img src="./res/img/xian.png" class="test-icon" />
|
||||
发言趋势图
|
||||
</div>
|
||||
<div id="chart" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.getElementById('chart'));
|
||||
// 指定图表的配置项和数据
|
||||
var option = {
|
||||
backgroundColor: "#E0899E",
|
||||
animation: false,
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: {{data.chart_date|tojson}},
|
||||
axisLabel: {
|
||||
color: "#fff",
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
axisLabel: {
|
||||
color: "#fff",
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
series: [{
|
||||
data: {{data.count_list|tojson}},
|
||||
type: "line",
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
// 设置线条颜色
|
||||
color: '#fff',
|
||||
// 可选:设置线条宽度
|
||||
width: 2
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
textStyle: {
|
||||
fontSize: 20,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ],
|
||||
};
|
||||
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
</script>
|
||||
</body>
|
||||
<script type="text/javascript" src="main.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
// var chartDom = document.getElementById("chart")
|
||||
// var myChart = echarts.init(chartDom)
|
||||
// var option
|
||||
|
||||
// option = {
|
||||
// backgroundColor: "#E0899E",
|
||||
// animation: false,
|
||||
// xAxis: {
|
||||
// type: "category",
|
||||
// data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
// axisLabel: {
|
||||
// color: "#fff",
|
||||
// fontSize: 20,
|
||||
// },
|
||||
// },
|
||||
// yAxis: {
|
||||
// type: "value",
|
||||
// axisLabel: {
|
||||
// color: "#fff",
|
||||
// fontSize: 20,
|
||||
// },
|
||||
// },
|
||||
// series: [
|
||||
// {
|
||||
// data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
// type: "line",
|
||||
// smooth: true,
|
||||
// },
|
||||
// ],
|
||||
// }
|
||||
|
||||
// option && myChart.setOption(option)
|
||||
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@latest/dist/echarts.min.js"></script>
|
||||
<title>test</title>
|
||||
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
<div class="top-date">2024/08/14......
|
||||
<img src="./res/img/sun.png" alt="sun" class="weather-img" />
|
||||
<span style="margin-left: 180px;">24℃</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-info-ava">
|
||||
<img src="./res/img/test.jpg" alt="test" class="ava-img" />
|
||||
<div class="nickname">勇 者</div>
|
||||
</div>
|
||||
<div class="user-info-data">
|
||||
<div>
|
||||
<span style="font-size: 35px;">姓名:</span>
|
||||
<span>副驾驶啦啦啦</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">种族:</span>
|
||||
<span>人类</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">性别:</span>
|
||||
<span>男</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">生日:</span>
|
||||
<span>2021/10/01</span>
|
||||
</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<span style="font-size: 35px;">UID:</span>
|
||||
<span>0021</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-des">
|
||||
<p>个人简介:</p>
|
||||
<div style="line-height: 60px;">
|
||||
这是人类勇者的传奇在这片古老而神秘的土地上,
|
||||
每当黑暗的力量威胁到和平与正义之时总会有一位英雄挺身而出。
|
||||
他们不仅拥有超凡的力量更重要的是更重要的是更重要的是
|
||||
</div>
|
||||
<img src="./res/img/1.png" class="des-img" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 50px;"></div>
|
||||
<div class="main-content">
|
||||
<div class="menu">
|
||||
<div class="menu-item" style="background-color: #E8BC89;">信息</div>
|
||||
<div class="menu-item" style="background-color: #EDCCC3;">仓库</div>
|
||||
<div class="menu-item" style="background-color: #F0DAC6;">卡组</div>
|
||||
<div class="menu-item" style="background-color: #F7E5E4;">设置</div>
|
||||
</div>
|
||||
<div class="tag-img">
|
||||
<div class="tag-img-content">
|
||||
<div class="tag-img-test"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img src="./res/img/2.png" class="main-img" />
|
||||
<div class="sign-data">
|
||||
<p class="base-title">好感度等级: 5级</p>
|
||||
<div>
|
||||
<span class="sign-level">路人</span>
|
||||
<span class="sign-level">陌生</span>
|
||||
<span class="sign-level">初识</span>
|
||||
<span class="sign-level select">普通</span>
|
||||
<span class="sign-level">熟悉</span>
|
||||
<span class="sign-level">信赖</span>
|
||||
<span class="sign-level">相知</span>
|
||||
<span class="sign-level">厚谊</span>
|
||||
<span class="sign-level">亲密</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="test-content" style="margin-top: 30px">
|
||||
<img src="./res/img/level.png" class="test-icon" />
|
||||
权限等级: 9
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-item">
|
||||
<div class="test-content" style="width: 50%;">
|
||||
<img src="./res/img/gold.png" class="test-icon" />
|
||||
金币数量: 999
|
||||
</div>
|
||||
<div class="test-content">
|
||||
<img src="./res/img/prop.png" class="test-icon" />
|
||||
道具数量: 933
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-item">
|
||||
<div class="test-content" style="width: 50%;">
|
||||
<img src="./res/img/call.png" class="test-icon" />
|
||||
调用次数: 6443
|
||||
</div>
|
||||
<div class="test-content">
|
||||
<img src="./res/img/say.png" class="test-icon" />
|
||||
发言统计: 233
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="line"></div> -->
|
||||
<div class="text-chart">
|
||||
<div class="test-title">
|
||||
<img src="./res/img/xian.png" class="test-icon" />
|
||||
发言趋势图
|
||||
</div>
|
||||
<div id="chart" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript" src="main.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 777 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 483 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -34,11 +34,6 @@
|
||||
src: url("./res/font/jcyt.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: yshsFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("/resources/font/YSHaoShenTi-2.ttf");
|
||||
}
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
@@ -79,10 +74,17 @@ body {
|
||||
margin-top: 43px;
|
||||
margin-left: 30px;
|
||||
font-size: 47px;
|
||||
height: 57px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex
|
||||
/* align-items: center;
|
||||
justify-content: center; */
|
||||
/* display: flex; */
|
||||
width: 275px;
|
||||
height: 67px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.uid {
|
||||
@@ -122,6 +124,7 @@ body {
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
left: 130px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.sign-content{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
@@ -10,12 +10,13 @@
|
||||
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="top-head">
|
||||
<div style="display: flex;">
|
||||
<div class="avatar">
|
||||
<img class="avatar-img" src="{{data.ava}}" alt="">
|
||||
<img class="avatar-img" src="{{data.ava_url}}" alt="">
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<p class="nickname" style="font-size: {{ data.font_size }}px;">{{data.name}}</p>
|
||||
@@ -40,20 +41,21 @@
|
||||
<p class="gift">{{data.impression}}</p>
|
||||
</div>
|
||||
<div class="abs-text" style="top: 83px;">
|
||||
<p class="gift">{{data.gold}}</p></div>
|
||||
<div class="abs-text" style="top: 123px;">
|
||||
<p class="gift">{{data.gold}}</p>
|
||||
</div>
|
||||
<div class="abs-text" style="top: 123px;">
|
||||
<p class="gift">{{data.gift}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="bottom-foot">
|
||||
<p class="cur-text">当前好感度: {{data.cur_impression}}</p>
|
||||
<div class="heart-list">
|
||||
{% for i in data.heart2 %}
|
||||
<img class="heart-img" src="res/img/h2.png" alt=""/>
|
||||
<img class="heart-img" src="res/img/h2.png" alt="" />
|
||||
{% endfor %}
|
||||
{% for i in data.heart1 %}
|
||||
<img class="heart-img" src="res/img/h1.png" alt=""/>
|
||||
<img class="heart-img" src="res/img/h1.png" alt="" />
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="bot-text">
|
||||
@@ -61,11 +63,11 @@
|
||||
<p style="position: absolute; top: 58px">{{data.attitude}}</p>
|
||||
<p style="position: absolute; top: 111px"">距离升级还差{{data.interpolation}}好感度</p>
|
||||
</div>
|
||||
<div class="progress-border">
|
||||
<div class="progress-bar" style="width: {{ data.process }}%;"></div>
|
||||
<div class=" progress-border">
|
||||
<div class="progress-bar" style="width: {{ data.process }}%;"></div>
|
||||
</div>
|
||||
<img class="weather-img" src="res/img/weather/0.png" alt="">
|
||||
<p class="wd" >28℃</p>
|
||||
<p class="wd">28℃</p>
|
||||
<img class="mbl-img" src="res/img/2.png" alt="">
|
||||
<div class="date">
|
||||
{{data.date}}
|
||||
@@ -75,4 +77,5 @@
|
||||
</body>
|
||||
<script type="text/javascript" src="main.js">
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -3,19 +3,19 @@
|
||||
@font-face {
|
||||
font-family: fzrzFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("./res/font/fzrzExtraBold.ttf");
|
||||
src: url("../../font/fzrzExtraBold.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: syhtFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("./res/font/syht.otf");
|
||||
src: url("../../font/syht.otf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: systFont;
|
||||
/* 导入的字体文件 */
|
||||
src: url("./res/font/syst.otf");
|
||||
src: url("../../font/syst.otf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||