1
0
forked from bot/app

177 Commits
v6.3.2 ... beta

Author SHA1 Message Date
3bd40e7271 🐛 修复生命周期钩子函数的问题 2024-08-08 18:10:18 +08:00
8ace3e68f4 Merge remote-tracking branch 'origin/main' 2024-08-08 18:06:14 +08:00
f69feb1def 🐛 修复生命周期钩子函数的问题 2024-08-08 18:06:03 +08:00
9fb423d5e0 Merge pull request #55 from ElapsingDreams/main
📝Hot Fixed Dev judgment
2024-08-08 16:57:16 +08:00
e9df67a661 📝Hot Fixed Dev judgment 2024-08-08 16:55:58 +08:00
b6871ea13a Merge pull request #54 from ElapsingDreams/main
Add Dev judgement
2024-08-08 16:49:00 +08:00
cb84a7d0d9 Add Dev judgment 2024-08-08 16:41:23 +08:00
25f7540f86 Add Dev judgment 2024-08-08 16:40:34 +08:00
c29a3fd6d4 🐛 数据库未进行迁移前初始化 2024-08-05 07:23:04 +08:00
ab48396db9 🐛 数据库未进行迁移前初始化 2024-08-05 07:13:41 +08:00
51982b63c3 📝 新增全球统计 2024-08-05 06:05:37 +08:00
2b537d27ec 📝 新增全球统计 2024-08-05 06:02:53 +08:00
16930e96aa 📝 新增全球统计 2024-08-05 06:00:07 +08:00
d63ba4943a 📝 新增全球统计 2024-08-01 13:17:48 +08:00
5d22f20ce3 📝 新增全球统计 2024-08-01 13:11:05 +08:00
2451849fd6 📝 新增全球统计 2024-08-01 12:33:36 +08:00
61680d9e87 📝 新增全球统计 2024-08-01 12:28:44 +08:00
850dd75822 📝 新增全球统计 2024-08-01 12:23:56 +08:00
6ba983fae3 🐛 fix: Channel的接收者过滤器的问题,优化重启部分 2024-07-31 02:35:05 +08:00
ca34f9c2a1 🐛 fix: Channel的接收者过滤器的问题,优化重启部分 2024-07-31 02:28:25 +08:00
0fb5b84392 🐛 移除liteyuki.channel.on_receive() wrapper对async和sync function的判断冗余代码 2024-07-27 10:21:31 +08:00
39a9c39924 添加liteyuki.channel.Channel通道,可安全跨进程通信 2024-07-27 10:12:45 +08:00
13692228c6 添加进程及生命周期管理器,添加轻雪框架支持 2024-07-26 14:35:47 +08:00
f22f4d229d 添加进程及生命周期管理器,添加轻雪框架支持 2024-07-24 02:58:16 +08:00
97dbf42a4d 添加进程及生命周期管理器,添加轻雪框架支持 2024-07-24 02:57:35 +08:00
041a219151 添加进程及生命周期管理器,添加轻雪框架支持 2024-07-24 02:54:59 +08:00
c137f2f916 添加进程及生命周期管理器,添加轻雪框架支持 2024-07-24 02:36:46 +08:00
6ef3b09ec9 📝 恢复pyproject.toml 2024-07-21 16:06:50 +08:00
263b78e995 📝 修改文档语言风格 2024-07-21 00:40:55 +08:00
0d87848a7e 📝 修改文档语言风格 2024-07-20 18:32:40 +08:00
44ad0832ba 📝 修改文档语言风格 2024-07-20 18:31:51 +08:00
93ced26e07 Merge pull request #52 from EillesWan/main
🎇修复liteyuki_pacman在执行disable时因session_id导致的反馈不当 & 🀄优化文言文翻译,修复翻译漏洞
2024-07-15 02:40:30 +08:00
363daf6251 🎇修复liteyuki_pacman在执行disable时因session_id导致的反馈不当
🀄优化文言文翻译,修复翻译漏洞
2024-07-15 02:31:47 +08:00
a6b1d1c9e0 Merge pull request #51 from EillesWan/main
🥠修复状态缓存出错的问题 & 📃规范化文言文本地化文本
2024-07-15 02:26:07 +08:00
576d8c23b3 🥠修复状态缓存出错的问题
🧾规范化文言文本地化文本
2024-07-15 02:07:15 +08:00
c232c6e5f6 插件商店及资源商店新增搜索功能 2024-07-15 00:22:52 +08:00
5d6ae52157 插件商店及资源商店新增搜索功能 2024-07-15 00:03:22 +08:00
01e6256ed4 插件商店及资源商店新增搜索功能 2024-07-14 23:50:31 +08:00
dbc114a529 插件商店及资源商店新增搜索功能 2024-07-14 13:46:54 +08:00
4d77af8f0c 插件商店及资源商店新增搜索功能 2024-07-14 13:43:47 +08:00
c36a925bb5 插件商店及资源商店新增搜索功能 2024-07-14 13:38:25 +08:00
605dd035d4 Merge pull request #49 from EillesWan/main
完善翻译文本 & 优化状态缓存
2024-07-14 02:38:51 +08:00
7526ae13d7 补一个错 2024-07-14 02:30:04 +08:00
0eb41f70d2 Merge branch 'main' of https://github.com/LiteyukiStudio/LiteyukiBot 2024-07-14 02:21:10 +08:00
a4c7ee738c 更改内容:
1. 更新文言文翻译
2. 删除重复的翻译键
3. 重写status缓存机制并black格式化
请注意:未经过本地测试,需要观察者辅助测试,谢谢!
2024-07-14 02:19:57 +08:00
8d4602c40d 📝 add more background rp 2024-07-13 01:26:32 +08:00
bb20d9623d 🐛 fix typo 2024-07-06 12:01:09 +08:00
73cc28d1cf 📝 update README.md 2024-07-06 11:29:05 +08:00
ae54cd923c 📝 update README.md 2024-07-06 11:25:36 +08:00
9d3c9a7d70 📝 update and rename Lisence to LICENSE 2024-07-06 10:59:22 +08:00
92a4274be7 Merge pull request #47 from Nanaloveyuki/main
📄 更新许可证 MIT -> LSO & LSO-AGC
2024-07-06 10:54:07 +08:00
4800b3f46c 📄 更新许可证文件
完成MIT->LSO的更迭工作
2024-07-06 10:35:47 +08:00
cce593b2f4 📄 移除原MIT许可 2024-07-06 10:32:08 +08:00
c491642713 📄 移除AGC非许可形式协议 2024-07-06 10:31:50 +08:00
5b4dd638a4 📄 移除原LSO许可 2024-07-06 10:31:31 +08:00
8440952167 📝 update repo url in src 2024-07-06 02:26:33 +08:00
4e4227e204 📝 update repo url 2024-07-06 02:25:12 +08:00
effb01d43c 📝 add 2nd license 2024-07-04 02:08:07 +08:00
28d730a2ca Merge pull request #46 from Nanaloveyuki/main
🌐 📝 日常优化文档, 提供多语言支持
2024-07-01 17:30:16 +08:00
0bd135a5c9 🌐 为statistics提供基础多语言支持 2024-07-01 16:08:15 +08:00
5522391942 Add files via upload 2024-07-01 16:00:46 +08:00
7576355e95 📝 商店新增轻雪Kakyo语言包 2024-06-30 03:28:28 +08:00
c9e518f2ed Merge pull request #45 from Nanaloveyuki/main
📄 更新反克隆文件`Anti-Gitcode-Clone.md`
2024-06-29 23:09:22 +08:00
593cf2407b Merge branch 'main' into main 2024-06-29 00:53:23 +08:00
bdb4c76d70 📄 Add Anti-Gitcode-Clone md-file 2024-06-29 00:40:14 +08:00
7cce805d39 📄 Delete Dis-CSDN-Gitcode.md 2024-06-29 00:25:59 +08:00
b9d3ecc15d Merge pull request #44 from Nanaloveyuki/main
📄 Add Anti-Gitcode-Clone md-file
2024-06-29 00:20:19 +08:00
ec5eae08f7 📄 Add Anti-Gitcode-Clone md-file 2024-06-29 00:15:44 +08:00
c1edf31577 🔥 create dependabot.yml 2024-06-27 04:17:39 +08:00
5262c04e46 🔥 小型重构 2024-06-26 13:53:15 +08:00
8b01943d14 🔥 小型重构 2024-06-26 13:52:04 +08:00
35823be13e 新增 stat rank 功能 2024-06-25 20:06:49 +08:00
4162ea33ff orm框架新增@db.on_save回调函数,用于检测数据库更新时的变动 2024-06-22 14:17:14 +08:00
1787ef4db7 📝 更新夜间logo 2024-06-19 23:38:53 +08:00
38b13611c9 📝 更新夜间logo 2024-06-19 23:31:11 +08:00
52fa143e75 📝 更新夜间logo 2024-06-19 22:30:27 +08:00
89047a0c8a 📝 更新小logo 2024-06-19 17:59:57 +08:00
9fbded7d6a 📝 部署部分文档优化 2024-06-19 12:34:38 +08:00
c657781599 🔥 移除测试工作流 2024-06-19 12:16:42 +08:00
ecbe1ff79e 新logo 2024-06-19 12:15:22 +08:00
d6811ab9b3 新logo 2024-06-18 23:57:15 +08:00
d45170db3e 新logo 2024-06-18 23:36:04 +08:00
6e63768c71 新logo 2024-06-18 23:32:58 +08:00
1424bc2cf6 Merge remote-tracking branch 'origin/main' 2024-06-18 23:25:33 +08:00
051fe3d15d 新logo 2024-06-18 23:25:23 +08:00
fcae485071 新logo 2024-06-18 23:23:44 +08:00
2e9a7fdf94 Merge pull request #42 from Nanaloveyuki/main
📝  主要更新06-17
2024-06-17 18:46:12 +08:00
570d7e18a4 Docs 主要更新: 优化表格
主要改动:
- 表格
- 标点符号
- 愚人节主页
2024-06-16 23:33:58 +08:00
8ac53970a3 Merge remote-tracking branch 'origin/main' 2024-06-16 02:02:25 +08:00
dd3e108e10 新logo 2024-06-16 02:01:53 +08:00
c29bd81ffb Merge pull request #41 from MiaoMioLint/patch-1
详细化 lyfunc.md
2024-06-13 09:54:15 +08:00
a1173e4d84 详细化 lyfunc.md 2024-06-13 09:47:13 +08:00
d877e30a05 📝 添加2次quote的解释 2024-06-10 07:33:44 +08:00
33ad54090d 新版本npm 2024-06-09 22:36:58 +08:00
83ee6cfdbd 新版本npm 2024-06-04 18:04:25 +08:00
f48971a0c4 🐛 orm框架在解析字段值时遇到None报错的问题 2024-06-04 18:00:38 +08:00
a4b71aa73c 🐛 orm框架在解析字段值时遇到None报错的问题 2024-06-03 17:58:46 +08:00
4b7df662e8 Merge remote-tracking branch 'origin/main' 2024-06-03 17:54:32 +08:00
4cd7b6718b 🐛 orm框架在解析字段值时遇到None报错的问题 2024-06-03 17:53:44 +08:00
ac2a94dda0 Merge pull request #40 from yuhan2680/patch-1
添加词库w
2024-06-03 13:54:18 +08:00
de7e65b32a 添加词库w 2024-06-03 13:52:34 +08:00
1b283261c3 词库性格区分 2024-06-02 23:21:14 +08:00
39cbfc1baa 词库性格区分 2024-06-02 23:18:00 +08:00
e563f18d31 词库性格区分 2024-06-02 23:15:38 +08:00
1d03b3f28f 词库性格区分 2024-06-02 23:12:24 +08:00
ae0025a203 词库性格区分 2024-06-02 23:09:59 +08:00
b5bd7acb7f 词库性格区分 2024-06-02 22:56:37 +08:00
657e7e52ac 📝 空字符串 2024-06-02 15:03:50 +08:00
d6341c88cd 📝 函数指引 2024-06-02 13:58:41 +08:00
bdb1191f9e 🐛 智障回复分词bug 2024-06-02 02:32:54 +08:00
3b29b67c0b 📝 函数指引 2024-06-02 02:23:00 +08:00
cc43e53c4b npm 会话开关功能新增群号可选项 2024-06-02 02:22:16 +08:00
a25c900d49 智障回复功能支持切断回复 2024-06-02 01:41:46 +08:00
206651da94 智障回复功能 2024-06-02 01:32:52 +08:00
be28116a98 优化状态卡片速度 2024-06-01 15:32:41 +08:00
4cdf29557c 新增启动时对本地仓库的检测 2024-06-01 15:26:13 +08:00
62928e47eb 新增启动时对本地仓库的检测 2024-06-01 15:25:54 +08:00
9b50b719d9 🚀 对lyfunction命令添加鉴权 2024-06-01 15:05:45 +08:00
def60bf298 对lyfunction命令添加鉴权 2024-06-01 15:01:14 +08:00
6496b6e463 对lyfunction命令添加鉴权 2024-06-01 15:01:04 +08:00
6ce4c972a0 对lyfunction命令添加鉴权 2024-06-01 14:16:04 +08:00
70e3c9968a 添加对lyfunction的支持 2024-05-31 23:15:40 +08:00
074882f092 添加对lyfunction的支持 2024-05-31 22:59:02 +08:00
c2b3018908 添加对lyfunction的支持 2024-05-31 22:42:04 +08:00
96c85d9dca Merge remote-tracking branch 'origin/main' 2024-05-31 19:17:31 +08:00
e15aafd781 添加对zip格式的资源包的支持,对function的支持 2024-05-31 19:17:25 +08:00
9e17b84a5d 🐛 一些单词复数形式 2024-05-28 18:28:20 +08:00
c66d470166 🐛 mdts反转义错误的问题 2024-05-27 18:32:58 +08:00
4deb7d11a1 新增开发者选项是否允许更新 2024-05-26 18:15:57 +08:00
6f069f83d4 📝 文档新增特性 2024-05-26 18:11:18 +08:00
fa53df1e8a 📝 文档新增特性 2024-05-26 17:25:20 +08:00
ba17f9d159 📝 文档新增特性 2024-05-26 17:19:32 +08:00
b558b51601 📝 文档新增特性 2024-05-26 17:12:06 +08:00
3ea0acd48b 修改启动逻辑和插件加载逻辑 2024-05-26 16:38:38 +08:00
c171873fa6 优化圆角样式 2024-05-25 12:09:54 +08:00
1ccf94883a 消息统计新增指定用户选项 2024-05-25 00:13:29 +08:00
b26f8e0d24 消息统计新增指定用户选项 2024-05-24 23:46:24 +08:00
5bc2725d1b 消息统计新增指定用户选项 2024-05-24 23:41:33 +08:00
8e06244311 🐛 修复了消息模型储存时类型不兼容的问题 2024-05-23 23:37:24 +08:00
0f35613e50 🐛 修复了消息模型储存时类型不兼容的问题 2024-05-23 23:32:02 +08:00
4cfad0b5ca 添加了外部资源热重载 2024-05-21 13:13:16 +08:00
c2593e71c0 Merge remote-tracking branch 'origin/main' 2024-05-20 23:31:45 +08:00
bb331232ca 添加了外部资源热重载 2024-05-20 23:31:11 +08:00
96e8293bf4 Merge pull request #39 from expliyh/qweather_on_satori
🐛 在使用 satori 适配器时不能响应例如 武汉天气 的指令
2024-05-20 13:54:04 +08:00
e13464cb7c 拼写错了(尴尬) 2024-05-20 08:51:21 +08:00
c5f8fbe86d 🐛 在使用 satori 适配器时不能响应例如 武汉天气 的指令 2024-05-20 08:22:06 +08:00
8667706377 使用 alc 发送例如 天气武汉 的回复 2024-05-20 08:20:20 +08:00
86e47ab226 Merge pull request #38 from expliyh/satori
对昨晚 PR 的一些修正
2024-05-19 23:27:43 +08:00
10c383d66a 删除无用的 print 2024-05-17 18:16:57 +08:00
4c65a308d6 🐛 当没有 .env 文件时意外加载错误的环境变量 2024-05-17 18:00:07 +08:00
246e43317f 📝 添加 onebot_v12_event_monitor 2024-05-17 17:53:38 +08:00
974b97b744 📝 初次启动生成默认配置文件时添加 satori 相关配置
📝 将适配器配置初始化和注册移动到 utils.adapter_manager
2024-05-17 17:51:42 +08:00
c914ddc0ee 📝 NapCat.Onebot 显示头像 2024-05-17 17:48:37 +08:00
6509b293db 📝 使用 driver_manager 自动管理启用的驱动器,无需手动配置环境变量 (当配置了环境变量时环境变量优先) 2024-05-17 16:33:48 +08:00
a72eeb4c3f 📝 使用 driver_manager自动管理启用的驱动器,无需手动配置环境变量 (当配置了环境变量时环境变量优先) 2024-05-17 16:26:30 +08:00
309397b72c 📝 将从事件中获取信息的工具函数移动到单独的 utils.event 2024-05-17 15:00:01 +08:00
077658c68d 📝 将 Satori 用户缓存更新日志的输出时机由原来的每次调用函数调整为每次用户信息发生变更 2024-05-17 14:42:37 +08:00
322ad19889 🐛 在使用 satori 时部分指令无响应
🐛 使用 onebot 时部分事件在 postprocessor 阶段报错
2024-05-17 14:18:55 +08:00
ab9d3d3d3e 🐛 satori.Adapter TypeError: 'HeartbeatMetaEvent' object is not subscriptable 2024-05-17 00:25:27 +08:00
06a109d2b5 Merge pull request #37 from expliyh/satori
试着添加了对于 NoneBot-Adapter-Satori 的支持
2024-05-16 23:31:54 +08:00
351743068a 判断配置文件是否启用satori 2024-05-16 22:15:04 +08:00
4e6532ff0d pacman和profile适配satori 2024-05-16 21:28:18 +08:00
eaf57f2c33 status适配satori 2024-05-16 21:17:10 +08:00
7abdac7c9c statistic适配satori 2024-05-16 20:28:46 +08:00
002df66878 weather适配satori(bind city未找到) 2024-05-16 20:22:07 +08:00
251bfaf410 Core适配satori 2024-05-16 20:09:20 +08:00
24722447da 使用satori时维护一个有昵称的用户列表
get_plugin_session_enable 判断当前使用的适配器
2024-05-16 19:20:54 +08:00
90e7a90bcf 添加 nonebot-adapter-satori~=0.11.5 2024-05-16 17:32:06 +08:00
6d3d3fc52c output to latest 2024-05-14 10:37:32 +08:00
e843d790b1 message 统计 2024-05-13 19:37:10 +08:00
c90ac1d21a message 统计 2024-05-12 03:04:26 +08:00
041ceb81d8 message 统计 2024-05-12 02:47:14 +08:00
c6f2a29320 使用webp背景图压缩资源包大小 2024-05-12 00:25:51 +08:00
0532d7592e Merge remote-tracking branch 'origin/main' 2024-05-12 00:23:36 +08:00
f9fe1922d4 使用webp背景图压缩资源包大小 2024-05-12 00:18:53 +08:00
88b5b55062 Update issue templates 2024-05-11 10:57:36 +08:00
afe501a06d 使用webp背景图压缩资源包大小 2024-05-11 10:05:01 +08:00
344 changed files with 29596 additions and 21289 deletions

53
.github/ISSUE_TEMPLATE/问题反馈.md vendored Normal file
View 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
View 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"

View File

@ -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
View File

@ -15,13 +15,13 @@ _config.yml
config.yml config.yml
config.example.yml config.example.yml
compile.bat compile.bat
liteyuki/resources/templates/latest-debug.html src/resources/templates/latest-debug.html
# vuepress # vuepress
.github .github
pyproject.toml
test.py test.py
line_count.py line_count.py
mypy.ini
# nuitka # nuitka
main.build/ main.build/
@ -34,4 +34,5 @@ docs/.vuepress/dist/
prompt.txt prompt.txt
# js # js
**/echarts.js **/echarts.js
.env

31
EN.LICENSE Normal file
View 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
View File

@ -1,21 +1,31 @@
MIT License LSO license
LiteyukiStudio Opensource 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 版权所有 © 2024 Snowykami
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. - 使用本软件及其副本时仍需保持与原有形式相同
- 在使用时仍需将本软件的副本以相同许可公开表现:
- 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利
---
该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示:
包括但不限于适销性保证, 适用于特定目的非侵权
在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付

57
MIT-LSO-AGC.LICENSE Normal file
View File

@ -0,0 +1,57 @@
# 开源声明
MIT-LSO-AGC许可(cn)
本声明推荐Markdown格式阅读
## 版权声明
版权所有(c) 2024 Snowykami
## 开源许可声明
现授予任何人以同仓库许可的开源,包含:
本软件或本库和相关文档文件(以下简称“软件”)
在本软件或本库中不受限制,包括但不限于**非侵犯MIT-LSO-AGC许可**的:
- 使用
- 复制
- 修改
- 合并
- 发布
- 分发
- 再许可和/或受到许可的销售本软件的副本
并要求或强烈建议使用本软件或库时:
- 将上述版权声明和本许可声明包含在所有本软件或本库的副本或主要部分
---
本软件或本库按“原样”提供,不提供任何形式的、明确或隐含的,包括但不限于适销性保证,适合特定用途和非侵权性
## 责任声明
在任何情况下都不得在使用本软件或库时,在本协议许可的情况下,以损害或其他责任追责,无论是在合同诉讼、侵权行为或其他情况下,抑或是由以下原因引起:
由于或与本软件有关,或是使用,或其他交易
都与本软件,库作者无责任关系,非作者使用本软件或本库并造成任何损失的行为与作者无关
---
## 搬运声明
在前提开源许可的基础上:
**未经允许不得**搬运至商业性平台**并且**进行**付费行为或疑似未得到许可的交易行为**
**不得**在搬运本仓库至**其他私有/公有开源镜像代码托管网站**时未经允许修改**任何**关于**原库的内容**
### 包括但不限于:
- 将`github`修改为`gitcode`
- 篡改外链网址
- 修改贡献者名单
- 未经原作者允许私自占有仓库并声明为自己所有
- 未经允许修改库原先所有的开源协议许可
---

View File

@ -1,37 +1,37 @@
<div align="center"> <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] [![][banner]][lightyuki-link]
<h2><a href="https://bot.liteyuki.icu"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">6</span></a></h2> <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> <h4> <span style="color: #a2d8f4">✨ 轻量,高效,易于扩展✨</span></h4>
[![][OneBot]][onebot-link] [![][OneBot]][onebot-link]
[![][Nonebot2]][nonebot-link] [![][NoneBot2]][nonebot-link]
[![][Liteyuki6.0]][lightyuki-link] [![][Liteyuki6.0]][lightyuki-link]
[![][Python3.10+]][python-link] [![][Python3.10+]][python-link]
[![][Usage]][usage-link] [![][Usage]][usage-link]
- 基于[Nonebot2](https://github.com/nonebot/nonebot2),有良好的生态支持 - 基于[Nonebot2](https://github.com/nonebot/nonebot2),有良好的生态支持
- 开箱即用,无需复杂配置 - 开箱即用,无需复杂配置
- 新的点击交互模式,拒绝手打指令 - 集成包管理器,支持一键安装插件
- 可视化插件管理包管理,支持一键安装插件
- 支持OneBot标准通信但不限于此 - 支持OneBot标准通信但不限于此
- 自定义主题支持,满足审美需求 - 自定义主题支持,满足审美需求
- 国际化支持,支持多种语言 - 国际化支持,支持多种语言
- 高性能500插件2s内启动
<h3>👇所有内容已迁移至👇</h3> <h3>👇更多内容请访问👇</h3>
<h2><a href="https://bot.liteyuki.icu">轻雪主页</a></h2> <h2><a href="https://bot.liteyuki.icu">轻雪机器人主页</a></h2>
</div> </div>
### 感谢 ### 感谢
- [Nonebot2](https://nonebot.dev)提供的框架支持 - [NoneBot2](https://nonebot.dev)提供的框架支持
- [nonebot-plugin-htmlrender](https://github.com/kexue-z/nonebot-plugin-htmlrender)提供的渲染功能 - [nonebot-plugin-htmlrender](https://github.com/kexue-z/nonebot-plugin-htmlrender)提供的渲染功能
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能 - [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
[OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge [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 [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/ [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

View File

@ -1,14 +1,20 @@
import {defineClientConfig} from "vuepress/client"; import {defineClientConfig} from "vuepress/client";
import resourceStoreComp from "./components/res_store.vue";
import pluginStoreComp from "./components/plugin_store.vue"; import resourceStoreComp from "./components/ResStore.vue";
//导入element-plus import pluginStoreComp from "./components/PluginStore.vue";
import ElementPlus from 'element-plus'; 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({ export default defineClientConfig({
enhance: ({app, router, siteData}) => { enhance: ({app, router, siteData}) => {
app.component("homeComp", homeComp);
app.component("dashComp", dashComp);
app.component("resourceStoreComp", resourceStoreComp); app.component("resourceStoreComp", resourceStoreComp);
app.component("pluginStoreComp", pluginStoreComp); app.component("pluginStoreComp", pluginStoreComp);
app.use(ElementPlus); app.component("geoComp", geoComp);
// app.use(ElementPlus);
}, },
}); });

View 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>

View 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>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped>
</style>

View 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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -10,6 +10,8 @@ export default defineUserConfig({
description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人", description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人",
head: [ head: [
// 设置 favor.ico.vuepress/public 下 // 设置 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: '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'}], ['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, theme,
// 和 PWA 一起启用 // 和 PWA 一起启用
// shouldPrefetch: false, // shouldPrefetch: false,

View File

@ -3,17 +3,17 @@ import {navbar} from "vuepress-theme-hope";
export default navbar([ export default navbar([
"/", "/",
{ {
text: "项目部署", text: "安装及部署",
link: "/deployment/", link: "/deployment/",
prefix: "deployment/", prefix: "deployment/",
}, },
{ {
text: "使用手册", text: "使用及开发",
link: "/usage/", link: "/usage/",
prefix: "usage/", prefix: "usage/",
}, },
{ {
text: "商店", text: "资源及插件",
link: "/store/resource", link: "/store/resource",
prefix: "store/", prefix: "store/",
} }

View 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
}
]

View File

@ -3,8 +3,7 @@
"name": "KawaiiStatus", "name": "KawaiiStatus",
"author": "SnowyKami", "author": "SnowyKami",
"description": "可爱的状态卡片仿照koishi的制作", "description": "可爱的状态卡片仿照koishi的制作",
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip", "link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip"
"icon": "https://cdn.jsdelivr.net/gh/SnowyKami/CDN/img/KawaiiStatus.png"
}, },
{ {
"name": "MiSans字体包", "name": "MiSans字体包",
@ -25,15 +24,33 @@
"link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip" "link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip"
}, },
{ {
"name": "示例包2", "name": "自定义设备信息",
"author": "SnowyKami", "author": "SnowyKami",
"description": "A simple bot that shows the status of the bot and the server.", "description": "自定义服务端的设备信息,自行修改使用",
"link": "" "link": "https://cdn.liteyuki.icu/static/lrp/custom-device.zip"
}, },
{ {
"name": "示例包3", "name": "轻雪傲娇系词库",
"author": "SnowyKami", "author": "SnowyKami",
"description": "A simple bot that shows the status of the bot and the server.", "description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
"link": "" "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"
} }
] ]

View 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

View 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));

View 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>&nbsp;&nbsp;&nbsp;&nbsp;当前在线:<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秒更新一次页面显示

View 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)

File diff suppressed because one or more lines are too long

View File

@ -4,19 +4,19 @@ export default sidebar({
"/": [ "/": [
"", "",
{ {
text: "项目部署", text: "安装及部署",
icon: "laptop-code", icon: "laptop-code",
prefix: "deployment/", prefix: "deployment/",
children: "structure", children: "structure",
}, },
{ {
text: "使用手册", text: "使用及开发",
icon: "book", icon: "book",
prefix: "usage/", prefix: "usage/",
children: "structure", children: "structure",
}, },
{ {
text: "商店", text: "资源及插件",
icon: "store", icon: "store",
prefix: "store/", prefix: "store/",
children: "structure", children: "structure",

View File

@ -1,5 +1,10 @@
// place your custom styles here // place your custom styles here
:root {
--code-window-border-radius: 10px;
--button-distance: 8px;
}
#main-title { #main-title {
font-family: ColorTube, "Fira Code", serif; font-family: ColorTube, "Fira Code", serif;
color: #ff0000 !important; /* 你想要的颜色 */ color: #ff0000 !important; /* 你想要的颜色 */
@ -8,9 +13,88 @@
@font-face { @font-face {
font-family: ColorTube; font-family: ColorTube;
src: url("/assets/fonts/ColorTube.woff") format("woff") src: url("/assets/fonts/ColorTube.woff") format("woff")
} }
code { code {
font-family: "Fira Code", monospace !important; 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%;
} }

View File

@ -8,14 +8,14 @@ export default hopeTheme({
author: { author: {
name: "远野千束", name: "远野千束",
url: "https://snowykami.me", url: "https://sfkm.me",
}, },
iconAssets: "fontawesome-with-brands", iconAssets: "fontawesome-with-brands",
logo: "https://cdn.liteyuki.icu/static/img/liteyuki_icon_640.png", 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", docsDir: "docs",
@ -38,7 +38,7 @@ export default hopeTheme({
// 多语言配置 // 多语言配置
metaLocales: { metaLocales: {
editLink: "在 GitHub 上编辑此页", editLink: "在 GitHub 上编辑",
}, },
// 如果想要实时查看任何改变,启用它。注: 这对更新性能有很大负面影响 // 如果想要实时查看任何改变,启用它。注: 这对更新性能有很大负面影响
@ -62,6 +62,7 @@ export default hopeTheme({
// 此处开启了很多功能用于演示,你应仅保留用到的功能。 // 此处开启了很多功能用于演示,你应仅保留用到的功能。
mdEnhance: { mdEnhance: {
echarts: true,
alert: true, alert: true,
align: true, align: true,
attrs: true, attrs: true,

View File

@ -2,17 +2,18 @@
home: true home: true
icon: home icon: home
title: 首页 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: bgImage:
bgImageDark: bgImageDark:
bgImageStyle: bgImageStyle:
background-attachment: fixed background-attachment: fixed
heroText: LiteyukiBot heroText: LiteyukiBot
tagline: 轻雪机器人,一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人 tagline: LiteyukiBot 轻雪机器人基于NoneBot2构建的综合应用型聊天机器人
actions: actions:
- text: 快速部署 - text: 快速部署
icon: lightbulb icon: rocket
link: ./deployment/install.html link: ./deployment/install.html
type: primary type: primary
@ -21,7 +22,6 @@ actions:
link: ./usage/basic_command.html link: ./usage/basic_command.html
highlights: highlights:
- header: 简洁至上 - header: 简洁至上
image: /assets/image/layout.svg image: /assets/image/layout.svg
bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg
@ -30,71 +30,54 @@ highlights:
background-repeat: repeat background-repeat: repeat
background-size: initial background-size: initial
features: features:
- title: 基于Nonebot2 - title: 基于NoneBot2
icon: robot icon: robot
details: 拥有良好的生态支持 details: 拥有良好的生态支持
link: https://nonebot.dev/ link: https://nonebot.dev/
- title: 可视化插件管理 - title: 便捷管理
icon: plug icon: plug
details: 使用<code>npm</code>,无需命令行操作即可安装/卸载插件 details: 使用包管理器,便捷管理插件及资源包
- title: 点击交互
icon: mouse-pointer
details: 新的点击交互模式,拒绝手打指令
- title: 主题支持 - title: 主题支持
icon: paint-brush icon: paint-brush
details: 支持多种主题,可自定义资源包,满足你的审美需求 details: 使用资源包对外观进行完全自定义
link: https://bot.liteyuki.icu/usage/resource_pack.html link: https://bot.liteyuki.icu/usage/resource_pack.html
- title: 国际化 - title: 国际化
icon: globe icon: globe
details: 支持多种语言包括i18n部分语言和自行扩展的语言代码 details: 通过资源包支持多种语言
link: https://baike.baidu.com/item/i18n/6771940 link: https://baike.baidu.com/item/i18n/6771940
- title: 简易配置 - title: 简易使用
icon: cog icon: cog
details: 无需过多配置,开箱即用 details: 无需繁琐前期过程,开箱即用
link: https://bot.liteyuki.icu/deployment/config.html link: https://bot.liteyuki.icu/deployment/config.html
- title: 低占用 - title: 超高性能
icon: memory icon: tachometer-alt
details: 使用更少的依赖和资源 details: 500个插件2s内启动
- title: OneBot标准 - title: 滚动更新
icon: link
details: 支持OneBotv11/12标准的四种通信协议
link: https://onebot.dev/
- title: Alconna命令解析
icon: link
details: 使用Alconna实现高效命令解析
link: https://github.com/nonebot/plugin-alconna
- title: 便捷更新
icon: cloud-download icon: cloud-download
details: 聊天窗口命令更新,无需手动下载 details: 让你的机器人保持最新提交
- title: 服务支持 - title: 开源项目
icon: server
details: 内置轻雪API可自动收集错误提供图床服务
- title: 开源
icon: code icon: code
details: 项目遵循MIT协议开源,欢迎各位的贡献 details: 项目遵循MIT LICENCE开源,欢迎各位的贡献
- header: 快速部署 - header: 快速部署
image: /assets/image/box.svg image: /assets/image/box.svg
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
highlights: highlights:
- title: 安装 Git Python3.10+ - title: 安装 Git Python3.10+
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至本地。 - title: 使用 <code>git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1</code> 以克隆项目至本地。
details: 如果无法连接到GitHub可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code> details: 如果无法连接到GitHub可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot --depth=1</code>
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。 - title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
- title: 使用 <code>pip install -r requirements.txt</code> 安装项目依赖。 - title: 使用 <code>pip install -r requirements.txt</code> 安装项目依赖。
details: 如果你有多个 Python 环境,请使用 <code>pythonx -m pip install -r requirements.txt</code> details: 如果你有多个 Python 环境,请使用 <code>pythonx -m pip install -r requirements.txt</code>
- title: 使用 <code>python main.py</code> 启动项目。 - title: 使用 <code>python main.py</code> 启动项目。
copyright: © 2021-2024 SnowyKami All Rights Reserved copyright: © 2021-2024 SnowyKami All Rights Reserved
--- ---

86
docs/childrensdaybak.md Normal file
View 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
---

View File

@ -8,7 +8,7 @@ tag:
- 部署 - 部署
--- ---
首次运行后生成`config.yml`,你可以修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers``nickname`字段即可 首次运行后生成`config.yml`,你可以修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers``nickname`字段即可
## **基础配置项** ## **基础配置项**
@ -29,11 +29,12 @@ onebot_access_token: "" # 访问令牌,对公开放时建议设置
default_language: "zh-CN" # 默认语言 default_language: "zh-CN" # 默认语言
alconna_auto_completion: false # alconna是否自动补全指令默认false建议开启 alconna_auto_completion: false # alconna是否自动补全指令默认false建议开启
# 开发者选项 # 开发者选项
allow_update: true # 是否允许更新
log_level: "INFO" # 日志等级 log_level: "INFO" # 日志等级
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用) log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
auto_report: true # 是否自动上报问题给轻雪服务器 auto_report: true # 是否自动上报问题给轻雪服务器
auto_update: true # 是否自动更新轻雪每天4点检查更新 auto_update: true # 是否自动更新轻雪每天4点检查更新
debug: false # 轻雪调试,开启后在调试时修改代码或资源会自动重载相应内容 debug: false # 轻雪调试,开启会自动重载Bot或者资源其他插件自带的调试功能也将开启
safe_mode: false # 安全模式,开启后将不会加载任何第三方插件 safe_mode: false # 安全模式,开启后将不会加载任何第三方插件
# 其他Nonebot插件的配置项 # 其他Nonebot插件的配置项
custom_config_1: "custom_value1" custom_config_1: "custom_value1"

View File

@ -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/) 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` 3. 进入轻雪目录 `cd LiteyukiBot`
4. 构建镜像 `docker build -t liteyukibot .` 4. 构建镜像 `docker build -t liteyukibot .`
5. 启动容器 `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot` 5. 启动容器 `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
> [!tip] > [!tip]
> Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br> > Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br>
> 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号 > 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号
### **使用TRSS Scripts部署**
[TRSS_Liteyuki轻雪机器人管理脚本](https://timerainstarsky.github.io/TRSS_Liteyuki/)该功能由TRSS提供支持不是LiteyukiBot官方提供的功能推荐使用`Arch Linux`
## **设备要求** ## **设备要求**
- Windows系统版本最低`Windows10+`/`Windows Server 2019+` - Windows系统版本最低`Windows10+`/`Windows Server 2019+`
- Linux系统要支持Python3.10+,推荐`Ubuntu 20.04+`(~~别用你那b CentOS~~) - Linux系统要支持Python3.10+,推荐`Ubuntu 20.04+`(~~别用你那b CentOS~~)
- CPU: 至少`1vCPU` - CPU: 至少`1vCPU`
- 内存: Bot无其他插件会占用`200~300MB`,其他插件占用视具体插件而定,建议`1GB`以上 - 内存: Bot无其他插件会占用`300~500MB`包括`chromium``node`等进程,其他插件占用视具体插件而定,建议`1GB`以上
- 硬盘: 至少`1GB`空间 - 硬盘: 至少`1GB`空间
> [!warning] > [!warning]
> 如果设备上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`为你的Python可执行文件路径 > 如果设备上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`为你的Python可执行文件路径
> [!tip]
> 推荐使用虚拟环境来运行轻雪,以避免依赖冲突,你可以使用`python -m venv venv`来创建虚拟环境,然后使用`venv\Scripts\activate`来激活虚拟环境
> [!warning] > [!warning]
> 轻雪的更新功能依赖Git如果你没有安装Git你将无法使用更新功能 > 轻雪的更新功能依赖Git如果你没有安装Git直接下载源代码运行,你将无法使用更新功能
#### 其他问题请移步至[答疑](/deployment/fandq) #### 其他问题请移步至[答疑](/deployment/fandq)
[//]: # (#### 想在Linux命令行中拥有更好的体验试试[TRSS_Liteyuki轻雪机器人管理脚本]&#40;https://timerainstarsky.github.io/TRSS_Liteyuki/&#41;该功能仅供参考不是LiteyukiBot官方提供的功能)

View File

@ -2,14 +2,15 @@
home: true home: true
icon: home icon: home
title: 首页 title: 首页
heroImage: https://cdn.liteyuki.icu/static/img/logo.png heroImage: https://cdn.liteyuki.icu/static/img/lykwi.png
bgImage: bgImage:
bgImageDark: bgImageDark:
bgImageStyle: bgImageStyle:
background-attachment: fixed background-attachment: fixed
heroText: HeavyyukiBot666 # LiteyukiBot 6 heroText: HeavylavaBot666 # LiteyukiBot 6
tagline: 重机器人一个以笨重和复杂为设计理念基于Koishi114514的OneBotv1919810标准聊天机器人可用于雪地清扫使用Typethon编写 tagline: 重机器人一个以笨重和复杂为设计理念基于Koishi114514的TwoBotv1919810标准聊天机器人可用于雪地清扫使用Typethon编写
#tagline: 轻雪机器人一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人 #tagline: 轻雪机器人一个以轻量和简洁为设计理念基于Nonebot2的OneBot标准聊天机器人
# 泰普森(X
actions: actions:
- text: 快速结束 # 快速开始 - text: 快速结束 # 快速开始
@ -39,22 +40,22 @@ highlights:
features: features:
- title: 基于Koishi.js233 - title: 基于Koishi.js233
icon: robot icon: robot
details: 拥有良好的生态支持 details: 拥有辣鸡的生态支持
link: https://nonebot.dev/ link: https://nonebot.dev/
- title: 盲目插件管理 - title: 盲目插件管理
icon: plug icon: plug
details: 基于nbshi使用<code>npmx和pip</code>,让你无法安装/卸载插件 details: 基于nbshi使用<code>xmpn和bib</code>,让你无法安装/卸载插件
- title: 点击无法交互 - title: 纯命令行
icon: mouse-pointer icon: mouse-pointer
details: 老的的点击交互模式,必须手打指令 details: 老的掉牙的交互模式,必须手打指令
- title: 猪蹄支持 - title: 猪蹄支持
icon: paint-brush icon: paint-brush
details: 支持多种主题,可自定义资源包,满足你的审美需求 details: 支持多种烤猪蹄样式,丢弃烤箱,拥抱烧烤架,满足你的干饭需求
- title: 国际化 - title: 国际化
icon: globe icon: globe
details: 支持多种语言包括i18n部分语言和自行扩展的语言代码 details: 支持多种语言包括i18n部分语言和自行扩展的语言代码
link: https://baike.baidu.com/item/i18n/6771940 link: https://baike.baidu.com/item/i18n/6771940
@ -80,27 +81,27 @@ highlights:
- title: 不准更新 - title: 不准更新
icon: cloud-download icon: cloud-download
details: 要更新自己新版本去 details: 要更新自己新版本去
- title: 服务支持 - title: 服务支持
icon: server icon: server
details: 内置轻雪API但随时可能跑路 details: 内置重浆API但随时可能提桶跑路
- title: 闭源 - title: 闭源
icon: code icon: code
details: 要源代码自己逆向去 details: 要源代码自己逆向去
- header: 快速部署 - header: 慢速部署 # 快速部署
image: /assets/image/box.svg image: /assets/image/box.svg
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
highlights: highlights:
- title: 安装 Git 和 node.js+ - title: 安装 winget 和 nothing.js # git & node.js+
- title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至本地 - title: 使用 <code>git clone https://github.com/snowykami/LiteyukiBot</code> 以克隆项目至FTP。 # 本地
details: 如果无法连接到GitHub可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code> details: 如果无法连接到PoonHub可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot</code>
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。 - title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
- title: 使用 <code>npm install -r requirements.txt</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> 启动项目。 - title: 使用 <code>node main.py</code> 启动项目。
copyright: © 2021-2024 SnowyKami All Rights Reserved copyright: © 2021-2024 SnowyKami All Rights Reserved
--- ---

View File

@ -20,7 +20,10 @@
}, },
"dependencies": { "dependencies": {
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"echarts": "^5.5.1",
"element-plus": "^2.7.0", "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
View File

@ -8,12 +8,21 @@ dependencies:
clipboard: clipboard:
specifier: ^2.0.11 specifier: ^2.0.11
version: 2.0.11 version: 2.0.11
echarts:
specifier: ^5.5.1
version: 5.5.1
element-plus: element-plus:
specifier: ^2.7.0 specifier: ^2.7.0
version: 2.7.0(vue@3.4.21) version: 2.7.0(vue@3.4.21)
element-ui: element-ui:
specifier: ^2.15.14 specifier: ^2.15.14
version: 2.15.14(vue@3.4.21) 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: devDependencies:
'@vuepress/bundler-vite': '@vuepress/bundler-vite':
@ -33,7 +42,7 @@ devDependencies:
version: 2.0.0-rc.34(vuepress@2.0.0-rc.9) version: 2.0.0-rc.34(vuepress@2.0.0-rc.9)
vuepress-theme-hope: vuepress-theme-hope:
specifier: 2.0.0-rc.32 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: packages:
@ -716,6 +725,7 @@ packages:
resolution: {integrity: sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==} resolution: {integrity: sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -724,6 +734,7 @@ packages:
resolution: {integrity: sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==} resolution: {integrity: sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -732,6 +743,7 @@ packages:
resolution: {integrity: sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==} resolution: {integrity: sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -740,6 +752,7 @@ packages:
resolution: {integrity: sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==} resolution: {integrity: sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -748,6 +761,7 @@ packages:
resolution: {integrity: sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==} resolution: {integrity: sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -756,6 +770,7 @@ packages:
resolution: {integrity: sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==} resolution: {integrity: sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
@ -938,7 +953,6 @@ packages:
/@vue/devtools-api@6.6.1: /@vue/devtools-api@6.6.1:
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==} resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
dev: true
/@vue/reactivity@3.4.21: /@vue/reactivity@3.4.21:
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
@ -985,7 +999,7 @@ packages:
rollup: 4.13.1 rollup: 4.13.1
vite: 5.2.6 vite: 5.2.6
vue: 3.4.21 vue: 3.4.21
vue-router: 4.3.0(vue@3.4.21) vue-router: 4.4.0(vue@3.4.21)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@ -1021,7 +1035,7 @@ packages:
'@vue/devtools-api': 6.6.1 '@vue/devtools-api': 6.6.1
'@vuepress/shared': 2.0.0-rc.9 '@vuepress/shared': 2.0.0-rc.9
vue: 3.4.21 vue: 3.4.21
vue-router: 4.3.0(vue@3.4.21) vue-router: 4.4.0(vue@3.4.21)
transitivePeerDependencies: transitivePeerDependencies:
- typescript - typescript
dev: true dev: true
@ -1719,6 +1733,12 @@ packages:
domhandler: 5.0.3 domhandler: 5.0.3
dev: true 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: /electron-to-chromium@1.4.719:
resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==} resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==}
dev: true dev: true
@ -2656,6 +2676,9 @@ packages:
is-number: 7.0.0 is-number: 7.0.0
dev: true dev: true
/tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
/uc.micro@2.1.0: /uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
dev: true dev: true
@ -2725,6 +2748,21 @@ packages:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true 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): /vue-demi@0.14.7(vue@3.4.21):
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -2739,14 +2777,30 @@ packages:
dependencies: dependencies:
vue: 3.4.21 vue: 3.4.21
/vue-router@4.3.0(vue@3.4.21): /vue-echarts@7.0.0-beta.0(echarts@5.5.1)(vue@3.4.21):
resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==} 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: peerDependencies:
vue: ^3.2.0 vue: ^3.2.0
dependencies: dependencies:
'@vue/devtools-api': 6.6.1 '@vue/devtools-api': 6.6.1
vue: 3.4.21 vue: 3.4.21
dev: true
/vue@3.4.21: /vue@3.4.21:
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
@ -2805,7 +2859,7 @@ packages:
- typescript - typescript
dev: true 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==} resolution: {integrity: sha512-zZK8aEfbq26J5w8o9xGWXCHHrL3PYk25tloTPcx96nZWYPeD+5fMFAtVpHte0rXBWUf0MBtDQxddSeATteBE7Q==}
engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
peerDependencies: peerDependencies:
@ -2882,6 +2936,7 @@ packages:
'@vuepress/helper': 2.0.0-rc.21(vuepress@2.0.0-rc.9) '@vuepress/helper': 2.0.0-rc.21(vuepress@2.0.0-rc.9)
'@vueuse/core': 10.9.0(vue@3.4.21) '@vueuse/core': 10.9.0(vue@3.4.21)
balloon-css: 1.2.0 balloon-css: 1.2.0
echarts: 5.5.1
js-yaml: 4.1.0 js-yaml: 4.1.0
vue: 3.4.21 vue: 3.4.21
vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.9)(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 - typescript
dev: true 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==} resolution: {integrity: sha512-5S5qg5xa0TErqVjpubhBN5oy0QmROd+ja5EQKfORUCKdXKQvx0soojQZnCPkXsUEXonwiZ12oCLN2+UGO03eng==}
engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
peerDependencies: peerDependencies:
@ -3059,7 +3114,7 @@ packages:
vue: 3.4.21 vue: 3.4.21
vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.9)(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-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-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-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) vuepress-shared: 2.0.0-rc.32(vuepress@2.0.0-rc.9)
@ -3178,3 +3233,8 @@ packages:
y18n: 4.0.3 y18n: 4.0.3
yargs-parser: 18.1.3 yargs-parser: 18.1.3
dev: true dev: true
/zrender@5.6.0:
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
dependencies:
tslib: 2.3.0

View File

@ -4,4 +4,5 @@ icon: box
order: 1 order: 1
category: 使用手册 category: 使用手册
--- ---
<resourceStoreComp />
<resourceStoreComp />

View File

@ -7,8 +7,8 @@ category: 使用手册
1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息 1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容 2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
- 运行环境的设备信息CPU内存系统信息Python信息 - 运行环境的设备信息CPU内存系统信息Python信息
- 插件信息(不含插件数据) - 插件信息(不含插件数据)
- 部分异常信息, - 部分异常信息,
- 会话负载信息(不含隐私部分) - 会话负载信息(不含隐私部分)
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false` 以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`

View File

@ -5,98 +5,120 @@ order: 1
category: 使用手册 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] > [!tip]
> **参数**`<param>`为必填参数,`[option]`为可选参数。 > **参数**`<param>`为必填参数,`[option]`为可选参数。
> >
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。 > **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`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` | 查询 |

View File

@ -1,29 +1,70 @@
--- ---
title: 功能命令 title: 功能命令
icon: comment icon: comment
order: 2 order: 2
category: 使用手册 category: 使用手册
--- ---
## 功能插件命令 ## 功能插件命令
### **轻雪天气`liteyuki_weather`** ### **轻雪天气`liteyuki_weather`**
配置项 查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
```yaml 配置项
weather_key: "" # 和风天气的天气key会自动判断key版本
``` ```yaml
weather_key: "" # 和风天气的天气key会自动判断key版本
命令 ```
```shell 命令
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
bind-city <keywords...> # 绑定查询城市,个人全局生效 ```shell
``` weather <keywords...> # Keywords为城市名支持中英文
```
命令别名 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
```shell ```shell
weather 天气, bind-city 绑定城市 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 |

View File

@ -1,7 +1,7 @@
--- ---
title: 轻雪API title: 轻雪API
icon: code icon: code
order: 4 order: 5
category: 使用指南 category: 使用指南
tag: tag:
- 配置 - 配置
@ -10,36 +10,4 @@ tag:
## **轻雪API** ## **轻雪API**
轻雪API是轻雪运行中部分服务的支持`go`语言编写,例如错误反馈,图床链接等,目前服务由轻雪服务器提供,用户无需额外部署 轻雪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` 图床链接

72
docs/usage/lyfunc.md Normal file
View 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都会被截停销毁

View File

@ -13,6 +13,9 @@ category: 使用手册
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改 - 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
- 欢迎各位投稿资源包到轻雪资源商店 - 欢迎各位投稿资源包到轻雪资源商店
请注意主题包中的html渲染使用Js来规定数据的渲染位置请确保您所编写的html代码能被Bot解析否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码以避免出现无法预料的问题。
---
## 加载资源包 ## 加载资源包
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的 - 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
```shell ```shell

21
liteyuki/__init__.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()

View File

@ -0,0 +1,14 @@
from . import (
satori,
onebot
)
def init(config: dict):
onebot.init()
satori.init(config)
def register():
onebot.register()
satori.register()

View 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)

View 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)

View File

@ -0,0 +1,6 @@
from .auto_set_env import auto_set_env
def init(config: dict):
auto_set_env(config)
return

View 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

View 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)}"

View 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
View File

@ -0,0 +1,10 @@
"""exception模块包含了liteyuki运行中的所有错误
"""
from typing import Any, Optional
class LiteyukiException(BaseException):
"""Liteyuki的异常基类。"""
def __str__(self) -> str:
return self.__repr__()

View File

@ -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)}"))

View File

@ -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"

View File

@ -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()

View File

@ -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
View 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}")

View 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
View 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

View 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
View 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)

View 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("生命周期监控器:启动完成")

View File

@ -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

View File

@ -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")

View 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

View File

@ -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=程

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

75
liteyuki/utils.py Normal file
View 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,))

View File

@ -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

View File

@ -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
View File

@ -1,29 +1,6 @@
import nonebot from liteyuki import LiteyukiBot
from nonebot.adapters.onebot import v11, v12 from src.utils import load_from_yaml
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__()))
if __name__ == "__main__": if __name__ == "__main__":
from liteyuki.utils.base.reloader import Reloader bot = LiteyukiBot(**load_from_yaml("config.yml"))
nonebot.run() bot.run()

58
pyproject.toml Normal file
View 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"

View File

@ -3,12 +3,15 @@ aiofiles~=23.2.1
colored~=2.2.4 colored~=2.2.4
GitPython~=3.1.42 GitPython~=3.1.42
httpx~=0.27.0 httpx~=0.27.0
melobot~=2.6.5
nb-cli~=1.4.1 nb-cli~=1.4.1
nonebot2[fastapi,httpx,websockets]~=2.3.0 nonebot2[fastapi,httpx,websockets]~=2.3.0
nonebot-plugin-htmlrender~=0.3.1 nonebot-plugin-htmlrender~=0.3.1
nonebot-adapter-onebot~=2.4.3 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_plugin_apscheduler~=0.4.0
nonebot-adapter-satori~=0.11.5
numpy~=2.0.0
packaging~=23.1 packaging~=23.1
psutil~=5.9.8 psutil~=5.9.8
py-cpuinfo~=9.0.0 py-cpuinfo~=9.0.0
@ -16,9 +19,13 @@ pydantic~=2.7.0
Pygments~=2.17.2 Pygments~=2.17.2
pytz~=2024.1 pytz~=2024.1
PyYAML~=6.0.1 PyYAML~=6.0.1
pillow~=10.0.0
starlette~=0.36.3 starlette~=0.36.3
loguru~=0.7.2 loguru~=0.7.2
importlib_metadata~=7.0.2 importlib_metadata~=7.0.2
requests~=2.31.0 requests~=2.31.0
watchdog~=4.0.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

Binary file not shown.

View 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
View 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.")

View File

@ -1,324 +1,419 @@
import base64 import base64
import time import time
from typing import Any from typing import Any, AnyStr
import nonebot import nonebot
import pip import pip
from nonebot import Bot, get_driver, require from nonebot import Bot, get_driver, require
from nonebot.adapters.onebot.v11 import Message, escape, unescape from nonebot.adapters import onebot, satori
from nonebot.exception import MockApiException from nonebot.adapters.onebot.v11 import Message, escape, unescape
from nonebot.internal.matcher import Matcher from nonebot.exception import MockApiException
from nonebot.permission import SUPERUSER 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 import Channel
from liteyuki.utils.base.language import get_user_lang from src.utils.base.config import get_config, load_from_yaml
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent from src.utils.base.data_manager import StoredConfig, TempConfig, common_db
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers from src.utils.base.language import get_user_lang
from liteyuki.utils.base.reloader import Reloader from src.utils.base.ly_typing import T_Bot, T_MessageEvent
from .api import update_liteyuki from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
# from src.liteyuki.core import Reloader
require("nonebot_plugin_alconna") from src.utils import event as event_utils, satori_utils
require("nonebot_plugin_apscheduler") from liteyuki.core.spawn_process import chan_in_spawn_nb
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
from nonebot_plugin_apscheduler import scheduler from .api import update_liteyuki
from liteyuki.bot import get_bot
driver = get_driver() from ..utils.base import reload
from ..utils.base.ly_function import get_function
markdown_image = common_db.first(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
require("nonebot_plugin_alconna")
require("nonebot_plugin_apscheduler")
@on_alconna( from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
command=Alconna( from nonebot_plugin_apscheduler import scheduler
"liteecho",
Args["text", str, ""], driver = get_driver()
),
permission=SUPERUSER markdown_image = common_db.where_one(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
).handle()
async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
if result.main_args.get("text"): @on_alconna(
await matcher.finish(Message(unescape(result.main_args.get("text")))) command=Alconna(
else: "liteecho",
await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}") Args["text", str, ""],
),
permission=SUPERUSER
@on_alconna( ).handle()
aliases={"更新轻雪"}, # Satori OK
command=Alconna( async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
"update-liteyuki" if result.main_args.get("text"):
), await matcher.finish(Message(unescape(result.main_args.get("text"))))
permission=SUPERUSER else:
).handle() await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}")
async def _(bot: T_Bot, event: T_MessageEvent):
# 使用git pull更新
ulang = get_user_lang(str(event.user_id)) @on_alconna(
success, logs = update_liteyuki() aliases={"更新轻雪"},
reply = "Liteyuki updated!\n" command=Alconna(
reply += f"```\n{logs}\n```\n" "update-liteyuki"
btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki") ),
pip.main(["install", "-r", "requirements.txt"]) permission=SUPERUSER
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}" ).handle()
await md.send_md(reply, bot, event=event, at_sender=False) # Satori OK
async def _(bot: T_Bot, event: T_MessageEvent):
# 使用git pull更新
@on_alconna(
aliases={"重启轻雪"}, ulang = get_user_lang(str(event.user.id if isinstance(event, satori.event.Event) else event.user_id))
command=Alconna( success, logs = update_liteyuki()
"reload-liteyuki" reply = "Liteyuki updated!\n"
), reply += f"```\n{logs}\n```\n"
permission=SUPERUSER btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki")
).handle() pip.main(["install", "-r", "requirements.txt"])
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent): reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
await matcher.send("Liteyuki reloading") await md.send_md(reply, bot, event=event, at_sender=False)
temp_data = common_db.first(TempConfig(), default=TempConfig())
temp_data.data.update( @on_alconna(
{ aliases={"重启轻雪"},
"reload" : True, command=Alconna(
"reload_time" : time.time(), "reload-liteyuki"
"reload_bot_id" : bot.self_id, ),
"reload_session_type": event.message_type, permission=SUPERUSER
"reload_session_id" : event.group_id if event.message_type == "group" else event.user_id, ).handle()
"delta_time" : 0 # Satori OK
} async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
) global channel_in_spawn_process
await matcher.send("Liteyuki reloading")
common_db.save(temp_data) temp_data = common_db.where_one(TempConfig(), default=TempConfig())
Reloader.reload(0)
temp_data.data.update(
{
@on_alconna( "reload" : True,
aliases={"配置"}, "reload_time" : time.time(),
command=Alconna( "reload_bot_id" : bot.self_id,
"config", "reload_session_type": event_utils.get_message_type(event),
Subcommand( "reload_session_id" : (event.group_id if event.message_type == "group" else event.user_id) if not isinstance(event,
"set", satori.event.Event) else event.chan.id,
Args["key", str]["value", Any], "delta_time" : 0
alias=["设置"], }
)
),
Subcommand( common_db.save(temp_data)
"get", reload()
Args["key", str, None],
alias=["查询", "获取"]
), @on_alconna(
Subcommand( aliases={"配置"},
"remove", command=Alconna(
Args["key", str], "config",
alias=["删除"] Subcommand(
) "set",
), Args["key", str]["value", Any],
permission=SUPERUSER alias=["设置"],
).handle()
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher): ),
ulang = get_user_lang(str(event.user_id)) Subcommand(
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig()) "get",
if result.subcommands.get("set"): Args["key", str, None],
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value") alias=["查询", "获取"]
try: ),
value = eval(value) Subcommand(
except: "remove",
pass Args["key", str],
stored_config.config[key] = value alias=["删除"]
common_db.save(stored_config) )
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}") ),
elif result.subcommands.get("get"): permission=SUPERUSER
key = result.subcommands.get("get").args.get("key") ).handle()
file_config = load_from_yaml("config.yml") # Satori OK
reply = f"{ulang.get('liteyuki.current_config')}" async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher):
if key: ulang = get_user_lang(str(event_utils.get_user_id(event)))
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```" stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
else: if result.subcommands.get("set"):
reply = f"{ulang.get('liteyuki.current_config')}" key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv" try:
for k, v in file_config.items(): value = eval(value)
reply += f"\n{k}={v}" except:
reply += "\n```" pass
if len(stored_config.config) > 0: stored_config.config[key] = value
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv" common_db.save(stored_config)
for k, v in stored_config.config.items(): await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
reply += f"\n{k}={v} {type(v)}" elif result.subcommands.get("get"):
reply += "\n```" key = result.subcommands.get("get").args.get("key")
await md.send_md(reply, bot, event=event) file_config = load_from_yaml("config.yml")
elif result.subcommands.get("remove"): reply = f"{ulang.get('liteyuki.current_config')}"
key = result.subcommands.get("remove").args.get("key") if key:
if key in stored_config.config: reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
stored_config.config.pop(key) else:
common_db.save(stored_config) reply = f"{ulang.get('liteyuki.current_config')}"
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}") reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
else: for k, v in file_config.items():
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}") reply += f"\n{k}={v}"
reply += "\n```"
if len(stored_config.config) > 0:
@on_alconna( reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
aliases={"切换图片模式"}, for k, v in stored_config.config.items():
command=Alconna( reply += f"\n{k}={v} {type(v)}"
"switch-image-mode" reply += "\n```"
), await md.send_md(reply, bot, event=event)
permission=SUPERUSER elif result.subcommands.get("remove"):
).handle() key = result.subcommands.get("remove").args.get("key")
async def _(event: T_MessageEvent, matcher: Matcher): if key in stored_config.config:
global markdown_image stored_config.config.pop(key)
# 切换图片模式False以图片形式发送True以markdown形式发送 common_db.save(stored_config)
ulang = get_user_lang(str(event.user_id)) await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig()) else:
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False) await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
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(
aliases={"切换图片模式"},
command=Alconna(
@on_alconna( "switch-image-mode"
command=Alconna( ),
"liteyuki-docs", permission=SUPERUSER
), ).handle()
aliases={"轻雪文档"}, # Satori OK
).handle() async def _(event: T_MessageEvent, matcher: Matcher):
async def _(matcher: Matcher): global markdown_image
await matcher.finish("https://bot.liteyuki.icu/usage") # 切换图片模式False以图片形式发送True以markdown形式发送
ulang = get_user_lang(str(event_utils.get_user_id(event)))
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
@on_alconna( stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
command=Alconna( markdown_image = stored_config.config["markdown_image"]
"/api", common_db.save(stored_config)
Args["api", str]["args", MultiVar(str), ()], await matcher.finish(
), ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
permission=SUPERUSER
).handle()
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher): @on_alconna(
""" command=Alconna(
调用API "liteyuki-docs",
Args: ),
result: aliases={"轻雪文档"},
bot: ).handle()
event: # Satori OK
async def _(matcher: Matcher):
Returns: await matcher.finish("https://bot.liteyuki.icu/usage")
"""
api_name = result.main_args.get("api") @on_alconna(
args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数但每个参数间用空格分隔空格是%20 command=Alconna(
args_dict = {} "/function",
Args["function", str]["args", MultiVar(str), ()],
for arg in args: ),
key, value = arg.split("=", 1) permission=SUPERUSER
args_dict[key] = unescape(value.replace("%20", " ")) ).handle()
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
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:
args_dict["group_id"] = str(event.group_id) result:
bot:
if "message" in args_dict: event:
args_dict["message"] = Message(args_dict["message"])
Returns:
try:
result = await bot.call_api(api_name, **args_dict) """
except Exception as e: function_name = result.main_args.get("function")
result = str(e) args: tuple[str] = result.main_args.get("args", ())
_args = []
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items()) _kwargs = {
print(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}") "USER_ID" : str(event.user_id),
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}") "GROUP_ID": str(event.group_id) if event.message_type == "group" else "0",
"BOT_ID" : str(bot.self_id)
}
# system hook
@Bot.on_calling_api # 图片模式检测 for arg in args:
async def test_for_md_image(bot: T_Bot, api: str, data: dict): arg = arg.replace("\\=", "EQUAL_SIGN")
# 截获大图发送转换为markdown发送 if "=" in arg:
if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get("user_id") != bot.self_id: key, value = arg.split("=", 1)
if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg": value = unescape(value.replace("EQUAL_SIGN", "="))
session_type = "private" try:
session_id = data.get("user_id") value = eval(value)
elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg": except:
session_type = "group" value = value
session_id = data.get("group_id") _kwargs[key] = value
else: else:
return _args.append(arg.replace("EQUAL_SIGN", "="))
if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
file: str = data["message"][0].data.get("file") ly_func = get_function(function_name)
# file:// http:// base64:// ly_func.bot = bot if "BOT_ID" not in _kwargs else nonebot.get_bot(_kwargs["BOT_ID"])
if file.startswith("http"): ly_func.matcher = matcher
result = await md.send_md(await md.image_async(file), bot, message_type=session_type, session_id=session_id)
elif file.startswith("file"): await ly_func(*tuple(_args), **_kwargs)
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"): @on_alconna(
file_bytes = base64.b64decode(file.replace("base64://", "")) command=Alconna(
result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id) "/api",
else: Args["api", str]["args", MultiVar(AnyStr), ()],
return ),
raise MockApiException(result=result) permission=SUPERUSER
).handle()
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
@driver.on_startup """
async def on_startup(): 调用API
temp_data = common_db.first(TempConfig(), default=TempConfig()) Args:
# 储存重启信息 result:
if temp_data.data.get("reload", False): bot:
delta_time = time.time() - temp_data.data.get("reload_time", 0) event:
temp_data.data["delta_time"] = delta_time
common_db.save(temp_data) # 更新数据 Returns:
"""
@driver.on_shutdown api_name = result.main_args.get("api")
async def on_shutdown(): args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数但每个参数间用空格分隔空格是%20
pass args_dict = {}
for arg in args:
@driver.on_bot_connect key, value = arg.split("=", 1)
async def _(bot: T_Bot):
temp_data = common_db.first(TempConfig(), default=TempConfig()) args_dict[key] = unescape(value.replace("%20", " "))
# 用于重启计时
if temp_data.data.get("reload", False): if api_name in need_user_id and "user_id" not in args_dict:
temp_data.data["reload"] = False args_dict["user_id"] = str(event.user_id)
reload_bot_id = temp_data.data.get("reload_bot_id", 0) if api_name in need_group_id and "group_id" not in args_dict and event.message_type == "group":
if reload_bot_id != bot.self_id: args_dict["group_id"] = str(event.group_id)
return
reload_session_type = temp_data.data.get("reload_session_type", "private") if "message" in args_dict:
reload_session_id = temp_data.data.get("reload_session_id", 0) args_dict["message"] = Message(eval(args_dict["message"]))
delta_time = temp_data.data.get("delta_time", 0)
common_db.save(temp_data) # 更新数据 if "messages" in args_dict:
await bot.call_api( args_dict["messages"] = Message(eval(args_dict["messages"]))
"send_msg",
message_type=reload_session_type, try:
user_id=reload_session_id, result = await bot.call_api(api_name, **args_dict)
group_id=reload_session_id, except Exception as e:
message="Liteyuki reloaded in %.2f s" % delta_time 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}")
# 每天4点更新
@scheduler.scheduled_job("cron", hour=4)
async def every_day_update(): # system hook
if get_config("auto_update", default=True): @Bot.on_calling_api # 图片模式检测
result, logs = update_liteyuki() async def test_for_md_image(bot: T_Bot, api: str, data: dict):
pip.main(["install", "-r", "requirements.txt"]) # 截获大图发送转换为markdown发送
if result: if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get(
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```") "user_id") != bot.self_id:
nonebot.logger.info(f"Liteyuki updated: {logs}") if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg":
Reloader.reload(5) session_type = "private"
else: session_id = data.get("user_id")
nonebot.logger.info(logs) elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg":
session_type = "group"
session_id = data.get("group_id")
# 安全的需要用户id的api else:
need_user_id = ( return
"send_private_msg", if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
"send_msg", file: str = data["message"][0].data.get("file")
"set_group_card", # file:// http:// base64://
"set_group_special_title", if file.startswith("http"):
"get_stranger_info", result = await md.send_md(await md.image_async(file), bot, message_type=session_type,
"get_group_member_info" session_id=session_id)
) elif file.startswith("file"):
file = file.replace("file://", "")
need_group_id = ( result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type,
"send_group_msg", session_id=session_id)
"send_msg", elif file.startswith("base64"):
"set_group_card", file_bytes = base64.b64decode(file.replace("base64://", ""))
"set_group_name", result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id)
"set_group_special_title", else:
"get_group_member_info", return
"get_group_member_list", raise MockApiException(result=result)
"get_group_honor_info"
)
@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
View 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()

View 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.")

View File

@ -1,15 +1,18 @@
from nonebot.plugin import PluginMetadata import multiprocessing
from .rt_guide import *
from nonebot.plugin import PluginMetadata
__plugin_meta__ = PluginMetadata( from .rt_guide import *
name="CRT生成工具", from .crt_matchers import *
description="一些CRT牌子生成器",
usage="我觉得你应该会用", __plugin_meta__ = PluginMetadata(
type="application", name="CRT生成工具",
homepage="https://github.com/snowykami/LiteyukiBot", description="一些CRT牌子生成器",
extra={ usage="我觉得你应该会用",
"liteyuki" : True, type="application",
"toggleable" : True, homepage="https://github.com/snowykami/LiteyukiBot",
"default_enable": True, extra={
} "liteyuki" : True,
) "toggleable" : True,
"default_enable": True,
}
)

Some files were not shown because too many files have changed in this diff Show More