Compare commits
177 Commits
Author | SHA1 | Date | |
---|---|---|---|
3bd40e7271 | |||
8ace3e68f4 | |||
f69feb1def | |||
9fb423d5e0 | |||
e9df67a661 | |||
b6871ea13a | |||
cb84a7d0d9 | |||
25f7540f86 | |||
c29a3fd6d4 | |||
ab48396db9 | |||
51982b63c3 | |||
2b537d27ec | |||
16930e96aa | |||
d63ba4943a | |||
5d22f20ce3 | |||
2451849fd6 | |||
61680d9e87 | |||
850dd75822 | |||
6ba983fae3 | |||
ca34f9c2a1 | |||
0fb5b84392 | |||
39a9c39924 | |||
13692228c6 | |||
f22f4d229d | |||
97dbf42a4d | |||
041a219151 | |||
c137f2f916 | |||
6ef3b09ec9 | |||
263b78e995 | |||
0d87848a7e | |||
44ad0832ba | |||
93ced26e07 | |||
363daf6251 | |||
a6b1d1c9e0 | |||
576d8c23b3 | |||
c232c6e5f6 | |||
5d6ae52157 | |||
01e6256ed4 | |||
dbc114a529 | |||
4d77af8f0c | |||
c36a925bb5 | |||
605dd035d4 | |||
7526ae13d7 | |||
0eb41f70d2 | |||
a4c7ee738c | |||
8d4602c40d | |||
bb20d9623d | |||
73cc28d1cf | |||
ae54cd923c | |||
9d3c9a7d70 | |||
92a4274be7 | |||
4800b3f46c | |||
cce593b2f4 | |||
c491642713 | |||
5b4dd638a4 | |||
8440952167 | |||
4e4227e204 | |||
effb01d43c | |||
28d730a2ca | |||
0bd135a5c9 | |||
5522391942 | |||
7576355e95 | |||
c9e518f2ed | |||
593cf2407b | |||
bdb4c76d70 | |||
7cce805d39 | |||
b9d3ecc15d | |||
ec5eae08f7 | |||
c1edf31577 | |||
5262c04e46 | |||
8b01943d14 | |||
35823be13e | |||
4162ea33ff | |||
1787ef4db7 | |||
38b13611c9 | |||
52fa143e75 | |||
89047a0c8a | |||
9fbded7d6a | |||
c657781599 | |||
ecbe1ff79e | |||
d6811ab9b3 | |||
d45170db3e | |||
6e63768c71 | |||
1424bc2cf6 | |||
051fe3d15d | |||
fcae485071 | |||
2e9a7fdf94 | |||
570d7e18a4 | |||
8ac53970a3 | |||
dd3e108e10 | |||
c29bd81ffb | |||
a1173e4d84 | |||
d877e30a05 | |||
33ad54090d | |||
83ee6cfdbd | |||
f48971a0c4 | |||
a4b71aa73c | |||
4b7df662e8 | |||
4cd7b6718b | |||
ac2a94dda0 | |||
de7e65b32a | |||
1b283261c3 | |||
39cbfc1baa | |||
e563f18d31 | |||
1d03b3f28f | |||
ae0025a203 | |||
b5bd7acb7f | |||
657e7e52ac | |||
d6341c88cd | |||
bdb1191f9e | |||
3b29b67c0b | |||
cc43e53c4b | |||
a25c900d49 | |||
206651da94 | |||
be28116a98 | |||
4cdf29557c | |||
62928e47eb | |||
9b50b719d9 | |||
def60bf298 | |||
6496b6e463 | |||
6ce4c972a0 | |||
70e3c9968a | |||
074882f092 | |||
c2b3018908 | |||
96c85d9dca | |||
e15aafd781 | |||
9e17b84a5d | |||
c66d470166 | |||
4deb7d11a1 | |||
6f069f83d4 | |||
fa53df1e8a | |||
ba17f9d159 | |||
b558b51601 | |||
3ea0acd48b | |||
c171873fa6 | |||
1ccf94883a | |||
b26f8e0d24 | |||
5bc2725d1b | |||
8e06244311 | |||
0f35613e50 | |||
4cfad0b5ca | |||
c2593e71c0 | |||
bb331232ca | |||
96e8293bf4 | |||
e13464cb7c | |||
c5f8fbe86d | |||
8667706377 | |||
86e47ab226 | |||
10c383d66a | |||
4c65a308d6 | |||
246e43317f | |||
974b97b744 | |||
c914ddc0ee | |||
6509b293db | |||
a72eeb4c3f | |||
309397b72c | |||
077658c68d | |||
322ad19889 | |||
ab9d3d3d3e | |||
06a109d2b5 | |||
351743068a | |||
4e6532ff0d | |||
eaf57f2c33 | |||
7abdac7c9c | |||
002df66878 | |||
251bfaf410 | |||
24722447da | |||
90e7a90bcf | |||
6d3d3fc52c | |||
e843d790b1 | |||
c90ac1d21a | |||
041ceb81d8 | |||
c6f2a29320 | |||
0532d7592e | |||
f9fe1922d4 | |||
88b5b55062 | |||
afe501a06d |
53
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
name: 问题反馈
|
||||
about: 反馈你在使用轻雪中遇到的问题
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# 问题反馈
|
||||
|
||||
## **请确保**
|
||||
|
||||
- 已认真阅读[文档]("https://bot.liteyuki.icu"),该问题不是文档提及的或你自己操作不当造成的
|
||||
- 你的问题是在最新版本的代码上测试的
|
||||
- 请勿重复提交相同或类似的issue
|
||||
|
||||
|
||||
## **描述问题**
|
||||
|
||||
请在此简单描述问题
|
||||
|
||||
|
||||
|
||||
## **如何复现**
|
||||
|
||||
请阐述一下如何重现这个问题
|
||||
### 预期
|
||||
|
||||
描述你期望发生的事情
|
||||
|
||||
### 实际
|
||||
|
||||
描述实际发生的事情
|
||||
|
||||
|
||||
|
||||
## **日志或截图**
|
||||
```
|
||||
日志内容
|
||||
```
|
||||
|
||||
|
||||
## **设备信息**
|
||||
- **系统**: [例如 Ubuntu 22.04]
|
||||
- **CPU**: [例如 Intel i7-7700K]
|
||||
- **内存**: [例如 16GB]
|
||||
- **Python**: [例如CPython 3.10.7]
|
||||
|
||||
|
||||
**补充内容**
|
||||
|
||||
可选,推荐提供`pip freeze`的输出,以及其他相关信息,以及你的建议
|
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
57
.github/workflows/ISSUE_TEMPLATE.md
vendored
@ -1,33 +1,44 @@
|
||||
---
|
||||
name: BUG 反馈
|
||||
about: 使用轻雪时遇到了问题?
|
||||
---
|
||||
## 问题反馈
|
||||
# 问题反馈
|
||||
|
||||
### **描述**
|
||||
请详细描述一下你所遇到的bug
|
||||
## **请确保**
|
||||
|
||||
### **确保**
|
||||
- 已认真阅读[文档]("https://bot.liteyuki.icu"),该问题不是文档提及的或你自己操作不当造成的
|
||||
- 你的问题是在最新版本的代码上测试的
|
||||
- 请勿重复提交相同或类似的issue
|
||||
|
||||
- [ ] 我已查阅所有issues,没有相似或已被证实的内容
|
||||
- [ ] 我已按照文档指引进行正确的操作,仍会复现该问题
|
||||
|
||||
### **预期效果**
|
||||
你想要什么样的预期效果
|
||||
## **描述问题**
|
||||
|
||||
### **实际效果**
|
||||
实际上是怎么样的
|
||||
请在此简单描述问题
|
||||
|
||||
### **运行环境**
|
||||
- 系统及版本:
|
||||
- Python环境:
|
||||
- commit哈希或版本:
|
||||
- 硬件信息(可选):
|
||||
|
||||
### **运行日志**
|
||||
|
||||
## **如何复现**
|
||||
|
||||
请阐述一下如何重现这个问题
|
||||
### 预期
|
||||
|
||||
描述你期望发生的事情
|
||||
|
||||
### 实际
|
||||
|
||||
描述实际发生的事情
|
||||
|
||||
|
||||
|
||||
## **日志或截图**
|
||||
```
|
||||
将相关日志粘贴到此处
|
||||
日志内容
|
||||
```
|
||||
|
||||
### **严重等级**
|
||||
致命|严重|警告|轻微
|
||||
|
||||
## **设备信息**
|
||||
- **系统**: [例如 Ubuntu 22.04]
|
||||
- **CPU**: [例如 Intel i7-7700K]
|
||||
- **内存**: [例如 16GB]
|
||||
- **Python**: [例如CPython 3.10.7]
|
||||
|
||||
|
||||
**补充内容**
|
||||
|
||||
可选,推荐提供`pip freeze`的输出,以及其他相关信息,以及你的建议
|
||||
|
7
.gitignore
vendored
@ -15,13 +15,13 @@ _config.yml
|
||||
config.yml
|
||||
config.example.yml
|
||||
compile.bat
|
||||
liteyuki/resources/templates/latest-debug.html
|
||||
src/resources/templates/latest-debug.html
|
||||
# vuepress
|
||||
.github
|
||||
pyproject.toml
|
||||
|
||||
test.py
|
||||
line_count.py
|
||||
mypy.ini
|
||||
|
||||
# nuitka
|
||||
main.build/
|
||||
@ -34,4 +34,5 @@ docs/.vuepress/dist/
|
||||
prompt.txt
|
||||
|
||||
# js
|
||||
**/echarts.js
|
||||
**/echarts.js
|
||||
.env
|
||||
|
31
EN.LICENSE
Normal file
@ -0,0 +1,31 @@
|
||||
LSO license
|
||||
LiteyukiStudio Opensource license
|
||||
|
||||
---
|
||||
|
||||
Copyright © 2024 Snowykami
|
||||
|
||||
---
|
||||
|
||||
Free to grant the same license-based rights to any person or organization who obtains a copy
|
||||
|
||||
including but not limited to using, copying, modifying, merging, publishing, distributing, sublicenseing, and/or selling copies of the software
|
||||
|
||||
This software and related documentation files (hereinafter referred to as "this software") are licensed in the same way as the base, and are released in the form of open source on the Internet or other media platforms
|
||||
|
||||
Everyone has the right to obtain a copy and obtain permission to distribute and/or use it in the above manner
|
||||
|
||||
However, when obtaining a copy, it is still necessary to pay attention to the following:
|
||||
|
||||
- The above copyright notice and this permission notice shall be included in a copy of the Software
|
||||
- When using this software and its copies, it is still necessary to maintain the same form as the original
|
||||
|
||||
- When using this software, you still need to disclose the copy of this software under the same license:
|
||||
- Do not profit from copies of this software in a non-original license without the permission of the original author
|
||||
|
||||
---
|
||||
|
||||
The software is provided as a "copy as is" without any warranty of any kind, either express or implied:
|
||||
including but not limited to the warranty of merchantability, non-infringement for specific purposes
|
||||
|
||||
In any case, the author or copyright owner shall not be liable for any claims, damages, or other liabilities arising from the use of the software by the author or copyright owner, whether in contract litigation, infringement litigation, or other litigation. The author and its copyright owner have the right to refuse compensation for any losses caused by the user for personal reasons
|
52
LICENSE
@ -1,21 +1,31 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Snowykami
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
LSO license
|
||||
LiteyukiStudio Opensource license
|
||||
|
||||
---
|
||||
|
||||
版权所有 © 2024 Snowykami
|
||||
|
||||
---
|
||||
|
||||
免费向任何获得副本的人或组织授予以相同许可为基础的权利
|
||||
|
||||
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件的副本
|
||||
|
||||
本软件及相关文档文件(以下简称"本软件")在相同方式许可为基础, 以开源的形式发布于互联网抑或其他媒体介质平台
|
||||
|
||||
任何人都有权利获取副本并以上述方式获取许可传播和/或使用
|
||||
|
||||
但获取副本时仍需注意:
|
||||
|
||||
- 上述版权声明和本许可声明应包含在本软件的副本中
|
||||
- 使用本软件及其副本时仍需保持与原有形式相同
|
||||
|
||||
- 在使用时仍需将本软件的副本以相同许可公开表现:
|
||||
- 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利
|
||||
|
||||
---
|
||||
|
||||
该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示:
|
||||
包括但不限于适销性保证, 适用于特定目的非侵权
|
||||
|
||||
在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付
|
||||
|
57
MIT-LSO-AGC.LICENSE
Normal file
@ -0,0 +1,57 @@
|
||||
# 开源声明
|
||||
|
||||
MIT-LSO-AGC许可(cn)
|
||||
本声明推荐Markdown格式阅读
|
||||
|
||||
## 版权声明
|
||||
|
||||
版权所有(c) 2024 Snowykami
|
||||
|
||||
## 开源许可声明
|
||||
|
||||
现授予任何人以同仓库许可的开源,包含:
|
||||
本软件或本库和相关文档文件(以下简称“软件”)
|
||||
|
||||
在本软件或本库中不受限制,包括但不限于**非侵犯MIT-LSO-AGC许可**的:
|
||||
- 使用
|
||||
- 复制
|
||||
- 修改
|
||||
- 合并
|
||||
- 发布
|
||||
- 分发
|
||||
- 再许可和/或受到许可的销售本软件的副本
|
||||
|
||||
并要求或强烈建议使用本软件或库时:
|
||||
- 将上述版权声明和本许可声明包含在所有本软件或本库的副本或主要部分
|
||||
|
||||
---
|
||||
|
||||
本软件或本库按“原样”提供,不提供任何形式的、明确或隐含的,包括但不限于适销性保证,适合特定用途和非侵权性
|
||||
|
||||
## 责任声明
|
||||
|
||||
在任何情况下都不得在使用本软件或库时,在本协议许可的情况下,以损害或其他责任追责,无论是在合同诉讼、侵权行为或其他情况下,抑或是由以下原因引起:
|
||||
由于或与本软件有关,或是使用,或其他交易
|
||||
都与本软件,库作者无责任关系,非作者使用本软件或本库并造成任何损失的行为与作者无关
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 搬运声明
|
||||
|
||||
在前提开源许可的基础上:
|
||||
|
||||
**未经允许不得**搬运至商业性平台**并且**进行**付费行为或疑似未得到许可的交易行为**
|
||||
|
||||
**不得**在搬运本仓库至**其他私有/公有开源镜像代码托管网站**时未经允许修改**任何**关于**原库的内容**
|
||||
|
||||
### 包括但不限于:
|
||||
|
||||
- 将`github`修改为`gitcode`
|
||||
- 篡改外链网址
|
||||
- 修改贡献者名单
|
||||
- 未经原作者允许私自占有仓库并声明为自己所有
|
||||
- 未经允许修改库原先所有的开源协议许可
|
||||
等
|
||||
|
||||
---
|
18
README.md
@ -1,37 +1,37 @@
|
||||
<div align="center">
|
||||
|
||||
[//]: # (<img src="https://cdn.liteyuki.icu/static/img/logo.png" style="align-content: center; width: 50%; margin-top:10%;" alt="a">)
|
||||
[//]: # (<img src="https://cdn.liteyuki.icu/static/svg/lylogo-full.svg" style="align-content: center; width: 50%; margin-top:10%;" alt="a">)
|
||||
[![][banner]][lightyuki-link]
|
||||
<h2><a href="https://bot.liteyuki.icu"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">6</span></a></h2>
|
||||
<h4> <span style="color: #a2d8f4">✨ 轻量,高效,易于扩展✨</span></h4>
|
||||
|
||||
[![][OneBot]][onebot-link]
|
||||
[![][Nonebot2]][nonebot-link]
|
||||
[![][NoneBot2]][nonebot-link]
|
||||
[![][Liteyuki6.0]][lightyuki-link]
|
||||
[![][Python3.10+]][python-link]
|
||||
[![][Usage]][usage-link]
|
||||
|
||||
- 基于[Nonebot2](https://github.com/nonebot/nonebot2),有良好的生态支持
|
||||
- 开箱即用,无需复杂配置
|
||||
- 新的点击交互模式,拒绝手打指令
|
||||
- 可视化插件管理包管理,支持一键安装插件
|
||||
- 集成包管理器,支持一键安装插件
|
||||
- 支持OneBot标准通信但不限于此
|
||||
- 自定义主题支持,满足审美需求
|
||||
- 国际化支持,支持多种语言
|
||||
- 高性能,500插件2s内启动
|
||||
|
||||
<h3>👇所有内容已迁移至👇</h3>
|
||||
<h2><a href="https://bot.liteyuki.icu">轻雪主页</a></h2>
|
||||
<h3>👇更多内容请访问👇</h3>
|
||||
<h2><a href="https://bot.liteyuki.icu">轻雪机器人主页</a></h2>
|
||||
</div>
|
||||
|
||||
### 感谢
|
||||
- [Nonebot2](https://nonebot.dev)提供的框架支持
|
||||
- [NoneBot2](https://nonebot.dev)提供的框架支持
|
||||
- [nonebot-plugin-htmlrender](https://github.com/kexue-z/nonebot-plugin-htmlrender)提供的渲染功能
|
||||
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
|
||||
|
||||
|
||||
[OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge
|
||||
|
||||
[Nonebot2]: https://img.shields.io/badge/Nonebot-2-red?style=for-the-badge
|
||||
[NoneBot2]: https://img.shields.io/badge/Nonebot-2-red?style=for-the-badge
|
||||
|
||||
[Liteyuki6.0]: https://img.shields.io/badge/Liteyuki-6.0-blue?style=for-the-badge
|
||||
|
||||
@ -49,4 +49,4 @@
|
||||
|
||||
[usage-link]:https://bot.liteyuki.icu/
|
||||
|
||||
[banner]: https://socialify.git.ci/snowykami/LiteyukiBot/image?description=1&forks=1&issues=1&Plus&pulls=1&stargazers=1&theme=Auto&logo=https%3A%2F%2Fcdn.liteyuki.icu%2Fstatic%2Fimg%2Flogo.png
|
||||
[banner]: https://socialify.git.ci/LiteyukiStudio/LiteyukiBot/image?description=1&forks=1&issues=1&Plus&pulls=1&stargazers=1&theme=Auto&logo=https%3a%2f%2fcdn.liteyuki.icu%2fstatic%2fsvg%2flylogo-full.svg
|
||||
|
@ -1,14 +1,20 @@
|
||||
import {defineClientConfig} from "vuepress/client";
|
||||
import resourceStoreComp from "./components/res_store.vue";
|
||||
import pluginStoreComp from "./components/plugin_store.vue";
|
||||
//导入element-plus
|
||||
import ElementPlus from 'element-plus';
|
||||
|
||||
import resourceStoreComp from "./components/ResStore.vue";
|
||||
import pluginStoreComp from "./components/PluginStore.vue";
|
||||
import dashComp from "./components/Dash.vue";
|
||||
import homeComp from "./components/Home.vue";
|
||||
import geoComp from "./components/Geo.vue";
|
||||
|
||||
// import ElementPlus from 'element-plus';
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance: ({app, router, siteData}) => {
|
||||
app.component("homeComp", homeComp);
|
||||
app.component("dashComp", dashComp);
|
||||
app.component("resourceStoreComp", resourceStoreComp);
|
||||
app.component("pluginStoreComp", pluginStoreComp);
|
||||
app.use(ElementPlus);
|
||||
|
||||
app.component("geoComp", geoComp);
|
||||
// app.use(ElementPlus);
|
||||
},
|
||||
});
|
38
docs/.vuepress/components/Dash.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
|
||||
let total = ref(0);
|
||||
let online = ref(0);
|
||||
fetch("https://api.liteyuki.icu/count")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
total.value = data.register;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
fetch("https://api.liteyuki.icu/online")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
online.value = data.online;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="info-box">
|
||||
<h1>Dashboard</h1>
|
||||
<div class="info">
|
||||
<div class="info-item">
|
||||
<h2>Total</h2>
|
||||
<p>{{ total }}</p>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<h2>Online</h2>
|
||||
<p>{{ online }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
20
docs/.vuepress/components/Geo.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<h2>地理分布</h2>
|
||||
<p>数据来源于Liteyuki API</p>
|
||||
</div>
|
||||
<div id="main-chart" style="width: 100%; height: 600px;"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#main-chart {
|
||||
width: 100px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
10
docs/.vuepress/components/Home.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
62
docs/.vuepress/components/PluginStore.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './PluginItemCard.vue'
|
||||
|
||||
|
||||
let filteredItems = computed(() => {
|
||||
if (!search.value) {
|
||||
return items.value
|
||||
}
|
||||
return items.value.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.desc.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.module_name.toLowerCase().includes(search.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
// 从官方拉取
|
||||
fetch("/assets/plugins.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
|
||||
//追加
|
||||
fetch('https://registry.nonebot.dev/plugins.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = items.value.concat(data)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>插件商店</h1>
|
||||
<p>内容来自<a href="https://nonebot.dev/store/plugins">NoneBot插件商店</a>和轻雪商店,在此仅作引用,具体请访问NoneBot插件商店</p>
|
||||
<!-- 搜索框-->
|
||||
<div class="search-box-div"><input class="item-search-box" type="text" placeholder="搜索插件" v-model="search"/></div>
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
53
docs/.vuepress/components/ResStore.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './ResItemCard.vue'
|
||||
import * as url from "node:url";
|
||||
|
||||
// 从public/assets/resources.json加载插件
|
||||
let filteredItems = computed(() => {
|
||||
if (!search.value) {
|
||||
return items.value.reverse()
|
||||
}
|
||||
return items.value.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.description.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase())
|
||||
).reverse()
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
fetch("/assets/resources.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
// 列表倒序
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>主题/资源商店</h1>
|
||||
<div class="search-box-div"><input class="item-search-box" type="text" placeholder="搜索资源" v-model="search" /></div>
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
@ -1,39 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import ItemCard from './plugin_item_card.vue'
|
||||
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
fetch('https://registry.nonebot.dev/plugins.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>插件商店</h1>
|
||||
<p>所有内容来自<a href="https://nonebot.dev/store/plugins">NoneBot插件商店</a>,在此仅作引用,具体请访问NoneBot插件商店</p>
|
||||
<div class="market">
|
||||
<!-- 布局商品-->
|
||||
<ItemCard v-for="item in items" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.market {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
@ -1,38 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import ItemCard from './res_item_card.vue'
|
||||
|
||||
// 从public/assets/resources.json加载插件
|
||||
let items = ref([])
|
||||
fetch('https://bot.liteyuki.icu/assets/resources.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>主题/资源商店</h1>
|
||||
<div class="market">
|
||||
<!-- 布局商品-->
|
||||
<ItemCard v-for="item in items" :key="item.id" :item="item" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.market {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
@ -10,6 +10,8 @@ export default defineUserConfig({
|
||||
description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人",
|
||||
head: [
|
||||
// 设置 favor.ico,.vuepress/public 下
|
||||
["script", {src: "/js/style.js", "type": "module"}],
|
||||
["script", {src: "/js/get_data.js", "type": "module"}],
|
||||
['link', {rel: 'icon', href: 'https://cdn.liteyuki.icu/favicon.ico'},],
|
||||
|
||||
['link', {rel: 'stylesheet', href: 'https://cdn.bootcdn.net/ajax/libs/firacode/6.2.0/fira_code.min.css'}],
|
||||
@ -23,7 +25,6 @@ export default defineUserConfig({
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
theme,
|
||||
// 和 PWA 一起启用
|
||||
// shouldPrefetch: false,
|
||||
|
@ -3,17 +3,17 @@ import {navbar} from "vuepress-theme-hope";
|
||||
export default navbar([
|
||||
"/",
|
||||
{
|
||||
text: "项目部署",
|
||||
text: "安装及部署",
|
||||
link: "/deployment/",
|
||||
prefix: "deployment/",
|
||||
},
|
||||
{
|
||||
text: "使用手册",
|
||||
text: "使用及开发",
|
||||
link: "/usage/",
|
||||
prefix: "usage/",
|
||||
},
|
||||
{
|
||||
text: "商店",
|
||||
text: "资源及插件",
|
||||
link: "/store/resource",
|
||||
prefix: "store/",
|
||||
}
|
||||
|
23
docs/.vuepress/public/assets/plugins.json
Normal file
@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"module_name": "nonebot_plugin_status",
|
||||
"project_link": "nonebot-plugin-status",
|
||||
"name": "测试",
|
||||
"desc": "测试",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/nonebot/plugin-status",
|
||||
"tags": [
|
||||
{
|
||||
"label": "server",
|
||||
"color": "#aeeaa8"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"supported_adapters": null,
|
||||
"valid": true,
|
||||
"version": "0.8.1",
|
||||
"time": "2024-03-04T06:57:10.250823Z",
|
||||
"skip_test": false
|
||||
}
|
||||
]
|
@ -3,8 +3,7 @@
|
||||
"name": "KawaiiStatus",
|
||||
"author": "SnowyKami",
|
||||
"description": "可爱的状态卡片,仿照koishi的制作",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip",
|
||||
"icon": "https://cdn.jsdelivr.net/gh/SnowyKami/CDN/img/KawaiiStatus.png"
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip"
|
||||
},
|
||||
{
|
||||
"name": "MiSans字体包",
|
||||
@ -25,15 +24,33 @@
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip"
|
||||
},
|
||||
{
|
||||
"name": "示例包2",
|
||||
"name": "自定义设备信息",
|
||||
"author": "SnowyKami",
|
||||
"description": "A simple bot that shows the status of the bot and the server.",
|
||||
"link": ""
|
||||
"description": "自定义服务端的设备信息,自行修改使用",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/custom-device.zip"
|
||||
},
|
||||
{
|
||||
"name": "示例包3",
|
||||
"name": "轻雪傲娇系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "A simple bot that shows the status of the bot and the server.",
|
||||
"link": ""
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪可爱系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_kawaii.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪Kakyo语言包",
|
||||
"author": "Nanaloveyuki",
|
||||
"description": "Liteyuki Bot的语言包,用于提供多种语言的翻译。",
|
||||
"link": "https://github.com/Nanaloveyuki/liteyuki-langpack/releases/download/KakyoVer/Kakyo-pack.zip"
|
||||
},
|
||||
{
|
||||
"name": "更多背景模板包",
|
||||
"author": "snowykami",
|
||||
"description": "自定义各种卡片的背景",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
291
docs/.vuepress/public/full.svg
Normal file
@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1366 768">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: url(#_未命名的渐变_8);
|
||||
}
|
||||
|
||||
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9, .cls-10, .cls-11, .cls-12, .cls-13, .cls-14, .cls-15, .cls-16, .cls-17, .cls-18, .cls-19, .cls-20, .cls-21, .cls-22, .cls-23, .cls-24, .cls-25, .cls-26, .cls-27, .cls-28, .cls-29, .cls-30, .cls-31, .cls-32, .cls-33, .cls-34 {
|
||||
stroke-width: 0px;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: url(#_未命名的渐变_9-3);
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: url(#_未命名的渐变_11);
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: url(#_未命名的渐变_11-4);
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: url(#_未命名的渐变_5);
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: url(#_未命名的渐变_55);
|
||||
}
|
||||
|
||||
.cls-7 {
|
||||
fill: url(#_未命名的渐变_11-5);
|
||||
}
|
||||
|
||||
.cls-8 {
|
||||
fill: url(#_未命名的渐变_65);
|
||||
}
|
||||
|
||||
.cls-9 {
|
||||
fill: url(#_未命名的渐变_3-2);
|
||||
}
|
||||
|
||||
.cls-10 {
|
||||
fill: url(#_未命名的渐变_7-3);
|
||||
}
|
||||
|
||||
.cls-11 {
|
||||
fill: url(#_未命名的渐变_7);
|
||||
}
|
||||
|
||||
.cls-12 {
|
||||
fill: url(#_未命名的渐变_58);
|
||||
}
|
||||
|
||||
.cls-13 {
|
||||
fill: url(#_未命名的渐变_3-3);
|
||||
}
|
||||
|
||||
.cls-14 {
|
||||
fill: url(#_未命名的渐变_67);
|
||||
}
|
||||
|
||||
.cls-15 {
|
||||
fill: url(#_未命名的渐变_11-2);
|
||||
}
|
||||
|
||||
.cls-16 {
|
||||
fill: url(#_未命名的渐变_72);
|
||||
}
|
||||
|
||||
.cls-17 {
|
||||
fill: url(#_未命名的渐变_71);
|
||||
}
|
||||
|
||||
.cls-18, .cls-35 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.cls-19 {
|
||||
fill: url(#_未命名的渐变_4);
|
||||
}
|
||||
|
||||
.cls-20 {
|
||||
fill: url(#_未命名的渐变_3);
|
||||
}
|
||||
|
||||
.cls-21 {
|
||||
fill: url(#_未命名的渐变_240);
|
||||
}
|
||||
|
||||
.cls-22 {
|
||||
fill: url(#_未命名的渐变_9-2);
|
||||
}
|
||||
|
||||
.cls-23 {
|
||||
fill: url(#_未命名的渐变_6);
|
||||
}
|
||||
|
||||
.cls-24 {
|
||||
fill: url(#_未命名的渐变_9-4);
|
||||
}
|
||||
|
||||
.cls-25 {
|
||||
fill: url(#_未命名的渐变_66);
|
||||
}
|
||||
|
||||
.cls-26 {
|
||||
fill: url(#_未命名的渐变_7-2);
|
||||
}
|
||||
|
||||
.cls-35 {
|
||||
stroke: #fff;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.cls-27 {
|
||||
fill: url(#_未命名的渐变_9);
|
||||
}
|
||||
|
||||
.cls-28 {
|
||||
fill: url(#_未命名的渐变_9-5);
|
||||
}
|
||||
|
||||
.cls-29 {
|
||||
fill: url(#_未命名的渐变_68);
|
||||
}
|
||||
|
||||
.cls-30 {
|
||||
fill: url(#_未命名的渐变_11-3);
|
||||
}
|
||||
|
||||
.cls-31 {
|
||||
fill: url(#_未命名的渐变_69);
|
||||
}
|
||||
|
||||
.cls-32 {
|
||||
fill: #9cf;
|
||||
}
|
||||
|
||||
.cls-33 {
|
||||
fill: url(#_未命名的渐变_58-2);
|
||||
}
|
||||
|
||||
.cls-34 {
|
||||
fill: url(#_未命名的渐变_4-2);
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="_未命名的渐变_11" data-name="未命名的渐变 11" x1="445.89" y1="235.02" x2="528.25" y2="235.02" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#73fff1"/>
|
||||
<stop offset="1" stop-color="#5cefff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_11-2" data-name="未命名的渐变 11" x1="549.98" y1="292.99" x2="584.05" y2="292.99" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-3" data-name="未命名的渐变 11" x1="553" y1="212" x2="585" y2="212" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-4" data-name="未命名的渐变 11" x1="609.82" y1="260.3" x2="698.34" y2="260.3" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-5" data-name="未命名的渐变 11" x1="702.75" y1="282.24" x2="784.94" y2="282.24" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_240" data-name="未命名的渐变 240" x1="835" y1="294" x2="960" y2="294" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#eadeff"/>
|
||||
<stop offset=".28" stop-color="#e0efff"/>
|
||||
<stop offset=".43" stop-color="#d9ecff"/>
|
||||
<stop offset=".59" stop-color="#aedbff"/>
|
||||
<stop offset="1" stop-color="#00d7ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_9" data-name="未命名的渐变 9" x1="664.92" y1="424.34" x2="766.76" y2="424.34" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#2ebbff"/>
|
||||
<stop offset="1" stop-color="#006bff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_9-2" data-name="未命名的渐变 9" x1="761.74" y1="467.72" x2="842.05" y2="467.72" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-3" data-name="未命名的渐变 9" x1="951.96" y1="394" x2="977.04" y2="394" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-4" data-name="未命名的渐变 9" x1="952.19" y1="456.86" x2="977.56" y2="456.86" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-5" data-name="未命名的渐变 9" x1="848.96" y1="437.15" x2="934.09" y2="437.15" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_4" data-name="未命名的渐变 4" x1="418.01" y1="402.81" x2="470.25" y2="402.81" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#6445ff"/>
|
||||
<stop offset="1" stop-color="#5c86ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_4-2" data-name="未命名的渐变 4" x1="445.81" y1="464.45" x2="466.06" y2="464.45" xlink:href="#_未命名的渐变_4"/>
|
||||
<linearGradient id="_未命名的渐变_72" data-name="未命名的渐变 72" x1="634.1" y1="393.61" x2="649.62" y2="390.81" gradientTransform="translate(-320.36 273.4) rotate(-11.09) scale(1.44 .77)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#c4aeff"/>
|
||||
<stop offset=".43" stop-color="#ce9fff"/>
|
||||
<stop offset=".55" stop-color="#af8eff"/>
|
||||
<stop offset=".72" stop-color="#7d74ff"/>
|
||||
<stop offset=".9" stop-color="#6333ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_58" data-name="未命名的渐变 58" x1="620.28" y1="400.01" x2="636.02" y2="397.18" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#b1ccff"/>
|
||||
<stop offset=".34" stop-color="#b8c0ff"/>
|
||||
<stop offset=".94" stop-color="#cba2ff"/>
|
||||
<stop offset="1" stop-color="#ce9fff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_3" data-name="未命名的渐变 3" x1="469.91" y1="455" x2="478" y2="455" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#5c86ff"/>
|
||||
<stop offset="1" stop-color="#64a2ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_3-2" data-name="未命名的渐变 3" x1="470.95" y1="475.89" x2="477.01" y2="475.89" xlink:href="#_未命名的渐变_3"/>
|
||||
<linearGradient id="_未命名的渐变_65" data-name="未命名的渐变 65" x1="485.09" y1="467.16" x2="502.74" y2="469.91" gradientTransform="translate(-109.75 153.43) rotate(-15.93)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".11" stop-color="#64a2ff"/>
|
||||
<stop offset=".28" stop-color="#62a6ff"/>
|
||||
<stop offset="1" stop-color="#5cb5ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_66" data-name="未命名的渐变 66" x1="506.01" y1="471.79" x2="532.2" y2="471.17" gradientTransform="translate(117.47 -103.02) rotate(12.81)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#5cb5ff"/>
|
||||
<stop offset="1" stop-color="#74e0ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_68" data-name="未命名的渐变 68" x1="559.81" y1="469.17" x2="583.24" y2="468.62" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#9cd1ff"/>
|
||||
<stop offset="1" stop-color="#bae0ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_71" data-name="未命名的渐变 71" x1="584.93" y1="460.83" x2="609.05" y2="460.83" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a6d3ff"/>
|
||||
<stop offset=".7" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_58-2" data-name="未命名的渐变 58" x1="605.79" y1="473.79" x2="643.63" y2="466.98" xlink:href="#_未命名的渐变_58"/>
|
||||
<linearGradient id="_未命名的渐变_3-3" data-name="未命名的渐变 3" x1="479.91" y1="397.6" x2="514.17" y2="397.6" xlink:href="#_未命名的渐变_3"/>
|
||||
<linearGradient id="_未命名的渐变_5" data-name="未命名的渐变 5" x1="525.59" y1="402.28" x2="558.08" y2="402.28" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64a2ff"/>
|
||||
<stop offset="1" stop-color="#5cc9ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_69" data-name="未命名的渐变 69" x1="583.43" y1="398.41" x2="604.15" y2="398.41" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#84c9ff"/>
|
||||
<stop offset=".42" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_67" data-name="未命名的渐变 67" x1="531.83" y1="464.83" x2="558.02" y2="464.22" gradientTransform="translate(-86.67 132.78) rotate(-12.37)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#74e0ff"/>
|
||||
<stop offset=".56" stop-color="#8dd6ff"/>
|
||||
<stop offset="1" stop-color="#9cd1ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_6" data-name="未命名的渐变 6" x1="531" y1="411" x2="539" y2="411" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64a2ff"/>
|
||||
<stop offset="1" stop-color="#5cbfff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_7" data-name="未命名的渐变 7" x1="553.98" y1="380.5" x2="559" y2="380.5" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64ccff"/>
|
||||
<stop offset="1" stop-color="#84c9ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_7-2" data-name="未命名的渐变 7" x1="546.98" y1="383.5" x2="552" y2="383.5" xlink:href="#_未命名的渐变_7"/>
|
||||
<linearGradient id="_未命名的渐变_7-3" data-name="未命名的渐变 7" y1="404.39" x2="561.98" y2="404.39" xlink:href="#_未命名的渐变_7"/>
|
||||
<linearGradient id="_未命名的渐变_8" data-name="未命名的渐变 8" x1="578.42" y1="391.26" x2="586.42" y2="391.26" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".01" stop-color="#84c9ff"/>
|
||||
<stop offset="1" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_55" data-name="未命名的渐变 55" x1="623.35" y1="385.26" x2="623.75" y2="385.26" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#b1c9ff"/>
|
||||
<stop offset=".42" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="_图层_3" data-name="图层 3">
|
||||
<path class="cls-32" d="m447,485c11.04,8.56,30.35.82,33,0,14.37-4.43,15.38-15.33,26-15,10.44.33,13.16,10.98,24,10,6.48-.59,7.04-4.53,16-6,8-1.31,8.66,1.66,17,1,12.76-1.01,15.73-8.33,24-6,8.42,4.47,11.58,6.51,22,5,0,0,11.3-1.75,32-10,1.42-9.07,4.72,6.12,11.99,12,10.1,9.09,19.95-.4,31,8,10.86,8.26,13.5,26.66,13,31-1.65,4.39,6.37,10.16,6,10-4.41-.77-20.72-10.82-25-11-6.8-1.26-15.53-2.26-24-5-17.1-5.24-15.17-10.82-34-10-7.53.04-6.69-.47-12,1-2.54-.23-9.74.73-13-1-18.29-11.87-9.94,11.78-36,3-9.47,1.86-14.72.65-18.02-1.12-2.21-1.19-4.61-5.33-6.74-4.61-.57.19-.74.58-2.24,1.74-1.2.93-2.27,1.58-3,2-.91.36-1.91.7-3,1-2.93.8-5.59,1.02-7.75,1.02-.74,0-1.87-.01-3.25-.02-3.97,0-3.61.03-4,0-2.87-.21-3.75-.93-7-1-.42,0-.77,0-1,0h-6c-3,.22-5.39.65-7,1-1.72.37-3.36.82-10,3-3.07,1.01-4.59,1.59-6,2-1.81.53-5.13,1.49-9,2-5.07.67-9,.3-12,0-6.54-.65-11.56-2.18-14-3-4.6-1.55-26.12-13.37-20-10,0,0,0,0,0,0-.03-.02-20.88-13.37-28-32-1.09-2.85-2-6-2-6-.52-1.8-1.44-5.03-2-9-.44-3.12-1.34-9.53,0-10,1.69-.59,6.23,8.51,7.27,10.48,5.15,9.76,18.12,22.54,52.73,35.52Z"/>
|
||||
<path class="cls-32" d="m633,394c-13.63,7.43-2.93,57.29,2,57,1.82-.11,1.13-6.97,8-16,3-3.94,3.81-3.37,5-6,.88-3.8,2.06-11.66-2-19-.61-1.11-1.29-2.11-2-3-.31-.36-.8-1.04-1-2-.06-.32-.19-.91,0-1,.26-.13.82.87,2,2,.84.81,1.71,1.61,3,2,1.06.32,1.94.17,3,0,1.38-.23,2.37-.69,3-1,1.31-.64,2.25-1.09,3-2,1.84-2.22,1.14-5.41,1-6-.33-1.36-.69-1.29-2-4-.87-1.8-.57-1.55-2-5-.49-1.19-1-2.79-2-6-.12-.39-.37-1.22,0-2,0,0,.29-.6,1-1,1.98-1.11,11.6.55,15,1,2.66.35,13.92,1.61,15.83,2.95.42.3.8.64,1.17,1.05,2.43,2.73.93,4.31,3,9,3.83,8.69,13.16,12.57,19,15,5.09,2.12,8.54,2.5,13,3,6.22.69,7.53-.22,8-1,.74-1.23-.27-2.7-1-5-1.53-4.78-.3-8.09,0-12,.51-6.64-1.77-13.74-4-17-9.26-13.54-41.29,4.77-71-2-1.28-.29-9.89-2.3-12,1-1.87,2.94,2.99,7.55,1,12-1.49,3.33-5.5,3.63-8,5Z"/>
|
||||
<path class="cls-32" d="m683,228c-22.42,19.49,74.7,203.11,32,245-9.08,8.91-26.9,13.94-28,28-.65,8.33,4.78,18.3,13,23,13.88,7.94,28.83,2.05,33-1,2.68-1.96,4-4,4-4,1.44-2.22,1.56-3.91,3-8,.76-2.17,1.5-3.9,2-5,.43-1.13.95-2.86,1-5,.04-1.88-.32-2.29-.25-4.13.04-1.01.22-5.22,2.25-5.87.27-.09,1.3-.35,3,1-.17-.41-.33-.87-.21-.95.24-.16,1.16,1.42,2.21,1.95,2.87,1.44,7.56-.07,8-2-10.79-5.85-14.4-6.84-15-6-.79,1.11,3.49,5.56,3,6-.37.33-3.05-1.93-3-2,.05-.07,3.08,1.97,4,3,.08.09.44.51,1,1,.23.2.43.36.55.46.1.12.26.31.45.54,2.9,3.62,4.2,7.01,4.62,8.05.92,2.28,2.67,5.77,6.38,10.95,15.05,16.54,34.83,19.18,54,9,16.33,3.81,7.85,10.54,33,5,25.9-4.5,50.39,1.98,49-25-1.26-10.66-5.86-19.02,0-10,8.68,14.17,21.69,39.51,43,27,6.44-4.99,5.9-9.29,8-16,1.22-1.48,4.53-4.38,6-4,.01.08.16,1-.41,1.5-.6.53-1.8.43-2.59-.5,2.97,10.52,12.31,17.02,21,16,7.93-.94,13.74-10.13,14.27-11.06.54-.96.98-1.97.98-1.97s.41-.95.75-1.97c1.19-3.64,1.46-7.4,1.46-7.4.88-11.86.98-15.27.98-15.27.35-11.02.5-9.78.67-17.66.19-8.34-.04-7.03.13-13.62,0,0,.55-21.79-3.25-26.05-.48-.54-1-1-1-1-1.02-.91-1.79-1.28-3-2,0,0-1-.67-3-2-1-.67-2.08-1.22-3-2-1.25-1.06-1.85-1.94-3-2-.39-.02-.52.07-1,0-1.2-.18-1.99-1.02-2-1-.02.03.5.41,3,2,.1-.62,1.27-2.67,2-3,5.54-4.98,5.58-5.38,8-10,6.12-15.03-2.92-19.21-14-26-1.23-1.16-1.87-.9-3-1-.59-2.25-2.17-3.18-3-6-1.01-2.71-2.29-10.67-2-12,.57-5.41,2.39-8.84,4-11,2.1-2.82,3.85-3.49,6-7,1.22-2,2.84-4.65,3-8,.25-5.38-3.39-9.63-8-15-2.47-2.87-5.06-5.17-6-6-2.42-2.13-4.22-3.42-5-4-5.14-3.8-8.31-10.94-9.7-14.15-5.32-12.26-7.9-23.86-8.3-32.85-1.17-26.5-38.54-51.77-63-35-7.04,5.65-2.81,10.14-11,18-10.42,10-22.57,7.8-34,13-5.71,3.43-10.18.22-16-2-9.95-3.09-18.5-13.48-24-22-1.52-1.57-3.98-3.73-7.79-5.64-30.14-4.8-47.97-7.45-51.21-7.36-.78.02-4.27.18-7.57-1.74-7.85-9.54-30.21,6.06-40.43,13.74Z"/>
|
||||
<path class="cls-32" d="m667,174c-4.66.36-8.11,1.64-10.79,3.57-13.35,9.62-7.67,24.47-21.35,37.56-3.88,3.71-4.86,2.87-33.86,12.87-.06.02-2.46.85-4,0-.23-.13-.75-.41-1-1-.23-.54-.15-1.16-.12-1.33.02-.16.09-.51.12-.67h0c1.24-1.71,8.11-11.55,4.97-21.02-3.76-11.34-20.93-19.28-39.97-12.98-12.11,2.27-15.97.27-17.33-1.85-1.94-3.02.84-6.86,5.33-19.16,3.86-10.58,5.89-16.13,6-22,.1-5.49-1.43-13.73-10-24-23.21-14.8-47-13.43-56-2-1.46,1.85-5.64,7.92-4,20-3.3,4.02-9.31,1.14-20,4-10.63,1.96-20.69,9.46-13,23,5.63,9.91,94.53,145.36,192.09,115.49,50.17-15.36,72.52-65.2,72.91-66.49.03-.1.51-1.68,0-2-.15-.1-.3-.03-1,0h-3c-1.55,0-3.44-.77-4-1-.5-.2-1.8-.75-5-3-1.04-.73-2.42-1.73-4-3-3.1-.72-4.4-1.96-5-3-.3-.52-.38-.9-1-2-1.21-2.14-1.61-1.92-2-3-.34-.93-.04-1.09,0-5,0-.35,0-.06,0-2,0-3.6.02-3.67,0-4-.12-1.85-.9-3.16-2-5-2.63-4.41-6.25-6.57-7-7-1.67-.96-2.99-1.38-5-2-2.56-.79-6.28-1.69-11-2Z"/>
|
||||
</g>
|
||||
<g id="_背景" data-name="背景">
|
||||
<path class="cls-18" d="m572,182c-16.42-3.08-26.21,8.1-31,4-7.63-6.54,18.33-33.97,9-55-6.88-15.51-30.12-21.13-45-21-3.33.03-16.49.44-31,9-30.76,18.15-37.62,55.27-56,109-20.03,58.55-43.71,77.3-45,119-.4,13.04.88,31.37,1,33,2.91,40.64,9.7,47.97,12,64,.27,1.85.98,7.34,5,12,.83.96,1.59,1.64,2.01,2,.47.48,1.15,1.18,1.99,2,10.84,10.63,27.33,20.18,27.33,20.18,17.89,10.12,26.84,15.18,35.66,13.82,3.19-.49,6.88-1.68,16.62-3.27,3.46-.56,6.46-.79,12.39-1.73,3.95-.63,7.36-.89,10-1,3.64-.15,6.36-.03,7,0,4.66.22,8.01.84,9,1,3.81.61,10.14.32,21-4,3.96.13,4.67,2.49,9,4,4.59,1.6,5.73-.37,15,0,16.7,2.72,12.4-.95,25-4,5.48-.56,5.78,1.97,14,3,9.86,2.45,36.35-8.51,48,1,6.44,5.27,9.1,4.24,16,6,7.05,3.78,15.85,4.35,23,8,18.2,11.17,34.14,20.96,51,2,5.59-6.98,3.83-12.43,8-14,7.13-2.81,31.59,30.21,58,23,15.36-6.57,13.31-4.51,28,1,5.68,1.04,23.32-3.84,33-4,12.6-.87,15.51.92,21-2,11.5-6.32,8.35-12.82,9-21,6.52,3,17.82,31.94,43,18,6.28-4.14,7.41-10.27,11-10,3.81.29,3.43,7.28,9,11,7.22,4.82,20.84,1.79,26-6,1.88-2.84,1.95-5.28,2-16,.06-12.86,2.5-34.29,1-57-.27-4.14-.62-6.12-2-8-1.81-2.47-4.68-3.81-7.73-8.57-.11-.18-.21-.33-.27-.43,0-.08-.02-.19-.02-.33.03-1.6,1.77-2.75,2.36-3.17,1.19-.85,2.8-2.4,4.66-5.5,10.77-33.99-34.41-21.61-20-59,3.12-6.89,6.55-6.37,8-12,2.32-9.04-4.5-18.26-8-23-5.99-8.1-8.37-6.45-17-17-2.93-3.58-6.84-8.36-9.5-15.5-2.32-6.24-2.48-11.05-2.5-12.5,0-.42,0-.77,0-1,.13-4.73-.28-14.34-6-24-8.66-14.64-25.27-22.26-40-22-22.75.41-39.01,19.59-41,22-4.52,5.48-5.53,9.19-11,12-8.17,4.19-19.82,3.24-29-1-1.04-.48-1.78-.88-2-1-13.03-7.1-12.62-18.04-23-25-.78-.52-2.86-1.85-9.5-3.93-18.42-5.77-40.67-7.51-55.5-8.07-1.93.07-4.76-.05-8-1-3.67-1.08-6.36-2.77-8-4-5.34-3.78-6.82-2.84-9-5-3.59-3.56.14-10.39-3-17-2.84-5.97-10.15-7.96-14-9-2.47-.67-11.2-2.68-20,2-1.88,1-6.08,3.3-9,8-2.37,3.82-1.56,5.46-4,14-2.08,7.28-3.18,10.97-6,13-1.17.85-5.1,3.27-13,1-3.07-.18-7.45,0-12,2-8.86,3.88-12,11.82-14,11-2.79-1.15,5.52-15.64,0-28-5.58-12.5-22.22-15.85-23-16Z"/>
|
||||
<g id="_主体" data-name="主体">
|
||||
<path id="lite-l" class="cls-3" d="m516,141c.03-1.1-.07-2.97-1-5-.89-1.93-2.11-3.1-2.75-3.7-.4-.38-1.57-1.42-3.37-2.29-1.11-.53-2.12-.83-2.88-1.01-.48,0-1.16,0-1.98.04-1.4.09-4.59.32-7.8,1.81-3.86,1.79-6.04,4.59-7.21,6.15-1.03,1.36-.93,1.77-4,8-1.08,2.19-1.79,3.57-2,4-5.9,11.79-12,35-12,35-7.65,29.08-7.7,25.76-12,44-2.8,11.87-4.71,21.49-6,28-.61,3.07-2.21,11.25-4,22,0,0-1.57,9.87-3,30-.1,1.34-.46,7.05,1,14,.85,4.05,3,8,3,8h0c2.28,2.98,4.52,4.83,5.99,5.89,11.06,8,23.32,4.35,57.01,4.11,3.06-.02,5.53-.01,7,0,1.02-.28,2.51-.84,4-2,.52-.41,1.27-1.06,2-2,.45-.58,1.59-1.91,2-4,.21-1.08.43-2.87,0-5-.36-1.17-.7-2.18-1-3-.3-.83-.53-1.39-1-2-.36-.46-.73-.79-1-1-.24-.23-.76-.76-1-1-1.73-1.73-1.87-1.87-2-2-1.25-1.2-2.82-1.65-4-2-5.84-1.71-23-2-23-2-4.12-.07-10.14-.62-17.53-2.99,1.73-17.19,5.56-49.39,13.31-81.46,4.95-20.51,4.35-18.09,8.22-30.55,5.85-18.81,8.77-28.22,11-34,0,0,3.24-8.41,4.63-15.69.44-2.32.84-4.8.84-4.8.18-1.17.35-2.34.53-3.5Z"/>
|
||||
<path id="lite-i-1" class="cls-15" d="m569,240c4.11.31,6.91,2.96,8,4,6.83,6.48,6.93,17.68,7,26,0,.06,0,.03,0,7,0,14.32,0,21.48,0,22,.04,13.27.66,35.34-4,42-1.29,1.84-3,3-3,3-2.97,2.01-6.17,1.99-8,2-1.45,0-7.74-.12-13-5-6.34-5.87-6.06-13.87-6-15,0-.07,0-9.93,0-10,0-38.04,0-37.85,0-38-.02-1.29-.01-3.53,0-8,.03-9.9.15-12.06,1-15,1.28-4.42,3.15-6.95,4-8,1.1-1.36,2.19-2.34,3-3,.7-.58,1.7-1.32,3-2,.98-.52,4.28-2.28,8-2Z"/>
|
||||
<path id="lite-i-1-1" class="cls-30" d="m566.78,196.07c8.85-.96,18.71,7.52,18.2,17.09-.5,9.28-10.62,16.63-19.33,14.46-6.93-1.73-13.7-9.65-12.51-18.4,1.06-7.79,7.93-12.52,13.65-13.14Z"/>
|
||||
<path id="lite-t" class="cls-4" d="m643,219c-5.46,4.9-13.26-2.45-24,3-1.76.89-7.94,4.02-9,10-.72,4.08.94,7.61,1.38,8.43.48.92,2.17,4.11,5.62,5.57,1.09.46,1.2.26,6,1,5.06.78,4.17.82,6,1,2.02.19,4,.68,6,1,.54.09,1.55.25,2,1,.42.71.13,1.6,0,2-.61,1.93-.44,4.05-1,6-1.01,3.51-.87,8.39-1,13-.29,10.62-1.82,12.94-2,20-.11,4.19-.42,18.97,9,31,1.49,1.9,10.06,12.84,21,12,1.28-.1,5.89-.45,9-4,4.48-5.11,2.13-12.61,2-13-1.5-4.54-4.19-4.73-8-10-4-5.53-5.15-11-6-15-1.67-7.89-.83-14.05,0-20,1.22-8.76,3.02-10.14,4-16,.13-.81.46-2.94,2-5,0,0,.85-1.13,2-2,2.17-1.64,8.68-.49,12.72-.42,3.25.06,5.06.09,7.28-.58,1.17-.35,8.31-2.66,10-9,1.37-5.14-1.51-11.55-6-14-3.8-2.07-12.48-2.26-15-3-1.03-.3-1.9-.93-2-1-.47-.34-.82-.6-1-1-.56-1.23.89-2.87,1-3,.54-.96,1.27-2.32,2-4,1.51-3.46,2.99-6.85,3-11,0-1.91.01-6.19-3-10-3.3-4.17-8.02-4.99-9.42-5.24-.92-.16-6.37-1-11.58,2.24-1.61,1-4.15,3-7,9-5.75,12.08-1.75,17.18-6,21Z"/>
|
||||
<path id="lite-e" class="cls-7" d="m735,276c-.58.39-5.22,3.65-5,8,.19,3.92,4.21,6.49,5,7,5.42,3.48,9.31.51,25-1,10.15-.98,16.08-.47,21-5,1.89-1.74,2.72-3.39,3-4,2.32-5.01-.29-10.09-1-14-1.83-10.01-12.11-17.03-15-19-1.39-.95-8.43-5.63-19-7-4.47-.58-13.46-1.75-23,3-8.94,4.45-13.31,11.6-16,16-5.84,9.56-6.79,18.48-7,21-.4,4.87-1.09,14.86,5,25,4.96,8.26,11.87,12.39,15,14,8.24,4.25,15.6,4.12,23,4,9.99-.17,16.58-2.61,23-5,2-.74,5.17-2.02,8-5,1.68-1.77,4.08-4.29,4-8-.07-3.36-2.16-6.53-5-8-2.95-1.53-5.9-.81-9,0-6.07,1.58-6.81,4.01-12,6-6.49,2.49-12.52,1.46-15,1-3.77-.69-9.81-1.81-14-7-3.24-4.01-3.72-8.41-4-11-.26-2.43-1.1-10.07,4-17,.43-.59,7.21-9.48,18-9,1.43.06,9.65.59,14,7,1.16,1.71,2.94,4.33,2,6-2.22,3.93-15.76-4.29-25,2Z"/>
|
||||
<path id="logo-snow" class="cls-21" d="m875,272l13.33,22,9.67-15-8-20,4-6,6,2,9,13,7-11,9,1,3,7-11,13,17,2,4,8-5,4-22-4-13.5,16.8,27.5,5.2,11-14,7,1,3,7-9,11,19,3,4,7-4,5-18-3,7,14-4,4-8-1-10-20-40-8-2-2-20-37-23-4-3-7,4-5,16,3-9-15,4-6,7,1,9,16,9-10,7,2,3,6-10,14Z"/>
|
||||
<path id="yuki-y" class="cls-27" d="m717,426c-18.61-18.39-62.05-50.01-50-71,2.85-4.5,7.34-9.45,17-7,15.19,5.99,14.64,39.18,45,55,8.67-18.92,10.28-33.18,10-43-.12-4.37-.74-12.15,4-16,.82-.55,5.22-3.4,10.85-2.15,3.86.85,6.25,3.17,7.15,4.15.5.6,1.28,1.58,2.09,2.87,8.99,14.33.03,32.06-11.49,69.45-4.1,13.3-6.65,21.58-9.6,33.68-5.81,23.86-8.17,40.38-20,51-11.03,9.91-22.32.03-23-9-2.89-15.02,23.13-56.98,18-68Z"/>
|
||||
<path id="yuki-u" class="cls-22" d="m776,433c-.54-.11-5.36-1.02-9,2-2.31,1.92-3.04,4.55-4,8-.74,2.66-.91,4.8-1,6-.59,8.25,0,15,0,15,1.18,13.55,1.77,20.33,3,23,1.04,2.27,4.07,8.71,11,13,6.21,3.85,12.31,3.91,21,4,.81,0,16.83.09,21-4,.22-.21,1.2-1.26,2-1,.93.3.69,2.05,2,4,0,0,.63.93,2,2,3.54,2.75,12.53,3.41,16-1,.62-.79,1.75-2.64,2-5,.07-.68.05-1.22.02-1.76-.13-2.75-.88-4.83-1.02-5.24-1.31-3.73-3.37-15.69-4-23-.58-6.71.42-5.3,0-19-.11-3.63-.12-9.19-3-15-1.36-2.75-2.63-4.09-4-5-2.95-1.96-6.82-2.22-10-1-.8.31-3.48,1.34-5,4-1.36,2.37-1.11,4.83-1,6,.66,6.98.45,14.01,1,21,.72,9.13.57,11.51-1,14-1.8,2.86-4.42,4.2-6,5-7.58,3.86-15,.82-17,0-2.54-1.04-3.49-2.11-4-3-.74-1.29-.88-2.78-1-4-.13-1.35-.05-2.34,0-3v-16c-.19-7.44-.28-11.16-2-14-.52-.85-3.02-4.98-8-6Z"/>
|
||||
<path id="yuki-i-1-1" class="cls-2" d="m964,382c-6.65.3-12.55,6.12-12,13,.53,6.56,6.7,11.28,13,11,6.65-.3,12.55-6.12,12-13-.53-6.56-6.7-11.28-13-11Z"/>
|
||||
<path id="yuki-i-1" class="cls-24" d="m964,413c-.8.03-3.41.23-6,2,0,0-1.73,1.18-3,3-3.44,4.92-2.32,14.54-2,25,.14,4.66,0,9.33,0,14,0,11.92,0,17.88,0,18-.51,8.41-1.54,11.05,0,16,.77,2.5,1.84,5.92,5,8,3.21,2.11,6.76,1.77,8.36,1.62.77-.07,5.65-.54,8.64-3.62,3.4-3.49,2.66-8.56,2-17-.09-1.11-1.18-15.43-1-28,.1-7.33-.76-14.71,0-22,.11-1.03.52-4.77-1-9-.64-1.78-1.55-4.19-4-6-2.89-2.13-6.05-2.04-7-2Z"/>
|
||||
<path id="yuki-k" class="cls-28" d="m918,409c-.26-.11-.66-.27-1.17-.44,0,0-4.46-1.53-9.91-1-4.47.44-18.59,11.34-31.93,31.43-.35-10.53-.23-21.87.56-33.9.37-5.55.85-10.92,1.44-16.1.26-1.2.52-2.94.41-5.03-.09-1.81-.25-4.96-2.27-8.04-.56-.85-2.9-4.42-7.52-5.51-5.61-1.33-10.01,2.08-10.62,2.58-3.44,2.76-4.45,6.67-5,9-3.6,15.32-3,27-3,27,.42,8.15,1.67,32.57,1,50-.22,5.66.05,11.33,0,17-.05,6.02-.13,9.05,1,13,1.45,5.07,4.08,8.75,6,11,1.19,1.11,3.93,3.36,8,4,3.99.63,7.79-.51,9.84-1.85,11.06-7.21,1.65-39.59,4.55-40.56.39-.13,1.19.88,2.61,3.4,8.89,11.22,14.99,15.39,30,29,7.7,10.13,23.45,3.59,22-8-.19-16.59-39.62-33.08-31.91-39.47,1.89-2.38,6-8.21,6.91-9.53,5.47-7.8,10.83-8.44,13-15,1.34-4.04.69-6.57.43-7.38-1.01-3.15-3.39-4.94-4.43-5.62Z"/>
|
||||
<path id="lite-jp-1" class="cls-19" d="m452,384c-1.75-1-2.61-.17-10,1-2.22.35-7.11,1.06-13,3-1.36.45-2.68.93-3,2-.49,1.65,1.56,4.27,4,5,.32.09.56.13.74.16,2.19.34,3.89-.56,5.26-1.16,1.96-.87,3.64-1.24,7-2,0,0,4.44-1,5,0,.02.04.04.07.04.07.07.18-.02.5-.04,2.93,0,.06,0,.71,0,2,0,5.01.09,5.71,0,7-.2,2.74-.3,4.11-1,5-1.25,1.58-3.23,1.52-7,2-5.62.72-4.41,1.27-10,2-7.19.94-9.49.07-11,2-1.32,1.68-1.32,4.57,0,6,1.74,1.88,5.46.88,9,0,7.35-1.82,19.55-4.84,29-6,4.81-.59,12-1.09,13-4,.57-1.65,0-3,0-3-.06-.14-.62-1.43-2-2-1.19-.49-2.36-.18-3,0-6.78,1.91-9.23,2.82-10,2-.45-.48-.17-1.39,0-2,1.77-6.29,1.22-12.36,1-14-.23-1.67-.84-6.18-4-8Z"/>
|
||||
<path id="liteecho-1" class="cls-34" d="m448,440c-1.02.34-1.72,1.18-2,2-.31.93-.06,1.78,0,2,.38,1.39.25,7.26,0,19-.12,5.74-.36,7.73,0,12,.21,2.49.58,5.11,2,8,.87,1.76,1.83,3.72,4,5,2.08,1.23,4.09,1.14,7,1,2.69-.13,6.51-.31,7-2,.37-1.27-1.18-3.23-3-4-2.78-1.18-4.84.97-7,0-2.17-.97-2.69-4.29-3-7-1.07-9.45-.9-24.04-1-30-.02-1.36-.12-5.21-2-6-.58-.25-1.37-.21-2,0Z"/>
|
||||
<path id="yuki-jp-4-1" class="cls-16" d="m636.06,390.95c-2.11.2-3.55,1.9-3.93,2.35-.57.68-.91,1.34-1.11,1.8,1.09,1.05,6.06,5.64,11.22,4.91,2.33-.33,5.1-1.81,5.34-2.63.03-.09.03-.17.03-.17,0-.29-.33-.71-1.89-1.83-1.33-.96-2.23-1.61-3.3-2.18-.69-.37-1.23-.6-1.68-.79-2.41-1.04-3.62-1.55-4.68-1.45Z"/>
|
||||
<path id="yuki-jp-4" class="cls-12" d="m621,375c.37-.49,1.34-1.8,3-2,2.33-.28,3.93,1.89,4,2,.33.46.44.81,1,3,.59,2.3.88,3.45,1,4,.33,1.56.71,3.58,1,6,.65,5.37.61,6.78,1,10,.79,6.47,1.28,10.5,3,15,.07.18,1.23,3.21,1,7-.08,1.29-.29,2.25-1,3-1,1.07-2.67,1.39-4,1-.58-.17-1.69-.65-3-3-1.53-2.75-1.78-5.26-2-7-.3-2.39-.86-5.79-2-10-1-6.33-2-12.67-3-19-.33-1.67-.67-3.33-1-5-.1-.53-.53-2.95,1-5Z"/>
|
||||
<path id="liteecho-i-1-1" class="cls-20" d="m474,451c-1.86-.04-3.59,1.27-4,3-.56,2.38,1.47,5.01,4,5,2.03-.01,4-1.72,4-4,0-2.25-1.92-3.96-4-4Z"/>
|
||||
<path id="liteecho-i-1" class="cls-9" d="m475,464c-.61-.3-1.38-.31-2,0-.14.07-.62.33-1,1-1.18,2.08-1.01,7.51-1,8,.16,5.45,0,9,0,9-.08,1.78-.22,3.7,1,5,.21.22.94,1,2,1,1.08,0,1.81-.8,2-1,1.05-1.14,1.02-2.72,1-4-.03-2.33.04-4.67,0-7-.05-3.31.17-8.95-1-11-.12-.2-.41-.71-1-1Z"/>
|
||||
<path id="liteecho-t" class="cls-8" d="m492.48,452.64c-.52.03-1.3.07-1.92.55-.25.19-.76.67-1.1,2.68-.15.92-.29,2.26-.14,3.91-1.17.01-3.52.16-5.32,1.22-.38.22-.77.51-1,1-.39.85-.08,1.91.41,2.59.74,1.01,1.99,1.19,3.59,1.41.85.12,1.57.12,2.05.1-.06.76-.12,1.85-.14,3.16-.01,1.22-.03,2.48.14,4.12.15,1.49.3,2.92.96,4.62.39,1.03.78,1.67,1,2,.25.39.74,1.08,1.48,1.76,1.12,1.05,2.67,2.5,4.52,2.24,1.2-.17,2.57-1.06,2.76-2.24.12-.76-.29-1.46-.55-1.92-.43-.76-.87-.99-1.51-1.65-.95-.97-1.44-1.93-1.78-2.61-.71-1.41-.9-2.61-1.1-3.85-.15-.97-.37-2.35-.14-4.12.09-.67.22-1.22.32-1.61.56,0,1.35-.04,2.29-.17,1.54-.21,2.32-.32,2.88-.82.8-.71,1.27-2.03.83-3.01-.13-.3-.38-.61-1-1-1.52-.94-3.22-1.02-4.36-.94.11-.49.27-1.25.41-2.2.49-3.18.14-3.81-.14-4.12-.26-.3-.59-.43-1.24-.69-.4-.16-1.18-.47-2.2-.41Z"/>
|
||||
<path id="liteecho-e" class="cls-25" d="m515.41,469.49c-1.24,0-1.82.11-2.17.53-.63.76-.29,2.24.31,3.15,1.51,2.31,5.53,2.25,7.8,1.77,1.4-.3,4.49-.94,5.98-3.77,1.7-3.21-.04-6.62-.4-7.27-1.86-3.4-5.47-4.29-7.14-4.7-3.37-.83-6.16.05-7.27.4-1.07.34-2.81.91-4.53,2.4-3.45,2.98-3.92,7.09-4,8-.27,3.05.7,5.35,1,6,.27.6,1.17,2.42,3,4,1.04.89,2.16,1.53,3,2,.99.56,1.75.89,2,1,1.02.44,1.88.81,3,1,1.17.2,2.07.1,3,0,.34-.04,1.85-.22,4-1,3.3-1.2,4.59-2.54,5-3,.47-.53.68-.9.71-1.34.06-1.15-1.21-2.31-2.48-2.62-.51-.12-.94-.08-1.23-.05-2.01.26-3.04,1.28-4.31,1.86-2.59,1.19-5.84.41-7.69-.86-1.16-.8-1.95-1.93-2-2-.26-.38-.69-1.07-1-2-.26-.77-.95-2.86,0-5,.82-1.85,2.41-2.69,3-3,2.34-1.23,5.06-1.05,7,0,.69.37,1.64.9,2,2,.29.87.26,2.23-.52,2.84-1,.8-2.36-.34-6.07-.36Z"/>
|
||||
<path id="liteecho-c" class="cls-29" d="m570,456c-1.16,0-4.12.04-7,2-1.54,1.05-2.49,2.26-3,3-1.62,2.38-1.87,4.7-2,6-.12,1.26-.36,4,1,7,2.14,4.72,6.61,6.52,8,7,1.04.36,4.93,1.64,9,0,.94-.38,2.74-1.13,4-3,.45-.66,1.99-2.94,1-5-.54-1.12-1.88-2.25-3-2-1.41.32-1.04,2.48-3,4-1.92,1.48-4.45,1.09-5,1-.54-.08-2.42-.41-4-2-2.4-2.42-2.04-5.66-2-6,.07-.51.43-3.37,3-5,2.25-1.43,5.33-1.38,7,0,.93.77.92,1.55,2,2,1.26.52,3.2.27,4-1,.72-1.15.17-2.56,0-3-.15-.38-.69-1.63-3-3-.98-.58-3.45-2.01-7-2Z"/>
|
||||
<path id="liteecho-h" class="cls-17" d="m590,439c-.92-.49-2.08-.44-3,0-1.8.87-1.99,2.91-2,3v9c0,4,.04,8,0,12-.03,3,.1,6,0,9-.1,3.04-.29,5.38,1,8,.45.91,1.42,2.89,3,3,1.63.11,2.9-1.85,3-2,1.44-2.28.03-4.1,0-9-.02-2.85,0-5.82,2-8,.37-.4,1.8-1.97,4-2,2.44-.04,3.91,1.89,4,2,.92,1.22,1,2.52,1,3-.33,1.67-.67,3.33-1,5-.41,1.12-.91,2.85-1,5-.09,2.3-.16,3.99,1,5,1.28,1.11,3.61,1.02,5,0,1.65-1.21,1.8-3.61,2-7,.08-1.27.04-2.31,0-3v-8c-.02-.84-.16-2.36-1-4-.51-1-1.73-2.93-4-4-3.65-1.72-7.65-.14-8,0-1.5.62-2.24,1.36-3,1-.71-.34-.94-1.4-1-4-.12-5.04.61-6.66,0-10-.56-3.06-1.54-3.76-2-4Z"/>
|
||||
<path id="liteecho-o" class="cls-33" d="m625,458c-4.27-.18-7.17,2.27-8,3-3.05,2.66-3.79,6-4,7-.18.88-1.21,5.84,2,10,2.08,2.69,4.82,3.62,6,4,1.29.42,4.46,1.39,8,0,2.95-1.16,4.51-3.31,5-4,1.43-2.02,1.81-3.97,2-5,.46-2.51.1-4.49,0-5-.67-3.35-2.52-5.47-3-6-.69-.76-3.46-3.8-8-4Z"/>
|
||||
<path id="liteecho-o-white" class="cls-35" d="m623.5,464.5c-2.49.24-3.75,2.55-4,3-.09.17-1.81,3.42,0,6,1.68,2.39,5.6,3.06,8,1,2.1-1.8,2.13-4.93,1-7-.84-1.54-2.7-3.22-5-3Z"/>
|
||||
<path id="lite-jp-2" class="cls-13" d="m481,384c-.71.63-1.32,1.81-1,3,.38,1.44,1.86,1.95,2,2,.77.25,1.44.12,2,0,4.88-1.04,5.31-1.31,6-1,1.18.54,1.54,2.08,2,4,.41,1.72.73,3.08,0,4-.65.81-2.11.89-5,1-1.25.05-2.29.03-3,0-.53.14-1.96.61-3,2-.4.54-1.21,1.61-1,3,.24,1.62,1.7,2.78,3,3,.44.08.79.03,1,0,4.59-.65,6.89-.98,7-1,2.55-.43,3.84-.64,4.45-.34,1.26.62,1.41,1.86,2.55,5.34,0,0,1.48,4.5,4,10,.8,1.75,1.53,3.13,2,4,.16.13,2.42,1.9,5,1,.16-.06,2.44-.88,3-3,.21-.78.1-1.42,0-2-.27-1.54-.96-2.26-2-4-.12-.21.03.04-2-4-.67-1.33-1.32-2.67-2-4-1.91-3.76-2.3-4.3-2-5,.2-.46.49-.53,4-2,1.13-.48,2.45-1.03,4-2,1.13-.71,1.71-1.21,2-2,.06-.17.35-1.03,0-2-.48-1.32-1.88-2.03-3-2-.31,0-.54.07-.66.11-2.2.63-4.77,1.67-5.34,1.89-2.21.88-3.31,1.31-4,1-.42-.19-.77-.54-2-4-1.21-3.42-1.11-3.77-1-4,.42-.85,1.37-.79,6-2,2.46-.64,3.57-1.02,4-2,.3-.68.14-1.39,0-2-.09-.4-.54-2.17-2-3-1.17-.66-2.4-.38-4,0-3.65.88-4.88,2.55-6,2-.55-.27-1.15-1.13-1-4-.42-2-.76-3-1-3h0c-.76-1.48-2.4-2.29-4-2-1.51.27-2.7,1.47-3,3-.17,2.45.41,4.01,1,5,.57.95,1.14,1.36,1,2-.24,1.14-2.45,1.64-4,2-2.38.55-3.11.2-4,1Z"/>
|
||||
<path id="yuki-jp-1" class="cls-5" d="m526,396c-.46,1.12-.62,2.69,0,4,.19.41.79,1.49,2,2,1.22.51,2.43.16,3,0,5-1.43,7.51-2.15,8-2,4.42,1.34,3.64,20.08,7,22,2.14,1.22,4,1,4,1,1.43-.17,3.03-.97,3.02-1,0,0,0,0-.02,0,.27-.2.65-.52,1-1,1.53-2.12.85-5.32-1-10-3.3-8.33-6.24-11.07-5-13,.71-1.1,1.84-.46,5.57-2.36,2.51-1.27,4.06-2.06,4.43-3.64.46-1.98-1.11-4.46-3-5-1.01-.29-2.23.21-4.65,1.22-3.32,1.39-3.58,2.02-4.35,1.78-2.25-.7-.9-6.34-4-8-1.22-.65-2.84-.54-4,0-.31.14-1.56.73-2,2-.24.7-.14,1.29,0,2,.51,2.66,2.08,3.57,2,5.99,0,0,0,0,0,0,.01,0,.02,0,.02,0,.01.1-10.37-.01-12.02,4Z"/>
|
||||
<path id="yuki-jp-2-3" class="cls-31" d="m603,386c-.23-.2-.95-.74-2-1-.58-.14-1.39-.34-2,0-.79.44-.86,1.6-1,3,0,0-.62,6.23-2,9-.43.86-.97,1.66-.97,1.66-.12.18-.24.35-.35.5-.2.27-.42.55-.68.84-1.47,1.68-5.93,4.2-5.93,4.2-3.23,1.82-3.66,2.04-4.07,2.8-.61,1.13-.9,2.9,0,4,.8.97,2.17.99,3,1,.7,0,3.05-.07,7.17-3.17,1.64-1.24,3.78-2.89,5.83-5.83,3.15-4.53,3.72-8.76,4-11,.52-4.2-.4-5.48-1-6Z"/>
|
||||
<path id="liteecho-e2" class="cls-14" d="m540.81,469.18c-1.13.52-1.6.87-1.74,1.41-.25.96.69,2.15,1.62,2.72,2.35,1.44,5.96-.31,7.81-1.71,1.14-.86,3.66-2.76,3.81-5.96.17-3.63-2.85-5.97-3.45-6.41-3.13-2.29-6.78-1.55-8.46-1.22-3.41.68-5.55,2.66-6.41,3.45-.83.76-2.15,2.02-3.08,4.1-1.85,4.16-.53,8.09-.22,8.94,1.05,2.88,2.91,4.54,3.46,5,.5.42,2.09,1.69,4.42,2.34,1.32.37,2.61.46,3.57.53,1.13.08,1.96.06,2.24.05,1.11-.04,2.04-.06,3.14-.37,1.14-.32,1.92-.79,2.71-1.28.29-.18,1.58-.98,3.19-2.61,2.48-2.49,3.07-4.25,3.25-4.84.2-.68.24-1.11.07-1.51-.43-1.07-2.08-1.58-3.36-1.31-.51.11-.88.32-1.13.48-1.71,1.09-2.2,2.45-3.11,3.52-1.84,2.18-5.11,2.86-7.32,2.49-1.39-.23-2.59-.92-2.66-.96-.4-.23-1.08-.67-1.76-1.38-.56-.59-2.08-2.19-2.13-4.52-.04-2.02,1.04-3.46,1.44-3.99,1.59-2.11,4.14-3.1,6.33-2.98.79.04,1.86.12,2.66.96.63.67,1.18,1.91.74,2.79-.57,1.15-2.28.69-5.65,2.26Z"/>
|
||||
</g>
|
||||
<circle class="cls-23" cx="535" cy="411" r="4"/>
|
||||
<ellipse class="cls-11" cx="556.49" cy="380.5" rx="2.51" ry="2.5"/>
|
||||
<ellipse class="cls-26" cx="549.49" cy="383.5" rx="2.51" ry="2.5"/>
|
||||
<circle class="cls-10" cx="557.98" cy="404.39" r="4"/>
|
||||
<circle class="cls-1" cx="582.42" cy="391.26" r="4"/>
|
||||
<circle class="cls-6" cx="590.42" cy="385.26" r="4"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 31 KiB |
93
docs/.vuepress/public/js/geo.js
Normal file
@ -0,0 +1,93 @@
|
||||
echart = require('echarts');
|
||||
let chart = echarts.init(document.getElementById('main-chart'));
|
||||
const color = ['#9ae5fc', '#dcbf71']; // 自定义图中要用到的颜色
|
||||
console.log("加载图标");
|
||||
// 在地图加载完成后设置所有地区不可选
|
||||
function setAllRegionsUnselectable(geoModel) {
|
||||
const regions = geoModel.get('regions');
|
||||
|
||||
// 遍历所有地区并设置selected为false
|
||||
for (let i = 0; i < regions.length; i++) {
|
||||
const region = regions[i];
|
||||
region.selected = false;
|
||||
}
|
||||
|
||||
// 更新模型以反映更改
|
||||
geoModel.set('regions', regions);
|
||||
|
||||
// 更新图表以显示更改
|
||||
chart.setOption({
|
||||
geo: {
|
||||
regions: regions
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取数据并初始化图表
|
||||
fetch('https://api.liteyuki.icu/distribution')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 构造 ECharts 需要的数据格式
|
||||
const locations = data.locations;
|
||||
const seriesData = locations.map(location => ({
|
||||
value: [location[1], location[0]] // 直接使用经纬度数组
|
||||
}));
|
||||
console.log(seriesData);
|
||||
// 初始化图表选项
|
||||
chart.setOption({
|
||||
backgroundColor: '#454545',
|
||||
title: {
|
||||
text: 'LiteyukiBot分布demo',
|
||||
subtext: 'LiteyukiBot分布',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 20
|
||||
},
|
||||
top: '10px',
|
||||
left: '10px'
|
||||
},
|
||||
geo: {
|
||||
map: 'world',
|
||||
roam: false,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#000',
|
||||
borderType: null, // 设置边界线类型为无
|
||||
borderColor: '#000', // 设置边界线颜色
|
||||
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#000',
|
||||
borderType: null, // 设置边界线类型为无
|
||||
borderColor: '#000', // 设置边界线颜色
|
||||
}
|
||||
},
|
||||
regions: [] // 先保留为空
|
||||
},
|
||||
series: [{
|
||||
// 散点效果
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo', // 表示使用的坐标系为地理坐标系
|
||||
symbolSize: 5, // 设置散点的大小为20
|
||||
itemStyle: {
|
||||
color: '#ffeb3b', // 黄色
|
||||
},
|
||||
data: seriesData
|
||||
}],
|
||||
textStyle: {
|
||||
fontSize: 1
|
||||
}
|
||||
});
|
||||
|
||||
// 在地图加载完成后设置所有地区不可选
|
||||
chart.on('ready', function () {
|
||||
const geoModel = chart.getModel().componentModels.geo[0];
|
||||
setAllRegionsUnselectable(geoModel);
|
||||
});
|
||||
|
||||
// 自适应窗口大小变化
|
||||
window.addEventListener("resize", function () {
|
||||
chart.resize();
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('Error fetching data:', error));
|
39
docs/.vuepress/public/js/get_data.js
Normal file
@ -0,0 +1,39 @@
|
||||
// 定义全局变量来存储数据
|
||||
let globalTotal = 0;
|
||||
let globalOnline = 0;
|
||||
|
||||
// 从API获取数据并更新全局变量
|
||||
function fetchAndUpdateData() {
|
||||
Promise.all([
|
||||
fetch("https://api.liteyuki.icu/count").then(res => res.json()),
|
||||
fetch("https://api.liteyuki.icu/online").then(res => res.json())
|
||||
])
|
||||
.then(([countRes, onlineRes]) => {
|
||||
globalTotal = countRes.register;
|
||||
globalOnline = onlineRes.online;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error fetching data:", err);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新页面显示,使用全局变量中的数据
|
||||
function updatePageDisplay() {
|
||||
let countInfo = document.getElementById("count-info");
|
||||
if (!countInfo) {
|
||||
let info = `<div id="count-info" style="text-align: center; font-size: 20px; font-weight: 500">
|
||||
全球实例:<span id="total">${globalTotal}</span> 当前在线:<span id="online">${globalOnline}</span></div>`;
|
||||
let mainDescription = document.querySelector("#main-description");
|
||||
if (mainDescription) {
|
||||
mainDescription.insertAdjacentHTML('afterend', info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始调用更新数据
|
||||
fetchAndUpdateData();
|
||||
updatePageDisplay();
|
||||
|
||||
// 设置定时器,分别以不同频率调用更新数据和更新页面的函数
|
||||
setInterval(fetchAndUpdateData, 10000); // 每10秒更新一次数据
|
||||
setInterval(updatePageDisplay, 1000); // 每1秒更新一次页面显示
|
31
docs/.vuepress/public/js/style.js
Normal file
@ -0,0 +1,31 @@
|
||||
function applyStyle() {
|
||||
// 先检测页面中是否有macos-tab,有则不再添加
|
||||
let tabs = document.body.querySelectorAll('.macos-tab')
|
||||
if (tabs.length > 0) {
|
||||
return
|
||||
}
|
||||
let lineNumbers = document.body.querySelectorAll('[class^="language-"].line-numbers-mode')
|
||||
lineNumbers.forEach((item) => {
|
||||
// 插入现成的html文本
|
||||
let title = item.getAttribute('data-title')
|
||||
let tabStr =
|
||||
"<div class='tab macos-tab' style='display: flex; background-color: #d0e9ff'>" +
|
||||
" <div class='tab-buttons'>" +
|
||||
" <div class='tab-button' style='background-color: #FF5F57'></div>" +
|
||||
" <div class='tab-button' style='background-color: #FFBD2E'></div>" +
|
||||
" <div class='tab-button' style='background-color: #27C93F'></div>" +
|
||||
" </div>" +
|
||||
` <div class='tab-title'>${title}</div>` +
|
||||
" <div style='flex: 1'></div>" +
|
||||
"</div>"
|
||||
// 在代码块前插入选项卡
|
||||
item.insertAdjacentHTML('beforebegin', tabStr);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
applyStyle()
|
||||
// 定时器,每隔1s检查一次
|
||||
setInterval(() => {
|
||||
applyStyle()
|
||||
}, 1000)
|
1116
docs/.vuepress/public/js/world.js
Normal file
@ -4,19 +4,19 @@ export default sidebar({
|
||||
"/": [
|
||||
"",
|
||||
{
|
||||
text: "项目部署",
|
||||
text: "安装及部署",
|
||||
icon: "laptop-code",
|
||||
prefix: "deployment/",
|
||||
children: "structure",
|
||||
},
|
||||
{
|
||||
text: "使用手册",
|
||||
text: "使用及开发",
|
||||
icon: "book",
|
||||
prefix: "usage/",
|
||||
children: "structure",
|
||||
},
|
||||
{
|
||||
text: "商店",
|
||||
text: "资源及插件",
|
||||
icon: "store",
|
||||
prefix: "store/",
|
||||
children: "structure",
|
||||
|
@ -1,5 +1,10 @@
|
||||
// place your custom styles here
|
||||
|
||||
:root {
|
||||
--code-window-border-radius: 10px;
|
||||
--button-distance: 8px;
|
||||
}
|
||||
|
||||
#main-title {
|
||||
font-family: ColorTube, "Fira Code", serif;
|
||||
color: #ff0000 !important; /* 你想要的颜色 */
|
||||
@ -8,9 +13,88 @@
|
||||
|
||||
@font-face {
|
||||
font-family: ColorTube;
|
||||
src: url("/assets/fonts/ColorTube.woff") format("woff")
|
||||
src: url("/assets/fonts/ColorTube.woff") format("woff")
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Fira Code", monospace !important;
|
||||
}
|
||||
|
||||
.vp-hero-image {
|
||||
overflow: hidden;
|
||||
padding: -50px;
|
||||
}
|
||||
|
||||
#main-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-hope-content pre {
|
||||
overflow: auto;
|
||||
margin: 0 0;
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
line-height: 1.375;
|
||||
}
|
||||
|
||||
// 移除该before
|
||||
.theme-hope-content pre::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.theme-hope-content > div[class*=language-] {
|
||||
margin: 0 0 0 0;
|
||||
// 仅下半部分有圆弧
|
||||
border-radius: 0 0 var(--code-window-border-radius) var(--code-window-border-radius);
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
height: 25px;
|
||||
margin-bottom: 0;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top-left-radius: var(--code-window-border-radius);
|
||||
border-top-right-radius: var(--code-window-border-radius);
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
padding: 7px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
height: 60%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
margin-right: var(--button-distance);
|
||||
border-radius: 50%;
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
border-radius: 100px;
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-box-div {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
width: 80%;
|
||||
}
|
@ -8,14 +8,14 @@ export default hopeTheme({
|
||||
|
||||
author: {
|
||||
name: "远野千束",
|
||||
url: "https://snowykami.me",
|
||||
url: "https://sfkm.me",
|
||||
},
|
||||
|
||||
iconAssets: "fontawesome-with-brands",
|
||||
|
||||
logo: "https://cdn.liteyuki.icu/static/img/liteyuki_icon_640.png",
|
||||
|
||||
repo: "https://github.com/snowykami/LiteyukiBot",
|
||||
repo: "https://github.com/LiteyukiStudio/LiteyukiBot",
|
||||
|
||||
docsDir: "docs",
|
||||
|
||||
@ -38,7 +38,7 @@ export default hopeTheme({
|
||||
|
||||
// 多语言配置
|
||||
metaLocales: {
|
||||
editLink: "在 GitHub 上编辑此页",
|
||||
editLink: "在 GitHub 上编辑",
|
||||
},
|
||||
|
||||
// 如果想要实时查看任何改变,启用它。注: 这对更新性能有很大负面影响
|
||||
@ -62,6 +62,7 @@ export default hopeTheme({
|
||||
|
||||
// 此处开启了很多功能用于演示,你应仅保留用到的功能。
|
||||
mdEnhance: {
|
||||
echarts: true,
|
||||
alert: true,
|
||||
align: true,
|
||||
attrs: true,
|
||||
|
@ -2,17 +2,18 @@
|
||||
home: true
|
||||
icon: home
|
||||
title: 首页
|
||||
heroImage: https://cdn.liteyuki.icu/static/img/logo.png
|
||||
heroImage: https://cdn.liteyuki.icu/static/svg/lylogo-full.svg
|
||||
heroImageDark: https://cdn.liteyuki.icu/static/svg/lylogo-full-dark.svg
|
||||
bgImage:
|
||||
bgImageDark:
|
||||
bgImageStyle:
|
||||
background-attachment: fixed
|
||||
heroText: LiteyukiBot
|
||||
tagline: 轻雪机器人,一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人
|
||||
|
||||
tagline: LiteyukiBot 轻雪机器人,基于NoneBot2构建的综合应用型聊天机器人
|
||||
|
||||
actions:
|
||||
- text: 快速部署
|
||||
icon: lightbulb
|
||||
icon: rocket
|
||||
link: ./deployment/install.html
|
||||
type: primary
|
||||
|
||||
@ -21,7 +22,6 @@ actions:
|
||||
link: ./usage/basic_command.html
|
||||
|
||||
highlights:
|
||||
|
||||
- header: 简洁至上
|
||||
image: /assets/image/layout.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg
|
||||
@ -30,71 +30,54 @@ highlights:
|
||||
background-repeat: repeat
|
||||
background-size: initial
|
||||
features:
|
||||
- title: 基于Nonebot2
|
||||
- title: 基于NoneBot2
|
||||
icon: robot
|
||||
details: 拥有良好的生态支持
|
||||
link: https://nonebot.dev/
|
||||
|
||||
- title: 可视化插件管理
|
||||
- title: 便捷管理
|
||||
icon: plug
|
||||
details: 使用<code>npm</code>,无需命令行操作即可安装/卸载插件
|
||||
|
||||
- title: 点击交互
|
||||
icon: mouse-pointer
|
||||
details: 新的点击交互模式,拒绝手打指令
|
||||
details: 使用包管理器,便捷管理插件及资源包
|
||||
|
||||
- title: 主题支持
|
||||
icon: paint-brush
|
||||
details: 支持多种主题,可自定义资源包,满足你的审美需求
|
||||
details: 使用资源包对外观进行完全自定义
|
||||
link: https://bot.liteyuki.icu/usage/resource_pack.html
|
||||
|
||||
- title: 国际化
|
||||
icon: globe
|
||||
details: 支持多种语言,包括i18n部分语言和自行扩展的语言代码
|
||||
details: 通过资源包支持多种语言
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: 简易配置
|
||||
- title: 简易使用
|
||||
icon: cog
|
||||
details: 无需过多配置,开箱即用
|
||||
details: 无需繁琐前期过程,开箱即用
|
||||
link: https://bot.liteyuki.icu/deployment/config.html
|
||||
|
||||
- title: 低占用
|
||||
icon: memory
|
||||
details: 使用更少的依赖和资源
|
||||
- title: 超高性能
|
||||
icon: tachometer-alt
|
||||
details: 500个插件,2s内启动
|
||||
|
||||
- title: OneBot标准
|
||||
icon: link
|
||||
details: 支持OneBotv11/12标准的四种通信协议
|
||||
link: https://onebot.dev/
|
||||
|
||||
- title: Alconna命令解析
|
||||
icon: link
|
||||
details: 使用Alconna实现高效命令解析
|
||||
link: https://github.com/nonebot/plugin-alconna
|
||||
|
||||
- title: 便捷更新
|
||||
- title: 滚动更新
|
||||
icon: cloud-download
|
||||
details: 聊天窗口命令更新,无需手动下载
|
||||
details: 让你的机器人保持最新提交
|
||||
|
||||
- title: 服务支持
|
||||
icon: server
|
||||
details: 内置轻雪API,可自动收集错误,提供图床服务
|
||||
|
||||
- title: 开源
|
||||
- title: 开源项目
|
||||
icon: code
|
||||
details: 项目遵循MIT协议开源,欢迎各位的贡献
|
||||
details: 项目遵循MIT LICENCE开源,欢迎各位的贡献
|
||||
|
||||
- header: 快速部署
|
||||
image: /assets/image/box.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
|
||||
highlights:
|
||||
- title: 安装 Git 和 Python3.10+
|
||||
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至本地。
|
||||
details: 如果无法连接到GitHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code>。
|
||||
- title: 安装 Git 及 Python3.10+
|
||||
- title: 使用 <code>git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1</code> 以克隆项目至本地。
|
||||
details: 如果无法连接到GitHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot --depth=1</code>。
|
||||
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
|
||||
- title: 使用 <code>pip install -r requirements.txt</code> 安装项目依赖。
|
||||
details: 如果你有多个 Python 环境,请使用 <code>pythonx -m pip install -r requirements.txt</code>。
|
||||
- title: 使用 <code>python main.py</code> 启动项目。
|
||||
copyright: © 2021-2024 SnowyKami All Rights Reserved
|
||||
|
||||
---
|
||||
|
86
docs/childrensdaybak.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
home: true
|
||||
icon: home
|
||||
title: 首页
|
||||
heroImage: https://cdn.liteyuki.icu/static/img/logo.png
|
||||
bgImage:
|
||||
bgImageDark:
|
||||
bgImageStyle:
|
||||
background-attachment: fixed
|
||||
heroText: LiteyukiBot
|
||||
tagline: 轻雪机器人,一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人
|
||||
|
||||
actions:
|
||||
- text: 快速部署
|
||||
icon: lightbulb
|
||||
link: ./deployment/install.html
|
||||
type: primary
|
||||
|
||||
- text: 使用手册
|
||||
icon: book
|
||||
link: ./usage/basic_command.html
|
||||
|
||||
highlights:
|
||||
|
||||
- header: 简洁至上
|
||||
image: /assets/image/layout.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/2-dark.svg
|
||||
bgImageStyle:
|
||||
background-repeat: repeat
|
||||
background-size: initial
|
||||
features:
|
||||
- title: 基于Nonebot2
|
||||
icon: robot
|
||||
details: 拥有良好的生态支持
|
||||
link: https://nonebot.dev/
|
||||
|
||||
- title: 便捷插件管理
|
||||
icon: plug
|
||||
details: 使用<code>包管理器</code>,无需命令行操作即可安装/卸载插件
|
||||
|
||||
- title: 人性化交互
|
||||
icon: mouse-pointer
|
||||
details: 新的点击交互模式,拒绝手打指令
|
||||
|
||||
- title: 主题支持
|
||||
icon: paint-brush
|
||||
details: 使用资源包对外观进行完全自定义
|
||||
link: https://bot.liteyuki.icu/usage/resource_pack.html
|
||||
|
||||
- title: 国际化
|
||||
icon: globe
|
||||
details: 通过资源包支持多种语言
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: 简易配置
|
||||
icon: cog
|
||||
details: 无需繁琐前期过程,开箱即用
|
||||
link: https://bot.liteyuki.icu/deployment/config.html
|
||||
|
||||
- title: 高性能
|
||||
icon: tachometer-alt
|
||||
details: 500个插件,3s内启动
|
||||
|
||||
- title: 滚动更新
|
||||
icon: cloud-download
|
||||
details: 让你的机器人保持最新提交
|
||||
|
||||
- title: 开源
|
||||
icon: code
|
||||
details: 项目遵循MIT协议开源,欢迎各位的贡献
|
||||
|
||||
- header: 快速部署
|
||||
image: /assets/image/box.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
|
||||
highlights:
|
||||
- title: 安装 Git 和 Python3.10+
|
||||
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至本地。
|
||||
details: 如果无法连接到GitHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code>。
|
||||
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
|
||||
- title: 使用 <code>pip install -r requirements.txt</code> 安装项目依赖。
|
||||
details: 如果你有多个 Python 环境,请使用 <code>pythonx -m pip install -r requirements.txt</code>。
|
||||
- title: 使用 <code>python main.py</code> 启动项目。
|
||||
copyright: © 2021-2024 SnowyKami All Rights Reserved
|
||||
---
|
@ -8,7 +8,7 @@ tag:
|
||||
- 部署
|
||||
---
|
||||
|
||||
首次运行后生成`config.yml`,你可以修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`和`nickname`字段即可
|
||||
首次运行后生成`config.yml`,你可以修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`及`nickname`字段即可
|
||||
|
||||
## **基础配置项**
|
||||
|
||||
@ -29,11 +29,12 @@ onebot_access_token: "" # 访问令牌,对公开放时建议设置
|
||||
default_language: "zh-CN" # 默认语言
|
||||
alconna_auto_completion: false # alconna是否自动补全指令,默认false,建议开启
|
||||
# 开发者选项
|
||||
allow_update: true # 是否允许更新
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
debug: false # 轻雪调试,开启后在调试时修改代码或资源会自动重载相应内容
|
||||
debug: false # 轻雪调试,开启会自动重载Bot或者资源,其他插件自带的调试功能也将开启
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方插件
|
||||
# 其他Nonebot插件的配置项
|
||||
custom_config_1: "custom_value1"
|
||||
|
@ -7,44 +7,54 @@ tag:
|
||||
- 安装
|
||||
---
|
||||
|
||||
|
||||
|
||||
## **开始安装**
|
||||
### **常规方法**
|
||||
1. 安装 [`Git`](https://git-scm.com/download/) 和 [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) 环境
|
||||
2. 克隆项目 `git clone https://github.com/snowykami/LiteyukiBot`
|
||||
3. 进入轻雪目录 `cd LiteyukiBot`
|
||||
4. 安装依赖 `pip install -r requirements.txt`
|
||||
5. 启动 `python main.py`
|
||||
|
||||
### **使用Docker(测试中)**
|
||||
### **常规部署**
|
||||
|
||||
1. 安装 [`Git`](https://git-scm.com/download/) 和 [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) 环境
|
||||
|
||||
```bash
|
||||
# 克隆项目到本地,轻雪使用Git进行版本管理,该步骤为必要项
|
||||
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
|
||||
# 切换到Bot目录下
|
||||
cd LiteyukiBot
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
# 启动Bot
|
||||
python main.py
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> 推荐使用虚拟环境来运行轻雪,以避免依赖冲突,你可以使用`python -m venv venv`来创建虚拟环境,然后使用`venv\Scripts\activate`来激活虚拟环境
|
||||
|
||||
### **使用Docker构建镜像部署**
|
||||
|
||||
1. 安装 [`Docker`](https://docs.docker.com/get-docker/)
|
||||
2. 克隆项目 `git clone https://github.com/snowykami/LiteyukiBot`
|
||||
2. 克隆项目 `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
3. 进入轻雪目录 `cd LiteyukiBot`
|
||||
4. 构建镜像 `docker build -t liteyukibot .`
|
||||
5. 启动容器 `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
|
||||
|
||||
|
||||
> [!tip]
|
||||
> Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br>
|
||||
> 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号
|
||||
|
||||
### **使用TRSS Scripts部署**
|
||||
[TRSS_Liteyuki轻雪机器人管理脚本](https://timerainstarsky.github.io/TRSS_Liteyuki/),该功能由TRSS提供支持,不是LiteyukiBot官方提供的功能,推荐使用`Arch Linux`
|
||||
|
||||
|
||||
## **设备要求**
|
||||
|
||||
- Windows系统版本最低`Windows10+`/`Windows Server 2019+`
|
||||
- Linux系统要支持Python3.10+,推荐`Ubuntu 20.04+`(~~别用你那b CentOS~~)
|
||||
- CPU: 至少`1vCPU`
|
||||
- 内存: Bot无其他插件会占用`200~300MB`,其他插件占用视具体插件而定,建议`1GB`以上
|
||||
- 内存: Bot无其他插件会占用`300~500MB`,包括`chromium` 及 `node`等进程,其他插件占用视具体插件而定,建议`1GB`以上
|
||||
- 硬盘: 至少`1GB`空间
|
||||
|
||||
> [!warning]
|
||||
> 如果设备上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`为你的Python可执行文件路径
|
||||
|
||||
> [!tip]
|
||||
> 推荐使用虚拟环境来运行轻雪,以避免依赖冲突,你可以使用`python -m venv venv`来创建虚拟环境,然后使用`venv\Scripts\activate`来激活虚拟环境
|
||||
|
||||
> [!warning]
|
||||
> 轻雪的更新功能依赖Git,如果你没有安装Git,你将无法使用更新功能
|
||||
> 轻雪的更新功能依赖Git,如果你没有安装Git直接下载源代码运行,你将无法使用更新功能
|
||||
|
||||
#### 其他问题请移步至[答疑](/deployment/fandq)
|
||||
|
||||
[//]: # (#### 想在Linux命令行中拥有更好的体验?试试[TRSS_Liteyuki轻雪机器人管理脚本](https://timerainstarsky.github.io/TRSS_Liteyuki/),该功能仅供参考,不是LiteyukiBot官方提供的功能)
|
||||
#### 其他问题请移步至[答疑](/deployment/fandq)
|
@ -2,14 +2,15 @@
|
||||
home: true
|
||||
icon: home
|
||||
title: 首页
|
||||
heroImage: https://cdn.liteyuki.icu/static/img/logo.png
|
||||
heroImage: https://cdn.liteyuki.icu/static/img/lykwi.png
|
||||
bgImage:
|
||||
bgImageDark:
|
||||
bgImageStyle:
|
||||
background-attachment: fixed
|
||||
heroText: HeavyyukiBot666 # LiteyukiBot 6
|
||||
tagline: 重雪机器人,一个以笨重和复杂为设计理念基于Koishi114514的OneBotv1919810标准聊天机器人,可用于雪地清扫,使用Typethon编写
|
||||
heroText: HeavylavaBot666 # LiteyukiBot 6
|
||||
tagline: 重浆机器人,一个以笨重和复杂为设计理念基于Koishi114514的TwoBotv1919810标准聊天机器人,可用于雪地清扫,使用Typethon编写
|
||||
#tagline: 轻雪机器人,一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人
|
||||
# 泰普森(X
|
||||
|
||||
actions:
|
||||
- text: 快速结束 # 快速开始
|
||||
@ -39,22 +40,22 @@ highlights:
|
||||
features:
|
||||
- title: 基于Koishi.js233
|
||||
icon: robot
|
||||
details: 拥有良好的生态支持
|
||||
details: 拥有辣鸡的生态支持
|
||||
link: https://nonebot.dev/
|
||||
|
||||
- title: 盲目插件管理
|
||||
icon: plug
|
||||
details: 基于nbshi使用<code>npmx和pip</code>,让你无法安装/卸载插件
|
||||
details: 基于nbshi使用<code>xmpn和bib</code>,让你无法安装/卸载插件
|
||||
|
||||
- title: 点击无法交互
|
||||
- title: 纯命令行
|
||||
icon: mouse-pointer
|
||||
details: 老的的点击交互模式,必须手打指令
|
||||
details: 老的掉牙的交互模式,必须手打指令
|
||||
|
||||
- title: 猪蹄支持
|
||||
icon: paint-brush
|
||||
details: 支持多种主题,可自定义资源包,满足你的审美需求
|
||||
details: 支持多种烤猪蹄样式,丢弃烤箱,拥抱烧烤架,满足你的干饭需求
|
||||
|
||||
- title: 非国际化
|
||||
- title: 去国际化
|
||||
icon: globe
|
||||
details: 支持多种语言,包括i18n部分语言和自行扩展的语言代码
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
@ -80,27 +81,27 @@ highlights:
|
||||
|
||||
- title: 不准更新
|
||||
icon: cloud-download
|
||||
details: 要更新自己下新版本去
|
||||
details: 要更新自己写新版本去
|
||||
|
||||
- title: 服务支持
|
||||
icon: server
|
||||
details: 内置轻雪API,但随时可能跑路
|
||||
details: 内置重浆API,但随时可能提桶跑路
|
||||
|
||||
- title: 闭源
|
||||
icon: code
|
||||
details: 要源代码自己逆向去
|
||||
|
||||
- header: 快速部署
|
||||
- header: 慢速部署 # 快速部署
|
||||
image: /assets/image/box.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
|
||||
highlights:
|
||||
- title: 安装 Git 和 node.js+
|
||||
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至本地。
|
||||
details: 如果无法连接到GitHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code>。
|
||||
- title: 安装 winget 和 nothing.js # git & node.js+
|
||||
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至FTP。 # 本地
|
||||
details: 如果无法连接到PoonHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code>。
|
||||
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
|
||||
- title: 使用 <code>npm install -r requirements.txt</code> 安装项目依赖。
|
||||
details: 如果你有多个 node.js 环境,请使用 <code>pythonx -m npm install -r requirements.txt</code>。
|
||||
details: 如果你有多个 nothing.js 环境,请使用 <code>pythonx -m npm install -r requirements.txt</code>。
|
||||
- title: 使用 <code>node main.py</code> 启动项目。
|
||||
copyright: © 2021-2024 SnowyKami All Rights Reserved
|
||||
---
|
@ -20,7 +20,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clipboard": "^2.0.11",
|
||||
"echarts": "^5.5.1",
|
||||
"element-plus": "^2.7.0",
|
||||
"element-ui": "^2.15.14"
|
||||
"element-ui": "^2.15.14",
|
||||
"vue-echarts": "7.0.0-beta.0",
|
||||
"vue-router": "^4.4.0"
|
||||
}
|
||||
}
|
||||
|
80
docs/pnpm-lock.yaml
generated
@ -8,12 +8,21 @@ dependencies:
|
||||
clipboard:
|
||||
specifier: ^2.0.11
|
||||
version: 2.0.11
|
||||
echarts:
|
||||
specifier: ^5.5.1
|
||||
version: 5.5.1
|
||||
element-plus:
|
||||
specifier: ^2.7.0
|
||||
version: 2.7.0(vue@3.4.21)
|
||||
element-ui:
|
||||
specifier: ^2.15.14
|
||||
version: 2.15.14(vue@3.4.21)
|
||||
vue-echarts:
|
||||
specifier: 7.0.0-beta.0
|
||||
version: 7.0.0-beta.0(echarts@5.5.1)(vue@3.4.21)
|
||||
vue-router:
|
||||
specifier: ^4.4.0
|
||||
version: 4.4.0(vue@3.4.21)
|
||||
|
||||
devDependencies:
|
||||
'@vuepress/bundler-vite':
|
||||
@ -33,7 +42,7 @@ devDependencies:
|
||||
version: 2.0.0-rc.34(vuepress@2.0.0-rc.9)
|
||||
vuepress-theme-hope:
|
||||
specifier: 2.0.0-rc.32
|
||||
version: 2.0.0-rc.32(@vuepress/plugin-search@2.0.0-rc.24)(markdown-it@14.1.0)(vuepress-plugin-search-pro@2.0.0-rc.34)(vuepress@2.0.0-rc.9)
|
||||
version: 2.0.0-rc.32(@vuepress/plugin-search@2.0.0-rc.24)(echarts@5.5.1)(markdown-it@14.1.0)(vuepress-plugin-search-pro@2.0.0-rc.34)(vuepress@2.0.0-rc.9)
|
||||
|
||||
packages:
|
||||
|
||||
@ -716,6 +725,7 @@ packages:
|
||||
resolution: {integrity: sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -724,6 +734,7 @@ packages:
|
||||
resolution: {integrity: sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -732,6 +743,7 @@ packages:
|
||||
resolution: {integrity: sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -740,6 +752,7 @@ packages:
|
||||
resolution: {integrity: sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -748,6 +761,7 @@ packages:
|
||||
resolution: {integrity: sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -756,6 +770,7 @@ packages:
|
||||
resolution: {integrity: sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -938,7 +953,6 @@ packages:
|
||||
|
||||
/@vue/devtools-api@6.6.1:
|
||||
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
|
||||
dev: true
|
||||
|
||||
/@vue/reactivity@3.4.21:
|
||||
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
||||
@ -985,7 +999,7 @@ packages:
|
||||
rollup: 4.13.1
|
||||
vite: 5.2.6
|
||||
vue: 3.4.21
|
||||
vue-router: 4.3.0(vue@3.4.21)
|
||||
vue-router: 4.4.0(vue@3.4.21)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@ -1021,7 +1035,7 @@ packages:
|
||||
'@vue/devtools-api': 6.6.1
|
||||
'@vuepress/shared': 2.0.0-rc.9
|
||||
vue: 3.4.21
|
||||
vue-router: 4.3.0(vue@3.4.21)
|
||||
vue-router: 4.4.0(vue@3.4.21)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
dev: true
|
||||
@ -1719,6 +1733,12 @@ packages:
|
||||
domhandler: 5.0.3
|
||||
dev: true
|
||||
|
||||
/echarts@5.5.1:
|
||||
resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==}
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
zrender: 5.6.0
|
||||
|
||||
/electron-to-chromium@1.4.719:
|
||||
resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==}
|
||||
dev: true
|
||||
@ -2656,6 +2676,9 @@ packages:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/tslib@2.3.0:
|
||||
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||
|
||||
/uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
dev: true
|
||||
@ -2725,6 +2748,21 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.13.11(vue@3.4.21):
|
||||
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
dependencies:
|
||||
vue: 3.4.21
|
||||
dev: false
|
||||
|
||||
/vue-demi@0.14.7(vue@3.4.21):
|
||||
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -2739,14 +2777,30 @@ packages:
|
||||
dependencies:
|
||||
vue: 3.4.21
|
||||
|
||||
/vue-router@4.3.0(vue@3.4.21):
|
||||
resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
|
||||
/vue-echarts@7.0.0-beta.0(echarts@5.5.1)(vue@3.4.21):
|
||||
resolution: {integrity: sha512-DKmPak9+Al/GlIexC5OvvWIDyO39q/AmZm2aqF7+n2fTDyUq7l/dkdmg/9hBeLafZlU6Yf5J7io/BnDzzRK1+g==}
|
||||
peerDependencies:
|
||||
'@vue/runtime-core': ^3.0.0
|
||||
echarts: ^5.5.1
|
||||
vue: ^2.7.0 || ^3.1.1
|
||||
peerDependenciesMeta:
|
||||
'@vue/runtime-core':
|
||||
optional: true
|
||||
dependencies:
|
||||
echarts: 5.5.1
|
||||
vue: 3.4.21
|
||||
vue-demi: 0.13.11(vue@3.4.21)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
dev: false
|
||||
|
||||
/vue-router@4.4.0(vue@3.4.21):
|
||||
resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.1
|
||||
vue: 3.4.21
|
||||
dev: true
|
||||
|
||||
/vue@3.4.21:
|
||||
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
|
||||
@ -2805,7 +2859,7 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/vuepress-plugin-md-enhance@2.0.0-rc.32(markdown-it@14.1.0)(vuepress@2.0.0-rc.9):
|
||||
/vuepress-plugin-md-enhance@2.0.0-rc.32(echarts@5.5.1)(markdown-it@14.1.0)(vuepress@2.0.0-rc.9):
|
||||
resolution: {integrity: sha512-zZK8aEfbq26J5w8o9xGWXCHHrL3PYk25tloTPcx96nZWYPeD+5fMFAtVpHte0rXBWUf0MBtDQxddSeATteBE7Q==}
|
||||
engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
|
||||
peerDependencies:
|
||||
@ -2882,6 +2936,7 @@ packages:
|
||||
'@vuepress/helper': 2.0.0-rc.21(vuepress@2.0.0-rc.9)
|
||||
'@vueuse/core': 10.9.0(vue@3.4.21)
|
||||
balloon-css: 1.2.0
|
||||
echarts: 5.5.1
|
||||
js-yaml: 4.1.0
|
||||
vue: 3.4.21
|
||||
vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.9)(vue@3.4.21)
|
||||
@ -2999,7 +3054,7 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/vuepress-theme-hope@2.0.0-rc.32(@vuepress/plugin-search@2.0.0-rc.24)(markdown-it@14.1.0)(vuepress-plugin-search-pro@2.0.0-rc.34)(vuepress@2.0.0-rc.9):
|
||||
/vuepress-theme-hope@2.0.0-rc.32(@vuepress/plugin-search@2.0.0-rc.24)(echarts@5.5.1)(markdown-it@14.1.0)(vuepress-plugin-search-pro@2.0.0-rc.34)(vuepress@2.0.0-rc.9):
|
||||
resolution: {integrity: sha512-5S5qg5xa0TErqVjpubhBN5oy0QmROd+ja5EQKfORUCKdXKQvx0soojQZnCPkXsUEXonwiZ12oCLN2+UGO03eng==}
|
||||
engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
|
||||
peerDependencies:
|
||||
@ -3059,7 +3114,7 @@ packages:
|
||||
vue: 3.4.21
|
||||
vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.9)(vue@3.4.21)
|
||||
vuepress-plugin-components: 2.0.0-rc.32(vuepress@2.0.0-rc.9)
|
||||
vuepress-plugin-md-enhance: 2.0.0-rc.32(markdown-it@14.1.0)(vuepress@2.0.0-rc.9)
|
||||
vuepress-plugin-md-enhance: 2.0.0-rc.32(echarts@5.5.1)(markdown-it@14.1.0)(vuepress@2.0.0-rc.9)
|
||||
vuepress-plugin-sass-palette: 2.0.0-rc.32(vuepress@2.0.0-rc.9)
|
||||
vuepress-plugin-search-pro: 2.0.0-rc.34(vuepress@2.0.0-rc.9)
|
||||
vuepress-shared: 2.0.0-rc.32(vuepress@2.0.0-rc.9)
|
||||
@ -3178,3 +3233,8 @@ packages:
|
||||
y18n: 4.0.3
|
||||
yargs-parser: 18.1.3
|
||||
dev: true
|
||||
|
||||
/zrender@5.6.0:
|
||||
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
@ -4,4 +4,5 @@ icon: box
|
||||
order: 1
|
||||
category: 使用手册
|
||||
---
|
||||
<resourceStoreComp />
|
||||
|
||||
<resourceStoreComp />
|
||||
|
@ -7,8 +7,8 @@ category: 使用手册
|
||||
|
||||
1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息
|
||||
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
|
||||
- 运行环境的设备信息:CPU,内存,系统信息,Python信息
|
||||
- 插件信息(不含插件数据),
|
||||
- 运行环境的设备信息:CPU,内存,系统信息及Python信息
|
||||
- 插件信息(不含插件数据)
|
||||
- 部分异常信息,
|
||||
- 会话负载信息(不含隐私部分)
|
||||
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`
|
||||
|
@ -5,98 +5,120 @@ order: 1
|
||||
category: 使用手册
|
||||
---
|
||||
|
||||
## 基础插件
|
||||
|
||||
### **轻雪 `liteyuki`**
|
||||
|
||||
```shell
|
||||
# 仅超级用户
|
||||
reload-liteyuki # 重载轻雪
|
||||
update-liteyuki # 更新轻雪
|
||||
liteecho # 查看当前bot
|
||||
status # 查看统计信息和状态
|
||||
config set <key> value # 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的
|
||||
config get [key] # 查询配置项,不带key返回配置项列表,推荐私聊使用
|
||||
switch-image-mode # 在普通图片和Markdown大图之间切换,该功能需要commit:505468b及以后的Lagrange.OneBot,
|
||||
/api api_name [args] # 调用机器人API,例如/api get_group_member_list group_id=1234567,空格用%20
|
||||
# 仅超级用户,群聊仅群主、管理员、超级用户可用
|
||||
group enable/disable [group_id] # 在群聊启用/停用机器人,group_id仅超级用户可用
|
||||
# 所有人可用
|
||||
liteyuki-docs # 查看轻雪文档
|
||||
```
|
||||
|
||||
命令别名
|
||||
|
||||
```shell
|
||||
status 状态,
|
||||
reload-liteyuki 重启轻雪,
|
||||
update-liteyuki 更新轻雪,
|
||||
reload-resources 重载资源,
|
||||
config 配置 | set 设置 | get 查询,
|
||||
switch-image-mode 切换图片模式,
|
||||
liteyuki-docs 轻雪文档
|
||||
group 群聊 | enable 启用 | disable 停用
|
||||
```
|
||||
|
||||
### **插件/包管理器 `liteyuki_pacman`**
|
||||
|
||||
- 插件管理
|
||||
|
||||
```shell
|
||||
# 仅超级用户
|
||||
npm update # 更新插件商店索引
|
||||
npm install <plugin_name> # 安装插件
|
||||
npm uninstall <plugin_name> # 卸载插件
|
||||
npm search <keywords...> # 通过关键词搜索插件
|
||||
npm enable-global/disable-global <plugin_name> # 全局启用/停用插件
|
||||
|
||||
# 群聊仅群主、管理员、超级用户可用,私聊所有人可用
|
||||
npm enable/disable <plugin_name> # 当前会话启用/停用插件
|
||||
npm list [page] [num] # 列出所有插件 page为页数,num为每页显示数量
|
||||
|
||||
# 所有人
|
||||
help <plugin_name> # 查看插件帮助
|
||||
```
|
||||
|
||||
- 资源包管理
|
||||
|
||||
```shell
|
||||
# 仅超级用户
|
||||
rpm list [page] [num] # 列出所有资源包 page为页数,num为每页显示数量
|
||||
rpm load <pack_name> # 加载资源包
|
||||
rpm unload <pack_name> # 卸载资源包
|
||||
rpm change <pack_name> # 修改优先级
|
||||
rpm reload # 重载所有资源包
|
||||
```
|
||||
|
||||
命令别名
|
||||
|
||||
```shell
|
||||
npm 插件管理 | update 更新 | install 安装 | uninstall 卸载 | search 搜索
|
||||
enable 启用 | disable 停用 | enable-global 全局启用 | disable-global 全局停用
|
||||
rpm 资源包 | load 加载 | unload 卸载 | change 更改 | reload 重载 | list 列表
|
||||
help 帮助
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> 受限于NoneBot2钩子函数的依赖注入参数,插件停用只能阻断传入响应,对于主动推送的插件不生效,请阅读插件主页的说明。
|
||||
|
||||
### **用户管理`liteyuki_user`**
|
||||
|
||||
```shell
|
||||
# 所有人可用
|
||||
profile # 查看用户信息菜单
|
||||
profile set <key> [value] # 设置用户信息或打开属性设置菜单
|
||||
profile get <key> # 获取用户信息
|
||||
```
|
||||
|
||||
命令别名
|
||||
|
||||
```shell
|
||||
profile 个人信息 | set 设置 | get 查询
|
||||
```
|
||||
# 基础插件
|
||||
---
|
||||
|
||||
> [!tip]
|
||||
> **参数**:`<param>`为必填参数,`[option]`为可选参数。
|
||||
>
|
||||
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。
|
||||
|
||||
## **轻雪命令`liteyuki_command`**
|
||||
|
||||
|
||||
| 命令 | 说明 | 权限 | 举例 | 可用参数 |
|
||||
| :----------------------------------------: | :---------------------------------------------------------------------------------------------: | :----------------------------------------: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------: |
|
||||
| `reload-liteyuki` | 重载轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `update-liteyuki` | 更新轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `liteecho` | 查看当前bot 版本 | 超级用户 | ❌ | ❌ |
|
||||
| `status` | 查看统计信息和状态 | 超级用户 | ❌ | ❌ |
|
||||
| `config set <key> value` | 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的 | 超级用户 | `config set name 'liteyuki-bot'` | `<key>`: 若存在则覆盖, 若不存在则创建于`config.yml` ; `value`: yml格式的所有合法内容 |
|
||||
| `config get [key] ` | 查询配置项,不带key返回配置项列表,推荐私聊使用 | 超级用户 | `config get name` | `<key>`: 若存在则返回, 若不存在则返回空 |
|
||||
| `switch-image-mode ` | 在普通图片和Markdown大图之间切换,该功能需要commit:505468b及以后的Lagrange.OneBot,默认普通图片 | 超级用户 | `switch-image-mode` | ❌ |
|
||||
| `/api api_name [args] ` | 调用机器人API | 超级用户 | `/api get_group_member_list group_id=1234567` | `<args>`: 参数列表, 格式为onebot v11协议api, 可用%20代替空格 |
|
||||
| `/function function_name [args] [kwargs] ` | 调用机器人函数(`.lyfunction`语法) | 超级用户 | `/function send_group_msg group_id=1234567 message='hello'` | `<args>`和`<kwargs>`: 参数列表, api格式为onebot v11协议api |
|
||||
| group enable/disable [group_id] | 在群聊启用/停用机器人,group_id仅超级用户可用 | 超级用户,群聊仅群主、管理员、超级用户可用 | `group enable 1145141919810` | `<group_id>`: 群号 |
|
||||
| liteyuki-docs | 查看轻雪文档 | 所有人 | ❌ | ❌ |
|
||||
|
||||
|
||||
---
|
||||
### **命令别名**
|
||||
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :---------------: | :----------------------------------: |
|
||||
| status | 状态 |
|
||||
| reload-liteyuki | 重启轻雪 |
|
||||
| update-liteyuki | 更新轻雪 |
|
||||
| reload-resources | 重载资源 |
|
||||
| config | 配置, `set` 设置 / `get` 查询 |
|
||||
| switch-image-mode | 切换图片模式 |
|
||||
| liteyuki-docs | 轻雪文档 |
|
||||
| group | 群聊, `enable` 启用 / `disable` 停用 |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **插件/包管理器 `liteyuki_pacman`**
|
||||
|
||||
- 插件管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-----------------------------------------------------: | :----------------------------------------: | :----------------------------------------------: |
|
||||
| `npm update` | 更新插件商店索引 | 超级用户 |
|
||||
| `npm install <plugin_name>` | 安装插件 | 超级用户 |
|
||||
| `npm uninstall <plugin_name>` | 卸载插件 | 超级用户 |
|
||||
| `npm search <keywords...>` | 通过关键词搜索插件 | 超级用户 |
|
||||
| `npm enable-global/disable-global <plugin_name>` | 全局启用/停用插件 | 超级用户 |
|
||||
| `npm enable/disable <plugin_name> [--group <group_id>]` | 当前会话启用/停用插件 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `npm list [page] [num]` | 列出所有插件 page为页数,num为每页显示数量 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `help <plugin_name>` | 查看插件帮助 | 所有人 |
|
||||
|
||||
|
||||
- 资源包管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :----------------------: | :------------------------------------------: | :------: |
|
||||
| `rpm list [page] [num]` | 列出所有资源包 page为页数,num为每页显示数量 | 超级用户 |
|
||||
| `rpm load <pack_name>` | 加载资源包 | 超级用户 |
|
||||
| `rpm unload <pack_name>` | 卸载资源包 | 超级用户 |
|
||||
| `rpm change <pack_name>` | 修改优先级 | 超级用户 |
|
||||
| `rpm reload` | 重载所有资源包 | 超级用户 |
|
||||
|
||||
|
||||
### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :--------------: | :------: |
|
||||
| `npm` | 插件管理 |
|
||||
| `update` | 更新 |
|
||||
| `install` | 安装 |
|
||||
| `uninstall` | 卸载 |
|
||||
| `search` | 搜索 |
|
||||
| `enable` | 启用 |
|
||||
| `disable` | 停用 |
|
||||
| `enable-global` | 全局启用 |
|
||||
| `disable-global` | 全局停用 |
|
||||
| `rpm` | 资源包 |
|
||||
| `load` | 加载 |
|
||||
| `unload` | 卸载 |
|
||||
| `change` | 更改 |
|
||||
| `reload` | 重载 |
|
||||
| `list` | 列表 |
|
||||
| `help` | 帮助 |
|
||||
|
||||
> [!warning]
|
||||
> 受限于NoneBot2钩子函数的依赖注入参数,插件停用只能阻断传入响应,对于主动推送的插件不生效,请阅读插件主页的说明。
|
||||
>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## **用户管理`liteyuki_user`**
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-------------------------: | :----------------------------: | :----: |
|
||||
| `profile` | 查看用户信息菜单 | 所有人 |
|
||||
| `profile set <key> [value]` | 设置用户信息或打开属性设置菜单 | 所有人 |
|
||||
| `profile get <key>` | 获取用户信息 | 所有人 |
|
||||
|
||||
|
||||
###命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------: |
|
||||
| `profile` | 个人信息 |
|
||||
| `set` | 设置 |
|
||||
| `get` | 查询 |
|
||||
|
||||
|
||||
|
@ -1,29 +1,70 @@
|
||||
---
|
||||
title: 功能命令
|
||||
icon: comment
|
||||
order: 2
|
||||
category: 使用手册
|
||||
---
|
||||
|
||||
## 功能插件命令
|
||||
|
||||
### **轻雪天气`liteyuki_weather`**
|
||||
|
||||
配置项
|
||||
|
||||
```yaml
|
||||
weather_key: "" # 和风天气的天气key,会自动判断key版本
|
||||
```
|
||||
|
||||
命令
|
||||
|
||||
```shell
|
||||
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
||||
```
|
||||
|
||||
命令别名
|
||||
|
||||
```shell
|
||||
weather 天气, bind-city 绑定城市
|
||||
```
|
||||
---
|
||||
title: 功能命令
|
||||
icon: comment
|
||||
order: 2
|
||||
category: 使用手册
|
||||
---
|
||||
|
||||
## 功能插件命令
|
||||
|
||||
### **轻雪天气`liteyuki_weather`**
|
||||
|
||||
查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
|
||||
|
||||
配置项
|
||||
|
||||
```yaml
|
||||
weather_key: "" # 和风天气的天气key,会自动判断key版本
|
||||
```
|
||||
|
||||
命令
|
||||
|
||||
```shell
|
||||
weather <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||
|
||||
```shell
|
||||
bind-city <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
|
||||
绑定查询城市,个人全局生效
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------- |
|
||||
| weather | 天气 |
|
||||
| bind-city | 绑定城市 |
|
||||
|
||||
---
|
||||
|
||||
### **统计信息`liteyuki_statistics`**
|
||||
|
||||
统计信息
|
||||
命令
|
||||
|
||||
```shell
|
||||
statistic message --duration <duration> --period <period> --group [current|group_id] --bot [current|bot_id]
|
||||
```
|
||||
|
||||
功能: 用于统计Bot接收到的消息, 统计周期为`period`, 统计时间范围为`duration`
|
||||
|
||||
| 参数 | 格式 |
|
||||
| :------: | :------------------------------------------------------------: |
|
||||
| duration | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| period | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| group | `current` (当前群聊) 或 `group_id` (QQ群号) |
|
||||
| bot | `current` (当前Bot) 或 `bot_id` |
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :----------: | :---: |
|
||||
| `statistic` | stat |
|
||||
| `message` | m |
|
||||
| `--duration` | -d |
|
||||
| --period` | -p |
|
||||
| `--group` | -g |
|
||||
| `--bot` | -b |
|
||||
| `current` | c |
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: 轻雪API
|
||||
icon: code
|
||||
order: 4
|
||||
order: 5
|
||||
category: 使用指南
|
||||
tag:
|
||||
- 配置
|
||||
@ -10,36 +10,4 @@ tag:
|
||||
|
||||
## **轻雪API**
|
||||
|
||||
轻雪API是轻雪运行中部分服务的支持,由`go`语言编写,例如错误反馈,图床链接等,目前服务由轻雪服务器提供,用户无需额外部署
|
||||
|
||||
接口
|
||||
|
||||
- `url` `https://api.liteyuki.icu`
|
||||
|
||||
- `POST` `/register` 注册一个Bot
|
||||
- 参数
|
||||
- `name` `string` Bot名称
|
||||
- `version` `string` Bot版本
|
||||
- `version_id` `int` Bot版本ID
|
||||
- `python` `string` Python版本
|
||||
- `os` `string` 操作系统
|
||||
- 返回
|
||||
- `code` `int` 状态码
|
||||
- `liteyuki_id` `string` 轻雪ID
|
||||
|
||||
- `POST` `/bug_report` 上报错误
|
||||
- 参数
|
||||
- `liteyuki_id` `string` 轻雪ID
|
||||
- `content` `string` 错误信息
|
||||
- `device_info` `string` 设备信息
|
||||
- 返回
|
||||
- `code` `int` 状态码
|
||||
- `report_id` `string` 错误ID
|
||||
|
||||
- `POST` `/upload_image` 图床上传
|
||||
- 参数
|
||||
- `image` `file` 图片文件
|
||||
- `liteyuki_id` `string` 轻雪ID,用于鉴权
|
||||
- 返回
|
||||
- `code` `int` 状态码
|
||||
- `url` `string` 图床链接
|
||||
轻雪API是轻雪运行中部分服务的支持,由`go`语言编写,例如错误反馈,图床链接等,目前服务由轻雪服务器提供,用户无需额外部署
|
72
docs/usage/lyfunc.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: 轻雪函数
|
||||
icon: code
|
||||
order: 4
|
||||
category: 使用指南
|
||||
tag:
|
||||
- 配置
|
||||
---
|
||||
|
||||
## **轻雪函数**
|
||||
|
||||
轻雪函数 Liteyuki Function 是轻雪的一个功能,它允许你在轻雪中运行一些自定义的由数据驱动的命令,类似于Minecraft的mcfunction.
|
||||
|
||||
### **函数文件**
|
||||
|
||||
函数文件放在资源包的`functions`目录下,文件名以`.mcfunction` `.lyfunction` `.lyf`结尾,例如`test.mcfunction`,文件内容为一系列的命令,每行一个命令,支持单行注释`#`(编辑时的语法高亮可采取`shell`格式),例如:
|
||||
|
||||
```shell
|
||||
# 在发信器输出"hello world"
|
||||
cmd echo hello world
|
||||
|
||||
# 如果你想同时输出多行内容可以尝试换行符(Python格式)
|
||||
cmd echo hello world\nLiteyuki bot
|
||||
```
|
||||
|
||||
也支持句末注释,例如:
|
||||
```shell
|
||||
cmd echo hello world # 输出"hello world"
|
||||
```
|
||||
|
||||
### **命令文档**
|
||||
|
||||
```shell
|
||||
var <var1=value1> [var2=value2] ... # 定义变量
|
||||
cmd <command> # 在设备上执行命令
|
||||
api <api_name> [var=value...] # 调用Bot API
|
||||
function <func_name> # 调用函数,可递归
|
||||
sleep <time> # 异步等待,单位s
|
||||
nohup <command> # 使用新的task执行命令,即不等待
|
||||
end # 结束函数关键字,包括子task
|
||||
await # 等待所有异步任务结束,若函数中启动了其他task,需要在最后调用,否则task对象会被销毁
|
||||
```
|
||||
|
||||
|
||||
#### **示例**
|
||||
|
||||
```shell
|
||||
# 疯狂戳好友
|
||||
# 使用 /function poke user_id=123456 执行
|
||||
# 每隔0.2s戳两次,无限戳,会触发最大递归深度限制
|
||||
# 若要戳20s后停止,则需要删除await,添加sleep 20和end
|
||||
api friend_poke user_id=user_id
|
||||
api friend_poke user_id=user_id
|
||||
sleep 0.2
|
||||
nohup function poke
|
||||
await
|
||||
```
|
||||
|
||||
### **API**
|
||||
|
||||
理论上所有基于onebotv11的api都可调用,不同Adapter api也有差别.
|
||||
|
||||
[Onebot v11 API文档](https://283375.github.io/onebot_v11_vitepress/api/index.html)
|
||||
|
||||
### **结束关键字**
|
||||
|
||||
由于LiteyukiBot基于异步运行, 所以在编写lyfunction时也要注意异步的调用,避免出现"单线程走到底"的情况是效率提升的关键.
|
||||
|
||||
`await` 异步任务结束关键字,用于结束当前已完成function的执行
|
||||
|
||||
> [!warning]
|
||||
> 但若出现非单function的情况,有一个task任务没有完成而await被执行了,那么当前所有函数包的task都会被截停销毁
|
@ -13,6 +13,9 @@ category: 使用手册
|
||||
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
|
||||
- 欢迎各位投稿资源包到轻雪资源商店
|
||||
|
||||
请注意,主题包中的html渲染使用Js来规定数据的渲染位置,请确保您所编写的html代码能被Bot解析,否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码,以避免出现无法预料的问题。
|
||||
|
||||
---
|
||||
## 加载资源包
|
||||
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
|
||||
```shell
|
||||
|
21
liteyuki/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
from liteyuki.bot import (
|
||||
LiteyukiBot,
|
||||
get_bot
|
||||
)
|
||||
|
||||
from liteyuki.comm import (
|
||||
Channel,
|
||||
chan,
|
||||
Event
|
||||
)
|
||||
|
||||
from liteyuki.plugin import (
|
||||
load_plugin,
|
||||
load_plugins
|
||||
)
|
||||
|
||||
from liteyuki.log import (
|
||||
logger,
|
||||
init_log
|
||||
|
||||
)
|
191
liteyuki/bot/__init__.py
Normal file
@ -0,0 +1,191 @@
|
||||
import threading
|
||||
import time
|
||||
import asyncio
|
||||
from typing import Any, Optional
|
||||
from multiprocessing import freeze_support
|
||||
|
||||
from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan)
|
||||
from liteyuki.comm.channel import Channel
|
||||
from liteyuki.core import IS_MAIN_PROCESS
|
||||
from liteyuki.core.manager import ProcessManager
|
||||
from liteyuki.core.spawn_process import mb_run, nb_run
|
||||
from liteyuki.log import init_log, logger
|
||||
from liteyuki.plugin import load_plugins
|
||||
from liteyuki.utils import run_coroutine
|
||||
|
||||
__all__ = [
|
||||
"LiteyukiBot",
|
||||
"get_bot"
|
||||
]
|
||||
|
||||
"""是否为主进程"""
|
||||
|
||||
|
||||
class LiteyukiBot:
|
||||
def __init__(self, *args, **kwargs):
|
||||
global _BOT_INSTANCE
|
||||
_BOT_INSTANCE = self # 引用
|
||||
self.config: dict[str, Any] = kwargs
|
||||
self.init(**self.config) # 初始化
|
||||
|
||||
self.lifespan: Lifespan = Lifespan()
|
||||
self.chan = Channel() # 进程通信通道
|
||||
self.pm: ProcessManager = ProcessManager(bot=self, chan=self.chan)
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.loop_thread = threading.Thread(target=self.loop.run_forever, daemon=True)
|
||||
|
||||
print("\033[34m" + r"""
|
||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
""" + "\033[0m")
|
||||
|
||||
def run(self):
|
||||
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
||||
|
||||
self.loop_thread.start() # 启动事件循环
|
||||
asyncio.run(self.lifespan.before_start()) # 启动前钩子
|
||||
|
||||
self.pm.add_target("nonebot", nb_run, **self.config)
|
||||
self.pm.start("nonebot")
|
||||
|
||||
self.pm.add_target("melobot", mb_run, **self.config)
|
||||
self.pm.start("melobot")
|
||||
|
||||
asyncio.run(self.lifespan.after_start()) # 启动后钩子
|
||||
|
||||
def restart(self, name: Optional[str] = None):
|
||||
"""
|
||||
停止轻雪
|
||||
Args:
|
||||
name: 进程名称, 默认为None, 所有进程
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.info("Stopping LiteyukiBot...")
|
||||
|
||||
self.loop.create_task(self.lifespan.before_shutdown()) # 重启前钩子
|
||||
self.loop.create_task(self.lifespan.before_shutdown()) # 停止前钩子
|
||||
|
||||
if name:
|
||||
self.chan.send(1, name)
|
||||
else:
|
||||
for name in self.pm.targets:
|
||||
self.chan.send(1, name)
|
||||
|
||||
def init(self, *args, **kwargs):
|
||||
"""
|
||||
初始化轻雪, 自动调用
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.init_config()
|
||||
self.init_logger()
|
||||
|
||||
def init_logger(self):
|
||||
# 修改nonebot的日志配置
|
||||
init_log(config=self.config)
|
||||
|
||||
def init_config(self):
|
||||
pass
|
||||
|
||||
def on_before_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动前的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_start(func)
|
||||
|
||||
def on_after_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_start(func)
|
||||
|
||||
def on_before_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册停止前的函数,为子进程停止时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_shutdown(func)
|
||||
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册停止后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_shutdown(func)
|
||||
|
||||
def on_before_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启前的函数,为子进程重启时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
return self.lifespan.on_before_restart(func)
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_restart(func)
|
||||
|
||||
def on_after_nonebot_init(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册nonebot初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_nonebot_init(func)
|
||||
|
||||
|
||||
_BOT_INSTANCE: Optional[LiteyukiBot] = None
|
||||
|
||||
|
||||
def get_bot() -> Optional[LiteyukiBot]:
|
||||
"""
|
||||
获取轻雪实例
|
||||
Returns:
|
||||
LiteyukiBot: 当前的轻雪实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return _BOT_INSTANCE
|
||||
else:
|
||||
# 从多进程上下文中获取
|
||||
pass
|
189
liteyuki/bot/lifespan.py
Normal file
@ -0,0 +1,189 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/23 下午8:24
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : lifespan.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from typing import Any, Awaitable, Callable, TypeAlias
|
||||
|
||||
from liteyuki.log import logger
|
||||
from liteyuki.utils import is_coroutine_callable
|
||||
|
||||
SYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Any]
|
||||
ASYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Awaitable[Any]]
|
||||
LIFESPAN_FUNC: TypeAlias = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC
|
||||
|
||||
|
||||
class Lifespan:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
轻雪生命周期管理,启动、停止、重启
|
||||
"""
|
||||
|
||||
self.life_flag: int = 0 # 0: 启动前,1: 启动后,2: 停止前,3: 停止后
|
||||
|
||||
self._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
@staticmethod
|
||||
async def _run_funcs(funcs: list[LIFESPAN_FUNC]) -> None:
|
||||
"""
|
||||
运行函数
|
||||
Args:
|
||||
funcs:
|
||||
Returns:
|
||||
"""
|
||||
for func in funcs:
|
||||
if is_coroutine_callable(func):
|
||||
await func()
|
||||
else:
|
||||
func()
|
||||
|
||||
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_start_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_start_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止前的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
|
||||
"""
|
||||
self._after_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_restart_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启后的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_restart_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_nonebot_init(self, func):
|
||||
"""
|
||||
注册 NoneBot 初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._after_nonebot_init_funcs.append(func)
|
||||
return func
|
||||
|
||||
async def before_start(self) -> None:
|
||||
"""
|
||||
启动前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running before_start functions")
|
||||
await self._run_funcs(self._before_start_funcs)
|
||||
|
||||
async def after_start(self) -> None:
|
||||
"""
|
||||
启动后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running after_start functions")
|
||||
await self._run_funcs(self._after_start_funcs)
|
||||
|
||||
async def before_shutdown(self) -> None:
|
||||
"""
|
||||
停止前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running before_shutdown functions")
|
||||
await self._run_funcs(self._before_shutdown_funcs)
|
||||
|
||||
async def after_shutdown(self) -> None:
|
||||
"""
|
||||
停止后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running after_shutdown functions")
|
||||
await self._run_funcs(self._after_shutdown_funcs)
|
||||
|
||||
async def before_restart(self) -> None:
|
||||
"""
|
||||
重启前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running before_restart functions")
|
||||
await self._run_funcs(self._before_restart_funcs)
|
||||
|
||||
async def after_restart(self) -> None:
|
||||
"""
|
||||
重启后
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.debug("Running after_restart functions")
|
||||
await self._run_funcs(self._after_restart_funcs)
|
||||
|
||||
async def after_nonebot_init(self) -> None:
|
||||
"""
|
||||
NoneBot 初始化后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running after_nonebot_init functions")
|
||||
await self._run_funcs(self._after_nonebot_init_funcs)
|
19
liteyuki/comm/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/26 下午10:36
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py
|
||||
@Software: PyCharm
|
||||
该模块用于轻雪主进程和Nonebot子进程之间的通信
|
||||
"""
|
||||
from liteyuki.comm.channel import Channel, chan
|
||||
from liteyuki.comm.event import Event
|
||||
|
||||
__all__ = [
|
||||
"Channel",
|
||||
"chan",
|
||||
"Event",
|
||||
]
|
139
liteyuki/comm/channel.py
Normal file
@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/26 下午11:21
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : channel.py
|
||||
@Software: PyCharm
|
||||
|
||||
本模块定义了一个通用的通道类,用于进程间通信
|
||||
"""
|
||||
from multiprocessing import Pipe
|
||||
from typing import Any, Optional, Callable, Awaitable, List, TypeAlias
|
||||
|
||||
from liteyuki.utils import is_coroutine_callable, run_coroutine
|
||||
|
||||
SYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[Any], Any]
|
||||
ASYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[Any], Awaitable[Any]]
|
||||
ON_RECEIVE_FUNC: TypeAlias = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC
|
||||
|
||||
SYNC_FILTER_FUNC: TypeAlias = Callable[[Any], bool]
|
||||
ASYNC_FILTER_FUNC: TypeAlias = Callable[[Any], Awaitable[bool]]
|
||||
FILTER_FUNC: TypeAlias = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC
|
||||
|
||||
|
||||
class Channel:
|
||||
"""
|
||||
通道类,用于进程间通信
|
||||
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.receive_conn, self.send_conn = Pipe()
|
||||
self._closed = False
|
||||
self._on_receive_funcs: List[ON_RECEIVE_FUNC] = []
|
||||
self._on_receive_funcs_with_receiver: dict[str, List[ON_RECEIVE_FUNC]] = {}
|
||||
|
||||
def send(self, data: Any, receiver: Optional[str] = None):
|
||||
"""
|
||||
发送数据
|
||||
Args:
|
||||
data: 数据
|
||||
receiver: 接收者,如果为None则广播
|
||||
"""
|
||||
if self._closed:
|
||||
raise RuntimeError("Cannot send to a closed channel")
|
||||
self.send_conn.send((data, receiver))
|
||||
|
||||
def receive(self, receiver: str = None) -> Any:
|
||||
"""
|
||||
接收数据
|
||||
Args:
|
||||
receiver: 接收者,如果为None则接收任意数据
|
||||
"""
|
||||
if self._closed:
|
||||
raise RuntimeError("Cannot receive from a closed channel")
|
||||
while True:
|
||||
# 判断receiver是否为None或者receiver是否等于接收者,是则接收数据,否则不动数据
|
||||
data, receiver_ = self.receive_conn.recv()
|
||||
if receiver is None or receiver == receiver_:
|
||||
self._run_on_receive_funcs(data, receiver_)
|
||||
return data
|
||||
self.send_conn.send((data, receiver_))
|
||||
|
||||
def peek(self) -> Optional[Any]:
|
||||
"""
|
||||
查看管道中的数据,不移除
|
||||
Returns:
|
||||
"""
|
||||
if self._closed:
|
||||
raise RuntimeError("Cannot peek from a closed channel")
|
||||
if self.receive_conn.poll():
|
||||
data, receiver = self.receive_conn.recv()
|
||||
self.receive_conn.send((data, receiver))
|
||||
return data
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
关闭通道
|
||||
"""
|
||||
self._closed = True
|
||||
self.receive_conn.close()
|
||||
self.send_conn.close()
|
||||
|
||||
def on_receive(self, filter_func: Optional[FILTER_FUNC] = None, receiver: Optional[str] = None) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
|
||||
"""
|
||||
接收数据并执行函数
|
||||
Args:
|
||||
filter_func: 过滤函数,为None则不过滤
|
||||
receiver: 接收者, 为None则接收任意数据
|
||||
Returns:
|
||||
装饰器,装饰一个函数在接收到数据后执行
|
||||
"""
|
||||
|
||||
def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC:
|
||||
async def wrapper(data: Any) -> Any:
|
||||
if filter_func is not None:
|
||||
if is_coroutine_callable(filter_func):
|
||||
if not await filter_func(data):
|
||||
return
|
||||
else:
|
||||
if not filter_func(data):
|
||||
return
|
||||
return await func(data)
|
||||
|
||||
if receiver is None:
|
||||
self._on_receive_funcs.append(wrapper)
|
||||
else:
|
||||
if receiver not in self._on_receive_funcs_with_receiver:
|
||||
self._on_receive_funcs_with_receiver[receiver] = []
|
||||
self._on_receive_funcs_with_receiver[receiver].append(wrapper)
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def _run_on_receive_funcs(self, data: Any, receiver: Optional[str] = None):
|
||||
"""
|
||||
运行接收函数
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
if receiver is None:
|
||||
for func in self._on_receive_funcs:
|
||||
run_coroutine(func(data))
|
||||
else:
|
||||
for func in self._on_receive_funcs_with_receiver.get(receiver, []):
|
||||
run_coroutine(func(data))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self) -> Any:
|
||||
return self.receive()
|
||||
|
||||
|
||||
"""默认通道实例,可直接从模块导入使用"""
|
||||
chan = Channel()
|
21
liteyuki/comm/event.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/26 下午10:47
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : event.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Event:
|
||||
"""
|
||||
事件类
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, data: dict[str, Any]):
|
||||
self.name = name
|
||||
self.data = data
|
11
liteyuki/core/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
import multiprocessing
|
||||
|
||||
from .spawn_process import *
|
||||
from .manager import *
|
||||
|
||||
__all__ = [
|
||||
"IS_MAIN_PROCESS"
|
||||
]
|
||||
|
||||
IS_MAIN_PROCESS = multiprocessing.current_process().name == "MainProcess"
|
||||
|
105
liteyuki/core/manager.py
Normal file
@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/27 上午11:12
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : manager.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import asyncio
|
||||
import threading
|
||||
from multiprocessing import Process
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from liteyuki.comm import Channel
|
||||
from liteyuki.log import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from liteyuki.bot import LiteyukiBot
|
||||
|
||||
TIMEOUT = 10
|
||||
|
||||
__all__ = [
|
||||
"ProcessManager"
|
||||
]
|
||||
|
||||
|
||||
class ProcessManager:
|
||||
"""
|
||||
在主进程中被调用
|
||||
"""
|
||||
|
||||
def __init__(self, bot: "LiteyukiBot", chan: Channel):
|
||||
self.bot = bot
|
||||
self.chan = chan
|
||||
self.targets: dict[str, tuple[callable, tuple, dict]] = {}
|
||||
self.processes: dict[str, Process] = {}
|
||||
|
||||
def start(self, name: str, delay: int = 0):
|
||||
"""
|
||||
开启后自动监控进程,并添加到进程字典中
|
||||
Args:
|
||||
name:
|
||||
delay:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
if name not in self.targets:
|
||||
raise KeyError(f"Process {name} not found.")
|
||||
|
||||
def _start():
|
||||
should_exit = False
|
||||
while not should_exit:
|
||||
process = Process(target=self.targets[name][0], args=(self.chan, *self.targets[name][1]), kwargs=self.targets[name][2])
|
||||
self.processes[name] = process
|
||||
process.start()
|
||||
while not should_exit:
|
||||
# 0退出 1重启
|
||||
data = self.chan.receive(name)
|
||||
if data == 1:
|
||||
logger.info(f"Restarting process {name}")
|
||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
||||
asyncio.run(self.bot.lifespan.before_restart())
|
||||
self.terminate(name)
|
||||
break
|
||||
|
||||
elif data == 0:
|
||||
logger.info(f"Stopping process {name}")
|
||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
||||
should_exit = True
|
||||
self.terminate(name)
|
||||
else:
|
||||
logger.warning("Unknown data received, ignored.")
|
||||
|
||||
if delay:
|
||||
threading.Timer(delay, _start).start()
|
||||
else:
|
||||
threading.Thread(target=_start).start()
|
||||
|
||||
def add_target(self, name: str, target, *args, **kwargs):
|
||||
self.targets[name] = (target, args, kwargs)
|
||||
|
||||
def join(self):
|
||||
for name, process in self.targets:
|
||||
process.join()
|
||||
|
||||
def terminate(self, name: str):
|
||||
"""
|
||||
终止进程并从进程字典中删除
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.targets:
|
||||
raise logger.warning(f"Process {name} not found.")
|
||||
process = self.processes[name]
|
||||
process.terminate()
|
||||
process.join(TIMEOUT)
|
||||
if process.is_alive():
|
||||
process.kill()
|
14
liteyuki/core/nb/adapter_manager/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
from . import (
|
||||
satori,
|
||||
onebot
|
||||
)
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
onebot.init()
|
||||
satori.init(config)
|
||||
|
||||
|
||||
def register():
|
||||
onebot.register()
|
||||
satori.register()
|
12
liteyuki/core/nb/adapter_manager/onebot.py
Normal file
@ -0,0 +1,12 @@
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot import v11, v12
|
||||
|
||||
|
||||
def init():
|
||||
pass
|
||||
|
||||
|
||||
def register():
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(v11.Adapter)
|
||||
driver.register_adapter(v12.Adapter)
|
26
liteyuki/core/nb/adapter_manager/satori.py
Normal file
@ -0,0 +1,26 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import nonebot
|
||||
from nonebot.adapters import satori
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
if config.get("satori", None) is None:
|
||||
nonebot.logger.info("Satori config not found, skip Satori init.")
|
||||
return None
|
||||
satori_config = config.get("satori")
|
||||
if not satori_config.get("enable", False):
|
||||
nonebot.logger.info("Satori not enabled, skip Satori init.")
|
||||
return None
|
||||
if os.getenv("SATORI_CLIENTS", None) is not None:
|
||||
nonebot.logger.info("Satori clients already set in environment variable, skip.")
|
||||
os.environ["SATORI_CLIENTS"] = json.dumps(satori_config.get("hosts", []), ensure_ascii=False)
|
||||
config['satori_clients'] = satori_config.get("hosts", [])
|
||||
return
|
||||
|
||||
|
||||
def register():
|
||||
if os.getenv("SATORI_CLIENTS", None) is not None:
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(satori.Adapter)
|
6
liteyuki/core/nb/driver_manager/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .auto_set_env import auto_set_env
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
auto_set_env(config)
|
||||
return
|
20
liteyuki/core/nb/driver_manager/auto_set_env.py
Normal file
@ -0,0 +1,20 @@
|
||||
import os
|
||||
|
||||
import dotenv
|
||||
import nonebot
|
||||
|
||||
from .defines import *
|
||||
|
||||
|
||||
def auto_set_env(config: dict):
|
||||
dotenv.load_dotenv(".env")
|
||||
if os.getenv("DRIVER", None) is not None:
|
||||
nonebot.logger.info("Driver already set in environment variable, skip auto configure.")
|
||||
return
|
||||
if config.get("satori", {'enable': False}).get("enable", False):
|
||||
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER, HTTPX_DRIVER, WEBSOCKETS_DRIVER)
|
||||
nonebot.logger.info("Enable Satori, set driver to ASGI+HTTPX+WEBSOCKETS")
|
||||
else:
|
||||
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER)
|
||||
nonebot.logger.info("Disable Satori, set driver to ASGI")
|
||||
return
|
17
liteyuki/core/nb/driver_manager/defines.py
Normal file
@ -0,0 +1,17 @@
|
||||
ASGI_DRIVER = "~fastapi"
|
||||
HTTPX_DRIVER = "~httpx"
|
||||
WEBSOCKETS_DRIVER = "~websockets"
|
||||
|
||||
|
||||
def get_driver_string(*argv):
|
||||
output_string = ""
|
||||
if ASGI_DRIVER in argv:
|
||||
output_string += ASGI_DRIVER
|
||||
for arg in argv:
|
||||
if arg != ASGI_DRIVER:
|
||||
output_string = f"{output_string}+{arg}"
|
||||
return output_string
|
||||
|
||||
|
||||
def get_driver_full_string(*argv):
|
||||
return f"DRIVER={get_driver_string(argv)}"
|
51
liteyuki/core/spawn_process.py
Normal file
@ -0,0 +1,51 @@
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
import nonebot
|
||||
|
||||
from liteyuki.core.nb import adapter_manager, driver_manager
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from liteyuki.comm.channel import Channel
|
||||
|
||||
timeout_limit: int = 20
|
||||
|
||||
"""导出对象,用于主进程与nonebot通信"""
|
||||
chan_in_spawn_nb: Optional["Channel"] = None
|
||||
|
||||
|
||||
def nb_run(chan, *args, **kwargs):
|
||||
"""
|
||||
初始化NoneBot并运行在子进程
|
||||
Args:
|
||||
|
||||
chan:
|
||||
*args:
|
||||
**kwargs:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
global chan_in_spawn_nb
|
||||
chan_in_spawn_nb = chan
|
||||
nonebot.init(**kwargs)
|
||||
driver_manager.init(config=kwargs)
|
||||
adapter_manager.init(kwargs)
|
||||
adapter_manager.register()
|
||||
nonebot.load_plugin("src.liteyuki_main")
|
||||
nonebot.run()
|
||||
|
||||
|
||||
def mb_run(chan, *args, **kwargs):
|
||||
"""
|
||||
初始化MeloBot并运行在子进程
|
||||
Args:
|
||||
chan
|
||||
*args:
|
||||
**kwargs:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# bot = MeloBot(__name__)
|
||||
# bot.init(AbstractConnector(cd_time=0))
|
||||
# bot.run()
|
10
liteyuki/exception.py
Normal file
@ -0,0 +1,10 @@
|
||||
"""exception模块包含了liteyuki运行中的所有错误
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class LiteyukiException(BaseException):
|
||||
"""Liteyuki的异常基类。"""
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
@ -1,35 +0,0 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from .core import *
|
||||
from .loader import *
|
||||
from .dev_tools import *
|
||||
|
||||
__author__ = "snowykami"
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="轻雪核心插件",
|
||||
description="轻雪主程序插件,包含了许多初始化的功能",
|
||||
usage="",
|
||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||
extra={
|
||||
"liteyuki" : True,
|
||||
"toggleable": False,
|
||||
}
|
||||
)
|
||||
|
||||
from ..utils.base.language import Language, get_default_lang_code
|
||||
|
||||
print("\033[34m" + r"""
|
||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
""" + "\033[0m")
|
||||
|
||||
sys_lang = Language(get_default_lang_code())
|
||||
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
||||
# nonebot.logger.info(sys_lang.get("main.enable_webdash", URL=f"http://127.0.0.1:{config.get('port', 20216)}"))
|
@ -1,41 +0,0 @@
|
||||
import nonebot
|
||||
from git import Repo
|
||||
|
||||
remote_urls = [
|
||||
"https://github.com/snowykami/LiteyukiBot.git",
|
||||
"https://gitee.com/snowykami/LiteyukiBot.git"
|
||||
]
|
||||
|
||||
|
||||
def detect_update() -> bool:
|
||||
# 对每个远程仓库进行检查,只要有一个仓库有更新,就返回True
|
||||
for remote_url in remote_urls:
|
||||
repo = Repo(".")
|
||||
repo.remotes.origin.set_url(remote_url)
|
||||
repo.remotes.origin.fetch()
|
||||
if repo.head.commit != repo.commit('origin/main'):
|
||||
return True
|
||||
|
||||
|
||||
def update_liteyuki() -> tuple[bool, str]:
|
||||
"""更新轻雪
|
||||
:return: 是否更新成功,更新变动"""
|
||||
|
||||
new_commit_detected = detect_update()
|
||||
if new_commit_detected:
|
||||
repo = Repo(".")
|
||||
logs = ""
|
||||
# 对每个远程仓库进行更新
|
||||
for remote_url in remote_urls:
|
||||
try:
|
||||
logs += f"\nremote: {remote_url}"
|
||||
repo.remotes.origin.set_url(remote_url)
|
||||
repo.remotes.origin.pull()
|
||||
diffs = repo.head.commit.diff("origin/main")
|
||||
for diff in diffs.iter_change_type('M'):
|
||||
logs += f"\n{diff.a_path}"
|
||||
return True, logs
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
return False, "Nothing Changed"
|
@ -1,43 +0,0 @@
|
||||
import time
|
||||
|
||||
import nonebot
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from liteyuki.utils.base.config import get_config
|
||||
from liteyuki.utils.base.reloader import Reloader
|
||||
from liteyuki.utils.base.resource import load_resources
|
||||
|
||||
if get_config("debug", False):
|
||||
nonebot.logger.info("Liteyuki Reload is enable, watching for file changes...")
|
||||
|
||||
|
||||
class CodeModifiedHandler(FileSystemEventHandler):
|
||||
def on_modified(self, event):
|
||||
if "liteyuki/resources" not in event.src_path.replace("\\", "/"):
|
||||
nonebot.logger.debug(f"{event.src_path} has been modified, reloading bot...")
|
||||
Reloader.reload()
|
||||
|
||||
|
||||
class ResourceModifiedHandler(FileSystemEventHandler):
|
||||
def on_modified(self, event):
|
||||
if not event.is_directory:
|
||||
nonebot.logger.debug(f"{event.src_path} has been modified, reloading resource...")
|
||||
load_resources()
|
||||
|
||||
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
resource_modified_handle = ResourceModifiedHandler()
|
||||
|
||||
observer = Observer()
|
||||
observer.schedule(resource_modified_handle, path="liteyuki/resources", recursive=True)
|
||||
observer.schedule(resource_modified_handle, path="resources", recursive=True)
|
||||
observer.schedule(code_modified_handler, path="liteyuki", recursive=True)
|
||||
observer.start()
|
||||
|
||||
# try:
|
||||
# while True:
|
||||
# time.sleep(1)
|
||||
# except KeyboardInterrupt:
|
||||
# observer.stop()
|
||||
# observer.join()
|
@ -1,27 +0,0 @@
|
||||
import nonebot.plugin
|
||||
|
||||
from liteyuki.utils import init_log
|
||||
from liteyuki.utils.base.config import get_config
|
||||
from liteyuki.utils.base.data_manager import InstalledPlugin, plugin_db
|
||||
from liteyuki.utils.base.resource import load_resources
|
||||
from liteyuki.utils.message.tools import check_for_package
|
||||
|
||||
load_resources()
|
||||
init_log()
|
||||
|
||||
nonebot.plugin.load_plugins("liteyuki/plugins")
|
||||
# 从数据库读取已安装的插件
|
||||
if not get_config("safe_mode", False):
|
||||
# 安全模式下,不加载插件
|
||||
|
||||
installed_plugins: list[InstalledPlugin] = plugin_db.all(InstalledPlugin())
|
||||
if installed_plugins:
|
||||
for installed_plugin in installed_plugins:
|
||||
if not check_for_package(installed_plugin.module_name):
|
||||
nonebot.logger.error(f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
|
||||
else:
|
||||
nonebot.load_plugin(installed_plugin.module_name)
|
||||
|
||||
nonebot.plugin.load_plugins("plugins")
|
||||
else:
|
||||
nonebot.logger.info("Safe mode is on, no plugin loaded.")
|
84
liteyuki/log.py
Normal file
@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/27 上午9:12
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : log.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import sys
|
||||
import loguru
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
logger = loguru.logger
|
||||
if TYPE_CHECKING:
|
||||
# avoid sphinx autodoc resolve annotation failed
|
||||
# because loguru module do not have `Logger` class actually
|
||||
from loguru import Record
|
||||
|
||||
|
||||
def default_filter(record: "Record"):
|
||||
"""默认的日志过滤器,根据 `config.log_level` 配置改变日志等级。"""
|
||||
log_level = record["extra"].get("nonebot_log_level", "INFO")
|
||||
levelno = logger.level(log_level).no if isinstance(log_level, str) else log_level
|
||||
return record["level"].no >= levelno
|
||||
|
||||
|
||||
# DEBUG日志格式
|
||||
debug_format: str = (
|
||||
"<c>{time:YYYY-MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}]</lvl> "
|
||||
"<c><{name}.{module}.{function}:{line}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
# 默认日志格式
|
||||
default_format: str = (
|
||||
"<c>{time:MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}]</lvl> "
|
||||
"<c><{name}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
|
||||
def get_format(level: str) -> str:
|
||||
if level == "DEBUG":
|
||||
return debug_format
|
||||
else:
|
||||
return default_format
|
||||
|
||||
|
||||
logger = loguru.logger.bind()
|
||||
|
||||
|
||||
def init_log(config: dict):
|
||||
"""
|
||||
在语言加载完成后执行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
global logger
|
||||
|
||||
logger.remove()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=get_format(config.get("log_level", "INFO")),
|
||||
)
|
||||
show_icon = config.get("log_icon", True)
|
||||
|
||||
# debug = lang.get("log.debug", default="==DEBUG")
|
||||
# info = lang.get("log.info", default="===INFO")
|
||||
# success = lang.get("log.success", default="SUCCESS")
|
||||
# warning = lang.get("log.warning", default="WARNING")
|
||||
# error = lang.get("log.error", default="==ERROR")
|
||||
#
|
||||
# logger.level("DEBUG", color="<blue>", icon=f"{'🐛' if show_icon else ''}{debug}")
|
||||
# logger.level("INFO", color="<normal>", icon=f"{'ℹ️' if show_icon else ''}{info}")
|
||||
# logger.level("SUCCESS", color="<green>", icon=f"{'✅' if show_icon else ''}{success}")
|
||||
# logger.level("WARNING", color="<yellow>", icon=f"{'⚠️' if show_icon else ''}{warning}")
|
||||
# logger.level("ERROR", color="<red>", icon=f"{'⭕' if show_icon else ''}{error}")
|
18
liteyuki/plugin/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
from liteyuki.plugin.model import Plugin, PluginMetadata
|
||||
from liteyuki.plugin.load import load_plugin, load_plugins, _plugins
|
||||
|
||||
__all__ = [
|
||||
"PluginMetadata",
|
||||
"Plugin",
|
||||
"load_plugin",
|
||||
"load_plugins",
|
||||
]
|
||||
|
||||
|
||||
def get_loaded_plugins() -> dict[str, Plugin]:
|
||||
"""
|
||||
获取已加载的插件
|
||||
Returns:
|
||||
dict[str, Plugin]: 插件字典
|
||||
"""
|
||||
return _plugins
|
83
liteyuki/plugin/load.py
Normal file
@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/23 下午11:59
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : load.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import os
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
from liteyuki.plugin.model import Plugin, PluginMetadata
|
||||
from importlib import import_module
|
||||
|
||||
from liteyuki.utils import path_to_module_name
|
||||
|
||||
_plugins: dict[str, Plugin] = {}
|
||||
|
||||
__all__ = [
|
||||
"load_plugin",
|
||||
"load_plugins",
|
||||
]
|
||||
|
||||
|
||||
def load_plugin(module_path: str | Path) -> Optional[Plugin]:
|
||||
"""加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
||||
|
||||
参数:
|
||||
module_path: 插件名称 `path.to.your.plugin`
|
||||
或插件路径 `pathlib.Path(path/to/your/plugin)`
|
||||
"""
|
||||
module_path = path_to_module_name(Path(module_path)) if isinstance(module_path, Path) else module_path
|
||||
try:
|
||||
module = import_module(module_path)
|
||||
_plugins[module.__name__] = Plugin(
|
||||
name=module.__name__,
|
||||
module=module,
|
||||
module_name=module_path,
|
||||
metadata=module.__dict__.get("__plugin_metadata__", None)
|
||||
)
|
||||
logger.opt(colors=True).success(
|
||||
f'Succeeded to load liteyuki plugin "<y>{module.__name__.split(".")[-1]}</y>"'
|
||||
)
|
||||
return _plugins[module.__name__]
|
||||
|
||||
except Exception as e:
|
||||
logger.opt(colors=True).success(
|
||||
f'Failed to load liteyuki plugin "<r>{module_path}</r>"'
|
||||
)
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def load_plugins(*plugin_dir: str) -> set[Plugin]:
|
||||
"""导入文件夹下多个插件
|
||||
|
||||
参数:
|
||||
plugin_dir: 文件夹路径
|
||||
"""
|
||||
plugins = set()
|
||||
for dir_path in plugin_dir:
|
||||
# 遍历每一个文件夹下的py文件和包含__init__.py的文件夹,不递归
|
||||
for f in os.listdir(dir_path):
|
||||
path = Path(os.path.join(dir_path, f))
|
||||
|
||||
module_name = None
|
||||
if os.path.isfile(path) and f.endswith('.py') and f != '__init__.py':
|
||||
module_name = f"{path_to_module_name(Path(dir_path))}.{f[:-3]}"
|
||||
|
||||
elif os.path.isdir(path) and os.path.exists(os.path.join(path, '__init__.py')):
|
||||
module_name = path_to_module_name(path)
|
||||
|
||||
if module_name:
|
||||
load_plugin(module_name)
|
||||
if _plugins.get(module_name):
|
||||
plugins.add(_plugins[module_name])
|
||||
return plugins
|
10
liteyuki/plugin/manager.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/23 下午11:59
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : manager.py
|
||||
@Software: PyCharm
|
||||
"""
|
45
liteyuki/plugin/model.py
Normal file
@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/24 上午12:02
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : model.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from types import ModuleType
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class PluginMetadata(BaseModel):
|
||||
"""
|
||||
轻雪插件元数据,由插件编写者提供,name为必填项
|
||||
"""
|
||||
name: str
|
||||
description: str = ""
|
||||
usage: str = ""
|
||||
type: str = ""
|
||||
homepage: str = ""
|
||||
running_in_main: bool = True # 是否在主进程运行
|
||||
|
||||
|
||||
class Plugin(BaseModel):
|
||||
"""
|
||||
存储插件信息
|
||||
"""
|
||||
model_config = {
|
||||
'arbitrary_types_allowed': True
|
||||
}
|
||||
name: str
|
||||
"""插件名称 例如plugin_loader"""
|
||||
module: ModuleType
|
||||
"""插件模块对象"""
|
||||
module_name: str
|
||||
"""点分割模块路径 例如a.b.c"""
|
||||
metadata: Optional[PluginMetadata] = None
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.module_name)
|
37
liteyuki/plugins/lifespan_monitor.py
Normal file
@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
#
|
||||
# @Time : 2024/7/22 上午11:25
|
||||
# @Author : snowykami
|
||||
# @Email : snowykami@outlook.com
|
||||
# @File : asa.py
|
||||
# @Software: PyCharm
|
||||
from liteyuki.plugin import PluginMetadata
|
||||
from liteyuki import get_bot, logger
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="lifespan_monitor",
|
||||
)
|
||||
|
||||
bot = get_bot()
|
||||
|
||||
|
||||
@bot.on_before_start
|
||||
def _():
|
||||
logger.info("生命周期监控器:准备启动")
|
||||
|
||||
|
||||
@bot.on_before_shutdown
|
||||
def _():
|
||||
logger.info("生命周期监控器:准备停止")
|
||||
|
||||
|
||||
@bot.on_before_restart
|
||||
def _():
|
||||
logger.info("生命周期监控器:准备重启")
|
||||
|
||||
|
||||
@bot.on_after_start
|
||||
def _():
|
||||
logger.info("生命周期监控器:启动完成")
|
@ -1,257 +0,0 @@
|
||||
import platform
|
||||
import time
|
||||
|
||||
import nonebot
|
||||
import psutil
|
||||
from cpuinfo import cpuinfo
|
||||
from nonebot import require
|
||||
from liteyuki.utils import __NAME__, __VERSION__
|
||||
from liteyuki.utils.base.config import get_config
|
||||
from liteyuki.utils.base.data_manager import TempConfig, common_db
|
||||
from liteyuki.utils.base.language import Language
|
||||
from liteyuki.utils.base.resource import get_loaded_resource_packs, get_path
|
||||
from liteyuki.utils.message.html_tool import template2image
|
||||
|
||||
require("nonebot_plugin_apscheduler")
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
protocol_names = {
|
||||
0: "iPad",
|
||||
1: "Android Phone",
|
||||
2: "Android Watch",
|
||||
3: "Mac",
|
||||
5: "iPad",
|
||||
6: "Android Pad",
|
||||
}
|
||||
|
||||
"""
|
||||
Universal Interface
|
||||
data
|
||||
- bot
|
||||
- name: str
|
||||
icon: str
|
||||
id: int
|
||||
protocol_name: str
|
||||
groups: int
|
||||
friends: int
|
||||
message_sent: int
|
||||
message_received: int
|
||||
app_name: str
|
||||
- hardware
|
||||
- cpu
|
||||
- percent: float
|
||||
- name: str
|
||||
- mem
|
||||
- percent: float
|
||||
- total: int
|
||||
- used: int
|
||||
- free: int
|
||||
- swap
|
||||
- percent: float
|
||||
- total: int
|
||||
- used: int
|
||||
- free: int
|
||||
- disk: list
|
||||
- name: str
|
||||
- percent: float
|
||||
- total: int
|
||||
"""
|
||||
status_card_cache = {} # lang -> bytes
|
||||
|
||||
|
||||
# 60s刷新一次
|
||||
@scheduler.scheduled_job("cron", second="*/40")
|
||||
async def refresh_status_card():
|
||||
nonebot.logger.debug("Refreshing status card cache...")
|
||||
global status_card_cache
|
||||
bot_data = await get_bots_data()
|
||||
hardware_data = await get_hardware_data()
|
||||
liteyuki_data = await get_liteyuki_data()
|
||||
for lang in status_card_cache.keys():
|
||||
status_card_cache[lang] = await generate_status_card(
|
||||
bot_data,
|
||||
hardware_data,
|
||||
liteyuki_data,
|
||||
lang=lang,
|
||||
use_cache=False
|
||||
)
|
||||
|
||||
|
||||
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0", use_cache=False) -> bytes:
|
||||
if not use_cache:
|
||||
return await template2image(
|
||||
get_path("templates/status.html", abs_path=True),
|
||||
{
|
||||
"data": {
|
||||
"bot" : bot,
|
||||
"hardware" : hardware,
|
||||
"liteyuki" : liteyuki,
|
||||
"localization": get_local_data(lang)
|
||||
}
|
||||
},
|
||||
debug=True
|
||||
)
|
||||
else:
|
||||
if lang not in status_card_cache:
|
||||
status_card_cache[lang] = await generate_status_card(bot, hardware, liteyuki, lang=lang, bot_id=bot_id)
|
||||
return status_card_cache[lang]
|
||||
|
||||
|
||||
def get_local_data(lang_code) -> dict:
|
||||
lang = Language(lang_code)
|
||||
return {
|
||||
"friends" : lang.get("status.friends"),
|
||||
"groups" : lang.get("status.groups"),
|
||||
"plugins" : lang.get("status.plugins"),
|
||||
"bots" : lang.get("status.bots"),
|
||||
"message_sent" : lang.get("status.message_sent"),
|
||||
"message_received": lang.get("status.message_received"),
|
||||
"cpu" : lang.get("status.cpu"),
|
||||
"memory" : lang.get("status.memory"),
|
||||
"swap" : lang.get("status.swap"),
|
||||
"disk" : lang.get("status.disk"),
|
||||
|
||||
"usage" : lang.get("status.usage"),
|
||||
"total" : lang.get("status.total"),
|
||||
"used" : lang.get("status.used"),
|
||||
"free" : lang.get("status.free"),
|
||||
|
||||
"days" : lang.get("status.days"),
|
||||
"hours" : lang.get("status.hours"),
|
||||
"minutes" : lang.get("status.minutes"),
|
||||
"seconds" : lang.get("status.seconds"),
|
||||
"runtime" : lang.get("status.runtime"),
|
||||
"threads" : lang.get("status.threads"),
|
||||
"cores" : lang.get("status.cores"),
|
||||
"process" : lang.get("status.process"),
|
||||
"resources" : lang.get("status.resources"),
|
||||
"description" : lang.get("status.description"),
|
||||
}
|
||||
|
||||
|
||||
async def get_bots_data(self_id: str = "0") -> dict:
|
||||
"""获取当前所有机器人数据
|
||||
Returns:
|
||||
"""
|
||||
result = {
|
||||
"self_id": self_id,
|
||||
"bots" : [],
|
||||
}
|
||||
for bot_id, bot in nonebot.get_bots().items():
|
||||
groups = 0
|
||||
friends = 0
|
||||
status = {}
|
||||
bot_name = bot_id
|
||||
version_info = {}
|
||||
try:
|
||||
# API fetch
|
||||
bot_name = (await bot.get_login_info())["nickname"]
|
||||
groups = len(await bot.get_group_list())
|
||||
friends = len(await bot.get_friend_list())
|
||||
status = await bot.get_status()
|
||||
version_info = await bot.get_version_info()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
statistics = status.get("stat", {})
|
||||
app_name = version_info.get("app_name", "UnknownImplementation")
|
||||
if app_name in ["Lagrange.OneBot", "LLOneBot", "Shamrock"]:
|
||||
icon = f"https://q.qlogo.cn/g?b=qq&nk={bot_id}&s=640"
|
||||
else:
|
||||
icon = None
|
||||
bot_data = {
|
||||
"name" : bot_name,
|
||||
"icon" : icon,
|
||||
"id" : bot_id,
|
||||
"protocol_name" : protocol_names.get(version_info.get("protocol_name"), "Online"),
|
||||
"groups" : groups,
|
||||
"friends" : friends,
|
||||
"message_sent" : statistics.get("message_sent", 0),
|
||||
"message_received": statistics.get("message_received", 0),
|
||||
"app_name" : app_name
|
||||
}
|
||||
result["bots"].append(bot_data)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def get_hardware_data() -> dict:
|
||||
mem = psutil.virtual_memory()
|
||||
all_processes = psutil.Process().children(recursive=True)
|
||||
all_processes.append(psutil.Process())
|
||||
|
||||
mem_used_process = 0
|
||||
process_mem = {}
|
||||
for process in all_processes:
|
||||
try:
|
||||
ps_name = process.name().replace(".exe", "")
|
||||
if ps_name not in process_mem:
|
||||
process_mem[ps_name] = 0
|
||||
process_mem[ps_name] += process.memory_info().rss
|
||||
mem_used_process += process.memory_info().rss
|
||||
except Exception:
|
||||
pass
|
||||
swap = psutil.swap_memory()
|
||||
cpu_brand_raw = cpuinfo.get_cpu_info().get("brand_raw", "Unknown")
|
||||
if "AMD" in cpu_brand_raw:
|
||||
brand = "AMD"
|
||||
elif "Intel" in cpu_brand_raw:
|
||||
brand = "Intel"
|
||||
else:
|
||||
brand = "Unknown"
|
||||
result = {
|
||||
"cpu" : {
|
||||
"percent": psutil.cpu_percent(),
|
||||
"name" : f"{brand} {cpuinfo.get_cpu_info().get('arch', 'Unknown')}",
|
||||
"cores" : psutil.cpu_count(logical=False),
|
||||
"threads": psutil.cpu_count(logical=True),
|
||||
"freq" : psutil.cpu_freq().current # MHz
|
||||
},
|
||||
"memory": {
|
||||
"percent" : mem.percent,
|
||||
"total" : mem.total,
|
||||
"used" : mem.used,
|
||||
"free" : mem.free,
|
||||
"usedProcess": mem_used_process,
|
||||
},
|
||||
"swap" : {
|
||||
"percent": swap.percent,
|
||||
"total" : swap.total,
|
||||
"used" : swap.used,
|
||||
"free" : swap.free
|
||||
},
|
||||
"disk" : [],
|
||||
}
|
||||
|
||||
for disk in psutil.disk_partitions(all=True):
|
||||
try:
|
||||
disk_usage = psutil.disk_usage(disk.mountpoint)
|
||||
if disk_usage.total == 0:
|
||||
continue # 虚拟磁盘
|
||||
result["disk"].append({
|
||||
"name" : disk.mountpoint,
|
||||
"percent": disk_usage.percent,
|
||||
"total" : disk_usage.total,
|
||||
"used" : disk_usage.used,
|
||||
"free" : disk_usage.free
|
||||
})
|
||||
except:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def get_liteyuki_data() -> dict:
|
||||
temp_data: TempConfig = common_db.first(TempConfig(), default=TempConfig())
|
||||
result = {
|
||||
"name" : list(get_config("nickname", [__NAME__]))[0],
|
||||
"version" : __VERSION__,
|
||||
"plugins" : len(nonebot.get_loaded_plugins()),
|
||||
"resources": len(get_loaded_resource_packs()),
|
||||
"nonebot" : f"{nonebot.__version__}",
|
||||
"python" : f"{platform.python_implementation()} {platform.python_version()}",
|
||||
"system" : f"{platform.system()} {platform.release()}",
|
||||
"runtime" : time.time() - temp_data.data.get("start_time", time.time()), # 运行时间秒数
|
||||
"bots" : len(nonebot.get_bots())
|
||||
}
|
||||
return result
|
@ -1,52 +0,0 @@
|
||||
from nonebot import require
|
||||
|
||||
from liteyuki.utils.base.resource import get_path
|
||||
from liteyuki.utils.message.html_tool import template2image
|
||||
from liteyuki.utils.base.language import get_user_lang
|
||||
from .api import *
|
||||
from ...utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, UniMessage
|
||||
|
||||
status_alc = on_alconna(
|
||||
aliases={"状态"},
|
||||
command=Alconna(
|
||||
"status",
|
||||
Subcommand(
|
||||
"memory",
|
||||
alias={"mem", "m", "内存"},
|
||||
),
|
||||
Subcommand(
|
||||
"process",
|
||||
alias={"proc", "p", "进程"},
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@status_alc.handle()
|
||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
||||
ulang = get_user_lang(event.user_id)
|
||||
if ulang.lang_code in status_card_cache:
|
||||
image = status_card_cache[ulang.lang_code]
|
||||
else:
|
||||
image = await generate_status_card(
|
||||
bot=await get_bots_data(),
|
||||
hardware=await get_hardware_data(),
|
||||
liteyuki=await get_liteyuki_data(),
|
||||
lang=ulang.lang_code,
|
||||
bot_id=bot.self_id,
|
||||
use_cache=True
|
||||
)
|
||||
await status_alc.finish(UniMessage.image(raw=image))
|
||||
|
||||
|
||||
@status_alc.assign("memory")
|
||||
async def _():
|
||||
print("memory")
|
||||
|
||||
|
||||
@status_alc.assign("process")
|
||||
async def _():
|
||||
print("process")
|
19
liteyuki/plugins/what_litaco/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/25 上午2:28
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
#
|
||||
# @Time : 2024/7/22 上午11:25
|
||||
# @Author : snowykami
|
||||
# @Email : snowykami@outlook.com
|
||||
# @File : asa.py
|
||||
# @Software: PyCharm
|
@ -1,159 +0,0 @@
|
||||
language.name=文言
|
||||
|
||||
log.debug=觀其調
|
||||
log.info=見其訊
|
||||
log.warning=告之懼
|
||||
log.error=惡之誤
|
||||
log.success=喜之成
|
||||
|
||||
liteyuki.restart=復其初
|
||||
liteyuki.restart_now=即其初
|
||||
liteyuki.update_restart=更新既成,可{RESTART}或稍後手動復始以行是更新
|
||||
liteyuki.current_config=當前設之如左
|
||||
liteyuki.static_config=靜態設之
|
||||
liteyuki.stored_config=存之設也
|
||||
liteyuki.config_set_success=設之 {KEY}={VAL} 成焉
|
||||
liteyuki.stats.group=群
|
||||
liteyuki.stats.user=友
|
||||
liteyuki.stats.plugin=插
|
||||
liteyuki.stats.sent=遣
|
||||
liteyuki.stats.received=受
|
||||
liteyuki.stats.run_time=進行時
|
||||
liteyuki.stats.groups=羣
|
||||
liteyuki.stats.friends=友
|
||||
liteyuki.stats.plugins=插
|
||||
liteyuki.image_mode_on=開云標示式圖之術
|
||||
liteyuki.image_mode_off=閉云標示式圖之術
|
||||
liteyuki.invalid_command=無其可爲之語也 {TEXT}
|
||||
liteyuki.reload_resources=重載資
|
||||
liteyuki.list_resources=資列表
|
||||
liteyuki.reload_resources_success=資重載成,資數 {NUM} 也
|
||||
liteyuki.loaded_resources=載資 {NUM},優先之次是
|
||||
liteyuki.unloaded_resources=資未載
|
||||
liteyuki.load_resource_success=資 {NAME} 載
|
||||
liteyuki.unload_resource_success=資 {NAME} 拔
|
||||
liteyuki.load_resource_failed=資 {NAME} 載失
|
||||
liteyuki.unload_resource_failed=資 {NAME} 拔失
|
||||
liteyuki.resource_not_found=資 {NAME} 不存或無爲
|
||||
liteyuki.resource_already_loaded=資 {NAME} 已載,勿復
|
||||
liteyuki.resource_already_unloaded=資 {NAME} 已拔,勿復
|
||||
liteyuki.need_reload=請{BTN}以行是更新
|
||||
liteyuki.dont_repeat=勿復行
|
||||
liteyuki.change_priority_success=資 {NAME} 優先成
|
||||
liteyuki.change_priority_failed=資 {NAME} 優先不成
|
||||
liteyuki.group_already=羣 {GROUP} 之狀已是 {STATUS} ,勿再行
|
||||
liteyuki.group_success=羣 {GROUP} {STATUS} 成
|
||||
liteyuki.permission_denied=權不及也
|
||||
liteyuki.config_remove_success=設 {KEY} 移
|
||||
|
||||
main.current_language=當前言 {LANG} 爲是
|
||||
main.enable_webdash=啟網监塔: {URL}
|
||||
main.monitor.title=輕雪監塔
|
||||
main.monitor.description=輕雪機之監塔
|
||||
main.monitor.cpu=中药科大学
|
||||
main.monitor.memory=懷
|
||||
main.monitor.swap=易
|
||||
main.monitor.disk=磁
|
||||
main.monitor.usage=用之
|
||||
main.monitor.total=總
|
||||
main.monitor.used=所用
|
||||
main.monitor.free=閑也
|
||||
|
||||
data_manager.migrate_success=模{NAME}易
|
||||
|
||||
npm.loaded_plugins=載插
|
||||
npm.total=总 {TOTAL}
|
||||
npm.help=救
|
||||
npm.usage=法用
|
||||
npm.description=其形
|
||||
npm.disable=斷
|
||||
npm.disable_global=宇斷
|
||||
npm.enable=啟
|
||||
npm.enable_global=宇啟
|
||||
npm.install=法
|
||||
npm.uninstall=遣
|
||||
npm.installing=法 {NAME} 裝中
|
||||
npm.cannot_uninstall=法遣
|
||||
npm.no_description=无形
|
||||
npm.store_update_success=插庫成
|
||||
npm.store_update_failed=插庫敗
|
||||
npm.search_result=索所
|
||||
npm.search_no_result=無索
|
||||
npm.too_many_results=之多,{HIDE_NUM} 無現,審爲
|
||||
npm.install_success={NAME} 成
|
||||
npm.install_failed={NAME} 敗,語目以知,不知求于{HOMEPAGE}
|
||||
npm.uninstall_success={NAME} 遣,明{HOMEPAGE}
|
||||
npm.uninstall_failed={NAME} 遣
|
||||
npm.load_failed={NAME} 載,求于{HOMEPAGE}
|
||||
npm.plugin_not_found=無 {NAME} ,揭揭庫爲
|
||||
npm.plugin_not_installed={NAME} 未
|
||||
npm.plugin_already_installed={NAME} 已,勿復法
|
||||
npm.author=主
|
||||
npm.homepage=揭
|
||||
npm.pypi=PyPI
|
||||
npm.next_page=後
|
||||
npm.prev_page=前
|
||||
npm.update=兵
|
||||
npm.plugin_cannot_be_toggled={NAME} 不兵
|
||||
npm.plugin_already=插 {NAME} 之狀已是 {STATUS} ,勿再行
|
||||
npm.toggle_failed=插 {NAME} {STATUS} 敗:{ERROR}
|
||||
npm.toggle_success=插 {NAME} {STATUS} 成
|
||||
npm.page=第{PAGE}/{TOTAL} 節
|
||||
npm.update_index=兵索
|
||||
npm.list_plugins=插目
|
||||
npm.disable_session=在次斷
|
||||
npm.enable_session=在次啟
|
||||
npm.help=救
|
||||
npm.search=索
|
||||
|
||||
user.profile.edit=改
|
||||
user.profile.set=設
|
||||
user.profile_manager.query=汝之訊 {ATTR} 爲 {VALUE} 也
|
||||
user.profile_manager.set=汝之訊 {ATTR} 已設 {VALUE} 也
|
||||
user.profile.settings=設
|
||||
user.profile.info=訊
|
||||
user.profile.lang=言
|
||||
user.profile.lang.desc=言設
|
||||
user.profile.timezone=時
|
||||
user.profile.timezone.desc=時設
|
||||
user.profile.theme=題
|
||||
user.profile.theme.desc=題設
|
||||
user.profile.location=所
|
||||
user.profile.location.desc=所設
|
||||
user.profile.nickname=名
|
||||
user.profile.nickname.desc=設之之名
|
||||
user.profile.input_value=求輸 {ATTR} 之量
|
||||
user.profile.set_success={ATTR} 成 {VALUE} 也
|
||||
user.profile.set_failed={ATTR} 失,問求也
|
||||
|
||||
rpm.move_up=上
|
||||
rpm.move_down=下
|
||||
rpm.move_top=初
|
||||
|
||||
weather.city_not_found=城 {CITY} 不存也
|
||||
weather.weather_not_found=城 {CITY} 之氣也不存也
|
||||
weather.no_key=未設氣金鑰也,爲請增于設也
|
||||
|
||||
status.friends=友
|
||||
status.groups=羣
|
||||
status.plugins=插
|
||||
status.resources=資
|
||||
status.bots=機
|
||||
status.message_sent=遣訊
|
||||
status.message_received=受訊
|
||||
status.cpu=中药科大学
|
||||
status.memory=懷
|
||||
status.swap=易
|
||||
status.disk=磁
|
||||
status.usage=用
|
||||
status.total=總
|
||||
status.used=所用
|
||||
status.free=閑
|
||||
status.runtime=時
|
||||
status.days=日
|
||||
status.hours=時
|
||||
status.minutes=分
|
||||
status.seconds=秒
|
||||
status.cores=核
|
||||
status.threads=縷
|
||||
status.process=程
|
Before Width: | Height: | Size: 9.1 MiB |
Before Width: | Height: | Size: 5.1 MiB |
Before Width: | Height: | Size: 5.4 MiB |
Before Width: | Height: | Size: 5.4 MiB |
Before Width: | Height: | Size: 3.8 MiB |
Before Width: | Height: | Size: 2.9 MiB |
Before Width: | Height: | Size: 2.6 MiB |
75
liteyuki/utils.py
Normal file
@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
一些常用的工具类,部分来源于 nonebot 并遵循其许可进行修改
|
||||
"""
|
||||
import asyncio
|
||||
import inspect
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Coroutine
|
||||
|
||||
from liteyuki.log import logger
|
||||
|
||||
|
||||
def is_coroutine_callable(call: Callable[..., Any]) -> bool:
|
||||
"""
|
||||
判断是否为协程可调用对象
|
||||
Args:
|
||||
call: 可调用对象
|
||||
Returns:
|
||||
bool: 是否为协程可调用对象
|
||||
"""
|
||||
if inspect.isroutine(call):
|
||||
return inspect.iscoroutinefunction(call)
|
||||
if inspect.isclass(call):
|
||||
return False
|
||||
func_ = getattr(call, "__call__", None)
|
||||
return inspect.iscoroutinefunction(func_)
|
||||
|
||||
|
||||
def run_coroutine(*coro: Coroutine):
|
||||
"""
|
||||
运行协程
|
||||
Args:
|
||||
coro:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
# 检测是否有现有的事件循环
|
||||
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
# 如果事件循环正在运行,创建任务
|
||||
for c in coro:
|
||||
asyncio.ensure_future(c)
|
||||
else:
|
||||
# 如果事件循环未运行,运行直到完成
|
||||
for c in coro:
|
||||
loop.run_until_complete(c)
|
||||
except RuntimeError:
|
||||
# 如果没有找到事件循环,创建一个新的
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(asyncio.gather(*coro))
|
||||
loop.close()
|
||||
except Exception as e:
|
||||
# 捕获其他异常,防止协程被重复等待
|
||||
logger.error(f"Exception occurred: {e}")
|
||||
|
||||
|
||||
def path_to_module_name(path: Path) -> str:
|
||||
"""
|
||||
转换路径为模块名
|
||||
Args:
|
||||
path: 路径a/b/c/d -> a.b.c.d
|
||||
Returns:
|
||||
str: 模块名
|
||||
"""
|
||||
rel_path = path.resolve().relative_to(Path.cwd().resolve())
|
||||
if rel_path.stem == "__init__":
|
||||
return ".".join(rel_path.parts[:-1])
|
||||
else:
|
||||
return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
|
@ -1,7 +0,0 @@
|
||||
from nonebot.adapters.onebot import v11, v12
|
||||
|
||||
T_Bot = v11.Bot | v12.Bot
|
||||
T_GroupMessageEvent = v11.GroupMessageEvent | v12.GroupMessageEvent
|
||||
T_PrivateMessageEvent = v11.PrivateMessageEvent | v12.PrivateMessageEvent
|
||||
T_MessageEvent = v11.MessageEvent | v12.MessageEvent
|
||||
T_Message = v11.Message | v12.Message
|
@ -1,61 +0,0 @@
|
||||
import threading
|
||||
from multiprocessing import get_context
|
||||
|
||||
import nonebot
|
||||
from nonebot import logger
|
||||
|
||||
reboot_grace_time_limit: int = 20
|
||||
|
||||
_nb_run = nonebot.run
|
||||
|
||||
|
||||
class Reloader:
|
||||
event: threading.Event = None
|
||||
|
||||
@classmethod
|
||||
def reload(cls, delay: int = 0):
|
||||
if cls.event is None:
|
||||
raise RuntimeError()
|
||||
if delay > 0:
|
||||
threading.Timer(delay, function=cls.event.set).start()
|
||||
return
|
||||
cls.event.set()
|
||||
|
||||
|
||||
def _run(ev: threading.Event, *args, **kwargs):
|
||||
Reloader.event = ev
|
||||
_nb_run(*args, **kwargs)
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
should_exit = False
|
||||
ctx = get_context("spawn")
|
||||
while not should_exit:
|
||||
event = ctx.Event()
|
||||
process = ctx.Process(
|
||||
target=_run,
|
||||
args=(
|
||||
event,
|
||||
*args,
|
||||
),
|
||||
kwargs=kwargs,
|
||||
)
|
||||
process.start()
|
||||
while not should_exit:
|
||||
if event.wait(1):
|
||||
logger.info("Receive reboot event")
|
||||
process.terminate()
|
||||
process.join(reboot_grace_time_limit)
|
||||
if process.is_alive():
|
||||
logger.warning(
|
||||
f"Cannot shutdown gracefully in {reboot_grace_time_limit} second, force kill process."
|
||||
)
|
||||
process.kill()
|
||||
break
|
||||
elif process.is_alive():
|
||||
continue
|
||||
else:
|
||||
should_exit = True
|
||||
|
||||
|
||||
nonebot.run = run
|
31
main.py
@ -1,29 +1,6 @@
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot import v11, v12
|
||||
from liteyuki.utils import init
|
||||
from liteyuki.utils.base.config import load_from_yaml
|
||||
from liteyuki.utils.base.data_manager import StoredConfig, common_db
|
||||
from liteyuki.utils.base.ly_api import liteyuki_api
|
||||
|
||||
if __name__ == "__mp_main__":
|
||||
init()
|
||||
store_config: dict = common_db.first(StoredConfig(), default=StoredConfig()).config
|
||||
static_config = load_from_yaml("config.yml")
|
||||
store_config.update(static_config)
|
||||
nonebot.init(**store_config)
|
||||
adapters = [v11.Adapter, v12.Adapter]
|
||||
driver = nonebot.get_driver()
|
||||
|
||||
for adapter in adapters:
|
||||
driver.register_adapter(adapter)
|
||||
try:
|
||||
nonebot.load_plugin("liteyuki.liteyuki_main")
|
||||
nonebot.load_from_toml("pyproject.toml")
|
||||
except BaseException as e:
|
||||
if not isinstance(e, KeyboardInterrupt):
|
||||
nonebot.logger.error(f"An error occurred: {e}, Bug will be reported automatically.")
|
||||
liteyuki_api.bug_report(str(e.__repr__()))
|
||||
from liteyuki import LiteyukiBot
|
||||
from src.utils import load_from_yaml
|
||||
|
||||
if __name__ == "__main__":
|
||||
from liteyuki.utils.base.reloader import Reloader
|
||||
nonebot.run()
|
||||
bot = LiteyukiBot(**load_from_yaml("config.yml"))
|
||||
bot.run()
|
||||
|
58
pyproject.toml
Normal file
@ -0,0 +1,58 @@
|
||||
# PEP 621 project metadata
|
||||
# See https://www.python.org/dev/peps/pep-0621/
|
||||
# This file is for project use, but don`t use with nb-cli
|
||||
# 此文件为项目所用,请不要和nb-cli一起使用以防被修改
|
||||
[tool.poetry]
|
||||
name = "liteyuki-bot"
|
||||
version = "6"
|
||||
description = "based on nonebot2"
|
||||
authors = ["Snowykami"]
|
||||
license = "MIT & LSO"
|
||||
package-mode = false
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
aiofiles = "~23.2.1"
|
||||
aiohttp = "~3.9.3"
|
||||
aiosqlite3 = "~0.3.0"
|
||||
colored = "~2.2.4"
|
||||
fastapi = "~0.110.0"
|
||||
GitPython = "~3.1.42"
|
||||
httpx = "~0.27.0"
|
||||
importlib_metadata = "~7.0.2"
|
||||
jieba = "~0.42.1"
|
||||
loguru = "~0.7.2"
|
||||
nb-cli = "~1.4.1"
|
||||
nonebot-adapter-onebot = "~2.4.3"
|
||||
nonebot-adapter-satori = "~0.11.5"
|
||||
nonebot-plugin-alconna = "~0.46.3"
|
||||
nonebot-plugin-apscheduler = "~0.4.0"
|
||||
nonebot-plugin-htmlrender = "~0.3.1"
|
||||
nonebot2 = { version = "~2.3.0", extras = ["fastapi", "httpx", "websockets"] }
|
||||
numpy = "~2.0.0"
|
||||
packaging = "~23.1"
|
||||
psutil = "~5.9.8"
|
||||
py-cpuinfo = "~9.0.0"
|
||||
pydantic = "~2.7.0"
|
||||
Pygments = "~2.17.2"
|
||||
python-dotenv = "~1.0.1"
|
||||
pytest = "~8.3.1"
|
||||
pytz = "~2024.1"
|
||||
PyYAML = "~6.0.1"
|
||||
requests = "~2.31.0"
|
||||
starlette = "~0.36.3"
|
||||
watchdog = "~4.0.0"
|
||||
|
||||
|
||||
[[tool.poetry.source]]
|
||||
name = "tuna"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
|
||||
[tool.nonebot]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://bot.liteyuki.icu"
|
||||
repository = "https://github.com/LiteyukiStudio/LiteyukiBot"
|
||||
documentation = "https://bot.liteyuki.icu"
|
||||
|
@ -3,12 +3,15 @@ aiofiles~=23.2.1
|
||||
colored~=2.2.4
|
||||
GitPython~=3.1.42
|
||||
httpx~=0.27.0
|
||||
melobot~=2.6.5
|
||||
nb-cli~=1.4.1
|
||||
nonebot2[fastapi,httpx,websockets]~=2.3.0
|
||||
nonebot-plugin-htmlrender~=0.3.1
|
||||
nonebot-adapter-onebot~=2.4.3
|
||||
nonebot-plugin-alconna~=0.43.0
|
||||
nonebot-plugin-alconna~=0.46.3
|
||||
nonebot_plugin_apscheduler~=0.4.0
|
||||
nonebot-adapter-satori~=0.11.5
|
||||
numpy~=2.0.0
|
||||
packaging~=23.1
|
||||
psutil~=5.9.8
|
||||
py-cpuinfo~=9.0.0
|
||||
@ -16,9 +19,13 @@ pydantic~=2.7.0
|
||||
Pygments~=2.17.2
|
||||
pytz~=2024.1
|
||||
PyYAML~=6.0.1
|
||||
pillow~=10.0.0
|
||||
starlette~=0.36.3
|
||||
loguru~=0.7.2
|
||||
importlib_metadata~=7.0.2
|
||||
requests~=2.31.0
|
||||
watchdog~=4.0.0
|
||||
pillow~=10.2.0
|
||||
jieba~=0.42.1
|
||||
aiosqlite3~=0.3.0
|
||||
fastapi~=0.110.0
|
||||
python-dotenv~=1.0.1
|
BIN
src/libs/ly_api_windows_amd64.dll
Normal file
23
src/liteyuki_main/__init__.py
Normal file
@ -0,0 +1,23 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
from .core import *
|
||||
from .loader import *
|
||||
from .dev import *
|
||||
|
||||
__author__ = "snowykami"
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="轻雪核心插件",
|
||||
description="轻雪主程序插件,包含了许多初始化的功能",
|
||||
usage="",
|
||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||
extra={
|
||||
"liteyuki" : True,
|
||||
"toggleable": False,
|
||||
}
|
||||
)
|
||||
|
||||
from ..utils.base.language import Language, get_default_lang_code
|
||||
|
||||
|
||||
sys_lang = Language(get_default_lang_code())
|
||||
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
47
src/liteyuki_main/api.py
Normal file
@ -0,0 +1,47 @@
|
||||
import nonebot
|
||||
from git import Repo
|
||||
|
||||
from src.utils.base.config import get_config
|
||||
|
||||
remote_urls = [
|
||||
"https://github.com/LiteyukiStudio/LiteyukiBot.git",
|
||||
"https://gitee.com/snowykami/LiteyukiBot.git"
|
||||
]
|
||||
|
||||
|
||||
def detect_update() -> bool:
|
||||
# 对每个远程仓库进行检查,只要有一个仓库有更新,就返回True
|
||||
for remote_url in remote_urls:
|
||||
repo = Repo(".")
|
||||
repo.remotes.origin.set_url(remote_url)
|
||||
repo.remotes.origin.fetch()
|
||||
if repo.head.commit != repo.commit('origin/main'):
|
||||
return True
|
||||
|
||||
|
||||
def update_liteyuki() -> tuple[bool, str]:
|
||||
"""更新轻雪
|
||||
:return: 是否更新成功,更新变动"""
|
||||
|
||||
if get_config("allow_update", True):
|
||||
new_commit_detected = detect_update()
|
||||
if new_commit_detected:
|
||||
repo = Repo(".")
|
||||
logs = ""
|
||||
# 对每个远程仓库进行更新
|
||||
for remote_url in remote_urls:
|
||||
try:
|
||||
logs += f"\nremote: {remote_url}"
|
||||
repo.remotes.origin.set_url(remote_url)
|
||||
repo.remotes.origin.pull()
|
||||
diffs = repo.head.commit.diff("origin/main")
|
||||
for diff in diffs.iter_change_type('M'):
|
||||
logs += f"\n{diff.a_path}"
|
||||
return True, logs
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
return False, "Nothing Changed"
|
||||
|
||||
else:
|
||||
raise PermissionError("Update is not allowed.")
|
@ -1,324 +1,419 @@
|
||||
import base64
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import nonebot
|
||||
import pip
|
||||
from nonebot import Bot, get_driver, require
|
||||
from nonebot.adapters.onebot.v11 import Message, escape, unescape
|
||||
from nonebot.exception import MockApiException
|
||||
from nonebot.internal.matcher import Matcher
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from liteyuki.utils.base.config import get_config, load_from_yaml
|
||||
from liteyuki.utils.base.data_manager import StoredConfig, TempConfig, common_db
|
||||
from liteyuki.utils.base.language import get_user_lang
|
||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||
from liteyuki.utils.base.reloader import Reloader
|
||||
from .api import update_liteyuki
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
require("nonebot_plugin_apscheduler")
|
||||
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
markdown_image = common_db.first(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"liteecho",
|
||||
Args["text", str, ""],
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
|
||||
if result.main_args.get("text"):
|
||||
await matcher.finish(Message(unescape(result.main_args.get("text"))))
|
||||
else:
|
||||
await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"更新轻雪"},
|
||||
command=Alconna(
|
||||
"update-liteyuki"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(bot: T_Bot, event: T_MessageEvent):
|
||||
# 使用git pull更新
|
||||
ulang = get_user_lang(str(event.user_id))
|
||||
success, logs = update_liteyuki()
|
||||
reply = "Liteyuki updated!\n"
|
||||
reply += f"```\n{logs}\n```\n"
|
||||
btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki")
|
||||
pip.main(["install", "-r", "requirements.txt"])
|
||||
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
||||
await md.send_md(reply, bot, event=event, at_sender=False)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"重启轻雪"},
|
||||
command=Alconna(
|
||||
"reload-liteyuki"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
||||
await matcher.send("Liteyuki reloading")
|
||||
temp_data = common_db.first(TempConfig(), default=TempConfig())
|
||||
|
||||
temp_data.data.update(
|
||||
{
|
||||
"reload" : True,
|
||||
"reload_time" : time.time(),
|
||||
"reload_bot_id" : bot.self_id,
|
||||
"reload_session_type": event.message_type,
|
||||
"reload_session_id" : event.group_id if event.message_type == "group" else event.user_id,
|
||||
"delta_time" : 0
|
||||
}
|
||||
)
|
||||
|
||||
common_db.save(temp_data)
|
||||
Reloader.reload(0)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"配置"},
|
||||
command=Alconna(
|
||||
"config",
|
||||
Subcommand(
|
||||
"set",
|
||||
Args["key", str]["value", Any],
|
||||
alias=["设置"],
|
||||
|
||||
),
|
||||
Subcommand(
|
||||
"get",
|
||||
Args["key", str, None],
|
||||
alias=["查询", "获取"]
|
||||
),
|
||||
Subcommand(
|
||||
"remove",
|
||||
Args["key", str],
|
||||
alias=["删除"]
|
||||
)
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher):
|
||||
ulang = get_user_lang(str(event.user_id))
|
||||
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig())
|
||||
if result.subcommands.get("set"):
|
||||
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
|
||||
try:
|
||||
value = eval(value)
|
||||
except:
|
||||
pass
|
||||
stored_config.config[key] = value
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
|
||||
elif result.subcommands.get("get"):
|
||||
key = result.subcommands.get("get").args.get("key")
|
||||
file_config = load_from_yaml("config.yml")
|
||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||
if key:
|
||||
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
|
||||
else:
|
||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
|
||||
for k, v in file_config.items():
|
||||
reply += f"\n{k}={v}"
|
||||
reply += "\n```"
|
||||
if len(stored_config.config) > 0:
|
||||
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
||||
for k, v in stored_config.config.items():
|
||||
reply += f"\n{k}={v} {type(v)}"
|
||||
reply += "\n```"
|
||||
await md.send_md(reply, bot, event=event)
|
||||
elif result.subcommands.get("remove"):
|
||||
key = result.subcommands.get("remove").args.get("key")
|
||||
if key in stored_config.config:
|
||||
stored_config.config.pop(key)
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
|
||||
else:
|
||||
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"切换图片模式"},
|
||||
command=Alconna(
|
||||
"switch-image-mode"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(event: T_MessageEvent, matcher: Matcher):
|
||||
global markdown_image
|
||||
# 切换图片模式,False以图片形式发送,True以markdown形式发送
|
||||
ulang = get_user_lang(str(event.user_id))
|
||||
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig())
|
||||
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
|
||||
markdown_image = stored_config.config["markdown_image"]
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"liteyuki-docs",
|
||||
),
|
||||
aliases={"轻雪文档"},
|
||||
).handle()
|
||||
async def _(matcher: Matcher):
|
||||
await matcher.finish("https://bot.liteyuki.icu/usage")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"/api",
|
||||
Args["api", str]["args", MultiVar(str), ()],
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
||||
"""
|
||||
调用API
|
||||
Args:
|
||||
result:
|
||||
bot:
|
||||
event:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
api_name = result.main_args.get("api")
|
||||
args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数,但每个参数间用空格分隔,空格是%20
|
||||
args_dict = {}
|
||||
|
||||
for arg in args:
|
||||
key, value = arg.split("=", 1)
|
||||
args_dict[key] = unescape(value.replace("%20", " "))
|
||||
|
||||
if api_name in need_user_id and "user_id" not in args_dict:
|
||||
args_dict["user_id"] = str(event.user_id)
|
||||
if api_name in need_group_id and "group_id" not in args_dict and event.message_type == "group":
|
||||
args_dict["group_id"] = str(event.group_id)
|
||||
|
||||
if "message" in args_dict:
|
||||
args_dict["message"] = Message(args_dict["message"])
|
||||
|
||||
try:
|
||||
result = await bot.call_api(api_name, **args_dict)
|
||||
except Exception as e:
|
||||
result = str(e)
|
||||
|
||||
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items())
|
||||
print(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
||||
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
||||
|
||||
|
||||
# system hook
|
||||
@Bot.on_calling_api # 图片模式检测
|
||||
async def test_for_md_image(bot: T_Bot, api: str, data: dict):
|
||||
# 截获大图发送,转换为markdown发送
|
||||
if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get("user_id") != bot.self_id:
|
||||
if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg":
|
||||
session_type = "private"
|
||||
session_id = data.get("user_id")
|
||||
elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg":
|
||||
session_type = "group"
|
||||
session_id = data.get("group_id")
|
||||
else:
|
||||
return
|
||||
if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
|
||||
file: str = data["message"][0].data.get("file")
|
||||
# file:// http:// base64://
|
||||
if file.startswith("http"):
|
||||
result = await md.send_md(await md.image_async(file), bot, message_type=session_type, session_id=session_id)
|
||||
elif file.startswith("file"):
|
||||
file = file.replace("file://", "")
|
||||
result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type, session_id=session_id)
|
||||
elif file.startswith("base64"):
|
||||
file_bytes = base64.b64decode(file.replace("base64://", ""))
|
||||
result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id)
|
||||
else:
|
||||
return
|
||||
raise MockApiException(result=result)
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def on_startup():
|
||||
temp_data = common_db.first(TempConfig(), default=TempConfig())
|
||||
# 储存重启信息
|
||||
if temp_data.data.get("reload", False):
|
||||
delta_time = time.time() - temp_data.data.get("reload_time", 0)
|
||||
temp_data.data["delta_time"] = delta_time
|
||||
common_db.save(temp_data) # 更新数据
|
||||
|
||||
|
||||
@driver.on_shutdown
|
||||
async def on_shutdown():
|
||||
pass
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _(bot: T_Bot):
|
||||
temp_data = common_db.first(TempConfig(), default=TempConfig())
|
||||
# 用于重启计时
|
||||
if temp_data.data.get("reload", False):
|
||||
temp_data.data["reload"] = False
|
||||
reload_bot_id = temp_data.data.get("reload_bot_id", 0)
|
||||
if reload_bot_id != bot.self_id:
|
||||
return
|
||||
reload_session_type = temp_data.data.get("reload_session_type", "private")
|
||||
reload_session_id = temp_data.data.get("reload_session_id", 0)
|
||||
delta_time = temp_data.data.get("delta_time", 0)
|
||||
common_db.save(temp_data) # 更新数据
|
||||
await bot.call_api(
|
||||
"send_msg",
|
||||
message_type=reload_session_type,
|
||||
user_id=reload_session_id,
|
||||
group_id=reload_session_id,
|
||||
message="Liteyuki reloaded in %.2f s" % delta_time
|
||||
)
|
||||
|
||||
|
||||
# 每天4点更新
|
||||
@scheduler.scheduled_job("cron", hour=4)
|
||||
async def every_day_update():
|
||||
if get_config("auto_update", default=True):
|
||||
result, logs = update_liteyuki()
|
||||
pip.main(["install", "-r", "requirements.txt"])
|
||||
if result:
|
||||
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
||||
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
||||
Reloader.reload(5)
|
||||
else:
|
||||
nonebot.logger.info(logs)
|
||||
|
||||
|
||||
# 安全的需要用户id的api
|
||||
need_user_id = (
|
||||
"send_private_msg",
|
||||
"send_msg",
|
||||
"set_group_card",
|
||||
"set_group_special_title",
|
||||
"get_stranger_info",
|
||||
"get_group_member_info"
|
||||
)
|
||||
|
||||
need_group_id = (
|
||||
"send_group_msg",
|
||||
"send_msg",
|
||||
"set_group_card",
|
||||
"set_group_name",
|
||||
"set_group_special_title",
|
||||
"get_group_member_info",
|
||||
"get_group_member_list",
|
||||
"get_group_honor_info"
|
||||
)
|
||||
import base64
|
||||
import time
|
||||
from typing import Any, AnyStr
|
||||
|
||||
import nonebot
|
||||
import pip
|
||||
from nonebot import Bot, get_driver, require
|
||||
from nonebot.adapters import onebot, satori
|
||||
from nonebot.adapters.onebot.v11 import Message, escape, unescape
|
||||
from nonebot.exception import MockApiException
|
||||
from nonebot.internal.matcher import Matcher
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from liteyuki import Channel
|
||||
from src.utils.base.config import get_config, load_from_yaml
|
||||
from src.utils.base.data_manager import StoredConfig, TempConfig, common_db
|
||||
from src.utils.base.language import get_user_lang
|
||||
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||
from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||
# from src.liteyuki.core import Reloader
|
||||
from src.utils import event as event_utils, satori_utils
|
||||
from liteyuki.core.spawn_process import chan_in_spawn_nb
|
||||
|
||||
from .api import update_liteyuki
|
||||
from liteyuki.bot import get_bot
|
||||
from ..utils.base import reload
|
||||
from ..utils.base.ly_function import get_function
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
require("nonebot_plugin_apscheduler")
|
||||
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
markdown_image = common_db.where_one(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"liteecho",
|
||||
Args["text", str, ""],
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
|
||||
if result.main_args.get("text"):
|
||||
await matcher.finish(Message(unescape(result.main_args.get("text"))))
|
||||
else:
|
||||
await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"更新轻雪"},
|
||||
command=Alconna(
|
||||
"update-liteyuki"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(bot: T_Bot, event: T_MessageEvent):
|
||||
# 使用git pull更新
|
||||
|
||||
ulang = get_user_lang(str(event.user.id if isinstance(event, satori.event.Event) else event.user_id))
|
||||
success, logs = update_liteyuki()
|
||||
reply = "Liteyuki updated!\n"
|
||||
reply += f"```\n{logs}\n```\n"
|
||||
btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki")
|
||||
pip.main(["install", "-r", "requirements.txt"])
|
||||
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
||||
await md.send_md(reply, bot, event=event, at_sender=False)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"重启轻雪"},
|
||||
command=Alconna(
|
||||
"reload-liteyuki"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
||||
global channel_in_spawn_process
|
||||
await matcher.send("Liteyuki reloading")
|
||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||
|
||||
temp_data.data.update(
|
||||
{
|
||||
"reload" : True,
|
||||
"reload_time" : time.time(),
|
||||
"reload_bot_id" : bot.self_id,
|
||||
"reload_session_type": event_utils.get_message_type(event),
|
||||
"reload_session_id" : (event.group_id if event.message_type == "group" else event.user_id) if not isinstance(event,
|
||||
satori.event.Event) else event.chan.id,
|
||||
"delta_time" : 0
|
||||
}
|
||||
)
|
||||
|
||||
common_db.save(temp_data)
|
||||
reload()
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"配置"},
|
||||
command=Alconna(
|
||||
"config",
|
||||
Subcommand(
|
||||
"set",
|
||||
Args["key", str]["value", Any],
|
||||
alias=["设置"],
|
||||
|
||||
),
|
||||
Subcommand(
|
||||
"get",
|
||||
Args["key", str, None],
|
||||
alias=["查询", "获取"]
|
||||
),
|
||||
Subcommand(
|
||||
"remove",
|
||||
Args["key", str],
|
||||
alias=["删除"]
|
||||
)
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher):
|
||||
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
||||
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
||||
if result.subcommands.get("set"):
|
||||
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
|
||||
try:
|
||||
value = eval(value)
|
||||
except:
|
||||
pass
|
||||
stored_config.config[key] = value
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
|
||||
elif result.subcommands.get("get"):
|
||||
key = result.subcommands.get("get").args.get("key")
|
||||
file_config = load_from_yaml("config.yml")
|
||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||
if key:
|
||||
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
|
||||
else:
|
||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
|
||||
for k, v in file_config.items():
|
||||
reply += f"\n{k}={v}"
|
||||
reply += "\n```"
|
||||
if len(stored_config.config) > 0:
|
||||
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
||||
for k, v in stored_config.config.items():
|
||||
reply += f"\n{k}={v} {type(v)}"
|
||||
reply += "\n```"
|
||||
await md.send_md(reply, bot, event=event)
|
||||
elif result.subcommands.get("remove"):
|
||||
key = result.subcommands.get("remove").args.get("key")
|
||||
if key in stored_config.config:
|
||||
stored_config.config.pop(key)
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
|
||||
else:
|
||||
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
aliases={"切换图片模式"},
|
||||
command=Alconna(
|
||||
"switch-image-mode"
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(event: T_MessageEvent, matcher: Matcher):
|
||||
global markdown_image
|
||||
# 切换图片模式,False以图片形式发送,True以markdown形式发送
|
||||
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
||||
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
||||
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
|
||||
markdown_image = stored_config.config["markdown_image"]
|
||||
common_db.save(stored_config)
|
||||
await matcher.finish(
|
||||
ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"liteyuki-docs",
|
||||
),
|
||||
aliases={"轻雪文档"},
|
||||
).handle()
|
||||
# Satori OK
|
||||
async def _(matcher: Matcher):
|
||||
await matcher.finish("https://bot.liteyuki.icu/usage")
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"/function",
|
||||
Args["function", str]["args", MultiVar(str), ()],
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
||||
"""
|
||||
调用轻雪函数
|
||||
Args:
|
||||
result:
|
||||
bot:
|
||||
event:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
function_name = result.main_args.get("function")
|
||||
args: tuple[str] = result.main_args.get("args", ())
|
||||
_args = []
|
||||
_kwargs = {
|
||||
"USER_ID" : str(event.user_id),
|
||||
"GROUP_ID": str(event.group_id) if event.message_type == "group" else "0",
|
||||
"BOT_ID" : str(bot.self_id)
|
||||
}
|
||||
|
||||
for arg in args:
|
||||
arg = arg.replace("\\=", "EQUAL_SIGN")
|
||||
if "=" in arg:
|
||||
key, value = arg.split("=", 1)
|
||||
value = unescape(value.replace("EQUAL_SIGN", "="))
|
||||
try:
|
||||
value = eval(value)
|
||||
except:
|
||||
value = value
|
||||
_kwargs[key] = value
|
||||
else:
|
||||
_args.append(arg.replace("EQUAL_SIGN", "="))
|
||||
|
||||
ly_func = get_function(function_name)
|
||||
ly_func.bot = bot if "BOT_ID" not in _kwargs else nonebot.get_bot(_kwargs["BOT_ID"])
|
||||
ly_func.matcher = matcher
|
||||
|
||||
await ly_func(*tuple(_args), **_kwargs)
|
||||
|
||||
|
||||
@on_alconna(
|
||||
command=Alconna(
|
||||
"/api",
|
||||
Args["api", str]["args", MultiVar(AnyStr), ()],
|
||||
),
|
||||
permission=SUPERUSER
|
||||
).handle()
|
||||
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
||||
"""
|
||||
调用API
|
||||
Args:
|
||||
result:
|
||||
bot:
|
||||
event:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
api_name = result.main_args.get("api")
|
||||
args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数,但每个参数间用空格分隔,空格是%20
|
||||
args_dict = {}
|
||||
|
||||
for arg in args:
|
||||
key, value = arg.split("=", 1)
|
||||
|
||||
args_dict[key] = unescape(value.replace("%20", " "))
|
||||
|
||||
if api_name in need_user_id and "user_id" not in args_dict:
|
||||
args_dict["user_id"] = str(event.user_id)
|
||||
if api_name in need_group_id and "group_id" not in args_dict and event.message_type == "group":
|
||||
args_dict["group_id"] = str(event.group_id)
|
||||
|
||||
if "message" in args_dict:
|
||||
args_dict["message"] = Message(eval(args_dict["message"]))
|
||||
|
||||
if "messages" in args_dict:
|
||||
args_dict["messages"] = Message(eval(args_dict["messages"]))
|
||||
|
||||
try:
|
||||
result = await bot.call_api(api_name, **args_dict)
|
||||
except Exception as e:
|
||||
result = str(e)
|
||||
|
||||
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items())
|
||||
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
||||
|
||||
|
||||
# system hook
|
||||
@Bot.on_calling_api # 图片模式检测
|
||||
async def test_for_md_image(bot: T_Bot, api: str, data: dict):
|
||||
# 截获大图发送,转换为markdown发送
|
||||
if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get(
|
||||
"user_id") != bot.self_id:
|
||||
if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg":
|
||||
session_type = "private"
|
||||
session_id = data.get("user_id")
|
||||
elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg":
|
||||
session_type = "group"
|
||||
session_id = data.get("group_id")
|
||||
else:
|
||||
return
|
||||
if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
|
||||
file: str = data["message"][0].data.get("file")
|
||||
# file:// http:// base64://
|
||||
if file.startswith("http"):
|
||||
result = await md.send_md(await md.image_async(file), bot, message_type=session_type,
|
||||
session_id=session_id)
|
||||
elif file.startswith("file"):
|
||||
file = file.replace("file://", "")
|
||||
result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type,
|
||||
session_id=session_id)
|
||||
elif file.startswith("base64"):
|
||||
file_bytes = base64.b64decode(file.replace("base64://", ""))
|
||||
result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id)
|
||||
else:
|
||||
return
|
||||
raise MockApiException(result=result)
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def on_startup():
|
||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||
# 储存重启信息
|
||||
if temp_data.data.get("reload", False):
|
||||
delta_time = time.time() - temp_data.data.get("reload_time", 0)
|
||||
temp_data.data["delta_time"] = delta_time
|
||||
common_db.save(temp_data) # 更新数据
|
||||
"""
|
||||
该部分将迁移至轻雪生命周期
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@driver.on_shutdown
|
||||
async def on_shutdown():
|
||||
pass
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _(bot: T_Bot):
|
||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||
if isinstance(bot, satori.Bot):
|
||||
await satori_utils.user_infos.load_friends(bot)
|
||||
# 用于重启计时
|
||||
if temp_data.data.get("reload", False):
|
||||
temp_data.data["reload"] = False
|
||||
reload_bot_id = temp_data.data.get("reload_bot_id", 0)
|
||||
if reload_bot_id != bot.self_id:
|
||||
return
|
||||
reload_session_type = temp_data.data.get("reload_session_type", "private")
|
||||
reload_session_id = temp_data.data.get("reload_session_id", 0)
|
||||
delta_time = temp_data.data.get("delta_time", 0)
|
||||
common_db.save(temp_data) # 更新数据
|
||||
|
||||
if delta_time <= 20.0: # 启动时间太长就别发了,丢人
|
||||
if isinstance(bot, satori.Bot):
|
||||
await bot.send_message(
|
||||
channel_id=reload_session_id,
|
||||
message="Liteyuki reloaded in %.2f s" % delta_time
|
||||
)
|
||||
elif isinstance(bot, onebot.v11.Bot):
|
||||
await bot.send_msg(
|
||||
message_type=reload_session_type,
|
||||
user_id=reload_session_id,
|
||||
group_id=reload_session_id,
|
||||
message="Liteyuki reloaded in %.2f s" % delta_time
|
||||
)
|
||||
|
||||
elif isinstance(bot, onebot.v12.Bot):
|
||||
await bot.send_message(
|
||||
message_type=reload_session_type,
|
||||
user_id=reload_session_id,
|
||||
group_id=reload_session_id,
|
||||
message="Liteyuki reloaded in %.2f s" % delta_time,
|
||||
detail_type="group"
|
||||
)
|
||||
|
||||
|
||||
# 每天4点更新
|
||||
@scheduler.scheduled_job("cron", hour=4)
|
||||
async def every_day_update():
|
||||
if get_config("auto_update", default=True):
|
||||
result, logs = update_liteyuki()
|
||||
pip.main(["install", "-r", "requirements.txt"])
|
||||
if result:
|
||||
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
||||
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
||||
reload()
|
||||
else:
|
||||
nonebot.logger.info(logs)
|
||||
|
||||
|
||||
# 需要用户id的api
|
||||
need_user_id = (
|
||||
"send_private_msg",
|
||||
"send_msg",
|
||||
"set_group_card",
|
||||
"set_group_special_title",
|
||||
"get_stranger_info",
|
||||
"get_group_member_info"
|
||||
)
|
||||
|
||||
need_group_id = (
|
||||
"send_group_msg",
|
||||
"send_msg",
|
||||
"set_group_card",
|
||||
"set_group_name",
|
||||
|
||||
"set_group_special_title",
|
||||
"get_group_member_info",
|
||||
"get_group_member_list",
|
||||
"get_group_honor_info"
|
||||
)
|
62
src/liteyuki_main/dev.py
Normal file
@ -0,0 +1,62 @@
|
||||
import nonebot
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from liteyuki.bot import get_bot
|
||||
from src.utils.base import reload
|
||||
from src.utils.base.config import get_config
|
||||
from src.utils.base.resource import load_resources
|
||||
|
||||
if get_config("debug", False):
|
||||
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
src_directories = (
|
||||
"src/liteyuki_main",
|
||||
"src/plugins",
|
||||
"src/utils",
|
||||
)
|
||||
src_excludes_extensions = (
|
||||
"pyc",
|
||||
)
|
||||
|
||||
res_directories = (
|
||||
"src/resources",
|
||||
"resources",
|
||||
)
|
||||
|
||||
nonebot.logger.info("Liteyuki Reload enabled, watching for file changes...")
|
||||
|
||||
|
||||
class CodeModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for code file changes
|
||||
"""
|
||||
|
||||
def on_modified(self, event):
|
||||
if event.src_path.endswith(
|
||||
src_excludes_extensions) or event.is_directory or "__pycache__" in event.src_path:
|
||||
return
|
||||
nonebot.logger.info(f"{event.src_path} modified, reloading bot...")
|
||||
reload()
|
||||
|
||||
|
||||
class ResourceModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for resource file changes
|
||||
"""
|
||||
|
||||
def on_modified(self, event):
|
||||
nonebot.logger.info(f"{event.src_path} modified, reloading resource...")
|
||||
load_resources()
|
||||
|
||||
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
resource_modified_handle = ResourceModifiedHandler()
|
||||
|
||||
observer = Observer()
|
||||
for directory in src_directories:
|
||||
observer.schedule(code_modified_handler, directory, recursive=True)
|
||||
for directory in res_directories:
|
||||
observer.schedule(resource_modified_handle, directory, recursive=True)
|
||||
observer.start()
|
38
src/liteyuki_main/loader.py
Normal file
@ -0,0 +1,38 @@
|
||||
import asyncio
|
||||
|
||||
import nonebot.plugin
|
||||
from nonebot import get_driver
|
||||
from src.utils import init_log
|
||||
from src.utils.base.config import get_config
|
||||
from src.utils.base.data_manager import InstalledPlugin, plugin_db
|
||||
from src.utils.base.resource import load_resources
|
||||
from src.utils.message.tools import check_for_package
|
||||
|
||||
from liteyuki import get_bot, chan
|
||||
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
load_resources()
|
||||
init_log()
|
||||
|
||||
driver = get_driver()
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def load_plugins():
|
||||
nonebot.plugin.load_plugins("src/plugins")
|
||||
# 从数据库读取已安装的插件
|
||||
if not get_config("safe_mode", False):
|
||||
# 安全模式下,不加载插件
|
||||
installed_plugins: list[InstalledPlugin] = plugin_db.where_all(InstalledPlugin())
|
||||
if installed_plugins:
|
||||
for installed_plugin in installed_plugins:
|
||||
if not check_for_package(installed_plugin.module_name):
|
||||
nonebot.logger.error(
|
||||
f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
|
||||
else:
|
||||
nonebot.load_plugin(installed_plugin.module_name)
|
||||
nonebot.plugin.load_plugins("plugins")
|
||||
else:
|
||||
nonebot.logger.info("Safe mode is on, no plugin loaded.")
|
@ -1,15 +1,18 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from .rt_guide import *
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="CRT生成工具",
|
||||
description="一些CRT牌子生成器",
|
||||
usage="我觉得你应该会用",
|
||||
type="application",
|
||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||
extra={
|
||||
"liteyuki" : True,
|
||||
"toggleable" : True,
|
||||
"default_enable": True,
|
||||
}
|
||||
)
|
||||
import multiprocessing
|
||||
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from .rt_guide import *
|
||||
from .crt_matchers import *
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="CRT生成工具",
|
||||
description="一些CRT牌子生成器",
|
||||
usage="我觉得你应该会用",
|
||||
type="application",
|
||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||
extra={
|
||||
"liteyuki" : True,
|
||||
"toggleable" : True,
|
||||
"default_enable": True,
|
||||
}
|
||||
)
|