1
0
forked from bot/app

87 Commits
v6.3.12 ... v7

Author SHA1 Message Date
5566e9294f 🔧 更新 Dockerfile 和示例 docker-compose 文件中的镜像地址,修正 README.md 和相关配置文件中的链接 2025-06-15 23:40:05 +08:00
ae464ed3ea Merge branch 'v7' of https://github.com/LiteyukiStudio/LiteyukiBot into v7 2025-05-16 18:58:06 +08:00
add5174e93 🔧 更新依赖项,添加 hypercorn 及其相关依赖,修改 README.md 中的图标链接,新增测试文件 test_hypercorn.py 2025-05-16 18:58:03 +08:00
fe85f1e612 ✏️ 改善自述文件部分显示内容, 替换许可证内的命名空间 2025-05-02 02:11:00 +08:00
efb13340f0 🔧 添加获取 FastAPI 实例的函数,更新配置文件路径以支持新的源文件 2025-04-29 02:55:38 +08:00
56996ef082 🔧 重构配置管理,添加配置文件支持,更新日志系统,优化守护进程,完善测试用例 2025-04-29 02:51:37 +08:00
fec96e694d 🔧 更新 pyproject.toml 和 uv.lock,添加 yukilog 依赖及相关配置 2025-04-28 21:48:25 +08:00
a4e423168c 🔧 更新 Dockerfile 和 docker-compose 文件,添加配置目录并引入新依赖,重构配置加载功能 2025-04-28 15:03:56 +08:00
20eb1809f1 📝 更新 README.md,修改轻雪版本号至 7 2025-04-28 01:00:36 +08:00
66c88d8a30 🔧 更新 pytest 工作流,将上传工件的 GitHub Actions 版本升级至 v4 2025-04-28 00:59:54 +08:00
7aaa589a51 🔧 更新 pytest 工作流,升级上传工件的 GitHub Actions 版本至 v4 2025-04-28 00:58:49 +08:00
84133b2f27 🔧 更新 GitHub Actions 工作流,升级 uv 版本至 v6 2025-04-28 00:57:16 +08:00
1de7d81693 ️ Refactor LiteyukiBot structure and add configuration loading
- Updated .gitignore to include .pytest_cache
- Replaced FastAPI with Daemon in main.py for bot execution
- Enhanced pyproject.toml with new dependencies and dev group
- Added iniconfig package for pytest configuration
- Created initial structure for liteyukibot with context management
- Implemented configuration loading functions for YAML, JSON, and TOML
- Added tests for configuration loading with temporary files
- Set up GitHub Actions for pytest testing on push and pull requests
2025-04-28 00:56:07 +08:00
4cbf043547 📝 更新 .gitignore 文件以包含开发相关的忽略项;更新 Dockerfile 以支持 uvicorn 和虚拟环境;删除不再使用的 docker-compose 文件;重构 main.py 以使用 FastAPI;更新 pyproject.toml 和 uv.lock 文件以添加 uvicorn 依赖;创建 Makefile 和 docker-compose-dev.yaml 示例文件。 2025-04-27 23:30:39 +08:00
7d755581cb 📝 更新 .gitignore 文件以包含 Python 相关的忽略项;更新 README.md,添加开发状态说明并移除参考及鸣谢部分 2025-04-27 22:13:54 +08:00
b634352c95 📝 更新 README.md,添加项目特点、服务支持及鸣谢信息;更新 pyproject.toml,添加依赖项;创建 uv.lock 文件以管理依赖版本。 2025-04-27 22:11:35 +08:00
1d35ab21f8 📄 [许可证] 添加 LSO v1.3(Common) 许可证
添加:
- LISENCE
> 许可证文件
2025-04-27 18:30:37 +08:00
7a006bc6fd ⬆️ v7 2025-04-27 18:24:59 +08:00
26c9cb7e35 🚀 v7 2025-04-27 18:24:46 +08:00
fc017c8255 📝 文档与代码注释信息微调 2025-04-04 11:44:50 +08:00
908812a3d9 📝 重置了deploy下的文档
新添加了adapter.md用来转移配置器相关内容; 修改了部分文件的版权归属日期;
2025-04-03 21:54:03 +08:00
6c7d073cb1 📝 添加文档uptime 2025-03-19 02:12:28 +08:00
8677286bd4 📝 更新pages部署地址 2025-03-10 23:07:21 +08:00
232ffcf714 📝 修正文档中的LSO LICENSE拼写错误 2025-03-09 20:01:36 +08:00
6871bc0d22 更新配置,重新引入 liteyuki_footer.js 脚本 2025-03-08 23:26:54 +08:00
641c16ee7a 📝 文档引入神秘js 2025-03-08 23:21:14 +08:00
75c67bdffc 更新配置,禁用清理 URL 功能 2025-03-07 21:35:21 +08:00
3a60450358 📝 更新文档页脚信息,修正链接文本 2025-03-07 17:43:43 +08:00
7b97210a31 📝 更新文档页脚信息,添加网站部署链接 2025-03-07 17:21:01 +08:00
d97145ee5e 📝 更新文档部署工作流程,添加对状态的写入权限并恢复 GITHUB_TOKEN 环境变量 2025-03-07 17:09:01 +08:00
bc3a4355c3 🐛 移除文档部署工作流程中的 GITHUB_TOKEN 环境变量 2025-03-07 17:00:27 +08:00
d835a32683 🗑️ 删除文档部署工作流程文件 2025-03-07 16:49:25 +08:00
aedea1acb9 Merge branch 'main' of https://github.com/LiteyukiStudio/LiteyukiBot 2025-03-07 16:37:18 +08:00
3bce8325c1 📝 新增 Liteyuki PaaS 部署工作流程,优化文档构建和发布步骤 2025-03-07 16:37:15 +08:00
d1c1320963 📝 Create .domain 2025-03-07 00:12:44 +08:00
317e07eb71 🐛 更新文档页脚信息,添加Liteyukiflare CDN加速说明 2025-03-01 03:05:04 +08:00
37749ae15e 📝 使用Liteyukiflare对GitHub Page进行亚太地区加速 2025-03-01 03:04:34 +08:00
f94c10de61 🐛 修正 Docker 镜像标签的大小写,更新相关文档中的镜像拉取命令 2025-02-18 09:08:50 +08:00
5ccef735be 🐛 更新 Docker 镜像拉取地址,修正文档中的镜像源 2025-02-18 09:07:37 +08:00
262002b49a 🐛 更新 GitHub 容器注册表登录配置,修正 Docker 镜像构建和推送步骤 2025-02-18 09:06:52 +08:00
40c6ba6d9e 🐛 更新管道依赖,修复管道句柄错误 2025-02-18 06:47:31 +08:00
60093b562b 🐛 移除 Gotify 插件及相关配置,更新依赖项版本 2025-02-18 06:42:42 +08:00
30880ec13b 在 README 中添加 LiteyukiLab 的徽章链接 2025-01-13 00:57:50 +08:00
cc1d82312a 添加新的图标配置,更新链接以指向新的资源 2025-01-13 00:49:01 +08:00
efca13d397 🐛 移除 requirements.txt 中的 pip 依赖项 2024-12-14 02:59:55 +08:00
3a8c09d6db 🐛 删除 __pypackages__ 目录的 .gitignore 文件,并更新主 .gitignore 文件以忽略 pdm 相关文件 2024-12-14 02:57:51 +08:00
cc1bb8e5e4 添加 pre-commit 配置和工作流,集成代码格式化和静态检查工具 2024-12-13 19:35:07 +08:00
93c17b6026 添加新的配置加载器,支持从YAML、TOML、JSON和环境变量加载配置,并修改默认主机地址 2024-12-02 21:46:29 +08:00
fd3f6272f1 🐛 修复获取系统语言代码的逻辑,处理None值的情况 2024-11-29 00:04:34 +08:00
4d87a3c0b7 Merge branch 'main' of https://git.liteyuki.icu/bot/app 2024-11-29 00:03:14 +08:00
86f47ee411 🐛 修复获取系统语言代码的逻辑,添加对None值的处理 2024-11-29 00:01:49 +08:00
1d6b8d60f3 Merge pull request #89 from Asankilp/main
添加uninfo依赖项
2024-11-27 01:47:23 +08:00
3890704045 添加uninfo依赖项 2024-11-27 01:44:49 +08:00
b0761e9873 🐛 更新docker-compose.yml,修改时区设置为Asia/Chongqing 2024-11-23 22:31:51 +08:00
291314de93 新增docker-compose.yml文件,定义应用服务及其配置 2024-11-23 22:31:06 +08:00
fd835e9406 更新.gitignore,新增Python工具链缓存目录排除项;更新README.md,调整标题并添加monorepo说明 2024-11-22 20:15:04 +08:00
d681c5645a 🐛 更新Dockerfile,新增libpango-1.0-0和libcairo2依赖 2024-11-21 19:53:49 +08:00
d0619f1fe8 优化Dockerfile,移除不必要的sources.list复制步骤,简化pip安装命令 2024-11-16 02:24:47 +08:00
b022a364e3 更新GitHub Actions工作流,修改并发组名称为“docker-build” 2024-11-16 02:20:06 +08:00
df00c61dd8 新增GitHub Actions工作流以构建和推送Docker镜像,更新.gitignore以排除.github目录 2024-11-16 02:19:31 +08:00
94a021bab0 更新安装文档,替换Docker构建步骤为拉取最新夜间版镜像 2024-11-16 02:17:06 +08:00
6b20e9eae0 更新README.md,将“参考及鸣”更正为“参考及鸣谢” 2024-11-10 01:20:14 +08:00
0a35a3c6f8 修正README.md中的链接错误,将“lightyuki-link”更正为“liteyuki-link” 2024-11-10 01:19:58 +08:00
2e75c7bc65 更新README.md,新增GitHub和官方仓库链接,优化内容结构 2024-11-10 01:15:34 +08:00
3341505715 更新LiteyukiBot克隆命令中的镜像链接 2024-11-09 23:52:17 +08:00
bdde9c45fd 优化配置文件格式和清理无用调试信息,修改gitea仓库路径 2024-11-09 23:51:35 +08:00
7bf94a15c8 📝 文档新增插件通信部分内容 2024-10-26 02:34:58 +08:00
4510477026 Merge branch 'main' of https://github.com/LiteyukiStudio/LiteyukiBot 2024-10-23 01:06:47 +08:00
86e50e369b 初步对Uninfo的支持 2024-10-23 01:04:42 +08:00
796fc6f233 Merge pull request #88 from EillesWan/main
🌠也许,大家的测试环境都是*nix?
2024-10-22 13:36:57 +08:00
80c6875567 🌠也许,大家的测试环境都是*nix?
Update file.py
2024-10-22 13:34:09 +08:00
ab89cd1c72 性能提升200倍 2024-10-20 23:37:40 +08:00
5e454bc971 性能提升200倍 2024-10-20 23:36:49 +08:00
70bfb0fcee 性能提升200倍 2024-10-20 23:35:44 +08:00
13b95c2732 🐛 修复一些细节小问题 2024-10-20 20:59:30 +08:00
ef5866343d add: nonebot-plugin-gotify 2024-10-20 04:12:17 +08:00
d5ccd105a2 add: nonebot-plugin-gotify 2024-10-20 03:11:26 +08:00
20ad8dc53f 🐛 hotfix: from mypy import 2024-10-19 21:15:37 +08:00
de9c91d8bd 🐛 hotfix: from mypy import 2024-10-19 21:12:55 +08:00
6b64a0c379 🐛 hotfix: from mypy import 2024-10-19 21:10:18 +08:00
f117da7ff3 🎨 更新轻雪依赖版本 2024-10-14 20:57:30 +08:00
f548a07230 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 20:51:37 +08:00
e2e53c21fa 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 01:03:06 +08:00
3eaf23a56b 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 01:02:57 +08:00
4a5dd1f727 🐛 修复一些细节小问题 2024-10-14 00:57:33 +08:00
c2cb416b4e 🐛 hotfix: ubl 2024-10-13 17:44:24 +08:00
5cd528d5e9 🐛 hotfix: ubl 2024-10-13 17:44:17 +08:00
412 changed files with 1172 additions and 35538 deletions

View File

@ -1 +0,0 @@
blank_issues_enabled: false

View File

@ -1,59 +0,0 @@
name: Publish Resource
title: "Resource: {name}"
description: Publish the resource to the LiteyukiBot official store
labels: [ "Resource" ]
body:
- type: input
id: name
attributes:
label: Name
description: The readable name of the resource pack
placeholder: e.g. Cute UI Resource Pack
validations:
required: true
- type: input
id: description
attributes:
label: Description
description: A brief description of the resource
placeholder: e.g. Makes the rendering card style more beautiful
validations:
required: true
- type: input
id: author
attributes:
label: Author
description: The GitHub username of the author
placeholder: e.g. snowykami
validations:
required: true
- type: input
id: link
attributes:
label: Link
description: Direct download link of the resource pack
placeholder: e.g. https://aaa.com/r.zip
validations:
required: true
- type: input
id: homepage
attributes:
label: Homepage
description: Homepage of the resource pack
placeholder: e.g. https://github.com/user/repo
validations:
required: false
- type: input
id: tags
attributes:
label: Tags
description: Tags
placeholder: 'e.g. [{"label": "Tag Name", "color": "#ea5252"}]'
value: '[ { "label": "Text", "color": "#a2d8f4" } ]'
validations:
required: false

View File

@ -1,59 +0,0 @@
name: 发布资源
title: "Resource: {name}"
description: 发布资源到轻雪机器人官方商店
labels: [ "Resource" ]
body:
- type: input
id: name
attributes:
label: 名称
description: 资源包的可读名称
placeholder: e.g. 可爱UI资源包
validations:
required: true
- type: input
id: description
attributes:
label: 描述
description: 资源的简短描述
placeholder: e.g. 使渲染卡片的样式更美观
validations:
required: true
- type: input
id: author
attributes:
label: 作者
description: 作者的github用户名
placeholder: e.g. snowykami
validations:
required: true
- type: input
id: link
attributes:
label: 下载链接
description: 资源包直接下载链接
placeholder: e.g. https://aaa.com/r.zip
validations:
required: true
- type: input
id: homepage
attributes:
label: 主页
description: 资源包主页
placeholder: e.g. https://github.com/user/repo
validations:
required: false
- type: input
id: tags
attributes:
label: 标签
description: 标签
placeholder: 'e.g. [{"label": "标签名", "color": "#ea5252"}]'
value: '[ { "label": "标签名", "color": "#a2d8f4" } ]'
validations:
required: false

View File

@ -1,53 +0,0 @@
---
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`的输出,以及其他相关信息,以及你的建议

View File

@ -1,11 +0,0 @@
# 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,70 +0,0 @@
# 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程
#
name: Deploy VitePress site to Pages
on:
# 在针对 `main` 分支的推送上运行。如果你
# 使用 `master` 分支作为默认分支,请将其更改为 `master`
push:
branches: [main]
# 允许你从 Actions 选项卡手动运行此工作流程
workflow_dispatch:
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
permissions:
contents: write
# 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列
# 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成
concurrency:
group: pages
cancel-in-progress: false
jobs:
# 构建工作
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # 如果未启用 lastUpdated则不需要
# - uses: pnpm/action-setup@v3 # 如果使用 pnpm请取消注释
# - uses: oven-sh/setup-bun@v1 # 如果使用 Bun请取消注释
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Setup API markdown
run: |-
python -m pip install litedoc
litedoc liteyuki -o docs/zh/dev/api -l zh-Hans -cd class -fd func -md func -vd var -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
litedoc liteyuki -o docs/en/dev/api -l en -cd class -fd func -md func -vd var -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
- name: 安装 pnpm
uses: pnpm/action-setup@v2
with:
run_install: true
version: 8
- name: 设置 Node.js
run: |-
cd docs
pnpm install
- name: 构建文档
env:
NODE_OPTIONS: --max_old_space_size=8192
run: |-
cd docs
pnpm run docs:build
> .vitepress/dist/.nojekyll
- name: 部署文档
uses: JamesIves/github-pages-deploy-action@v4
with:
# 这是文档部署到的分支名称
branch: docs
folder: docs/.vitepress/dist

View File

@ -1,51 +0,0 @@
name: Issue Handler
on:
issues:
types: [ opened, edited, closed ]
concurrency:
group: issue_handler
cancel-in-progress: false
jobs:
check-issue:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Check issue title
id: check_title
run: |
ISSUE_TITLE="${{ github.event.issue.title }}"
if [[ "$ISSUE_TITLE" == Plugin:* || "$ISSUE_TITLE" == Resource:* ]]; then
echo "Title starts with Plugin: or Resource:."
echo "::set-output name=title_match::true"
else
echo "Title does not start with Plugin: or Resource:."
echo "::set-output name=title_match::false"
fi
- name: Set up Python
uses: actions/setup-python@v2
if: steps.check_title.outputs.title_match == 'true'
with:
python-version: '3.12'
- name: Install dependencies
if: steps.check_title.outputs.title_match == 'true'
run: |
echo "Installing dependencies."
pip install -r liteyuki_flow/requirements.txt
- name: Run Plugin/Resource issue handler
if: steps.check_title.outputs.title_match == 'true'
run: |
echo "Running Plugin/Resource issue handler."
python -m liteyuki_flow --handle
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
REPOSITORY: ${{ github.repository }}
ACT_TYPE: ${{ github.event.action }}

View File

@ -1,4 +1,4 @@
name: Publish
name: Liteyuki PyPI Publish
on:
push:
@ -6,8 +6,8 @@ on:
- 'v*'
jobs:
pypi-publish:
name: upload release to PyPI
liteyuki-pypi-publish:
name: upload release to PyPI (Nightly)
runs-on: ubuntu-latest
permissions:
contents: read
@ -18,4 +18,4 @@ jobs:
- uses: pdm-project/setup-pdm@v3
- name: Publish package distributions to PyPI
run: pdm publish
run: pdm publish

38
.github/workflows/pytest.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Pytest API Testing
on:
push:
branches: [ "v7" ]
pull_request:
branches: [ "v7" ]
permissions:
contents: read
jobs:
Pytes-API-Testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
version: "latest"
- name: Test with pytest
run: |
uv run pytest --junitxml=report/report.xml
- name: Archive Pytest test report
uses: actions/upload-artifact@v4
with:
name: SuperTest-test-report
path: report
- name: Upload Pytest report to GitHub
uses: actions/upload-artifact@v4
with:
name: Pytest-test-report
path: report

82
.gitignore vendored
View File

@ -1,67 +1,27 @@
.venv/
# python and toolchains
.mypy_cache/
__pycache__/
.pytest_cache/
# idea
.idea/
.vscode/
.cache/
.venv/
venv/
node_modules/
data/
db/
/resources/
__pycache__/
*.pyc
*.pyo
*.pyd
*.pyw
/plugins/
# platform
# macOS
**/.DS_Store
# windows
Thumbs.db
# linux
#config
/config/
!config/default.yml
_config.yml
config.yml
config.example.yml
# vuepress
.github
# mupy
mypy.ini
# nuitka
compile.bat
src/resources/templates/latest-debug.html
main.build/
main.dist/
main.exe
main.cmd
docs/.vuepress/.cache/
docs/.vuepress/.temp/
docs/.vuepress/dist/
prompt.txt
# js
**/echarts.js
# development
.env
# pdm
.pdm-python
.pdm-build
dist
doc
mkdoc2.py
result.json
# litedoc
docs/zh/dev/api
docs/en/dev/api
mkdoc.bat
# vitepress
docs/.vitepress/dist/
docs/.vitepress/cache
docs/.vitepress/.temp
.env.*
plugins/
data/
configs/
config.yaml
config-dev.yaml
config-prod.yaml

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.12

View File

@ -1,19 +1,16 @@
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/python:3.10-slim-bullseye
ENV TZ Asia/Shanghai
COPY docker/sources.list /etc/apt/sources.list
RUN apt-get update && apt-get install -y git
FROM python:3.12-alpine
WORKDIR /liteyukibot
COPY . /liteyukibot
COPY main.py .
COPY pyproject.toml .
COPY liteyukibot/ .
COPY uv.lock .
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install uv
RUN apt-get install -y libnss3 libnspr4 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libxkbcommon0 libasound2
ENV UV_COMPILE_BYTECODE=1
EXPOSE 20216
RUN uv venv --python 3.12 && uv sync
CMD ["python", "main.py"]
CMD [".venv/bin/python3", "main.py"]

31
LICENSE
View File

@ -1,31 +0,0 @@
LSO license
LiteyukiStudio Opensource license
---
版权所有 © 2024 Snowykami
---
免费向任何获得副本的人或组织授予以相同许可为基础的权利
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件的副本
本软件及相关文档文件(以下简称"本软件")在相同方式许可为基础, 以开源的形式发布于互联网抑或其他媒体介质平台
任何人都有权利获取副本并以上述方式获取许可传播和/或使用
但获取副本时仍需注意:
- 上述版权声明和本许可声明应包含在本软件的副本中
- 使用本软件及其副本时仍需保持与原有形式相同
- 在使用时仍需将本软件的副本以相同许可公开表现:
- 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利
---
该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示:
包括但不限于适销性保证, 适用于特定目的非侵权
在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付

42
LISENCE Normal file
View File

@ -0,0 +1,42 @@
LSO License
LiteyukiStudio Open Source License
---
Copyright © 2025 Liteyuki Studio & Snowykami
---
Any individual or organization that obtains a copy of this software is hereby granted, free of charge, the relevant rights under this license agreement.
These rights include, but are not limited to, the right to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software.
This software and its related documentation files (hereinafter referred to as "this software". The "software" includes user manuals, technical documents, API documentation, sample code, etc.) are released in an open - source form on the Internet or other media platforms under this license agreement.
Anyone has the right to obtain a copy of this software through proper channels and distribute and/or use it in accordance with this license agreement.
In case of a conflict with other open - source or non - open - source licenses,
unless otherwise specifically stated, all conflicting parts shall be subject to this open - source license agreement.
The conflicting parts mainly include:
1. Principles of commerciality or profitability
2. Legal liability
3. Licensed ways of publication and distribution
During the process of software distribution and dissemination through media or their media exchanges,
this license agreement shall be retained by default and distributed and redistributed in the same way. If the distributed project does not include this license agreement, the project can still continue to use this license agreement without additional addition.
When processing or re - processing the software and its copies for profit purposes,
if this license agreement is used, the individual or organization to which the re - processed software belongs can decide on its own to change, add, or delete non - essential license terms.
The essential license terms include:
1. Distribution of rights and their scope of application
2. Disclaimer clause and its final interpretation
3. Copyright statement and its legal handling
However, when obtaining a copy of the software, the following points should still be noted:
- The above copyright notice and this license notice must be included in the software copy, and the software and its copies must be used in the same form as the original.
- When using the software, the copy must be presented publicly under the same license agreement. The software copy shall not be used for external profit under a non - original license agreement without the permission of the original author.
---
The software is provided "as is" without any warranty of any kind, either express or implied,
including but not limited to the warranty of merchantability and 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, whether in contract litigation, infringement litigation, or other forms of litigation. The author and its copyright owner have the right to refuse compensation for any losses caused by the user for personal reasons.

View File

@ -1,49 +1,80 @@
<div align="center">
[//]: # (<img src="https://cdn.liteyuki.icu/static/svg/lylogo-full.svg" style="align-content: center; width: 50%; margin-top:10%;" alt="a">)
[![][banner]][lightyuki-link]
<h2><a href="https://bot.liteyuki.icu"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">6</span></a></h2>
[//]: # (<img src="https://cdn.liteyuki.org/logos/bot.svg" style="align-content: center; width: 50%; margin-top:10%;" alt="a">)
[![][banner]][liteyuki-link]
<h2><a href="https://bot.liteyuki.org"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">7</span></a></h2>
<h4> <span style="color: #a2d8f4">✨ 轻量,高效,易于扩展✨</span></h4>
[![][OneBot]][onebot-link]
[![][NoneBot2]][nonebot-link]
[![][Liteyuki6.0]][lightyuki-link]
[![][Python3.10+]][python-link]
[![][Liteyuki7.0]][liteyuki-link]
[![][Python3.12+]][python-link]
[![][Usage]][usage-link]
[![][Repo]][repo-link]
[![][Github]][github-link]
[![][LiteyukiLab]][liteyukilab-link]
![docs uptime](https://uptime.liteyuki.org/api/badge/8/uptime?labelPrefix=Docs+&style=for-the-badge)
- 原生支持与任意`Python`Bot框架互联有良好的生态支持
- 开箱即用,无需复杂配置
- 集成包管理器,支持一键安装插件
- 自定义主题支持,满足审美需求
- 国际化支持,支持多种语言
- 高性能500插件2s内启动
<h3>👇更多内容请访问👇</h3>
<h2><a href="https://bot.liteyuki.icu">轻雪机器人主页</a></h2>
**👇所有内容请访问👇**
[bot.liteyuki.org](https://bot.liteyuki.org)
</div>
### 感谢
- 所有贡献者们
> 受限的自由才是真正的自由
## 关于
开发中
访问[轻雪7.0](https://bot.liteyuki.org)主页获取更多信息
## 特点及优势
- 化繁为简, 加速开发
- 轻量级,快速启动
- 模块化设计,易于扩展
## 服务及支持(敬请期待)
- 提供Liteyuki Cloud官方的容器化托管服务(SaaS),无需担心服务器问题
[OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge
[Liteyuki7.0]: https://img.shields.io/badge/Liteyuki-7.0-blue?style=for-the-badge
[NoneBot2]: https://img.shields.io/badge/Nonebot-2-red?style=for-the-badge
[Python3.12+]: https://img.shields.io/badge/Python-3.12+-blue?style=for-the-badge
[Liteyuki6.0]: https://img.shields.io/badge/Liteyuki-6.0-blue?style=for-the-badge
[Usage]: https://img.shields.io/badge/主页-文档-blue?style=for-the-badge
[Python3.10+]: https://img.shields.io/badge/Python-3.10+-blue?style=for-the-badge
[Repo]: https://img.shields.io/badge/官方托管-仓库-blue?style=for-the-badge
[Usage]: https://img.shields.io/badge/文档-页面-blue?style=for-the-badge
[Github]: https://img.shields.io/badge/Github-仓库-blue?style=for-the-badge
[onebot-link]:https://onebot.dev/
[LiteyukiLab]: https://img.shields.io/badge/轻雪社区-官方-blue?style=for-the-badge
[nonebot-link]:https://nonebot.dev/
[lightyuki-link]:/
[python-link]:https://www.python.org/
[usage-link]:https://bot.liteyuki.icu/
[usage-link]:https://bot.liteyuki.org/
[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
[liteyuki-link]:https://bot.liteyuki.org/
[repo-link]:https://git.liteyuki.org/bot/app
[github-link]:https://github.com/LiteyukiStudio/LiteyukiBot
[liteyukilab-link]:https://lab.liteyuki.org/@LiteyukiBot
[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.org%2flogos%2fbot.svg
## 开发环境配置
1. 项目使用uv进行包管理你也可以使用uv进行环境管理[安装uv](https://docs.astral.sh/uv/#installation)
2. 进入项目目录使用uv同步环境和依赖
```bash
uv sync --all # 安装包括dev和prod的所有依赖
```
3. VSCode扩展
- Python
- Mypy
- Ruff
4. 环境变量指定ENVIRONMENT=dev或prod或其他然后加载.env.{}文件,环境变量

View File

@ -1,8 +0,0 @@
nonebot:
host: 127.0.0.1
port: 20216
command_start: ["", "/"]
nickname: [ "liteyuki" ]
default_language: zh
driver: ~fastapi+~httpx+~websockets
alconna_use_command_start: true

12
docker-compose-dev.yaml Normal file
View File

@ -0,0 +1,12 @@
services:
server:
container_name: liteyukibot
image: liteyukibot:v7
restart: always
volumes:
- ./plugins:/liteyukibot/plugins
- ./data:/liteyukibot/data
- ./configs:/liteyukibot/configs
- ./config.yaml:/liteyukibot/config.yaml
ports:
- "8090:8080"

View File

@ -0,0 +1,16 @@
services:
liteyukibot:
container_name: bot
# Liteyuki latest: reg.liteyuki.org/bot/app:latest
# Liteyuki nightly: reg.liteyuki.org/bot/app:nightly
# GHCR latest: ghcr.io/liteyukistudio/bot-app:latest 暂未发布
# Docker Hub latest: docker.io/liteyukistudio/bot-app:latest 暂未发布
image: reg.liteyuki.org/bot/app:latest
restart: always
volumes:
- ./configs:/liteyukibot/configs # 配置目录,包含配置文件
- ./data:/liteyukibot/data # 数据目录,包含下载器安装的插件目录
- ./plugins:/liteyukibot/plugins # 外部插件目录,插件开发者可用
- ./config.yaml:/liteyukibot/config.yaml # 配置文件,包含所有配置项
ports:
- "8090:8080"

View File

@ -1,10 +0,0 @@
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free

View File

@ -1,131 +0,0 @@
// 共有配置项导入index用
import {defineConfig} from 'vitepress'
import {generateSidebar} from 'vitepress-sidebar';
import {zh} from "./zh";
import {en} from "./en";
let defaultLocale = 'zh';
const commonSidebarOptions = {
collapsed: true,
convertSameNameSubFileToGroupIndexPage: true,
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
useFolderLinkFromIndexFile: true,
includeFolderIndexFile: true,
sortMenusByFrontmatterOrder: true,
rootGroupText: 'LITEYUKIBOT',
}
/**
* Generate sidebar config
* multiple languages and sections
* @returns {any[]}
*/
function generateSidebarConfig(): any[] {
let sections = ["dev", "store", "usage", "deploy"]
let languages = ['zh', 'en']
let ret = []
for (let language of languages) {
for (let section of sections) {
if (language === defaultLocale) {
ret.push({
basePath: `/${section}/`,
scanStartPath: `${language}/${section}`,
resolvePath: `/${section}/`,
...commonSidebarOptions
})
} else {
ret.push({
basePath: `/${language}/${section}/`,
scanStartPath: `${language}/${section}`,
resolvePath: `/${language}/${section}/`,
...commonSidebarOptions
})
}
}
}
return ret
}
console.log(generateSidebarConfig())
export const common = defineConfig({
head: [
// 配置favicon.ico
['link', {rel: 'icon', type: 'image/x-icon', href: 'favicon.ico'}],
['link', {rel: 'stylesheet', href: 'https://fonts.font.im/css?family=Cousine:400,400i,700,700i|Poppins:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i'}],
],
rewrites: {
[`${defaultLocale}/:rest*`]: ":rest*",
},
themeConfig: {
logo: {
light: '/liteyuki.svg',
dark: '/liteyuki-dark.svg',
alt: 'LiteyukiBot Logo'
},
sidebar: generateSidebar(
[
...generateSidebarConfig()
]
),
socialLinks: [
{icon: 'github', link: 'https://github.com/LiteyukiStudio/LiteyukiBot'},
{
icon: {
svg: '<svg t="1725391346807" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5067" width="256" height="256"><path d="M1004.692673 466.396616l-447.094409-447.073929c-25.743103-25.763582-67.501405-25.763582-93.264987 0l-103.873521 103.873521 78.171378 78.171378c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928 0 14.827372-3.358686 28.856031-9.359266 41.389666l127.97824 127.97824c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928s-42.98709 96.00928-96.00928 96.00928-96.00928-42.98709-96.00928-96.00928c0-14.827372 3.358686-28.856031 9.359266-41.389666l-127.97824-127.97824c-3.051489 1.454065-6.184898 2.744293-9.379746 3.870681l0 266.97461c37.273227 13.188988 63.99936 48.721433 63.99936 90.520695 0 53.02219-42.98709 96.00928-96.00928 96.00928s-96.00928-42.98709-96.00928-96.00928c0-41.799262 26.726133-77.331707 63.99936-90.520695l0-266.97461c-37.273227-13.188988-63.99936-48.721433-63.99936-90.520695 0-14.827372 3.358686-28.856031 9.359266-41.389666l-78.171378-78.171378-295.892081 295.871601c-25.743103 25.784062-25.743103 67.542365 0 93.285467l447.114889 447.073929c25.743103 25.743103 67.480925 25.743103 93.264987 0l445.00547-445.00547c25.763582-25.763582 25.763582-67.542365 0-93.285467z" fill="#a2d8f4" p-id="5068"></path></svg>'
},
link: "https://git.liteyuki.icu/LiteyukiStudio/LiteyukiBot"
}
],
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '打开搜索框',
},
modal: {
noResultsText: '没有找到搜索结果',
resetButtonTitle: '清除查询条件',
footer: {
selectText: '选择',
navigateText: '切换',
}
}
},
},
en: {
translations: {
button: {
buttonText: 'Search',
buttonAriaLabel: 'Search',
},
modal: {
noResultsText: 'No results found',
resetButtonTitle: 'Reset search query',
footer: {
selectText: 'Select',
navigateText: 'Navigate',
}
}
}
},
}
}
}
},
sitemap: {
hostname: 'https://bot.liteyuki.icu'
},
lastUpdated: true,
locales: {
root: {label: "简体中文", ...zh},
en: {label: "English", ...en},
},
})

View File

@ -1,35 +0,0 @@
import {defineConfig} from 'vitepress'
import {ThemeConfig} from "./utils";
export const en = defineConfig({
lang: "en-US",
title: "LiteyukiBot",
description: "A high-performance, easy-to-use chatbot framework and application",
themeConfig: {
nav: [
{text: 'Deploy', link: '/en/deploy/install'},
{text: 'Usage', link: '/en/usage/basic'},
{text: 'Extension', link: '/en/store/resource'},
{text: 'Develop', link: '/en/dev/guide'},
],
docFooter: {
prev: 'Prev Page',
next: 'Next Page'
},
editLink: ThemeConfig.getEditLink(
'Edit this page on GitHub',
),
footer: {
message: 'Documentation built with <a href="https://vitepress.dev/">VitePress</a> | API references generated by <a href="https://github.com/LiteyukiStudio/litedoc">litedoc</a>',
copyright: ThemeConfig.copyright
},
outline: ThemeConfig.getOutLine("Page Content"),
langMenuLabel: 'Language',
returnToTopLabel: 'Back to top',
sidebarMenuLabel: 'Menu',
darkModeSwitchLabel: 'Theme',
lightModeSwitchTitle: 'Light Mode',
darkModeSwitchTitle: 'Dark Mode',
},
})

View File

@ -1,6 +0,0 @@
import {defineConfig} from "vitepress";
import {common} from './common'
export default defineConfig({
...common,
})

View File

@ -1,40 +0,0 @@
// 本模块储存一些工具函数和引用
/**
* GetEditLink Options
* @param text Edit link text
*/
export const ThemeConfig = {
getEditLink: (editPageText: string): { pattern: (params: { filePath: string; }) => string; text: string; } => {
return {
pattern: ({filePath}: { filePath: string; }): string => {
// 匹配 /dev/api或 /{lang}/dev/api
const regex = /^[^\/]+\/dev\/api/;
console.log(filePath);
if (regex.test(filePath)) {
// remove {lang}/api prefix
filePath = filePath.replace(regex, '')
.replace('index.md', '__init__.py')
.replace('.md', '.py');
// 若文件名(不含扩展)和上级文件夹相同,返回文件夹/__init__.py
if (filePath.split('/').pop().split('.')[0] === filePath.split('/').slice(-2, -1)[0]) {
filePath = filePath.split('/').slice(0, -1).join('/') + '/__init__.py';
}
return `https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki${filePath}`;
} else {
return `https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/docs/${filePath}`;
}
},
text: editPageText
};
},
getOutLine: (label: string): { label: string; level: [number, number]; } => {
return {
label: label,
level: [2, 6]
};
},
copyright: 'Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved'
}

View File

@ -1,35 +0,0 @@
import {defineConfig} from 'vitepress'
import {ThemeConfig} from "./utils";
export const zh = defineConfig({
lang: "zh-Hans",
title: "轻雪机器人",
description: "一个综合性的机器人应用及管理框架",
themeConfig: {
nav: [
{text: '部署', link: '/deploy/install'},
{text: '使用', link: '/usage/basic'},
{text: '扩展', link: '/store/resource'},
{text: '开发', link: '/dev/guide'},
],
docFooter: {
prev: '上一页',
next: '下一页'
},
editLink: ThemeConfig.getEditLink(
'在 GitHub 上编辑此页',
),
footer: {
message: '文档由 <a href="https://vitepress.dev/">VitePress</a> 构建 | API引用由 <a href="https://github.com/LiteyukiStudio/litedoc">litedoc</a> 生成',
copyright: ThemeConfig.copyright
},
outline: ThemeConfig.getOutLine("页面内容"),
langMenuLabel: '语言',
returnToTopLabel: '返回顶部',
sidebarMenuLabel: '菜单',
darkModeSwitchLabel: '主题',
lightModeSwitchTitle: '轻色模式',
darkModeSwitchTitle: '深色模式',
},
})

View File

@ -1,18 +0,0 @@
import DefaultTheme from 'vitepress/theme'
import './liteyuki.scss'
import StatsBar from '../../components/StatsBar.vue'
import PluginStore from '../../components/PluginStore.vue'
import ResStore from '../../components/ResStore.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('StatsBar', StatsBar);
app.component('PluginStore', PluginStore);
app.component('ResStore', ResStore);
},
Layout: StatsBar
}

View File

@ -1,149 +0,0 @@
:root {
--vp-c-brand-1: #149ef8;
--vp-c-brand-2: #0434ad;
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #34a3fe 30%, #8d44ff);
--vp-home-hero-image-background-image: linear-gradient(-45deg, #d0e9ff 50%, #a2d8f4 50%);
--vp-home-hero-image-filter: blur(44px);
--vp-c-gray-1: #eee;
--vp-c-gray-2: #aaa;
--border-radius-1: 10px;
--border-radius-2: 20px;
--border-radius-3: 40px;
--vp-font-family-base: 'Poppins', 'Punctuation SC', 'Inter', ui-sans-serif, system-ui,
'PingFang SC', 'Noto Sans CJK SC', 'Noto Sans SC', 'Heiti SC',
'Microsoft YaHei', 'DengXian', sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--vp-font-family-mono: 'Cousine', monospace;
--red: #ef4444;
--liteyuki-color: #149ef8;
--button-radius: 6px;
}
.dark {
--vp-home-hero-image-background-image: linear-gradient(-45deg, #004785 50%, #0374ad 50%);
--vp-c-gray-1: #333;
--vp-c-gray-2: #666;
}
.tab-buttons {
padding: 7px;
flex: 1;
display: flex;
justify-content: flex-start;
height: 60%;
align-items: center;
}
.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%;
}
.item-card {
position: relative;
border-radius: 15px;
background-color: var(--vp-c-gray-1);
height: 160px;
padding: 16px;
margin: 10px;
box-sizing: border-box;
transition: background 0.3s ease;
}
.item-name {
font-size: 20px;
margin-bottom: 10px;
}
.item-description {
color: var(--vp-c-gray-2);
font-size: 13px;
white-space: pre-wrap;
}
.author-info {
display: flex;
justify-content: left;
align-items: center;
}
.author-name {
font-size: 15px;
font-weight: normal;
}
.avatar {
border-radius: 50%;
margin: 0 10px;
}
/*store*/
.store-tabs {
display: flex;
justify-content: flex-end;
margin: 10px 0;
}
.publish-button {
right: 10px;
bottom: 10px;
padding: 5px 10px;
border-radius: 10px;
background-color: var(--vp-c-brand-1);
color: white;
font-size: 15px;
cursor: pointer;
}
.pub-window {
input {
width: 100%;
padding: 10px;
margin: 10px 0;
border-radius: 10px;
border: 1px solid #ccc;
}
}
.pub-option {
margin: 10px;
padding: 5px;
border: 2px solid var(--vp-c-gray-1);
border-radius: var(--button-radius);
background-color: var(--vp-c-gray-1);
&.close {
}
&.submit {
background-color: var(--vp-c-brand-1);
}
}

View File

@ -1,26 +0,0 @@
<script setup lang="ts">
import {getTextRef} from "./scripts/i18n";
import {repoPath, repoURL} from "./scripts/const";
const contributorImgSrc = `https://contrib.rocks/image?repo=${repoPath}`
const contributorsUrl = `${repoURL}/graphs/contributors`
</script>
<template>
<div class="contributor-bar">
<h2>{{ getTextRef('thx_contributors') }}</h2>
<a :href="contributorsUrl">
<div class="contributor-list">
<img :src=contributorImgSrc alt="Contributors">
</div>
</a>
</div>
</template>
<style scoped>
.contributor-bar {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

View File

@ -1,38 +0,0 @@
<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

@ -1,20 +0,0 @@
<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

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

View File

@ -1,109 +0,0 @@
<template>
<div class="item-card">
<div class="item-name">{{ props.item.name }}</div>
<div class="item-description">{{ props.item.desc }}</div>
<div class="tags">
<span class="tag" v-for="tag in props.item.tags" :key="tag" :style=getTagStyle(tag.color)>{{ tag.label }}</span>
</div>
<div class="item-bar">
<!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
<a :href=props.item.homepage class="btn" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
<path fill="currentColor"
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
</svg>
</a>
<!-- <button class="copy-btn btn"><div @click="copyToClipboard">安装</div></button> 点击后把安装命令写入剪贴板-->
<button class="btn copy-btn" @click="copyToClipboard">复制安装命令</button>
<div class="btn">
<a class="author-info" :href="`https://github.com/${props.item.author }`">
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
<div class="author-name">{{ props.item.author }}</div>
</a>
</div>
<!-- 复制键复制安装命令npm install props.item.module_name-->
</div>
</div>
</template>
<script setup lang="ts">
import {defineProps, onMounted} from 'vue'
import Clipboard from 'clipboard'
// 复制安装命令按钮
// 构建复制成功和失败的提示
const props = defineProps({
item: Object
})
const copyToClipboard = () => {
const clipboard = new Clipboard('.copy-btn', {
text: () => `npm install ${props.item.module_name}`
})
clipboard.on('success', () => {
})
clipboard.on('error', () => {
})
}
const getTagStyle = (backgroundColor: string) => {
// 将颜色值转换为 RGB 格式
const rgb = backgroundColor.replace(/^#/, '');
const [r, g, b] = rgb.match(/.{2}/g).map(x => parseInt(x, 16));
// 计算亮度
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// 根据亮度决定文字颜色
return {
backgroundColor: backgroundColor,
color: brightness > 128 ? '#000' : '#fff'
};
};
// 复制到剪贴板的函数
</script>
<style scoped>
.btn {
margin-right: 15px;
}
button {
background-color: #00000000;
border: none;
}
.copy-btn {
cursor: pointer;
color: #666;
}
.icon {
width: 20px;
height: 20px;
}
.item-bar {
position: absolute;
bottom: 0;
height: 50px;
display: flex;
align-items: center;
box-sizing: border-box;
justify-content: space-between;
color: #00000055;
}
.tag {
display: inline-block;
padding: 0 5px;
margin-right: 5px;
border-radius: 5px;
font-size: 12px;
}
</style>

View File

@ -1,91 +0,0 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
import ItemCard from './PluginItemCard.vue'
import ToggleSwitch from "./ToggleSwitch.vue";
import {getTextRef} from "./scripts/i18n";
import pluginsJson from "../public/plugins.json"
let showLiteyukiPluginOnly = ref(false)
let filteredItems = computed(() => {
let filtered = items.value
if (search.value) {
filtered = filtered.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())
)
}
if (showLiteyukiPluginOnly.value) {
filtered = filtered.filter(item => item.is_liteyuki_plugin)
}
return filtered
})
// 插件商店Nonebot
let items = ref([])
let search = ref('')
// 从轻雪官方拉取,添加轻雪插件属性
items.value = pluginsJson
items.value.forEach(item => {
item.is_liteyuki_plugin = true
})
//追加
fetch('https://registry.nonebot.dev/plugins.json')
.then(response => response.json())
.then(data => {
// 遍历data的每一项把is_official设为false
data.forEach(item => {
item.is_official = false
})
items.value = items.value.concat(data)
})
</script>
<template>
<div class="market">
<h1>{{ getTextRef('pluginStore') }}</h1>
<p>{{ getTextRef('pluginStoreDesc') }}</p>
<!-- 搜索框-->
<div class="search-box-div">
<input class="item-search-box" type="text" v-model="search" :placeholder="getTextRef('search')"/>
<ToggleSwitch v-model:modelValue="showLiteyukiPluginOnly"/>
{{ getTextRef('liteyukiOnly') }}
</div>
<!-- 按钮们-->
<!-- <div class="tab">-->
<!-- <button @click="open"-->
<!-- </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;
}
.search-box-div {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
.search-box-div input {
margin-right: 10px;
}
</style>

View File

@ -1,54 +0,0 @@
<template>
<div class="item-card">
<div class="item-name">{{ props.item.name }}</div>
<div class="item-description">{{ props.item.description }}</div>
<div class="item-bar">
<!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
<a :href=props.item.link class="" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
<path fill="currentColor"
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
</svg>
</a>
<div><a class="author-info" :href="`https://github.com/${props.item.author }`">
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
<div class="author-name">{{ props.item.author }}</div>
</a>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
const props = defineProps({
item: Object
})
</script>
<style scoped>
.icon {
width: 20px;
height: 20px;
color: $themeColor;
}
.item-bar {
position: absolute;
bottom: 0;
height: 50px;
display: flex;
align-items: center;
box-sizing: border-box;
justify-content: space-between;
color: #00000055;
}
</style>

View File

@ -1,38 +0,0 @@
<template>
<div v-if="isVisible" class="floating-window">
<div class="window-content">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
const props = defineProps({
isVisible: Boolean,
})
</script>
<style scoped>
.floating-window {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.window-content {
background: var(--vp-c-gray-1);
padding: 20px;
border-radius: 5px;
max-width: 60%;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>

View File

@ -1,105 +0,0 @@
<script setup lang="ts">
import {computed, ref} from 'vue'
import ItemCard from './ResItemCard.vue'
import ResPubWindow from "./ResPubWindow.vue";
import {getTextRef, formatLang} from "./scripts/i18n";
import {RepoUrl} from "./scripts/statsApi";
import resourcesJson from "../public/resources.json"
import {useData} from "vitepress";
// formLan
// 从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('')
items.value = resourcesJson
// 列表倒序
const isPublishWindowOpen = ref(false)
let newRes = ref({
name: '',
})
function openPublishWindow() {
isPublishWindowOpen.value = true
}
function closePublishWindow() {
isPublishWindowOpen.value = false
}
let submitLang = ""
if (formatLang(useData().site.value.lang) === "zh") {
submitLang = "zh"
} else {
submitLang = "en"
}
function submitForm() {
const title = encodeURI(`Resource: ${newRes.value.name}`)
const issueURL = `${RepoUrl}/issues/new?assignees=&labels=Resource&template=resource_publish_${submitLang}.yml&title=${title}`
console.log(issueURL)
window.open(issueURL, '_blank')
}
</script>
<template>
<div class="market">
<h1>{{ getTextRef('resourceStore') }}</h1>
<div class="search-box-div"><input class="item-search-box" type="text" :placeholder="getTextRef('search')"
v-model="search"/></div>
<div class="store-tabs" style="display: flex">
<button class="store-button publish-button" @click="openPublishWindow">{{ getTextRef('publishRes') }}</button>
</div>
<div class="items">
<!-- 使用filteredItems来布局商品 -->
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
</div>
<ResPubWindow class="pub-window" :is-visible="isPublishWindowOpen">
<h2>{{ getTextRef("publishRes") }}</h2>
<form @submit.prevent="submitForm">
<label for="name">{{ getTextRef("resName") }}</label>
<input type="text" id="name" v-model="newRes.name" :placeholder="getTextRef('resNameText')"/>
<div class="pub-options" style="display: flex; justify-content: center">
<button class="pub-option close" type="button" @click="closePublishWindow">{{
getTextRef("closeButtonText")
}}
</button>
<button class="pub-option submit" type="submit" @click="submitForm">{{
getTextRef("submitButtonText")
}}
</button>
</div>
</form>
</ResPubWindow>
</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,301 +0,0 @@
<script setup lang="ts">
import DefaultTheme from "vitepress/theme";
import {ref, onMounted, onUnmounted} from "vue";
import {statsApi, GithubStats, RepoUrl, StarMapUrl, uploadVisitRecord} from "./scripts/statsApi";
import {getTextRef, updateRefData} from "./scripts/i18n";
import {onBeforeRouteUpdate} from 'vue-router';
const {Layout} = DefaultTheme;
let githubStats: GithubStats | null = null;
const dataSections = {
total: {
name: 'total',
color: '#00a6ff',
value: ref(-1),
link: StarMapUrl
},
online: {
name: 'online',
color: '#7eff7e',
value: ref(-1),
link: StarMapUrl
},
stars: {
name: 'stars',
color: '#ffcc00',
value: ref(-1),
link: `${RepoUrl}/stargazers`
},
forks: {
name: 'forks',
color: '#ff6600',
value: ref(-1),
link: `${RepoUrl}/forks`
},
issues: {
name: 'issues',
color: '#ff0000',
value: ref(-1),
link: `${RepoUrl}/issues`
},
prs: {
name: 'prs',
color: '#f15df1',
value: ref(-1),
link: `${RepoUrl}/pulls`
},
plugins: {
name: 'plugins',
color: '#a766ff',
value: ref(-1),
link: './store/plugin'
},
resources: {
name: 'resources',
color: '#5a54fa',
value: ref(-1),
link: './store/resource'
},
visitors: {
name: 'visitors',
color: '#00a6ff',
value: ref(-1),
link: RepoUrl
},
}
async function updateData() {
// dataSections.online.value.value = await statsApi.getOnline();
// dataSections.total.value.value = await statsApi.getTotal();
[
dataSections.online.value.value,
dataSections.total.value.value,
dataSections.plugins.value.value,
dataSections.resources.value.value,
dataSections.visitors.value.value,
githubStats,
] = await Promise.all([
statsApi.getOnline(),
statsApi.getTotal(),
statsApi.getPluginNum(),
statsApi.getResourceNum(),
statsApi.getVisitCount(),
statsApi.getGithubStats(),
]);
dataSections.stars.value.value = githubStats?.stars || 0;
dataSections.forks.value.value = githubStats?.forks || 0;
dataSections.issues.value.value = githubStats?.issues || 0;
dataSections.prs.value.value = githubStats?.prs || 0;
}
function formatNumber(num: { value: number }): string {
return num.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
onMounted(() => {
const intervalId = setInterval(updateData, 10000);
updateData();
uploadVisitRecord();
onUnmounted(() => {
clearInterval(intervalId);
});
});
onBeforeRouteUpdate(() => {
updateRefData();
});
console.log(
" _ _ _ _ _ ____ _ \n" +
" | | (_) | | | (_) _ \\ | | \n" +
" | | _| |_ ___ _ _ _ _| | ___| |_) | ___ | |_ \n" +
" | | | | __/ _ \\ | | | | | | |/ / | _ < / _ \\| __|\n" +
" | |____| | || __/ |_| | |_| | <| | |_) | (_) | |_ \n" +
" |______|_|\\__\\___|\\__, |\\__,_|_|\\_\\_|____/ \\___/ \\__|\n" +
" __/ | \n" +
" |___/ "
)
console.log(
getTextRef('easterEgg')
)
</script>
<template>
<Layout>
<template #home-features-before>
<div class="stats-bar-content">
<div class="stats-bar">
<div class="stats-info">
<div class="stats-title">{{ getTextRef('stats') }}</div>
<div class="sections">
<div v-for="section in Object.values(dataSections)" :key="section.name" class="section">
<a :href="section.link" target="_blank">
<div class="section-tab">
<span class="dot" :style="{backgroundColor: section.color}"></span>
<span class="text">{{ getTextRef(section.name) }}</span>
</div>
<div class="number">{{ formatNumber(section.value) }}</div>
</a>
</div>
</div>
</div>
<div class="starmap">
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px" class="gamma">
</iframe>
</div>
</div>
</div>
</template>
</Layout>
</template>
<style scoped>
.stats-bar-content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.gamma {
filter: brightness(1.8);
}
.dark .gamma {
filter: brightness(1.0);
}
.stats-bar {
width: 80%;
max-width: 1150px;
display: flex;
justify-content: space-between;
padding: 20px;
margin: 10px;
border-radius: var(--border-radius-2);
background-color: var(--vp-c-gray-1);
flex-direction: column; /* 默认纵向布局 */
}
.stats-info{
margin-bottom: 20px;
}
.stats-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 20px;
text-align: center;
}
.sections {
height: 100%;
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 15px;
margin: 10px;
}
.section {
display: flex;
flex-direction: column;
position: relative; /* 使伪元素相对于父元素定位 */
border-radius: var(--border-radius-2);
}
.section::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 0 solid transparent; /* 初始边框为透明 */
transition: border 0.1s ease-in-out; /* 添加过渡效果 */
border-radius: var(--border-radius-2);
pointer-events: none; /* 确保伪元素不会阻挡点击事件 */
}
.section:hover::before {
border: 1px solid #00a6ff; /* 悬停时添加边框 */
border-radius: var(--border-radius-2);
}
.section-tab {
margin-left: 15px;
display: flex;
justify-content: flex-start;
align-items: center; /* 确保垂直居中 */
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.text {
font-size: 14px;
white-space: nowrap;
align-items: center;
}
.number {
font-size: 27px;
font-weight: bold;
margin-top: 5px;
margin-left: 15px;
}
.starmap {
position: relative;
width: 100%;
height: 200px;
overflow: hidden;
border-radius: var(--border-radius-2);
}
.starmap iframe {
position: absolute;
top: -150px; /* 根据需要调整裁剪位置 */
left: -40px; /* 根据需要调整裁剪位置 */;
width: calc(100% + 80px); /* 根据需要调整裁剪宽度 */
height: calc(100% + 300px); /* 根据需要调整裁剪高度 */
}
@media (min-width: 768px) {
/* PC模式下的样式 */
.stats-bar {
flex-direction: row;
margin: 30px;
}
.stats-info {
width: 40%;
margin: 10px 30px 30px 30px;
}
.starmap {
width: 60%;
height: 400px;
}
.starmap iframe {
position: absolute;
top: -130px; /* 根据需要调整裁剪位置 */
left: -60px; /* 根据需要调整裁剪位置 */;
width: calc(100% + 120px); /* 根据需要调整裁剪宽度 */
height: calc(100% + 280px); /* 根据需要调整裁剪高度 */
}
}
</style>

View File

@ -1,67 +0,0 @@
<template>
<label class="switch">
<input type="checkbox" :checked="modelValue" @change="updateValue($event.target.checked)">
<span class="slider round"></span>
</label>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: Boolean
})
const emit = defineEmits(['update:modelValue'])
const updateValue = (value: boolean) => {
emit('update:modelValue', value)
}
</script>
<style scoped>
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #2196F3;
}
input:checked + .slider:before {
transform: translateX(26px); /* 调整为 26px 以确保对齐 */
}
</style>

View File

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

View File

@ -1,4 +0,0 @@
export const platformBaseURL = "https://github.com/"
export const repoPath = "LiteyukiStudio/LiteyukiBot"
export const repoURL = `${platformBaseURL}${repoPath}`

View File

@ -1,112 +0,0 @@
import {ref} from "vue";
import {useData} from "vitepress";
const i18nData = {
en: {
stats: 'Stats',
online: 'Online',
offline: 'Offline',
total: 'Total',
fetching: 'Fetching',
stars: 'Stars',
forks: 'Forks',
issues: 'Issues',
prs: 'Pull Requests',
visitors: 'Visitors',
size: 'Size',
plugins: 'Plugins',
resources: 'Resources',
pluginStore: 'Plugin Store',
pluginStoreDesc: 'Content from the LightSnow Plugin Store, LightSnow supports NoneBot through the lpnonebot plugin, and references some NoneBot plugins',
liteyukiOnly: 'Liteyuki Only',
search: 'Search',
resourceStore: 'Resources Store',
thx_contributors: 'Thanks the following contributors!',
easterEgg: 'Congratulations on finding the Easter egg!',
publishPlugin: 'Publish Plugin',
publishRes: 'Publish Resource',
closeButtonText: 'Close',
submitButtonText: 'Submit',
resName: 'Name',
resDesc: 'Description',
resAuthor: 'Author',
resLink: 'Download Link',
resHomepage: 'Homepage',
resNameText: 'Example: Kawaii Style Theme',
resDescText: 'Example: A kawaii style and color theme',
resAuthorText: 'Usually the github username, Example: yanyongyu',
resLinkText: 'Direct download link, usually zip package link',
resHomepageText: 'Optional, can be the name of the git platform repository"',
},
zh: {
stats: '统计信息',
online: '在线',
offline: '离线',
total: '实例',
fetching: '获取中',
stars: '星星',
forks: '分叉',
issues: '议题',
prs: '拉取请求',
visitors: '访客',
size: '大小',
plugins: '插件',
resources: '主题资源',
store: '商店',
pluginStore: '插件商店',
pluginStoreDesc: '内容来自轻雪插件商店轻雪通过lpnonebot插件对NoneBot实现支持引用了部分NoneBot插件',
liteyukiOnly: '仅轻雪',
search: '搜索',
resourceStore: '资源商店',
thx_contributors: '感谢以下贡献者!',
easterEgg: '恭喜你发现了彩蛋!',
publishPlugin: '发布插件',
publishRes: '发布资源',
closeButtonText: '关闭',
submitButtonText: '提交',
resName: '名称',
resDesc: '描述',
resAuthor: '作者',
resLink: '下载链接',
resHomepage: '主页',
resNameText: '示例:可爱风格主题',
resDescText: '示例:一个可爱风格和配色的主题',
resAuthorText: '通常为github用户名示例yanyongyu',
resLinkText: '直接下载链接通常为zip包链接',
resHomepageText: '可选可为git平台仓库名',
}
}
let refData = {}
function getText(lang: string, key: string): string {
lang = formatLang(lang);
return i18nData[lang][key];
}
export function formatLang(lang: string): string {
if (lang.includes('-')) {
return lang.split('-')[0];
}
return lang;
}
export function updateRefData() {
const lang = formatLang(useData().site.value.lang);
for (let key in refData) {
refData[key].value = getText(lang, key);
}
}
export function getTextRef(key: string): any {
const lang = formatLang(useData().site.value.lang);
refData[key] = getText(lang, key);
return refData[key] || key;
}

View File

@ -1,182 +0,0 @@
// URL
export const OWNER = "LiteyukiStudio"
export const REPO = "LiteyukiBot"
const githubAPIUrl = "https://api.github.com"
const giteaAPIUrl = "https://git.liteyuki.icu/api/v1"
const onlineFetchUrl = "https://api.liteyuki.icu/online";
const totalFetchUrl = "https://api.liteyuki.icu/count";
const visitRecordUrl = "https://api.liteyuki.icu/visit";
const visitCountUrl = "https://api.liteyuki.icu/visit_count";
export const RepoUrl = `https://github.com/${OWNER}/${REPO}`
export const StarMapUrl = "https://starmap.liteyuki.icu"
type GithubStats = {
stars: number;
forks: number;
watchers: number;
issues?: number;
prs?: number;
size?: number;
}
// 异步接口
interface StatsApi {
getTotal: () => Promise<number>;
getOnline: () => Promise<number>;
getGithubStats: () => Promise<GithubStats>;
getPluginNum: () => Promise<number>;
getResourceNum: () => Promise<number>;
getVisitCount: () => Promise<number>;
}
export type {GithubStats};
async function getGiteaStats() {
try {
const url = `${giteaAPIUrl}/repos/${OWNER}/${REPO}`;
console.log(url);
const res = await fetch(url);
const data = await res.json();
return {
stars: data.stars_count,
forks: data.forks_count,
watchers: data.watchers_count,
issues: 0,
prs: 0,
size: data.size,
};
} catch (e) {
return {
stars: -1,
forks: -1,
watchers: -1,
issues: -1,
prs: -1,
size: -1,
};
}
}
async function getGithubStats() {
try {
const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`);
const data = await res.json();
return {
stars: data.stargazers_count,
forks: data.forks_count,
watchers: data.watchers_count,
issues: data.open_issues_count,
prs: data.open_issues_count,
size: data.size,
};
} catch (e) {
return {
stars: -1,
forks: -1,
watchers: -1,
issues: -1,
prs: -1,
size: -1,
};
}
}
async function getRepoStats() {
// 两个接口各数据,加和返回
const githubStats = await getGithubStats();
const giteaStats = await getGiteaStats();
return {
stars: githubStats.stars + giteaStats.stars,
forks: githubStats.forks + giteaStats.forks,
watchers: githubStats.watchers + giteaStats.watchers,
issues: githubStats.issues + giteaStats.issues,
prs: githubStats.prs + giteaStats.prs,
size: githubStats.size + giteaStats.size,
};
}
// 实现接口
export const statsApi: StatsApi = {
getTotal: async () => {
try {
const res = await fetch(totalFetchUrl);
const data = await res.json();
return data.register;
} catch (e) {
return -1;
}
},
getOnline: async () => {
try {
const res = await fetch(onlineFetchUrl);
const data = await res.json();
return data.online;
} catch (e) {
return -1;
}
},
getGithubStats: getRepoStats,
getPluginNum: async () => {
try {
const res = await fetch('/plugins.json');
const data = await res.json();
return data.length;
} catch (e) {
return -1;
}
},
getResourceNum: async () => {
try {
const res = await fetch('/resources.json');
const data = await res.json();
return data.length;
} catch (e) {
return -1;
}
},
getVisitCount: async () => {
try {
const res = await fetch(visitCountUrl);
const data = await res.json();
return data.count;
} catch (e) {
return -1;
}
}
};
function getDeviceId(): string {
// 用户每次访问时生成一个唯一的设备ID储存在localStorage中用于统计用户数量
const deviceIdKey = 'deviceId';
let deviceId = localStorage.getItem(deviceIdKey);
if (!deviceId) {
deviceId = generateUUID();
localStorage.setItem(deviceIdKey, deviceId);
}
return deviceId;
}
export async function uploadVisitRecord() {
const deviceId = getDeviceId();
try {
await fetch(visitRecordUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({'device_id': deviceId}).toString(),
});
} catch (e) {
console.error('Failed to upload visit record:', e);
}
}
function generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

View File

@ -1,77 +0,0 @@
---
title: Configurations
order: 2
---
# Configurations
LiteyukiBot supports `yaml`, `json`, and `toml` as configuration files, depending on your personal preference.
When you first run LiteyukiBot, it will generate `config.yml` and the `config` directory. You can modify the configuration items and restart LiteyukiBot. In most cases, you only need to modify
the `superusers` and `nickname` fields.
When starting, LiteyukiBot will load all configuration files in the project directory `config.yml/yaml/json/toml` and the `config` directory. You can create multiple configuration files in
the `config` directory, and LiteyukiBot will automatically merge these configuration files.
## **Basic Configuration**
```yaml
# NoneBot configuration
nonebot:
command_start: [ "/", "" ] # Command prefix, if there is no "" empty command header, please enable alconna_use_command_start to ensure alconna parsing is normal
host: 127.0.0.1 # Listening address, default is local, if you want to receive external requests, please fill in
port: 20216 # Binding port
nickname: [ "liteyuki" ] # Bot nickname list
superusers: [ "1919810" ] # Superuser list
liteyuki:
log_level: "INFO" # Log level
log_icon: true # Whether to display the log level icon (some console fonts are not available)
auto_report: true # Whether to automatically report problems to Liteyuki server
auto_update: true # Whether to automatically update Liteyuki, check for updates at 4 am every day
plugins: [ ] # Liteyuki plugin list
plugin_dirs: [ ] # Liteyuki plugin directory list
```
## **Other configurations**
The following is the default value. If you need to customize it, please add it manually
```yaml
# Advanced configuration
nonebot:
onebot_access_token: "" # OneBot access token
default_language: "zh-CN" # Default language
alconna_auto_completion: false # alconna auto completion
safe_mode: false # Safe mode, if true, the bot will not load any plugins
# other nonebot configurations
custom_config_1: "custom_value1"
custom_config_2: "custom_value2"
# development configuration
liteyuki:
allow_update: true # Whether to allow Liteyuki to update
debug: false # Debug mode, if true, Liteyuki will output more detailed logs
dev_mode: false # development mode, if true, Liteyuki will load all plugins in the development directory
...
```
```yaml
```
## **Example: Configuration of OneBot implementation side connected to NoneBot**
In production environments, it is recommended to use reverse WebSocket
The fields provided by different implementation sides may be different, but basically the same. Here is a reference value
| Fields | Value | Description |
|-------------|------------------------------------|---------------------------------------------------------------------------------------|
| protocol | Reverse WebSocket | Liteyuki-NoneBot as server |
| address | ws://127.0.0.1:20216/onebot/v11/ws | The address depends on the configuration file, the default is ` |
| AccessToken | `""` | If you have configured `AccessToken` for Liteyuki, please fill in the same value here |
- To use other communication methods, please visit [OneBot Adapter](https://onebot.adapters.nonebot.dev/) for detailed information
## **Other**
- Liteyuki is not limited to the OneBot adapter and NoneBot2. You can use any adapter supported by NoneBot2 or use the Liteyuki message delivery plugin

View File

@ -1,60 +0,0 @@
---
title: FAQ
order: 3
---
# FAQ
## **Frequently Asked Questions**
- There are too many python interpreters on my computer, how do I know which one to use?
- You can specify which python interpreter to use by using the full path to the python executable, for example, `/path/to/python main.py`
- Use virtual environments to avoid conflicts between different python interpreters
- Why does the bot not respond after I start it?
- Please check the configuration file `command_start` or `superusers`, make sure you have permission to use the command and send it correctly
- Make sure the command header does not conflict with `nickname{}`, for example, a command is `help`, but the `Bot` nickname has a `help`, then it will be parsed as a nickname instead of a command
- Update Liteyuki failed, error `InvalidGitRepositoryError`
- Please install `Git` correctly and deploy Liteyuki using cloning instead of direct download
- How to log in to chat platforms such as Telegram?
- If you have this question, it means you don't know much about this project.
This project does not implement the login function, only the message processing and response.
The login function is provided by the implementation side (protocol side). The implementation side itself does not handle response logic.
It processes and reports messages to Liteyuki according to the OneBot standard.
You need to use an implementation side that complies with the OneBot standard to connect to Liteyuki and report messages to Liteyuki.
Some recommended implementation sides have been listed below
- `Playwright` installation failed
- Enter `playwright install` to install the browser
- Some plugins report errors after installation and cannot be started
- Please refer to the plugin documentation first, confirm that the necessary configuration items of the plugin are intact,
and if the problem persists, please contact the plugin author or start Liteyuki in safe mode `safe_mode: true`.
In safe mode, you can use `npm uninstall` to uninstall problematic plugins
## Other questions
- Join chat group[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
- If you don't have a QQ account, you can [submit an issue on GitHub](https://github.com/LiteyukiStudio/LiteyukiBot/issues/new?assignees=&labels=&projects=&template=%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88.md&title=)
## **Recommended Solution(QQ)**
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core), based on `Lagrange.Core`, a Linux QQ implementation, supports OneBotv11 protocol
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot), a plugin for `Liteloader NTQQ`, supports OneBotv11 protocol
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock), based on Lsposed, supports kritor protocol
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai), based on `Node.js`, supports OneBotv11 protocol
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp)A QQ Client based on `go`, supports OneBotv11 protocol
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo), use QQ protocol
## **Recommended Solution(Minecraft)**
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot), We develop a Minecraft server chat bot
Other project encountered issues, please prioritize the documentation and issues of the project itself, don't ask LiteyukiBot developers
## **Acknowledgements**
- [Nonebot2](https://nonebot.dev) provides the underlying framework
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna) provides the command parser
- [MiSans](https://hyperos.mi.com/font/zh/)[MapleMono](https://gitee.com/mirrors/Maple-Mono) provides the font

View File

@ -1,57 +0,0 @@
---
title: Install
order: 1
---
# Install
## **Normal Installation**
1. Install [`Git`](https://git-scm.com/download/) and [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) Environment.
```bash
# Clone the project
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
# change directory
cd LiteyukiBot
# install dependencies
pip install -r requirements.txt
# start the bot!
python main.py
```
> [!tip]
> It is recommended to use a virtual environment to run Liteyuki to avoid dependency conflicts.
> You can use `python -m venv .venv` to create a virtual environment, and then use `.venv\Scripts\activate` to activate the virtual environment
> (use `source .venv/bin/activate` to activate on Linux).
## **Run with Docker**
1. Install [`Docker`](https://docs.docker.com/get-docker/)
2. Clone Repo `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
3. Change directory `cd LiteyukiBot`
4. Build docker image `docker build -t liteyukibot .`
5. Run container `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
> [!tip]
> If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$&#40;pwd&#41;` <br>
> If you have modified the port number, please replace `20216:20216` with your port number
## **Use TRSS Script**
[TRSS_Liteyuki Management Script](https://timerainstarsky.github.io/TRSS_Liteyuki/), which provides a more convenient way to manage LiteyukiBot, recommended to use `Arch Linux`
## **Device Requirements**
- Windows system version minimum `Windows10+`/`Windows Server 2019+`
- Linux system requires Python3.10+, recommended `Ubuntu 20.04+`
- CPU: at least `1vCPU`
- Memory: Bot without other plugins will occupy `300~500MB`, including `chromium` and `node` processes, other plugins depend on specific plugins, recommended `1GB` or more
- Disk: at least `1GB` of space
> [!warning]
> If there are multiple environments on the device, please use `path/to/python -m pip install -r requirements.txt` to install dependencies, `path/to/python` is the path to your Python executable
> [!warning]
> Liteyuki's update function depends on Git. If you do not have Git installed and run the source code directly, you will not be able to use the update function
#### For other questions, please refer to [FAQ](./fandq)

View File

@ -1,21 +0,0 @@
---
title: Best Practices
order: 10
---
# Best Practices
## Bot applications
- [LiteyukiBot](https://github.com/LiteyukiStudio/LiteyukiBot): Liteyuki Official Bot
- [LiteyukiBot-TriM](https://github.com/TriM-Organization/LiteyukiBot-TriM): TriM Official Custom Liteyuki Bot
- [Liteyuki Marsho](https://git.liteyuki.icu/LiteyukiStudio/marsho-alpha): A cute cat girl bot based on Liteyuki and the github model
## plugins
- [liteyukibot-plugin-nonebot](https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot): Liteyuki Bot NoneBot plugin, allowing Liteyuki to support NoneBot
- [nonebot-plugin-liteyukibot](https://github.com/LiteyukiStudio/nonebot-plugin-liteyukibot): NoneBot plugin, allowing NoneBot to support Liteyuki
## Others
- [liteyuki starmap](https://starmap.liteyuki.icu): Liteyuki official star map, showing all instances of Liteyuki and their location distribution
- [TRSS_Liteyuki](https://timerainstarsky.github.io/TRSS_Liteyuki/): LiteyukiBot management script
- [litedoc](https://github.com/LiteyukiStudio/litedoc): Liteyuki API documentation generator
- liteyukibot-api (closed source): Liteyuki Bot stat interface

View File

@ -1,98 +0,0 @@
---
title: Process Communication
order: 4
---
# **Channel Communication**
### Introduction
LiteyukiBot is running in the main process MainProcess, and other plugin framework processes are child processes that come with it.
Therefore, it is impossible to communicate through shared memory and direct object transfer.
Liteyuki provides a channel [`Channel`](./api/comm/channel#class-channel-generic-t) for inter-process communication like `go`.
You can send messages to other processes through [`Channel`](./api/comm/channel#class-channel-generic-t) and listen to messages from other processes.
### Example
The channel is full-duplex, with two receiving modes, but only one mode can be used for a channel, that is, passive mode and active mode, passive mode is implemented by the `chan.on_receive()` decorator callback function, and active mode needs to call `chan.receive()` to implement
- 创建子进程的同时会初始化一个被动通道和一个主动通道,且通道标识为`{process_name}-active``{process_name}-passive`
- 主进程中通过`get_channel`函数获取通道对象
- 子进程中导入单例`active_channel``passive_channel`即可
> 在轻雪插件中(主进程中)
```python
import asyncio
from liteyuki.comm import get_channel, Channel
from liteyuki import get_bot
# get_channel函数获取通道对象参数为调用set_channel时的通道标识
channel_passive = get_channel("nonebot-passive") # 获取被动通道
channel_active = get_channel("nonebot-active") # 获取主动通道
liteyuki_bot = get_bot()
# 注册一个函数在轻雪启动后运行
@liteyuki_bot.on_after_start
async def send_data():
while True:
channel_passive.send("I am liteyuki main process passive")
channel_active.send("I am liteyuki main process active")
await asyncio.sleep(3) # 每3秒发送一次消息
```
> 在子进程中例如NoneBot插件中
```python
from nonebot import get_driver
from liteyuki.comm import active_channel, passive_channel # 子进程中获取通道直接导入进程全局单例即可
from liteyuki.log import logger
driver = get_driver()
# 被动模式,通过装饰器注册一个函数在接收到消息时运行,每次接收到字符串数据时都会运行
@passive_channel.on_receive(filter_func=lambda data: isinstance(data, str))
async def on_passive_receive(data):
logger.info(f"Passive receive: {data}")
# 注册一个函数在NoneBot启动后运行
@driver.on_startup
def on_startup():
while True:
data = active_channel.receive()
logger.info(f"Active receive: {data}")
```
> 启动后控制台输出
```log
0000-00-00 00:00:00 [ℹ️信息] Passive receive: I am liteyuki main process passive
0000-00-00 00:00:00 [ℹ️信息] Active receive: I am liteyuki main process active
0000-00-00 00:00:03 [ℹ️信息] Passive receive: I am liteyuki main process passive
0000-00-00 00:00:03 [ℹ️信息] Active receive: I am liteyuki main process active
...
```
## **共享内存通信**
### 简介
- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信,该模块封装通道实现
- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作
### 示例
> 在任意进程中均可使用
```python
from liteyuki.comm.storage import shared_memory
shared_memory.set("key", "value") # 设置共享内存
value = shared_memory.get("key") # 获取共享内存
```
源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py)

View File

@ -1,73 +0,0 @@
---
title: Development Guide
order: 0
---
<script setup>
import ContributorBar from '../../components/ContributorBar.vue'
</script>
# Development Guide
## How to Develop
Please read the [Plugin Development](./plugin) and [API Reference](./api/) documents if you are developing a plugin.
If you need to submit code to the Liteyuki repository, please read the following:
1. First [fork](https://github.com/LiteyukiStudio/LiteyukiBot/fork) a copy of the Liteyuki repository to your personal/organization account.
2. Develop in your repository.
3. Create a new branch in your repository and submit your code to this branch.
4. Create a Pull Request in your repository to merge your branch into the `main` branch of the Liteyuki repository.
- Participation in development assumes that you are already familiar with the basic usage of the Python language and the Liteyuki framework.
- If you are developing documentation, please ensure that you are familiar with Markdown syntax and basic front-end knowledge.
- Contact the repository maintainer in case of conflicts.
### Suggestions
- During development, you can use tools such as `mypy`, `flake8`, and `black` for code checking and formatting.
- Enable developer mode by setting `dev_mode: true` in `config.yml` to see more debugging information in the console.
## Project Structure
- `main.py`: Bot entry file
- `liteyuki`: Framework directory (liteyukibot)
- `src`: Bot application source code directory
- `liteyuki_plugins`: Liteyuki plugin directory
- `nonebot_plugins`: Nonebot plugin directory
- `resources`: Built-in resource packs directory
- `utils`: Utility directory
- `tests`: Unit test directory
- `docs`: Documentation directory
- `en`: English documentation
- `zh`: Chinese documentation
- `docker`: Docker related content directory
The `liteyuki` framework is included in the `LiteyukiBot` application, so this project is both the `LiteyukiBot` application repository and the `liteyuki` framework repository.
The `src` directory is the application part, please develop business logic in this directory. `liteyuki_plugins` and `nonebot_plugins` directories are plugin directories, please develop plugins in these directories.
## Standardization
- Code should follow [`PEP8`](https://pep8.org/) and [`Google Python Style Guide`](https://google.github.io/styleguide/pyguide.html).
- In addition, code submitted to the Liteyuki repository should follow the following guidelines:
- Ensure that the code is runnable and harmless.
- Ensure that the type hints in the code are correct.
- Ensure that the comment style is [`Google Docstring`](https://google.github.io/styleguide/pyguide.html) or
[`Liteyuki Docstring`](https://github.com/LiteyukiStudio/litedoc?tab=readme-ov-file#liteyuki-docstring)(recommended) to ensure that Litedoc can parse and generate documentation correctly.
- Strings should be enclosed in double quotes `"` as much as possible, and single quotes `'` should be used when nesting strings.
- If there is a part for ordinary users, ensure that the documentation is complete (each language has corresponding documentation).
- commit message should follow the following guidelines:
- You should commit the code after completing a feature or fixing a bug, and not mix the code of multiple features or bugs together.
- We use gitmoji to mark the type of commit, such as `:sparkles:` for introducing new features, `:bug:` for fixing bugs, etc., please refer to [gitmoji](https://gitmoji.dev/) for details.
- The format of the commit message is `<emoji> [type:] <message>`, such as `:sparkles: feat: add new field to event`,
where the type field is the type of commit, not much is required, but please fill in as much as possible; the message field is a brief description, in summary, what you did.
- There is no restriction on the language of the commit message, and bilingual use is possible if conditions permit.
- The documentation should follow [`Markdown`](https://www.markdownguide.org/) syntax and support vitepress-related content:
- Revise the documentation for each language when editing.
- Ensure that the links in the document are correct and do not lead to inaccessible links.
- Ensure that the **User Documentation** is easy to understand and the **Development Documentation** is detailed.
## Finally
- This project is a non-profit open-source project, and we welcome anyone to participate in development. Your contributions will make Liteyuki better.
<ContributorBar />

View File

@ -1,72 +0,0 @@
---
title: Liteyuki Function
order: 2
---
# **轻雪函数**
轻雪函数 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都会被截停销毁
> [!tip]
> 编写轻雪函数推荐你使用VS Code插件[Liteyuki Function](https://github.com/LiteyukiStudio/lyfunctionTextmate)实现语法高亮

View File

@ -1,80 +0,0 @@
---
title: Liteyuki Plugin
order: 3
---
# 简介
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
## 开始
### 创建插件
一个`.py`文件或一个包含`__init__.py`的文件夹即可被识别为插件
首先创建一个文件夹,例如`watchdog_plugin`,并在其中创建一个`__init__.py`文件,即可创建一个插件
`__init__.py`
```python
from liteyuki.plugin import PluginMetadata, PluginType
from .watch_dog import * # 导入逻辑部分
# 定义插件元数据
__plugin_meta__ = PluginMetadata(
name="NoneDog", # 插件名称
version="1.0.0", # 插件版本
description="A simple plugin for nonebot developer", # 插件描述
type=PluginType.SERVICE # 插件类型
)
# 你的插件代码
...
```
### 编写逻辑部分
轻雪主进程不涉及聊天部分,因此插件主要是一些后台任务或者与聊天机器人的通信
以下我们会编写一个简单的插件用于开发NoneBot时进行文件系统变更重载
`watch_dog.py`
```python
import os
from liteyuki.dev import observer # 导入文件系统观察器
from liteyuki import get_bot, logger # 导入轻雪Bot和日志
from watchdog.events import FileSystemEvent # 导入文件系统事件
liteyuki = get_bot() # 获取唯一的轻雪Bot实例
exclude_extensions = (".pyc", ".pyo") # 排除的文件扩展名
# 用observer的on_file_system_event装饰器监听文件系统事件
@observer.on_file_system_event(
directories=("src/nonebot_plugins",),
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path) and os.path.isfile(event.src_path)
)
def restart_nonebot_process(event: FileSystemEvent):
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
liteyuki.restart_process("nonebot") # 调用重启进程方法
```
### 加载插件
#### 方法1
- 在配置文件中的`liteyuki.plugins`中添加你的插件路径,例如`watchdog_plugin`,重启轻雪即可加载插件。
#### 方法2
- 使用开发工具快速运行插件,无需手动创建实例
- 创建入口文件,例如`main.py`,并在其中写入以下代码
```python
from liteyuki.dev.plugin import run_plugins
run_plugins("watchdog_plugin")
```
然后运行`python main.py`即可启动插件
启用插件后我们在src/nonebot_plugins下创建一个文件例如`test.py`并在其中写入一些代码保存后轻雪会自动重载NoneBot进程

View File

@ -1,51 +0,0 @@
---
title: Resource Pack
order: 1
---
# 简介
资源包,亦可根据用途称为主题包、字体包、语言包等,它允许你一定程度上自定义轻雪的外观,并且不用修改源代码
- [资源/主题商店](../store/resource)提供了一些资源包供你选择,你也可以自己制作资源包
- 资源包的制作很简单,如果你接触过`Minecraft`的资源包,那么你能够很快就上手,仅需按照原有路径进行文件替换即可,讲起打包成一个新的资源包。
- 部分内容制作需要一点点前端基础,例如`html``css`
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
- 欢迎各位投稿资源包到轻雪资源商店
请注意主题包中的html渲染使用Js来规定数据的渲染位置请确保您所编写的html代码能被Bot解析否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码以避免出现无法预料的问题。
---
## 加载资源包
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
```shell
main.py
resources
└─resource_pack_1
├─metadata.yml
├─templates
└───...
└─resource_pack_2
├─metadata.yml
└─...
```
- 你自己制作的资源包也应该遵循这个规则,并且应该在`metadata.yml`中填写一些信息
- 若没有`metadata.yml`文件,则该文件夹不会被识别为资源包
```yaml
name: "资源包名称"
version: "1.0.0"
description: "资源包描述"
# 你可以自定义一些信息,但请保证以上三个字段
...
```
- 资源包加载遵循一个优先级即后加载的资源包会覆盖前面的资源包例如你在A包中定义了一个`index.html`文件B包也定义了一个`index.html`文件那么加载B包后A包中的`index.html`文件会被覆盖
- 对于不同资源包的不同文件是可以相对引用的例如你在A中定义了`templates/index.html`在B中定义了`templates/style.css`可以在A的`index.html`中用`./style.css`相对路径引用B中的css
> [!tip]
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包

View File

@ -1,63 +0,0 @@
---
layout: home
hero:
name: "LiteyukiBot"
tagline: "An integrated chat bot application and management framework"
actions:
- theme: brand
text: 🚀 Get Started
link: ./deploy/install
- theme: alt
text: 📖 Manual
link: ./usage/basic
- theme: alt
text: 📦 Extensions
link: ./store/
- theme: alt
text: 🛠️ Development
link: ./dev/guide
image:
light: /liteyuki.svg
dark: /liteyuki-dark.svg
alt: Liteyuki Logo
features:
- title: Ecological Diversity
icon: 🛠️
details: Based on the liteyuki framework, compatible with any Python bot framework and application
link: ./dev/api
- title: Pluggable
icon: 🧩
details: Use package manager for convenient management of plugins and resource packs
link: ./store/plugin
- title: Customizable
icon: 🎨
details: Fully customize the appearance using resource packs
link: ./store/resource
- title: Localization
icon: 🌐
details: Support for multiple languages through resource packs
link: https://baike.baidu.com/item/i18n/6771940
- title: Easy to use
icon: ⚙️
details: No complicated pre-process, ready to use out of the box
link: ./deploy/config
- title: High Performance
icon: 🚀
details: 500 plugins, starts within 2 seconds
- title: Rolling Updates
icon: ☁️
details: Keep your robot up to date with the latest commits
- title: Open Source
icon: 📄
details: The project follows the LSO LICENSE and is open to contributions
link: https://github.com/LiteyukiStudio/LiteyukiStudioOpensourceLICENSE
---

View File

@ -1,6 +0,0 @@
---
title: Plugin Store
order: 2
---
<PluginStore />

View File

@ -1,6 +0,0 @@
---
title: Resource Store
order: 1
---
<ResStore />

View File

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

View File

@ -1,122 +0,0 @@
---
title: 基础插件
order: 1
---
# 基础插件
---
> [!tip]
> **参数**`<param>`为必填参数,`[option]`为可选参数。
>
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。
## **轻雪命令`liteyuki_command`**
| 命令 | 说明 | 权限 | 举例 | 可用参数 |
| :----------------------------------------: | :---------------------------------------------------------------------------------------------: | :----------------------------------------: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------: |
| `reload-liteyuki` | 重载轻雪 | 超级用户 | ❌ | ❌ |
| `update-liteyuki` | 更新轻雪 | 超级用户 | ❌ | ❌ |
| `liteecho` | 查看当前bot 版本 | 超级用户 | ❌ | ❌ |
| `status` | 查看统计信息和状态 | 超级用户 | ❌ | ❌ |
| `config set <key> value` | 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的 | 超级用户 | `config set name 'liteyuki-bot'` | `<key>`: 若存在则覆盖, 若不存在则创建于`config.yml` ; `value`: yml格式的所有合法内容 |
| `config get [key] ` | 查询配置项不带key返回配置项列表推荐私聊使用 | 超级用户 | `config get name` | `<key>`: 若存在则返回, 若不存在则返回空 |
| `switch-image-mode ` | 在普通图片和Markdown大图之间切换该功能需要commit:505468b及以后的Lagrange.OneBot默认普通图片 | 超级用户 | `switch-image-mode` | ❌ |
| `/api api_name [args] ` | 调用机器人API | 超级用户 | `/api get_group_member_list group_id=1234567` | `<args>`: 参数列表, 格式为onebot v11协议api, 可用%20代替空格 |
| `/function function_name [args] [kwargs] ` | 调用机器人函数(`.lyfunction`语法) | 超级用户 | `/function send_group_msg group_id=1234567 message='hello'` | `<args>``<kwargs>`: 参数列表, api格式为onebot v11协议api |
| group enable/disable [group_id] | 在群聊启用/停用机器人group_id仅超级用户可用 | 超级用户,群聊仅群主、管理员、超级用户可用 | `group enable 1145141919810` | `<group_id>`: 群号 |
| liteyuki-docs | 查看轻雪文档 | 所有人 | ❌ | ❌ |
---
### **命令别名**
| 命令 | 别名 |
| :---------------: | :----------------------------------: |
| status | 状态 |
| reload-liteyuki | 重启轻雪 |
| update-liteyuki | 更新轻雪 |
| reload-resources | 重载资源 |
| config | 配置, `set` 设置 / `get` 查询 |
| switch-image-mode | 切换图片模式 |
| liteyuki-docs | 轻雪文档 |
| group | 群聊, `enable` 启用 / `disable` 停用 |
---
## **插件/包管理器 `liteyuki_pacman`**
- 插件管理
| 命令 | 说明 | 权限 |
| :-----------------------------------------------------: | :----------------------------------------: | :----------------------------------------------: |
| `npm update` | 更新插件商店索引 | 超级用户 |
| `npm install <plugin_name>` | 安装插件 | 超级用户 |
| `npm uninstall <plugin_name>` | 卸载插件 | 超级用户 |
| `npm search <keywords...>` | 通过关键词搜索插件 | 超级用户 |
| `npm enable-global/disable-global <plugin_name>` | 全局启用/停用插件 | 超级用户 |
| `npm enable/disable <plugin_name> [--group <group_id>]` | 当前会话启用/停用插件 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
| `npm list [page] [num]` | 列出所有插件 page为页数num为每页显示数量 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
| `help <plugin_name>` | 查看插件帮助 | 所有人 |
- 资源包管理
| 命令 | 说明 | 权限 |
| :----------------------: | :------------------------------------------: | :------: |
| `rpm list [page] [num]` | 列出所有资源包 page为页数num为每页显示数量 | 超级用户 |
| `rpm load <pack_name>` | 加载资源包 | 超级用户 |
| `rpm unload <pack_name>` | 卸载资源包 | 超级用户 |
| `rpm change <pack_name>` | 修改优先级 | 超级用户 |
| `rpm reload` | 重载所有资源包 | 超级用户 |
### 命令别名
| 命令 | 别名 |
| :--------------: | :------: |
| `npm` | 插件管理 |
| `update` | 更新 |
| `install` | 安装 |
| `uninstall` | 卸载 |
| `search` | 搜索 |
| `enable` | 启用 |
| `disable` | 停用 |
| `enable-global` | 全局启用 |
| `disable-global` | 全局停用 |
| `rpm` | 资源包 |
| `load` | 加载 |
| `unload` | 卸载 |
| `change` | 更改 |
| `reload` | 重载 |
| `list` | 列表 |
| `help` | 帮助 |
> [!warning]
> 受限于NoneBot2钩子函数的依赖注入参数插件停用只能阻断传入响应对于主动推送的插件不生效请阅读插件主页的说明。
>
---
## **用户管理`liteyuki_user`**
| 命令 | 说明 | 权限 |
| :-------------------------: | :----------------------------: | :----: |
| `profile` | 查看用户信息菜单 | 所有人 |
| `profile set <key> [value]` | 设置用户信息或打开属性设置菜单 | 所有人 |
| `profile get <key>` | 获取用户信息 | 所有人 |
###命令别名
| 命令 | 别名 |
| :-------: | :------: |
| `profile` | 个人信息 |
| `set` | 设置 |
| `get` | 查询 |

View File

@ -1,68 +0,0 @@
---
title: 功能命令
order: 2
---
# 功能插件命令
### **轻雪天气`liteyuki_weather`**
查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
配置项
```yaml
weather_key: "" # 和风天气的天气key会自动判断key版本
```
命令
```shell
weather <keywords...> # Keywords为城市名支持中英文
```
查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
```shell
bind-city <keywords...> # Keywords为城市名支持中英文
```
绑定查询城市,个人全局生效
#### 命令别名
| 命令 | 别名 |
| :-------: | :------- |
| weather | 天气 |
| bind-city | 绑定城市 |
---
### **统计信息`liteyuki_statistics`**
统计信息
命令
```shell
statistic message --duration <duration> --period <period> --group [current|group_id] --bot [current|bot_id]
```
功能: 用于统计Bot接收到的消息, 统计周期为`period`, 统计时间范围为`duration`
| 参数 | 格式 |
| :------: | :------------------------------------------------------------: |
| duration | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
| period | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
| group | `current` (当前群聊) 或 `group_id` (QQ群号) |
| bot | `current` (当前Bot) 或 `bot_id` |
#### 命令别名
| 命令 | 别名 |
| :----------: | :---: |
| `statistic` | stat |
| `message` | m |
| `--duration` | -d |
| --period` | -p |
| `--group` | -g |
| `--bot` | -b |
| `current` | c |

View File

@ -1,20 +0,0 @@
{
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^4.0.0",
"sass-embedded": "^1.78.0",
"vitepress": "^1.3.4",
"vitepress-sidebar": "^1.25.3"
},
"scripts": {
"docs:dev": "vitepress dev --host",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"type": "module",
"dependencies": {
"clipboard": "^2.0.11",
"vue": "^3.4.38",
"vue-route": "^1.5.1",
"vue-router": "4"
}
}

1926
docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,151 +0,0 @@
<svg width="618" height="419" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient y2="0.49996" x2="1.00023" y1="0.49996" x1="0.00001" data-name="未命名的渐变 11" id="_未命名的渐变_11">
<stop stop-color="#73fff1" offset="0"/>
<stop stop-color="#5cefff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_11" y2="457.99" x2="869.05" y1="457.99" x1="834.98" data-name="未命名的渐变 11" id="_未命名的渐变_11-2"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="377" x2="870" y1="377" x1="838" data-name="未命名的渐变 11" id="_未命名的渐变_11-3"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="425.24" x2="981.27" y1="425.24" x1="894.96" data-name="未命名的渐变 11" id="_未命名的渐变_11-4"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="447.26" x2="1070.42" y1="447.26" x1="986.62" data-name="未命名的渐变 11" id="_未命名的渐变_11-5"/>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 240" id="_未命名的渐变_240">
<stop stop-color="#eadeff" offset="0"/>
<stop stop-color="#e0efff" offset="0.28"/>
<stop stop-color="#d9ecff" offset="0.43"/>
<stop stop-color="#aedbff" offset="0.59"/>
<stop stop-color="#00d7ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50003" x2="1.00001" y1="0.50003" x1="-0.00002" data-name="未命名的渐变 9" id="_未命名的渐变_9">
<stop stop-color="#2ebbff" offset="0"/>
<stop stop-color="#006bff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_9" y2="632.72" x2="1127.05" y1="632.72" x1="1046.74" data-name="未命名的渐变 9" id="_未命名的渐变_9-2"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="559" x2="1262.04" y1="559" x1="1236.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-3"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="621.97" x2="1262.59" y1="621.97" x1="1235.37" data-name="未命名的渐变 9" id="_未命名的渐变_9-4"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="602.15" x2="1219.09" y1="602.15" x1="1133.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-5"/>
<linearGradient y2="0.5" x2="0.99994" y1="0.5" x1="0" data-name="未命名的渐变 12" id="_未命名的渐变_12">
<stop stop-color="#64a4ff" offset="0"/>
<stop stop-color="#5c98ff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_12" y2="629.45" x2="751.06" y1="629.45" x1="730.81" data-name="未命名的渐变 12" id="_未命名的渐变_12-2"/>
<linearGradient y2="-0.34151" x2="1.51066" y1="0.36026" x1="0.21368" data-name="未命名的渐变 72" id="_未命名的渐变_72">
<stop stop-color="#c4aeff" offset="0"/>
<stop stop-color="#ce9fff" offset="0.43"/>
<stop stop-color="#af8eff" offset="0.55"/>
<stop stop-color="#7d74ff" offset="0.72"/>
<stop stop-color="#6333ff" offset="0.9"/>
</linearGradient>
<linearGradient y2="0.4727" x2="1.00087" y1="0.52857" x1="0.01203" data-name="未命名的渐变 58" id="_未命名的渐变_58">
<stop stop-color="#b1ccff" offset="0"/>
<stop stop-color="#b8c0ff" offset="0.34"/>
<stop stop-color="#cba2ff" offset="0.94"/>
<stop stop-color="#ce9fff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50006" x2="1" y1="0.50006" x1="0.00045" data-name="未命名的渐变 13" id="_未命名的渐变_13">
<stop stop-color="#5cb1ff" offset="0"/>
<stop stop-color="#64aaff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_13" y2="640.89" x2="762.01" y1="640.89" x1="755.95" data-name="未命名的渐变 13" id="_未命名的渐变_13-2"/>
<linearGradient y2="0.46723" x2="1.08059" y1="0.5373" x1="0.11482" data-name="未命名的渐变 65" id="_未命名的渐变_65">
<stop stop-color="#64a2ff" offset="0.11"/>
<stop stop-color="#62a6ff" offset="0.28"/>
<stop stop-color="#5cb5ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.61559" x2="1.13197" y1="0.40965" x1="0.09495" data-name="未命名的渐变 66" id="_未命名的渐变_66">
<stop stop-color="#5cb5ff" offset="0"/>
<stop stop-color="#74e0ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.48885" x2="1.08173" y1="0.51016" x1="0.08139" data-name="未命名的渐变 68" id="_未命名的渐变_68">
<stop stop-color="#9cd1ff" offset="0"/>
<stop stop-color="#bae0ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50005" x2="1.00016" y1="0.50005" x1="0.00009" data-name="未命名的渐变 71" id="_未命名的渐变_71">
<stop stop-color="#a6d3ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.7"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_58" y2="631.98" x2="928.63" y1="638.79" x1="890.79" data-name="未命名的渐变 58" id="_未命名的渐变_58-2"/>
<linearGradient y2="0.50004" x2="1.00001" y1="0.50004" x1="-0.00008" data-name="未命名的渐变 14" id="_未命名的渐变_14">
<stop stop-color="#5ca4ff" offset="0"/>
<stop stop-color="#64a2ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.4999" x2="0.99997" y1="0.4999" x1="-0.00009" data-name="未命名的渐变 5" id="_未命名的渐变_5">
<stop stop-color="#64a2ff" offset="0"/>
<stop stop-color="#5cc9ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50013" x2="0.99983" y1="0.50013" x1="0.00007" data-name="未命名的渐变 69" id="_未命名的渐变_69">
<stop stop-color="#84c9ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.42"/>
</linearGradient>
<linearGradient y2="0.36565" x2="1.03665" y1="0.6159" x1="0.09061" data-name="未命名的渐变 67" id="_未命名的渐变_67">
<stop stop-color="#74e0ff" offset="0"/>
<stop stop-color="#8dd6ff" offset="0.56"/>
<stop stop-color="#9cd1ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 6" id="_未命名的渐变_6">
<stop stop-color="#64a2ff" offset="0"/>
<stop stop-color="#5cbfff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 7" id="_未命名的渐变_7">
<stop stop-color="#64ccff" offset="0"/>
<stop stop-color="#84c9ff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_7" y2="548.5" x2="837" y1="548.5" x1="831.98" data-name="未命名的渐变 7" id="_未命名的渐变_7-2"/>
<linearGradient xlink:href="#_未命名的渐变_7" y2="569.39" x2="846.98" y1="569.39" data-name="未命名的渐变 7" id="_未命名的渐变_7-3"/>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 8" id="_未命名的渐变_8">
<stop stop-color="#84c9ff" offset="0.01"/>
<stop stop-color="#b1ccff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="4.66625" y1="0.5" x1="4.61625" data-name="未命名的渐变 55" id="_未命名的渐变_55">
<stop stop-color="#b1c9ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.42"/>
</linearGradient>
</defs>
<g>
<title>Layer 1</title>
<g data-name="图层 3" id="_图层_3">
<path id="svg_1" fill="#7a9ec2" stroke-width="0px" d="m74.07715,375.0022c11.04,8.56 30.35,0.82 33,0c14.37,-4.43 15.38,-15.33 26,-15c10.44,0.33 13.16,10.98 24,10c6.48,-0.59 7.04,-4.53 16,-6c8,-1.31 8.66,1.66 17,1c12.76,-1.01 15.73,-8.33 24,-6c8.42,4.47 11.58,6.51 22,5c0,0 11.3,-1.75 32,-10c1.42,-9.07 4.72,6.12 11.99,12c10.1,9.09 19.95,-0.4 31,8c10.86,8.26 13.5,26.66 13,31c-1.65,4.39 6.37,10.16 6,10c-4.41,-0.77 -20.72,-10.82 -25,-11c-6.8,-1.26 -15.53,-2.26 -24,-5c-17.1,-5.24 -15.17,-10.82 -34,-10c-7.53,0.04 -6.69,-0.47 -12,1c-2.54,-0.23 -9.74,0.73 -13,-1c-18.29,-11.87 -9.94,11.78 -36,3c-9.47,1.86 -14.72,0.65 -18.02,-1.12c-2.21,-1.19 -4.61,-5.33 -6.74,-4.61c-0.57,0.19 -0.74,0.58 -2.24,1.74c-1.2,0.93 -2.27,1.58 -3,2c-0.91,0.36 -1.91,0.7 -3,1c-2.93,0.8 -5.59,1.02 -7.75,1.02c-0.74,0 -1.87,-0.01 -3.25,-0.02c-3.97,0 -3.61,0.03 -4,0c-2.87,-0.21 -3.75,-0.93 -7,-1c-0.42,0 -0.77,0 -1,0l-6,0c-3,0.22 -5.39,0.65 -7,1c-1.72,0.37 -3.36,0.82 -10,3c-3.07,1.01 -4.59,1.59 -6,2c-1.81,0.53 -5.13,1.49 -9,2c-5.07,0.67 -9,0.3 -12,0c-6.54,-0.65 -11.56,-2.18 -14,-3c-4.6,-1.55 -26.12,-13.37 -20,-10c0,0 0,0 0,0c-0.03,-0.02 -20.88,-13.37 -28,-32c-1.09,-2.85 -2,-6 -2,-6c-0.52,-1.8 -1.44,-5.03 -2,-9c-0.44,-3.12 -1.34,-9.53 0,-10c1.69,-0.59 6.23,8.51 7.27,10.48c5.15,9.76 18.12,22.54 52.73,35.52l0.01,-0.01z" class="cls-10"/>
<path id="svg_2" fill="#7a9ec2" stroke-width="0px" d="m260.07715,284.0022c-13.63,7.43 -2.93,57.29 2,57c1.82,-0.11 1.13,-6.97 8,-16c3,-3.94 3.81,-3.37 5,-6c0.88,-3.8 2.06,-11.66 -2,-19c-0.61,-1.11 -1.29,-2.11 -2,-3c-0.31,-0.36 -0.8,-1.04 -1,-2c-0.06,-0.32 -0.19,-0.91 0,-1c0.26,-0.13 0.82,0.87 2,2c0.84,0.81 1.71,1.61 3,2c1.06,0.32 1.94,0.17 3,0c1.38,-0.23 2.37,-0.69 3,-1c1.31,-0.64 2.25,-1.09 3,-2c1.84,-2.22 1.14,-5.41 1,-6c-0.33,-1.36 -0.69,-1.29 -2,-4c-0.87,-1.8 -0.57,-1.55 -2,-5c-0.49,-1.19 -1,-2.79 -2,-6c-0.12,-0.39 -0.37,-1.22 0,-2c0,0 0.29,-0.6 1,-1c1.98,-1.11 11.6,0.55 15,1c2.66,0.35 13.92,1.61 15.83,2.95c0.42,0.3 0.8,0.64 1.17,1.05c2.43,2.73 0.93,4.31 3,9c3.83,8.69 13.16,12.57 19,15c5.09,2.12 8.54,2.5 13,3c6.22,0.69 7.53,-0.22 8,-1c0.74,-1.23 -0.27,-2.7 -1,-5c-1.53,-4.78 -0.3,-8.09 0,-12c0.51,-6.64 -1.77,-13.74 -4,-17c-9.26,-13.54 -41.29,4.77 -71,-2c-1.28,-0.29 -9.89,-2.3 -12,1c-1.87,2.94 2.99,7.55 1,12c-1.49,3.33 -5.5,3.63 -8,5z" class="cls-10"/>
<path id="svg_3" fill="#7a9ec2" stroke-width="0px" d="m310.07715,118.0022c-22.42,19.49 74.7,203.11 32,245c-9.08,8.91 -26.9,13.94 -28,28c-0.65,8.33 4.78,18.3 13,23c13.88,7.94 28.83,2.05 33,-1c2.68,-1.96 4,-4 4,-4c1.44,-2.22 1.56,-3.91 3,-8c0.76,-2.17 1.5,-3.9 2,-5c0.43,-1.13 0.95,-2.86 1,-5c0.04,-1.88 -0.32,-2.29 -0.25,-4.13c0.04,-1.01 0.22,-5.22 2.25,-5.87c0.27,-0.09 1.3,-0.35 3,1c-0.17,-0.41 -0.33,-0.87 -0.21,-0.95c0.24,-0.16 1.16,1.42 2.21,1.95c2.87,1.44 7.56,-0.07 8,-2c-10.79,-5.85 -14.4,-6.84 -15,-6c-0.79,1.11 3.49,5.56 3,6c-0.37,0.33 -3.05,-1.93 -3,-2c0.05,-0.07 3.08,1.97 4,3c0.08,0.09 0.44,0.51 1,1c0.23,0.2 0.43,0.36 0.55,0.46c0.1,0.12 0.26,0.31 0.45,0.54c2.9,3.62 4.2,7.01 4.62,8.05c0.92,2.28 2.67,5.77 6.38,10.95c15.05,16.54 34.83,19.18 54,9c16.33,3.81 7.85,10.54 33,5c25.9,-4.5 50.39,1.98 49,-25c-1.26,-10.66 -5.86,-19.02 0,-10c8.68,14.17 21.69,39.51 43,27c1.34,-1.14 2.88,-2.66 4.33,-4.64c2.87,-3.92 4.1,-7.81 4.67,-10.36c0.04,-0.43 0.28,-2.3 1.9,-3.7c1.23,-1.06 2.58,-1.26 3.1,-1.3c0.01,0.08 0.16,1 -0.41,1.5c-0.6,0.53 -1.8,0.43 -2.59,-0.5c0.43,2.03 1.75,6.85 6,11c1.06,1.04 7.1,6.7 16,6c9.28,-0.73 15.02,-7.84 17,-12c1.58,-3.32 2,-7 2,-7c0.25,-2.19 0.1,-3.79 0,-5c-0.36,-4.65 0,-9.33 0,-14l0,-27c0.04,-1.66 0.03,-8.44 0,-22c0,0 -0.42,-5.41 -3,-9c-0.96,-1.33 -2,-2 -2,-2c-0.29,-0.19 -0.31,-0.16 -2,-1l-2,-1l-2,-1c-0.51,-0.2 -1.22,-0.52 -2,-1c-4.12,-2.53 -6.05,-6.97 -6,-7c0.05,-0.03 2.59,3.37 2,4c-0.16,0.18 -0.53,0.07 -1,0c-1.2,-0.18 -1.99,-1.02 -2,-1c-0.02,0.03 0.5,0.41 3,2c0.12,-0.38 0.34,-0.98 0.76,-1.61c1.31,-1.93 3.18,-2.14 5.24,-3.39c1.95,-1.18 3.03,-2.66 4,-4c0.61,-0.85 2.43,-3.41 3,-7c0.69,-4.36 -0.85,-7.8 -1.68,-9.65c-3.9,-8.74 -12.88,-12.43 -15.32,-13.35c-0.13,0.06 -1.05,0.47 -2,0c-0.61,-0.3 -0.9,-0.8 -1,-1c-0.59,-2.25 -2.17,-3.18 -3,-6c-1.01,-2.71 -2.29,-10.67 -2,-12c0.57,-5.41 2.39,-8.84 4,-11c2.1,-2.82 3.85,-3.49 6,-7c1.06,-1.73 2.73,-4.47 3,-8c0.59,-7.63 -5.74,-13.78 -7,-15c-2.59,-2.51 -5.28,-3.88 -7,-6c-0.29,-0.36 -1.32,-1.69 -3.17,-2.63c-0.4,-0.2 -0.68,-0.31 -0.83,-0.37c-2.54,-1.08 -3.91,-5.69 -4,-6c-1.92,-6.71 -2.5,-10.84 -4,-16c-1.04,-3.56 -1.16,-2.94 -2,-6c-1.68,-6.11 -1.81,-10.75 -2,-14c-0.59,-9.95 -1.18,-14.9 -3,-20c0,0 -2.12,-5.94 -7,-12c-15.98,-19.87 -78.67,-21.15 -79,-18c-0.19,1.79 19.52,6.96 19,9c-0.13,0.51 -1.4,1.23 -3.15,3.43c0,0 0,0 0,0c-2.67,4.24 -1.37,8.35 -7.85,14.57c-10.42,10 -22.57,7.8 -34,13c-5.27,2.29 -9.81,3.41 -13,4c-5.08,0.94 -7.67,1.39 -10,0c-2.43,-1.45 -1.99,-3.22 -6,-8c-0.59,-0.71 -2.4,-2.82 -5,-5c-4.84,-4.06 -6.07,-3.05 -9,-6c-1.97,-1.98 -1.39,-2.41 -6,-10c-2.23,-3.66 -3.34,-5.49 -4,-6c-3.01,-2.31 -5.67,-1.38 -15,-3c-2.88,-0.5 -2.43,-0.55 -5,-1c-11.13,-1.94 -15.79,-0.28 -26,-2c-1.7,-0.29 -4.35,-0.8 -7.57,-1.74c-7.85,-9.54 -30.21,6.06 -40.43,13.74z" class="cls-10"/>
<path id="svg_4" fill="#7a9ec2" stroke-width="0px" d="m294.07715,64.0022c-4.66,0.36 -8.11,1.64 -10.79,3.57c-12.46,8.98 -7.88,22.29 -21.35,37.56c-8.93,10.12 -22.26,16.35 -26.86,15.87c-1.25,-0.13 -5.27,-1.1 -8,1c-0.59,0.45 -1.25,1.15 -2,1c-1.15,-0.23 -1.71,-2.29 -2,-4c0,0 1,-2 1,-2c2.92,-5.84 2.97,-5.95 3,-6c2,-4.02 5.56,-11.16 5,-17c-0.84,-8.77 -11.08,-15.9 -20,-18c-12.28,-2.89 -22.17,3.73 -24,5c-12.11,2.27 -15.97,0.27 -17.33,-1.85c-1.94,-3.02 0.84,-6.86 5.33,-19.16c3.86,-10.58 5.89,-16.13 6,-22c0.1,-5.49 -1.43,-13.73 -10,-24c-23.21,-14.8 -47,-13.43 -56,-2c-1.46,1.85 -5.64,7.92 -4,20c-3.3,4.02 -9.31,1.14 -20,4c-10.63,1.96 -20.69,9.46 -13,23c5.63,9.91 94.53,145.36 192.09,115.49c50.17,-15.36 72.52,-65.2 72.91,-66.49c0.03,-0.1 0.51,-1.68 0,-2c-0.15,-0.1 -0.3,-0.03 -1,0l-3,0c-1.55,0 -3.44,-0.77 -4,-1c-0.06,-0.02 -0.12,-0.05 -0.2,-0.08c-1.67,-0.72 -4.91,-2.37 -4.8,-2.92c0.11,-0.56 3.88,-0.54 16,1c-0.74,-0.29 -1.76,-0.66 -3,-1c-1.48,-0.41 -3.48,-0.96 -6,-1c-2.59,-0.05 -2.83,0.27 -5,0c-0.92,-0.12 -2.41,-0.3 -4,-1c-4.1,-1.8 -5.85,-5.64 -6,-6c-0.77,-1.78 -0.86,-3.52 -1,-7c-0.1,-2.49 0.1,-2.7 0,-5c-0.14,-3.15 -0.22,-5.06 -1,-7c-1.47,-3.67 -4.66,-5.59 -7,-7c-2.96,-1.78 -5.54,-2.42 -8,-3c-2.73,-0.65 -6.85,-1.32 -12,-1l0,0.01z" class="cls-10"/>
</g>
<g data-name="背景" id="_背景">
<path id="svg_5" fill="#666" stroke-width="0px" d="m199.07715,72.0022c-16.42,-3.08 -26.21,8.1 -31,4c-7.63,-6.54 18.33,-33.97 9,-55c-6.88,-15.51 -30.12,-21.13 -45,-21c-3.33,0.03 -16.49,0.44 -31,9c-30.76,18.15 -37.62,55.27 -56,109c-20.03,58.55 -43.71,77.3 -45,119c-0.4,13.04 0.88,31.37 1,33c2.91,40.64 9.7,47.97 12,64c0.27,1.85 0.98,7.34 5,12c0.83,0.96 1.59,1.64 2.01,2c0.47,0.48 1.15,1.18 1.99,2c10.84,10.63 27.33,20.18 27.33,20.18c17.89,10.12 26.84,15.18 35.66,13.82c3.19,-0.49 6.88,-1.68 16.62,-3.27c3.46,-0.56 6.46,-0.79 12.39,-1.73c3.95,-0.63 7.36,-0.89 10,-1c3.64,-0.15 6.36,-0.03 7,0c4.66,0.22 8.01,0.84 9,1c3.81,0.61 10.14,0.32 21,-4c3.96,0.13 4.67,2.49 9,4c4.59,1.6 5.73,-0.37 15,0c16.7,2.72 12.4,-0.95 25,-4c5.48,-0.56 5.78,1.97 14,3c9.86,2.45 36.35,-8.51 48,1c6.44,5.27 9.1,4.24 16,6c7.05,3.78 15.85,4.35 23,8c18.2,11.17 34.14,20.96 51,2c5.59,-6.98 3.83,-12.43 8,-14c7.13,-2.81 31.59,30.21 58,23c15.36,-6.57 13.31,-4.51 28,1c5.68,1.04 23.32,-3.84 33,-4c12.6,-0.87 15.51,0.92 21,-2c11.5,-6.32 8.35,-12.82 9,-21c6.52,3 17.82,31.94 43,18c6.28,-4.14 7.41,-10.27 11,-10c3.81,0.29 3.43,7.28 9,11c7.22,4.82 20.84,1.79 26,-6c1.88,-2.84 1.95,-5.28 2,-16c0.06,-12.86 2.5,-34.29 1,-57c-0.27,-4.14 -0.62,-6.12 -2,-8c-1.81,-2.47 -4.68,-3.81 -7.73,-8.57c-0.11,-0.18 -0.21,-0.33 -0.27,-0.43c0,-0.08 -0.02,-0.19 -0.02,-0.33c0.03,-1.6 1.77,-2.75 2.36,-3.17c1.19,-0.85 2.8,-2.4 4.66,-5.5c10.77,-33.99 -34.41,-21.61 -20,-59c3.12,-6.89 6.55,-6.37 8,-12c2.32,-9.04 -4.5,-18.26 -8,-23c-5.99,-8.1 -8.37,-6.45 -17,-17c-2.93,-3.58 -6.84,-8.36 -9.5,-15.5c-2.32,-6.24 -2.48,-11.05 -2.5,-12.5c0,-0.42 0,-0.77 0,-1c0.13,-4.73 -0.28,-14.34 -6,-24c-7.76,-13.12 -28.18,-18.94 -40,-22c-15.86,-4.11 -39.47,-10.22 -51,1c-3.37,3.28 -5.98,8.31 -13,11c-5.03,1.93 -10.55,1.97 -14,2c-4.92,0.04 -8.59,-0.58 -11,-1c-1.86,-0.32 -10.82,-1.96 -17,-6c-0.12,-0.08 -0.17,-0.11 -0.32,-0.21c-0.99,-0.63 -3.29,-1.88 -9.18,-3.72c-18.42,-5.77 -40.67,-7.51 -55.5,-8.07c-2.82,-0.11 -3.62,-0.51 -8,-1c-2.91,-0.33 -4.55,-0.47 -7,-1c-3.02,-0.65 -4.54,-0.98 -6,-2c-2.14,-1.49 -3.17,-3.66 -4,-6c-3.13,-8.82 -0.7,-12.16 -3,-17c-2.84,-5.97 -10.15,-7.96 -14,-9c-2.47,-0.67 -11.2,-2.68 -20,2c-1.88,1 -6.08,3.3 -9,8c-2.37,3.82 -1.56,5.46 -4,14c-2.08,7.28 -3.18,10.97 -6,13c-1.17,0.85 -5.1,3.27 -13,1c-3.07,-0.18 -7.45,0 -12,2c-8.86,3.88 -12,11.82 -14,11c-2.79,-1.15 5.52,-15.64 0,-28c-5.58,-12.5 -22.22,-15.85 -23,-16z" class="cls-6"/>
<g data-name="主体" id="_主体">
<path fill="url(#_未命名的渐变_11)" stroke-width="0px" d="m143.07715,31.0022c-0.07,-9.92 -10.48,-14.93 -19.79,-10.14c-26.5,20.43 -60.22,177.47 -46.22,199.15l0,0c0.82,1.48 2.09,3.31 4,5c0.71,0.63 3.63,3.11 8.06,4.22c0.06,0.02 0.12,0.03 0.15,0.04c0,0 0.25,0 0.25,0c6.98,0.1 42.56,0.64 49.53,0.74c8.08,0 14.43,-3.25 15.96,-8.03c1.6,-4.98 -2.25,-10.37 -3.96,-11.97c-10.75,-10.03 -32.41,-2.79 -46.53,-8.99c1.03,-45.36 25.88,-124.06 38.53,-170.01l0.02,-0.01z" class="cls-3" id="lite-l"/>
<path fill="url(#_未命名的渐变_11-2)" stroke-width="0px" d="m196.07715,130.0022c4.11,0.31 6.91,2.96 8,4c6.83,6.48 6.93,17.68 7,26c0,0.06 0,0.03 0,7c0,14.32 0,21.48 0,22c0.04,13.27 0.66,35.34 -4,42c-1.29,1.84 -3,3 -3,3c-2.97,2.01 -6.17,1.99 -8,2c-1.45,0 -7.74,-0.12 -13,-5c-6.34,-5.87 -6.06,-13.87 -6,-15c0,-0.07 0,-9.93 0,-10c0,-38.04 0,-37.85 0,-38c-0.02,-1.29 -0.01,-3.53 0,-8c0.03,-9.9 0.15,-12.06 1,-15c1.28,-4.42 3.15,-6.95 4,-8c1.1,-1.36 2.19,-2.34 3,-3c0.7,-0.58 1.7,-1.32 3,-2c0.98,-0.52 4.28,-2.28 8,-2z" class="cls-16" id="lite-i-1"/>
<path fill="url(#_未命名的渐变_11-3)" stroke-width="0px" d="m193.85715,86.0722c8.85,-0.96 18.71,7.52 18.2,17.09c-0.5,9.28 -10.62,16.63 -19.33,14.46c-6.93,-1.73 -13.7,-9.65 -12.51,-18.4c1.06,-7.79 7.93,-12.52 13.65,-13.14l-0.01,-0.01z" class="cls-31" id="lite-i-1-1"/>
<path fill="url(#_未命名的渐变_11-4)" stroke-width="0px" d="m270.07715,109.0022c-5.46,4.9 -13.26,-2.45 -24,3c-1.76,0.89 -7.94,4.02 -9,10c-0.32,7.28 1.09,10.38 7,14c1.09,0.46 1.2,0.26 6,1c5.06,0.78 4.17,0.82 6,1c2.65,0.64 5.81,0.21 8,2c-1.37,3.47 -2.84,7.85 -4,13c-1.54,6.84 -1.9,12.21 -2,14c-0.55,9.95 0.87,18.04 1.98,22.78c2.44,10.5 5.56,15.48 9.02,22.22c2.16,4.21 19.54,19.38 30,8c4.48,-5.11 2.13,-12.61 2,-13c-1.5,-4.54 -4.19,-4.73 -8,-10c-2.14,-2.97 -3.9,-6.54 -6,-15c0,0 -3.62,-14.55 -1,-31c0.07,-0.45 0.7,-4.15 3,-7c0.34,-0.42 0.64,-0.72 1,-1c2.08,-1.58 4.79,-1.16 6,-1c0,0 2.62,0.34 11,0c2.39,-0.1 5.82,-0.51 10,-2c1.2,-0.76 4.96,-3.37 6,-8c1.27,-5.69 -2.25,-11.34 -6,-14c-1.62,-1.15 -7.64,-4.84 -14,-5c-0.75,-0.02 -2.43,-0.01 -3,-1c-0.71,-1.24 0.71,-3.39 2,-5c17.38,-27.36 -15.06,-42.63 -26,-19c-5.75,12.08 -1.75,17.19 -6,21z" class="cls-4" id="lite-t"/>
<path fill="url(#_未命名的渐变_11-5)" stroke-width="0px" d="m362.07715,166.0022c-0.58,0.39 -5.22,3.65 -5,8c0.19,3.92 4.21,6.49 5,7c5.42,3.48 9.31,0.51 25,-1c10.15,-0.98 16.08,-0.47 21,-5c1.85,-1.71 2.74,-3.47 3,-4c2.71,-5.47 0.88,-11.23 0,-14c-3.43,-10.82 -12.5,-16.72 -16,-19c-7.86,-5.11 -15.39,-6.53 -19,-7c-4.34,-0.56 -13.27,-1.6 -23,3c-9.69,4.58 -14.55,12.09 -17,16c-1.5,2.4 -5.98,10.02 -7,21c-0.52,5.6 -1.49,16.1 5,26c5.1,7.79 12.21,11.23 16,13c9.03,4.23 17,4.1 23,4c9.99,-0.17 16.58,-2.61 23,-5c2,-0.74 5.17,-2.02 8,-5c1.68,-1.77 4.08,-4.29 4,-8c-0.07,-3.36 -2.16,-6.53 -5,-8c-3.53,-1.83 -7.16,-0.62 -9,0c-1.16,0.39 -1.55,0.69 -4,2c-3.13,1.68 -6.56,3.38 -8,4c-6.18,2.67 -12.7,1.44 -15,1c-3.93,-0.74 -9.92,-1.88 -14,-7c-2.99,-3.76 -3.69,-8.07 -4,-10c-0.23,-1.41 -0.55,-4.21 -0.03,-7.67c0.24,-1.6 0.98,-6.11 4.03,-10.33c0.37,-0.52 7.1,-9.49 18,-9c1.43,0.06 9.65,0.59 14,7c1.16,1.71 2.94,4.33 2,6c-2.22,3.93 -15.76,-4.29 -25,2z" class="cls-8" id="lite-e"/>
<path fill="url(#_未命名的渐变_240)" stroke-width="0px" d="m502.07715,162.0022l13.33,22l9.67,-15l-8,-20l4,-6l6,2l9,13l7,-11l9,1l3,7l-11,13l17,2l4,8l-5,4l-22,-4l-13.5,16.8l27.5,5.2l11,-14l7,1l3,7l-9,11l19,3l4,7l-4,5l-18,-3l7,14l-4,4l-8,-1l-10,-20l-40,-8l-2,-2l-20,-37l-23,-4l-3,-7l4,-5l16,3l-9,-15l4,-6l7,1l9,16l9,-10l7,2l3,6l-10,14z" class="cls-20" id="logo-snow"/>
<path fill="url(#_未命名的渐变_9)" stroke-width="0px" d="m344.07715,316.0022c-5.28,-2.41 -12.58,-6.43 -20,-13c-8.2,-7.27 -12.49,-14.08 -18,-23c-8.92,-14.43 -16.4,-26.53 -12,-35c2.45,-4.71 7.07,-6.66 7.07,-6.66c2.53,-1.08 5.74,-1.4 9.93,-0.34c15.19,5.99 14.64,39.18 45,55c8.67,-18.92 10.28,-33.18 10,-43c-0.12,-4.37 -0.74,-12.15 4,-16c0.82,-0.55 5.22,-3.4 10.85,-2.15c3.86,0.85 6.25,3.17 7.15,4.15c0.5,0.6 1.28,1.58 2.09,2.87c8.99,14.33 0.03,32.06 -11.49,69.45c-4.1,13.3 -6.65,21.58 -9.6,33.68c-5.81,23.86 -8.17,40.38 -20,51c-11.03,9.91 -22.32,0.03 -23,-9c-2.89,-15.02 23.13,-56.98 18,-68z" class="cls-27" id="yuki-y"/>
<path fill="url(#_未命名的渐变_9-2)" stroke-width="0px" d="m403.07715,323.0022c-0.54,-0.11 -5.36,-1.02 -9,2c-2.31,1.92 -3.04,4.55 -4,8c-0.74,2.66 -0.91,4.8 -1,6c-0.59,8.25 0,15 0,15c1.18,13.55 1.77,20.33 3,23c1.04,2.27 4.07,8.71 11,13c6.21,3.85 12.31,3.91 21,4c0.81,0 16.83,0.09 21,-4c0.22,-0.21 1.2,-1.26 2,-1c0.93,0.3 0.69,2.05 2,4c0,0 0.63,0.93 2,2c3.54,2.75 12.53,3.41 16,-1c0.62,-0.79 1.75,-2.64 2,-5c0.07,-0.68 0.05,-1.22 0.02,-1.76c-0.13,-2.75 -0.88,-4.83 -1.02,-5.24c-1.31,-3.73 -3.37,-15.69 -4,-23c-0.58,-6.71 0.42,-5.3 0,-19c-0.11,-3.63 -0.12,-9.19 -3,-15c-1.36,-2.75 -2.63,-4.09 -4,-5c-2.95,-1.96 -6.82,-2.22 -10,-1c-0.8,0.31 -3.48,1.34 -5,4c-1.36,2.37 -1.11,4.83 -1,6c0.66,6.98 0.45,14.01 1,21c0.72,9.13 0.57,11.51 -1,14c-1.8,2.86 -4.42,4.2 -6,5c-7.58,3.86 -15,0.82 -17,0c-2.54,-1.04 -3.49,-2.11 -4,-3c-0.74,-1.29 -0.88,-2.78 -1,-4c-0.13,-1.35 -0.05,-2.34 0,-3l0,-16c-0.19,-7.44 -0.28,-11.16 -2,-14c-0.52,-0.85 -3.02,-4.98 -8,-6z" class="cls-21" id="yuki-u"/>
<path fill="url(#_未命名的渐变_9-3)" stroke-width="0px" d="m591.07715,272.0022c-6.65,0.3 -12.55,6.12 -12,13c0.53,6.56 6.7,11.28 13,11c6.65,-0.3 12.55,-6.12 12,-13c-0.53,-6.56 -6.7,-11.28 -13,-11z" class="cls-2" id="yuki-i-1-1"/>
<path fill="url(#_未命名的渐变_9-4)" stroke-width="0px" d="m591.07715,303.0022c-15.96,1.04 -13.95,25.54 -13,36c1.45,14.24 -7.18,66.6 24,48c5.91,-7.41 -0.33,-25.41 1,-45c-2.16,-11.6 6.6,-38.49 -12,-39z" class="cls-23" id="yuki-i-1"/>
<path fill="url(#_未命名的渐变_9-5)" stroke-width="0px" d="m545.07715,299.0022c-0.26,-0.11 -0.66,-0.27 -1.17,-0.44c0,0 -4.46,-1.53 -9.91,-1c-4.47,0.44 -18.59,11.34 -31.93,31.43c-0.35,-10.53 -0.23,-21.87 0.56,-33.9c0.37,-5.55 0.85,-10.92 1.44,-16.1c0.26,-1.2 0.52,-2.94 0.41,-5.03c-0.09,-1.81 -0.25,-4.96 -2.27,-8.04c-0.56,-0.85 -2.9,-4.42 -7.52,-5.51c-5.61,-1.33 -10.01,2.08 -10.62,2.58c-3.44,2.76 -4.45,6.67 -5,9c-3.6,15.32 -3,27 -3,27c0.42,8.15 1.67,32.57 1,50c-0.22,5.66 0.05,11.33 0,17c-0.05,6.02 -0.13,9.05 1,13c1.45,5.07 4.08,8.75 6,11c1.19,1.11 3.93,3.36 8,4c3.99,0.63 7.79,-0.51 9.84,-1.85c11.06,-7.21 1.65,-39.59 4.55,-40.56c0.39,-0.13 1.19,0.88 2.61,3.4c8.89,11.22 14.99,15.39 30,29c7.7,10.13 23.45,3.59 22,-8c-0.19,-16.59 -39.62,-33.08 -31.91,-39.47c1.89,-2.38 6,-8.21 6.91,-9.53c5.47,-7.8 10.83,-8.44 13,-15c1.34,-4.04 0.69,-6.57 0.43,-7.38c-1.01,-3.15 -3.39,-4.94 -4.43,-5.62l0.01,0.02z" class="cls-28" id="yuki-k"/>
<path fill="url(#_未命名的渐变_12)" stroke-width="0px" d="m79.07715,274.0022c-1.75,-1 -2.61,-0.17 -10,1c-2.22,0.35 -7.11,1.06 -13,3c-1.36,0.45 -2.68,0.93 -3,2c-0.49,1.65 1.56,4.27 4,5c0.32,0.09 0.56,0.13 0.74,0.16c2.19,0.34 3.89,-0.56 5.26,-1.16c1.96,-0.87 3.64,-1.24 7,-2c0,0 4.44,-1 5,0c0.02,0.04 0.04,0.07 0.04,0.07c0.07,0.18 -0.02,0.5 -0.04,2.93c0,0.06 0,0.71 0,2c0,5.01 0.09,5.71 0,7c-0.2,2.74 -0.3,4.11 -1,5c-1.25,1.58 -3.23,1.52 -7,2c-5.62,0.72 -4.41,1.27 -10,2c-7.19,0.94 -9.49,0.07 -11,2c-1.32,1.68 -1.32,4.57 0,6c1.74,1.88 5.46,0.88 9,0c7.35,-1.82 19.55,-4.84 29,-6c4.81,-0.59 12,-1.09 13,-4c0.57,-1.65 0,-3 0,-3c-0.06,-0.14 -0.62,-1.43 -2,-2c-1.19,-0.49 -2.36,-0.18 -3,0c-6.78,1.91 -9.23,2.82 -10,2c-0.45,-0.48 -0.17,-1.39 0,-2c1.77,-6.29 1.22,-12.36 1,-14c-0.23,-1.67 -0.84,-6.18 -4,-8z" class="cls-24" id="lite-jp-1"/>
<path fill="url(#_未命名的渐变_12-2)" stroke-width="0px" d="m75.07715,330.0022c-1.02,0.34 -1.72,1.18 -2,2c-0.31,0.93 -0.06,1.78 0,2c0.38,1.39 0.25,7.26 0,19c-0.12,5.74 -0.36,7.73 0,12c0.21,2.49 0.58,5.11 2,8c0.87,1.76 1.83,3.72 4,5c2.08,1.23 4.09,1.14 7,1c2.69,-0.13 6.51,-0.31 7,-2c0.37,-1.27 -1.18,-3.23 -3,-4c-2.78,-1.18 -4.84,0.97 -7,0c-2.17,-0.97 -2.69,-4.29 -3,-7c-1.07,-9.45 -0.9,-24.04 -1,-30c-0.02,-1.36 -0.12,-5.21 -2,-6c-0.58,-0.25 -1.37,-0.21 -2,0z" class="cls-14" id="liteecho-1"/>
<path fill="url(#_未命名的渐变_72)" stroke-width="0px" d="m263.13715,280.9522c-2.11,0.2 -3.55,1.9 -3.93,2.35c-0.57,0.68 -0.91,1.34 -1.11,1.8c1.09,1.05 6.06,5.64 11.22,4.91c2.33,-0.33 5.1,-1.81 5.34,-2.63c0.03,-0.09 0.03,-0.17 0.03,-0.17c0,-0.29 -0.33,-0.71 -1.89,-1.83c-1.33,-0.96 -2.23,-1.61 -3.3,-2.18c-0.69,-0.37 -1.23,-0.6 -1.68,-0.79c-2.41,-1.04 -3.62,-1.55 -4.68,-1.45l0,-0.01z" class="cls-17" id="yuki-jp-4-1"/>
<path fill="url(#_未命名的渐变_58)" stroke-width="0px" d="m248.07715,265.0022c0.37,-0.49 1.34,-1.8 3,-2c2.33,-0.28 3.93,1.89 4,2c0.33,0.46 0.44,0.81 1,3c0.59,2.3 0.88,3.45 1,4c0.33,1.56 0.71,3.58 1,6c0.21,1.71 0.24,2.18 0.42,3.8c0.24,2.17 0.38,2.81 1.58,10.2c0.66,4.08 0.73,4.55 1,6c0.31,1.64 0.58,3.06 1,5c0.77,3.51 1.13,4.83 1,7c-0.08,1.29 -0.29,2.25 -1,3c-1,1.07 -2.67,1.39 -4,1c-1.46,-0.43 -2.18,-1.63 -3,-3c-0.55,-0.91 -1.1,-2.1 -2,-6c-0.28,-1.22 -0.65,-2.92 -1,-5c-0.33,-2 -0.67,-4 -1,-6c-0.53,-3.92 -1.07,-7.83 -1.6,-11.75c-0.47,-2.42 -0.93,-4.83 -1.4,-7.25c-0.78,-2.06 -0.97,-3.84 -1,-5c-0.05,-1.81 -0.12,-3.5 1,-5z" class="cls-13" id="yuki-jp-4"/>
<path fill="url(#_未命名的渐变_13)" stroke-width="0px" d="m101.07715,341.0022c-1.86,-0.04 -3.59,1.27 -4,3c-0.56,2.38 1.47,5.01 4,5c2.03,-0.01 4,-1.72 4,-4c0,-2.25 -1.92,-3.96 -4,-4z" class="cls-19" id="liteecho-i-1-1"/>
<path fill="url(#_未命名的渐变_13-2)" stroke-width="0px" d="m102.07715,354.0022c-0.61,-0.3 -1.38,-0.31 -2,0c-0.14,0.07 -0.62,0.33 -1,1c-1.18,2.08 -1.01,7.51 -1,8c0.16,5.45 0,9 0,9c-0.08,1.78 -0.22,3.7 1,5c0.21,0.22 0.94,1 2,1c1.08,0 1.81,-0.8 2,-1c1.05,-1.14 1.02,-2.72 1,-4c-0.03,-2.33 0.04,-4.67 0,-7c-0.05,-3.31 0.17,-8.95 -1,-11c-0.12,-0.2 -0.41,-0.71 -1,-1z" class="cls-29" id="liteecho-i-1"/>
<path fill="url(#_未命名的渐变_65)" stroke-width="0px" d="m119.55715,342.6422c-0.52,0.03 -1.3,0.07 -1.92,0.55c-0.25,0.19 -0.76,0.67 -1.1,2.68c-0.15,0.92 -0.29,2.26 -0.14,3.91c-1.17,0.01 -3.52,0.16 -5.32,1.22c-0.38,0.22 -0.77,0.51 -1,1c-0.39,0.85 -0.08,1.91 0.41,2.59c0.74,1.01 1.99,1.19 3.59,1.41c0.85,0.12 1.57,0.12 2.05,0.1c-0.06,0.76 -0.12,1.85 -0.14,3.16c-0.01,1.22 -0.03,2.48 0.14,4.12c0.15,1.49 0.3,2.92 0.96,4.62c0.39,1.03 0.78,1.67 1,2c0.25,0.39 0.74,1.08 1.48,1.76c1.12,1.05 2.67,2.5 4.52,2.24c1.2,-0.17 2.57,-1.06 2.76,-2.24c0.12,-0.76 -0.29,-1.46 -0.55,-1.92c-0.43,-0.76 -0.87,-0.99 -1.51,-1.65c-0.95,-0.97 -1.44,-1.93 -1.78,-2.61c-0.71,-1.41 -0.9,-2.61 -1.1,-3.85c-0.15,-0.97 -0.37,-2.35 -0.14,-4.12c0.09,-0.67 0.22,-1.22 0.32,-1.61c0.56,0 1.35,-0.04 2.29,-0.17c1.54,-0.21 2.32,-0.32 2.88,-0.82c0.8,-0.71 1.27,-2.03 0.83,-3.01c-0.13,-0.3 -0.38,-0.61 -1,-1c-1.52,-0.94 -3.22,-1.02 -4.36,-0.94c0.11,-0.49 0.27,-1.25 0.41,-2.2c0.49,-3.18 0.14,-3.81 -0.14,-4.12c-0.26,-0.3 -0.59,-0.43 -1.24,-0.69c-0.4,-0.16 -1.18,-0.47 -2.2,-0.41z" class="cls-9" id="liteecho-t"/>
<path fill="url(#_未命名的渐变_66)" stroke-width="0px" d="m142.48715,359.4922c-1.24,0 -1.82,0.11 -2.17,0.53c-0.63,0.76 -0.29,2.24 0.31,3.15c1.51,2.31 5.53,2.25 7.8,1.77c1.4,-0.3 4.49,-0.94 5.98,-3.77c1.7,-3.21 -0.04,-6.62 -0.4,-7.27c-1.86,-3.4 -5.47,-4.29 -7.14,-4.7c-3.37,-0.83 -6.16,0.05 -7.27,0.4c-1.07,0.34 -2.81,0.91 -4.53,2.4c-3.45,2.98 -3.92,7.09 -4,8c-0.27,3.05 0.7,5.35 1,6c0.27,0.6 1.17,2.42 3,4c1.04,0.89 2.16,1.53 3,2c0.99,0.56 1.75,0.89 2,1c1.02,0.44 1.88,0.81 3,1c1.17,0.2 2.07,0.1 3,0c0.34,-0.04 1.85,-0.22 4,-1c3.3,-1.2 4.59,-2.54 5,-3c0.47,-0.53 0.68,-0.9 0.71,-1.34c0.06,-1.15 -1.21,-2.31 -2.48,-2.62c-0.51,-0.12 -0.94,-0.08 -1.23,-0.05c-2.01,0.26 -3.04,1.28 -4.31,1.86c-2.59,1.19 -5.84,0.41 -7.69,-0.86c-1.16,-0.8 -1.95,-1.93 -2,-2c-0.26,-0.38 -0.69,-1.07 -1,-2c-0.26,-0.77 -0.95,-2.86 0,-5c0.82,-1.85 2.41,-2.69 3,-3c2.34,-1.23 5.06,-1.05 7,0c0.69,0.37 1.64,0.9 2,2c0.29,0.87 0.26,2.23 -0.52,2.84c-1,0.8 -2.36,-0.34 -6.07,-0.36l0.01,0.02z" class="cls-25" id="liteecho-e"/>
<path fill="url(#_未命名的渐变_68)" stroke-width="0px" d="m197.07715,346.0022c-1.16,0 -4.12,0.04 -7,2c-1.54,1.05 -2.49,2.26 -3,3c-1.62,2.38 -1.87,4.7 -2,6c-0.12,1.26 -0.36,4 1,7c2.14,4.72 6.61,6.52 8,7c1.04,0.36 4.93,1.64 9,0c0.94,-0.38 2.74,-1.13 4,-3c0.45,-0.66 1.99,-2.94 1,-5c-0.54,-1.12 -1.88,-2.25 -3,-2c-1.41,0.32 -1.04,2.48 -3,4c-1.92,1.48 -4.45,1.09 -5,1c-0.54,-0.08 -2.42,-0.41 -4,-2c-2.4,-2.42 -2.04,-5.66 -2,-6c0.07,-0.51 0.43,-3.37 3,-5c2.25,-1.43 5.33,-1.38 7,0c0.93,0.77 0.92,1.55 2,2c1.26,0.52 3.2,0.27 4,-1c0.72,-1.15 0.17,-2.56 0,-3c-0.15,-0.38 -0.69,-1.63 -3,-3c-0.98,-0.58 -3.45,-2.01 -7,-2z" class="cls-30" id="liteecho-c"/>
<path fill="url(#_未命名的渐变_71)" stroke-width="0px" d="m217.07715,329.0022c-0.92,-0.49 -2.08,-0.44 -3,0c-1.8,0.87 -1.99,2.91 -2,3l0,9c0,4 0.04,8 0,12c-0.03,3 0.1,6 0,9c-0.1,3.04 -0.29,5.38 1,8c0.45,0.91 1.42,2.89 3,3c1.63,0.11 2.9,-1.85 3,-2c1.44,-2.28 0.03,-4.1 0,-9c-0.02,-2.85 0,-5.82 2,-8c0.37,-0.4 1.8,-1.97 4,-2c2.44,-0.04 3.91,1.89 4,2c0.92,1.22 1,2.52 1,3c-0.33,1.67 -0.67,3.33 -1,5c-0.41,1.12 -0.91,2.85 -1,5c-0.09,2.3 -0.16,3.99 1,5c1.28,1.11 3.61,1.02 5,0c1.65,-1.21 1.8,-3.61 2,-7c0.08,-1.27 0.04,-2.31 0,-3l0,-8c-0.02,-0.84 -0.16,-2.36 -1,-4c-0.51,-1 -1.73,-2.93 -4,-4c-3.65,-1.72 -7.65,-0.14 -8,0c-1.5,0.62 -2.24,1.36 -3,1c-0.71,-0.34 -0.94,-1.4 -1,-4c-0.12,-5.04 0.61,-6.66 0,-10c-0.56,-3.06 -1.54,-3.76 -2,-4z" class="cls-18" id="liteecho-h"/>
<path fill="url(#_未命名的渐变_58-2)" stroke-width="0px" d="m252.07715,348.0022c-4.27,-0.18 -7.17,2.27 -8,3c-3.05,2.66 -3.79,6 -4,7c-0.18,0.88 -1.21,5.84 2,10c2.08,2.69 4.82,3.62 6,4c1.29,0.42 4.46,1.39 8,0c2.95,-1.16 4.51,-3.31 5,-4c1.43,-2.02 1.81,-3.97 2,-5c0.46,-2.51 0.1,-4.49 0,-5c-0.67,-3.35 -2.52,-5.47 -3,-6c-0.69,-0.76 -3.46,-3.8 -8,-4z" class="cls-33" id="liteecho-o"/>
<path fill="#666" stroke-width="0px" d="m250.57715,354.5022c-2.49,0.24 -3.75,2.55 -4,3c-0.09,0.17 -1.81,3.42 0,6c1.68,2.39 5.6,3.06 8,1c2.1,-1.8 2.13,-4.93 1,-7c-0.84,-1.54 -2.7,-3.22 -5,-3z" class="cls-6" id="liteecho-o-white"/>
<path fill="url(#_未命名的渐变_14)" stroke-width="0px" d="m108.07715,274.0022c-0.71,0.63 -1.32,1.81 -1,3c0.38,1.44 1.86,1.95 2,2c0.77,0.25 1.44,0.12 2,0c4.88,-1.04 5.31,-1.31 6,-1c1.18,0.54 1.54,2.08 2,4c0.41,1.72 0.73,3.08 0,4c-0.65,0.81 -2.11,0.89 -5,1c-1.25,0.05 -2.29,0.03 -3,0c-0.53,0.14 -1.96,0.61 -3,2c-0.4,0.54 -1.21,1.61 -1,3c0.24,1.62 1.7,2.78 3,3c0.44,0.08 0.79,0.03 1,0c4.59,-0.65 6.89,-0.98 7,-1c2.55,-0.43 3.84,-0.64 4.45,-0.34c1.26,0.62 1.41,1.86 2.55,5.34c0,0 1.48,4.5 4,10c0.8,1.75 1.53,3.13 2,4c0.16,0.13 2.42,1.9 5,1c0.16,-0.06 2.44,-0.88 3,-3c0.21,-0.78 0.1,-1.42 0,-2c-0.27,-1.54 -0.96,-2.26 -2,-4c-0.12,-0.21 0.03,0.04 -2,-4c-0.67,-1.33 -1.32,-2.67 -2,-4c-1.91,-3.76 -2.3,-4.3 -2,-5c0.2,-0.46 0.49,-0.53 4,-2c1.13,-0.48 2.45,-1.03 4,-2c1.13,-0.71 1.71,-1.21 2,-2c0.06,-0.17 0.35,-1.03 0,-2c-0.48,-1.32 -1.88,-2.03 -3,-2c-0.31,0 -0.54,0.07 -0.66,0.11c-2.2,0.63 -4.77,1.67 -5.34,1.89c-2.21,0.88 -3.31,1.31 -4,1c-0.42,-0.19 -0.77,-0.54 -2,-4c-1.21,-3.42 -1.11,-3.77 -1,-4c0.42,-0.85 1.37,-0.79 6,-2c2.46,-0.64 3.57,-1.02 4,-2c0.3,-0.68 0.14,-1.39 0,-2c-0.09,-0.4 -0.54,-2.17 -2,-3c-1.17,-0.66 -2.4,-0.38 -4,0c-3.65,0.88 -4.88,2.55 -6,2c-0.55,-0.27 -1.15,-1.13 -1,-4c-0.42,-2 -0.76,-3 -1,-3l0,0c-0.76,-1.48 -2.4,-2.29 -4,-2c-1.51,0.27 -2.7,1.47 -3,3c-0.17,2.45 0.41,4.01 1,5c0.57,0.95 1.14,1.36 1,2c-0.24,1.14 -2.45,1.64 -4,2c-2.38,0.55 -3.11,0.2 -4,1z" class="cls-34" id="lite-jp-2"/>
<path fill="url(#_未命名的渐变_5)" stroke-width="0px" d="m153.07715,286.0022c-0.46,1.12 -0.62,2.69 0,4c0.19,0.41 0.79,1.49 2,2c1.22,0.51 2.43,0.16 3,0c5,-1.43 7.51,-2.15 8,-2c4.42,1.34 3.64,20.08 7,22c2.14,1.22 4,1 4,1c1.43,-0.17 3.03,-0.97 3.02,-1c0,0 0,0 -0.02,0c0.27,-0.2 0.65,-0.52 1,-1c1.53,-2.12 0.85,-5.32 -1,-10c-3.3,-8.33 -6.24,-11.07 -5,-13c0.71,-1.1 1.84,-0.46 5.57,-2.36c2.51,-1.27 4.06,-2.06 4.43,-3.64c0.46,-1.98 -1.11,-4.46 -3,-5c-1.01,-0.29 -2.23,0.21 -4.65,1.22c-3.32,1.39 -3.58,2.02 -4.35,1.78c-2.25,-0.7 -0.9,-6.34 -4,-8c-1.22,-0.65 -2.84,-0.54 -4,0c-0.31,0.14 -1.56,0.73 -2,2c-0.24,0.7 -0.14,1.29 0,2c0.51,2.66 2.08,3.57 2,5.99c0,0 0,0 0,0c0.01,0 0.02,0 0.02,0c0.01,0.1 -10.37,-0.01 -12.02,4l0,0.01z" class="cls-5" id="yuki-jp-1"/>
<path fill="url(#_未命名的渐变_69)" stroke-width="0px" d="m230.07715,276.0022c-0.23,-0.2 -0.95,-0.74 -2,-1c-0.58,-0.14 -1.39,-0.34 -2,0c-0.79,0.44 -0.86,1.6 -1,3c0,0 -0.62,6.23 -2,9c-0.43,0.86 -0.97,1.66 -0.97,1.66c-0.12,0.18 -0.24,0.35 -0.35,0.5c-0.2,0.27 -0.42,0.55 -0.68,0.84c-1.47,1.68 -5.93,4.2 -5.93,4.2c-3.23,1.82 -3.66,2.04 -4.07,2.8c-0.61,1.13 -0.9,2.9 0,4c0.8,0.97 2.17,0.99 3,1c0.7,0 3.05,-0.07 7.17,-3.17c1.64,-1.24 3.78,-2.89 5.83,-5.83c3.15,-4.53 3.72,-8.76 4,-11c0.52,-4.2 -0.4,-5.48 -1,-6z" class="cls-32" id="yuki-jp-2-3"/>
<path fill="url(#_未命名的渐变_67)" stroke-width="0px" d="m167.88715,359.1822c-1.13,0.52 -1.6,0.87 -1.74,1.41c-0.25,0.96 0.69,2.15 1.62,2.72c2.35,1.44 5.96,-0.31 7.81,-1.71c1.14,-0.86 3.66,-2.76 3.81,-5.96c0.17,-3.63 -2.85,-5.97 -3.45,-6.41c-3.13,-2.29 -6.78,-1.55 -8.46,-1.22c-3.41,0.68 -5.55,2.66 -6.41,3.45c-0.83,0.76 -2.15,2.02 -3.08,4.1c-1.85,4.16 -0.53,8.09 -0.22,8.94c1.05,2.88 2.91,4.54 3.46,5c0.5,0.42 2.09,1.69 4.42,2.34c1.32,0.37 2.61,0.46 3.57,0.53c1.13,0.08 1.96,0.06 2.24,0.05c1.11,-0.04 2.04,-0.06 3.14,-0.37c1.14,-0.32 1.92,-0.79 2.71,-1.28c0.29,-0.18 1.58,-0.98 3.19,-2.61c2.48,-2.49 3.07,-4.25 3.25,-4.84c0.2,-0.68 0.24,-1.11 0.07,-1.51c-0.43,-1.07 -2.08,-1.58 -3.36,-1.31c-0.51,0.11 -0.88,0.32 -1.13,0.48c-1.71,1.09 -2.2,2.45 -3.11,3.52c-1.84,2.18 -5.11,2.86 -7.32,2.49c-1.39,-0.23 -2.59,-0.92 -2.66,-0.96c-0.4,-0.23 -1.08,-0.67 -1.76,-1.38c-0.56,-0.59 -2.08,-2.19 -2.13,-4.52c-0.04,-2.02 1.04,-3.46 1.44,-3.99c1.59,-2.11 4.14,-3.1 6.33,-2.98c0.79,0.04 1.86,0.12 2.66,0.96c0.63,0.67 1.18,1.91 0.74,2.79c-0.57,1.15 -2.28,0.69 -5.65,2.26l0.02,0.01z" class="cls-15" id="liteecho-e2"/>
</g>
<circle id="svg_6" fill="url(#_未命名的渐变_6)" stroke-width="0px" r="4" cy="301.0022" cx="162.07715" class="cls-22"/>
<ellipse id="svg_7" fill="url(#_未命名的渐变_7)" stroke-width="0px" ry="2.5" rx="2.51" cy="270.5022" cx="183.56715" class="cls-12"/>
<ellipse id="svg_8" fill="url(#_未命名的渐变_7-2)" stroke-width="0px" ry="2.5" rx="2.51" cy="273.5022" cx="176.56715" class="cls-26"/>
<circle id="svg_9" fill="url(#_未命名的渐变_7-3)" stroke-width="0px" r="4" cy="294.3922" cx="185.05715" class="cls-11"/>
<circle id="svg_10" stroke-width="0px" fill="url(#_未命名的渐变_8)" r="4" cy="281.2622" cx="209.49715" class="cls-1"/>
<circle id="svg_11" fill="url(#_未命名的渐变_55)" stroke-width="0px" r="4" cy="275.2622" cx="217.49715" class="cls-7"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,148 +0,0 @@
<svg width="617" height="419" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient y2="0.50001" x2="0.99992" y1="0.50001" x1="-0.00009" data-name="未命名的渐变 11" id="_未命名的渐变_11">
<stop stop-color="#73fff1" offset="0"/>
<stop stop-color="#5cefff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_11" y2="292.99" x2="584.05" y1="292.99" x1="549.98" data-name="未命名的渐变 11" id="_未命名的渐变_11-2"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="212" x2="585" y1="212" x1="553" data-name="未命名的渐变 11" id="_未命名的渐变_11-3"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="260.3" x2="698.34" y1="260.3" x1="609.82" data-name="未命名的渐变 11" id="_未命名的渐变_11-4"/>
<linearGradient xlink:href="#_未命名的渐变_11" y2="282.24" x2="784.94" y1="282.24" x1="702.75" data-name="未命名的渐变 11" id="_未命名的渐变_11-5"/>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 240" id="_未命名的渐变_240">
<stop stop-color="#eadeff" offset="0"/>
<stop stop-color="#e0efff" offset="0.28"/>
<stop stop-color="#d9ecff" offset="0.43"/>
<stop stop-color="#aedbff" offset="0.59"/>
<stop stop-color="#00d7ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50003" x2="1.00001" y1="0.50003" x1="0" data-name="未命名的渐变 9" id="_未命名的渐变_9">
<stop stop-color="#2ebbff" offset="0"/>
<stop stop-color="#006bff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_9" y2="467.72" x2="842.05" y1="467.72" x1="761.74" data-name="未命名的渐变 9" id="_未命名的渐变_9-2"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="394" x2="977.04" y1="394" x1="951.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-3"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="456.86" x2="977.56" y1="456.86" x1="952.19" data-name="未命名的渐变 9" id="_未命名的渐变_9-4"/>
<linearGradient xlink:href="#_未命名的渐变_9" y2="437.15" x2="934.09" y1="437.15" x1="848.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-5"/>
<linearGradient y2="0.5" x2="0.99994" y1="0.5" x1="0" data-name="未命名的渐变 4" id="_未命名的渐变_4">
<stop stop-color="#6445ff" offset="0"/>
<stop stop-color="#5c86ff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_4" y2="464.45" x2="466.06" y1="464.45" x1="445.81" data-name="未命名的渐变 4" id="_未命名的渐变_4-2"/>
<linearGradient y2="-0.23832" x2="1.47603" y1="0.46345" x1="0.17906" data-name="未命名的渐变 72" id="_未命名的渐变_72">
<stop stop-color="#c4aeff" offset="0"/>
<stop stop-color="#ce9fff" offset="0.43"/>
<stop stop-color="#af8eff" offset="0.55"/>
<stop stop-color="#7d74ff" offset="0.72"/>
<stop stop-color="#6333ff" offset="0.9"/>
</linearGradient>
<linearGradient y2="0.47289" x2="0.99937" y1="0.52818" x1="0.02548" data-name="未命名的渐变 58" id="_未命名的渐变_58">
<stop stop-color="#b1ccff" offset="0"/>
<stop stop-color="#b8c0ff" offset="0.34"/>
<stop stop-color="#cba2ff" offset="0.94"/>
<stop stop-color="#ce9fff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50005" x2="1" y1="0.50005" x1="0.00045" data-name="未命名的渐变 3" id="_未命名的渐变_3">
<stop stop-color="#5c86ff" offset="0"/>
<stop stop-color="#64a2ff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_3" y2="475.89" x2="477.01" y1="475.89" x1="470.95" data-name="未命名的渐变 3" id="_未命名的渐变_3-2"/>
<linearGradient y2="0.4675" x2="1.0805" y1="0.53756" x1="0.11473" data-name="未命名的渐变 65" id="_未命名的渐变_65">
<stop stop-color="#64a2ff" offset="0.11"/>
<stop stop-color="#62a6ff" offset="0.28"/>
<stop stop-color="#5cb5ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.61626" x2="1.13145" y1="0.41032" x1="0.09443" data-name="未命名的渐变 66" id="_未命名的渐变_66">
<stop stop-color="#5cb5ff" offset="0"/>
<stop stop-color="#74e0ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.48885" x2="1.08173" y1="0.51016" x1="0.08139" data-name="未命名的渐变 68" id="_未命名的渐变_68">
<stop stop-color="#9cd1ff" offset="0"/>
<stop stop-color="#bae0ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50005" x2="1.00016" y1="0.50005" x1="0.00009" data-name="未命名的渐变 71" id="_未命名的渐变_71">
<stop stop-color="#a6d3ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.7"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_58" y2="466.98" x2="643.63" y1="473.79" x1="605.79" data-name="未命名的渐变 58" id="_未命名的渐变_58-2"/>
<linearGradient xlink:href="#_未命名的渐变_3" y2="397.6" x2="514.17" y1="397.6" x1="479.91" data-name="未命名的渐变 3" id="_未命名的渐变_3-3"/>
<linearGradient y2="0.4999" x2="0.99997" y1="0.4999" x1="-0.00009" data-name="未命名的渐变 5" id="_未命名的渐变_5">
<stop stop-color="#64a2ff" offset="0"/>
<stop stop-color="#5cc9ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.50013" x2="0.99983" y1="0.50013" x1="0.00007" data-name="未命名的渐变 69" id="_未命名的渐变_69">
<stop stop-color="#84c9ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.42"/>
</linearGradient>
<linearGradient y2="0.36502" x2="1.03701" y1="0.61527" x1="0.09096" data-name="未命名的渐变 67" id="_未命名的渐变_67">
<stop stop-color="#74e0ff" offset="0"/>
<stop stop-color="#8dd6ff" offset="0.56"/>
<stop stop-color="#9cd1ff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 6" id="_未命名的渐变_6">
<stop stop-color="#64a2ff" offset="0"/>
<stop stop-color="#5cbfff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 7" id="_未命名的渐变_7">
<stop stop-color="#64ccff" offset="0"/>
<stop stop-color="#84c9ff" offset="1"/>
</linearGradient>
<linearGradient xlink:href="#_未命名的渐变_7" y2="383.5" x2="552" y1="383.5" x1="546.98" data-name="未命名的渐变 7" id="_未命名的渐变_7-2"/>
<linearGradient xlink:href="#_未命名的渐变_7" y2="404.39" x2="561.98" y1="404.39" data-name="未命名的渐变 7" id="_未命名的渐变_7-3"/>
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 8" id="_未命名的渐变_8">
<stop stop-color="#84c9ff" offset="0.01"/>
<stop stop-color="#b1ccff" offset="1"/>
</linearGradient>
<linearGradient y2="0.5" x2="4.66625" y1="0.5" x1="4.61625" data-name="未命名的渐变 55" id="_未命名的渐变_55">
<stop stop-color="#b1c9ff" offset="0"/>
<stop stop-color="#b1ccff" offset="0.42"/>
</linearGradient>
</defs>
<g>
<title>Layer 1</title>
<g data-name="图层 3" id="_图层_3">
<path id="svg_1" fill="#9cf" stroke-width="0px" d="m74.07715,375.0022c11.04,8.56 30.35,0.82 33,0c14.37,-4.43 15.38,-15.33 26,-15c10.44,0.33 13.16,10.98 24,10c6.48,-0.59 7.04,-4.53 16,-6c8,-1.31 8.66,1.66 17,1c12.76,-1.01 15.73,-8.33 24,-6c8.42,4.47 11.58,6.51 22,5c0,0 11.3,-1.75 32,-10c1.42,-9.07 4.72,6.12 11.99,12c10.1,9.09 19.95,-0.4 31,8c10.86,8.26 13.5,26.66 13,31c-1.65,4.39 6.37,10.16 6,10c-4.41,-0.77 -20.72,-10.82 -25,-11c-6.8,-1.26 -15.53,-2.26 -24,-5c-17.1,-5.24 -15.17,-10.82 -34,-10c-7.53,0.04 -6.69,-0.47 -12,1c-2.54,-0.23 -9.74,0.73 -13,-1c-18.29,-11.87 -9.94,11.78 -36,3c-9.47,1.86 -14.72,0.65 -18.02,-1.12c-2.21,-1.19 -4.61,-5.33 -6.74,-4.61c-0.57,0.19 -0.74,0.58 -2.24,1.74c-1.2,0.93 -2.27,1.58 -3,2c-0.91,0.36 -1.91,0.7 -3,1c-2.93,0.8 -5.59,1.02 -7.75,1.02c-0.74,0 -1.87,-0.01 -3.25,-0.02c-3.97,0 -3.61,0.03 -4,0c-2.87,-0.21 -3.75,-0.93 -7,-1c-0.42,0 -0.77,0 -1,0l-6,0c-3,0.22 -5.39,0.65 -7,1c-1.72,0.37 -3.36,0.82 -10,3c-3.07,1.01 -4.59,1.59 -6,2c-1.81,0.53 -5.13,1.49 -9,2c-5.07,0.67 -9,0.3 -12,0c-6.54,-0.65 -11.56,-2.18 -14,-3c-4.6,-1.55 -26.12,-13.37 -20,-10c0,0 0,0 0,0c-0.03,-0.02 -20.88,-13.37 -28,-32c-1.09,-2.85 -2,-6 -2,-6c-0.52,-1.8 -1.44,-5.03 -2,-9c-0.44,-3.12 -1.34,-9.53 0,-10c1.69,-0.59 6.23,8.51 7.27,10.48c5.15,9.76 18.12,22.54 52.73,35.52l0.01,-0.01z" class="cls-32"/>
<path id="svg_2" fill="#9cf" stroke-width="0px" d="m260.07715,284.0022c-13.63,7.43 -2.93,57.29 2,57c1.82,-0.11 1.13,-6.97 8,-16c3,-3.94 3.81,-3.37 5,-6c0.88,-3.8 2.06,-11.66 -2,-19c-0.61,-1.11 -1.29,-2.11 -2,-3c-0.31,-0.36 -0.8,-1.04 -1,-2c-0.06,-0.32 -0.19,-0.91 0,-1c0.26,-0.13 0.82,0.87 2,2c0.84,0.81 1.71,1.61 3,2c1.06,0.32 1.94,0.17 3,0c1.38,-0.23 2.37,-0.69 3,-1c1.31,-0.64 2.25,-1.09 3,-2c1.84,-2.22 1.14,-5.41 1,-6c-0.33,-1.36 -0.69,-1.29 -2,-4c-0.87,-1.8 -0.57,-1.55 -2,-5c-0.49,-1.19 -1,-2.79 -2,-6c-0.12,-0.39 -0.37,-1.22 0,-2c0,0 0.29,-0.6 1,-1c1.98,-1.11 11.6,0.55 15,1c2.66,0.35 13.92,1.61 15.83,2.95c0.42,0.3 0.8,0.64 1.17,1.05c2.43,2.73 0.93,4.31 3,9c3.83,8.69 13.16,12.57 19,15c5.09,2.12 8.54,2.5 13,3c6.22,0.69 7.53,-0.22 8,-1c0.74,-1.23 -0.27,-2.7 -1,-5c-1.53,-4.78 -0.3,-8.09 0,-12c0.51,-6.64 -1.77,-13.74 -4,-17c-9.26,-13.54 -41.29,4.77 -71,-2c-1.28,-0.29 -9.89,-2.3 -12,1c-1.87,2.94 2.99,7.55 1,12c-1.49,3.33 -5.5,3.63 -8,5z" class="cls-32"/>
<path id="svg_3" fill="#9cf" stroke-width="0px" d="m310.07715,118.0022c-22.42,19.49 74.7,203.11 32,245c-9.08,8.91 -26.9,13.94 -28,28c-0.65,8.33 4.78,18.3 13,23c13.88,7.94 28.83,2.05 33,-1c2.68,-1.96 4,-4 4,-4c1.44,-2.22 1.56,-3.91 3,-8c0.76,-2.17 1.5,-3.9 2,-5c0.43,-1.13 0.95,-2.86 1,-5c0.04,-1.88 -0.32,-2.29 -0.25,-4.13c0.04,-1.01 0.22,-5.22 2.25,-5.87c0.27,-0.09 1.3,-0.35 3,1c-0.17,-0.41 -0.33,-0.87 -0.21,-0.95c0.24,-0.16 1.16,1.42 2.21,1.95c2.87,1.44 7.56,-0.07 8,-2c-10.79,-5.85 -14.4,-6.84 -15,-6c-0.79,1.11 3.49,5.56 3,6c-0.37,0.33 -3.05,-1.93 -3,-2c0.05,-0.07 3.08,1.97 4,3c0.08,0.09 0.44,0.51 1,1c0.23,0.2 0.43,0.36 0.55,0.46c0.1,0.12 0.26,0.31 0.45,0.54c2.9,3.62 4.2,7.01 4.62,8.05c0.92,2.28 2.67,5.77 6.38,10.95c15.05,16.54 34.83,19.18 54,9c16.33,3.81 7.85,10.54 33,5c25.9,-4.5 50.39,1.98 49,-25c-1.26,-10.66 -5.86,-19.02 0,-10c8.68,14.17 21.69,39.51 43,27c6.44,-4.99 5.9,-9.29 8,-16c1.22,-1.48 4.53,-4.38 6,-4c0.01,0.08 0.16,1 -0.41,1.5c-0.6,0.53 -1.8,0.43 -2.59,-0.5c2.97,10.52 12.31,17.02 21,16c7.93,-0.94 13.74,-10.13 14.27,-11.06c0.54,-0.96 0.98,-1.97 0.98,-1.97s0.41,-0.95 0.75,-1.97c1.19,-3.64 1.46,-7.4 1.46,-7.4c0.88,-11.86 0.98,-15.27 0.98,-15.27c0.35,-11.02 0.5,-9.78 0.67,-17.66c0.19,-8.34 -0.04,-7.03 0.13,-13.62c0,0 0.55,-21.79 -3.25,-26.05c-0.48,-0.54 -1,-1 -1,-1c-1.02,-0.91 -1.79,-1.28 -3,-2c0,0 -1,-0.67 -3,-2c-1,-0.67 -2.08,-1.22 -3,-2c-1.25,-1.06 -1.85,-1.94 -3,-2c-0.39,-0.02 -0.52,0.07 -1,0c-1.2,-0.18 -1.99,-1.02 -2,-1c-0.02,0.03 0.5,0.41 3,2c0.1,-0.62 1.27,-2.67 2,-3c5.54,-4.98 5.58,-5.38 8,-10c6.12,-15.03 -2.92,-19.21 -14,-26c-1.23,-1.16 -1.87,-0.9 -3,-1c-0.59,-2.25 -2.17,-3.18 -3,-6c-1.01,-2.71 -2.29,-10.67 -2,-12c0.57,-5.41 2.39,-8.84 4,-11c2.1,-2.82 3.85,-3.49 6,-7c1.22,-2 2.84,-4.65 3,-8c0.25,-5.38 -3.39,-9.63 -8,-15c-2.47,-2.87 -5.06,-5.17 -6,-6c-2.42,-2.13 -4.22,-3.42 -5,-4c-5.14,-3.8 -8.31,-10.94 -9.7,-14.15c-5.32,-12.26 -7.9,-23.86 -8.3,-32.85c-1.17,-26.5 -38.54,-51.77 -63,-35c-7.04,5.65 -2.81,10.14 -11,18c-10.42,10 -22.57,7.8 -34,13c-5.71,3.43 -10.18,0.22 -16,-2c-9.95,-3.09 -18.5,-13.48 -24,-22c-1.52,-1.57 -3.98,-3.73 -7.79,-5.64c-30.14,-4.8 -47.97,-7.45 -51.21,-7.36c-0.78,0.02 -4.27,0.18 -7.57,-1.74c-7.85,-9.54 -30.21,6.06 -40.43,13.74l0.01,0z" class="cls-32"/>
<path id="svg_4" fill="#9cf" stroke-width="0px" d="m294.07715,64.0022c-4.66,0.36 -8.11,1.64 -10.79,3.57c-13.35,9.62 -7.67,24.47 -21.35,37.56c-3.88,3.71 -4.86,2.87 -33.86,12.87c-0.06,0.02 -2.46,0.85 -4,0c-0.23,-0.13 -0.75,-0.41 -1,-1c-0.23,-0.54 -0.15,-1.16 -0.12,-1.33c0.02,-0.16 0.09,-0.51 0.12,-0.67l0,0c1.24,-1.71 8.11,-11.55 4.97,-21.02c-3.76,-11.34 -20.93,-19.28 -39.97,-12.98c-12.11,2.27 -15.97,0.27 -17.33,-1.85c-1.94,-3.02 0.84,-6.86 5.33,-19.16c3.86,-10.58 5.89,-16.13 6,-22c0.1,-5.49 -1.43,-13.73 -10,-24c-23.21,-14.8 -47,-13.43 -56,-2c-1.46,1.85 -5.64,7.92 -4,20c-3.3,4.02 -9.31,1.14 -20,4c-10.63,1.96 -20.69,9.46 -13,23c5.63,9.91 94.53,145.36 192.09,115.49c50.17,-15.36 72.52,-65.2 72.91,-66.49c0.03,-0.1 0.51,-1.68 0,-2c-0.15,-0.1 -0.3,-0.03 -1,0l-3,0c-1.55,0 -3.44,-0.77 -4,-1c-0.5,-0.2 -1.8,-0.75 -5,-3c-1.04,-0.73 -2.42,-1.73 -4,-3c-3.1,-0.72 -4.4,-1.96 -5,-3c-0.3,-0.52 -0.38,-0.9 -1,-2c-1.21,-2.14 -1.61,-1.92 -2,-3c-0.34,-0.93 -0.04,-1.09 0,-5c0,-0.35 0,-0.06 0,-2c0,-3.6 0.02,-3.67 0,-4c-0.12,-1.85 -0.9,-3.16 -2,-5c-2.63,-4.41 -6.25,-6.57 -7,-7c-1.67,-0.96 -2.99,-1.38 -5,-2c-2.56,-0.79 -6.28,-1.69 -11,-2l0,0.01z" class="cls-32"/>
</g>
<g data-name="背景" id="_背景">
<path id="svg_5" fill="#fff" stroke-width="0px" d="m199.07715,72.0022c-16.42,-3.08 -26.21,8.1 -31,4c-7.63,-6.54 18.33,-33.97 9,-55c-6.88,-15.51 -30.12,-21.13 -45,-21c-3.33,0.03 -16.49,0.44 -31,9c-30.76,18.15 -37.62,55.27 -56,109c-20.03,58.55 -43.71,77.3 -45,119c-0.4,13.04 0.88,31.37 1,33c2.91,40.64 9.7,47.97 12,64c0.27,1.85 0.98,7.34 5,12c0.83,0.96 1.59,1.64 2.01,2c0.47,0.48 1.15,1.18 1.99,2c10.84,10.63 27.33,20.18 27.33,20.18c17.89,10.12 26.84,15.18 35.66,13.82c3.19,-0.49 6.88,-1.68 16.62,-3.27c3.46,-0.56 6.46,-0.79 12.39,-1.73c3.95,-0.63 7.36,-0.89 10,-1c3.64,-0.15 6.36,-0.03 7,0c4.66,0.22 8.01,0.84 9,1c3.81,0.61 10.14,0.32 21,-4c3.96,0.13 4.67,2.49 9,4c4.59,1.6 5.73,-0.37 15,0c16.7,2.72 12.4,-0.95 25,-4c5.48,-0.56 5.78,1.97 14,3c9.86,2.45 36.35,-8.51 48,1c6.44,5.27 9.1,4.24 16,6c7.05,3.78 15.85,4.35 23,8c18.2,11.17 34.14,20.96 51,2c5.59,-6.98 3.83,-12.43 8,-14c7.13,-2.81 31.59,30.21 58,23c15.36,-6.57 13.31,-4.51 28,1c5.68,1.04 23.32,-3.84 33,-4c12.6,-0.87 15.51,0.92 21,-2c11.5,-6.32 8.35,-12.82 9,-21c6.52,3 17.82,31.94 43,18c6.28,-4.14 7.41,-10.27 11,-10c3.81,0.29 3.43,7.28 9,11c7.22,4.82 20.84,1.79 26,-6c1.88,-2.84 1.95,-5.28 2,-16c0.06,-12.86 2.5,-34.29 1,-57c-0.27,-4.14 -0.62,-6.12 -2,-8c-1.81,-2.47 -4.68,-3.81 -7.73,-8.57c-0.11,-0.18 -0.21,-0.33 -0.27,-0.43c0,-0.08 -0.02,-0.19 -0.02,-0.33c0.03,-1.6 1.77,-2.75 2.36,-3.17c1.19,-0.85 2.8,-2.4 4.66,-5.5c10.77,-33.99 -34.41,-21.61 -20,-59c3.12,-6.89 6.55,-6.37 8,-12c2.32,-9.04 -4.5,-18.26 -8,-23c-5.99,-8.1 -8.37,-6.45 -17,-17c-2.93,-3.58 -6.84,-8.36 -9.5,-15.5c-2.32,-6.24 -2.48,-11.05 -2.5,-12.5c0,-0.42 0,-0.77 0,-1c0.13,-4.73 -0.28,-14.34 -6,-24c-8.66,-14.64 -25.27,-22.26 -40,-22c-22.75,0.41 -39.01,19.59 -41,22c-4.52,5.48 -5.53,9.19 -11,12c-8.17,4.19 -19.82,3.24 -29,-1c-1.04,-0.48 -1.78,-0.88 -2,-1c-13.03,-7.1 -12.62,-18.04 -23,-25c-0.78,-0.52 -2.86,-1.85 -9.5,-3.93c-18.42,-5.77 -40.67,-7.51 -55.5,-8.07c-1.93,0.07 -4.76,-0.05 -8,-1c-3.67,-1.08 -6.36,-2.77 -8,-4c-5.34,-3.78 -6.82,-2.84 -9,-5c-3.59,-3.56 0.14,-10.39 -3,-17c-2.84,-5.97 -10.15,-7.96 -14,-9c-2.47,-0.67 -11.2,-2.68 -20,2c-1.88,1 -6.08,3.3 -9,8c-2.37,3.82 -1.56,5.46 -4,14c-2.08,7.28 -3.18,10.97 -6,13c-1.17,0.85 -5.1,3.27 -13,1c-3.07,-0.18 -7.45,0 -12,2c-8.86,3.88 -12,11.82 -14,11c-2.79,-1.15 5.52,-15.64 0,-28c-5.58,-12.5 -22.22,-15.85 -23,-16z" class="cls-18"/>
<g data-name="主体" id="_主体">
<path fill="url(#_未命名的渐变_11)" stroke-width="0px" d="m143.07715,31.0022c0.03,-1.1 -0.07,-2.97 -1,-5c-0.89,-1.93 -2.11,-3.1 -2.75,-3.7c-0.4,-0.38 -1.57,-1.42 -3.37,-2.29c-1.11,-0.53 -2.12,-0.83 -2.88,-1.01c-0.48,0 -1.16,0 -1.98,0.04c-1.4,0.09 -4.59,0.32 -7.8,1.81c-3.86,1.79 -6.04,4.59 -7.21,6.15c-1.03,1.36 -0.93,1.77 -4,8c-1.08,2.19 -1.79,3.57 -2,4c-5.9,11.79 -12,35 -12,35c-7.65,29.08 -7.7,25.76 -12,44c-2.8,11.87 -4.71,21.49 -6,28c-0.61,3.07 -2.21,11.25 -4,22c0,0 -1.57,9.87 -3,30c-0.1,1.34 -0.46,7.05 1,14c0.85,4.05 3,8 3,8l0,0c2.28,2.98 4.52,4.83 5.99,5.89c11.06,8 23.32,4.35 57.01,4.11c3.06,-0.02 5.53,-0.01 7,0c1.02,-0.28 2.51,-0.84 4,-2c0.52,-0.41 1.27,-1.06 2,-2c0.45,-0.58 1.59,-1.91 2,-4c0.21,-1.08 0.43,-2.87 0,-5c-0.36,-1.17 -0.7,-2.18 -1,-3c-0.3,-0.83 -0.53,-1.39 -1,-2c-0.36,-0.46 -0.73,-0.79 -1,-1c-0.24,-0.23 -0.76,-0.76 -1,-1c-1.73,-1.73 -1.87,-1.87 -2,-2c-1.25,-1.2 -2.82,-1.65 -4,-2c-5.84,-1.71 -23,-2 -23,-2c-4.12,-0.07 -10.14,-0.62 -17.53,-2.99c1.73,-17.19 5.56,-49.39 13.31,-81.46c4.95,-20.51 4.35,-18.09 8.22,-30.55c5.85,-18.81 8.77,-28.22 11,-34c0,0 3.24,-8.41 4.63,-15.69c0.44,-2.32 0.84,-4.8 0.84,-4.8c0.18,-1.17 0.35,-2.34 0.53,-3.5l-0.01,-0.01z" class="cls-3" id="lite-l"/>
<path fill="url(#_未命名的渐变_11-2)" stroke-width="0px" d="m196.07715,130.0022c4.11,0.31 6.91,2.96 8,4c6.83,6.48 6.93,17.68 7,26c0,0.06 0,0.03 0,7c0,14.32 0,21.48 0,22c0.04,13.27 0.66,35.34 -4,42c-1.29,1.84 -3,3 -3,3c-2.97,2.01 -6.17,1.99 -8,2c-1.45,0 -7.74,-0.12 -13,-5c-6.34,-5.87 -6.06,-13.87 -6,-15c0,-0.07 0,-9.93 0,-10c0,-38.04 0,-37.85 0,-38c-0.02,-1.29 -0.01,-3.53 0,-8c0.03,-9.9 0.15,-12.06 1,-15c1.28,-4.42 3.15,-6.95 4,-8c1.1,-1.36 2.19,-2.34 3,-3c0.7,-0.58 1.7,-1.32 3,-2c0.98,-0.52 4.28,-2.28 8,-2z" class="cls-15" id="lite-i-1"/>
<path fill="url(#_未命名的渐变_11-3)" stroke-width="0px" d="m193.85715,86.0722c8.85,-0.96 18.71,7.52 18.2,17.09c-0.5,9.28 -10.62,16.63 -19.33,14.46c-6.93,-1.73 -13.7,-9.65 -12.51,-18.4c1.06,-7.79 7.93,-12.52 13.65,-13.14l-0.01,-0.01z" class="cls-30" id="lite-i-1-1"/>
<path fill="url(#_未命名的渐变_11-4)" stroke-width="0px" d="m270.07715,109.0022c-5.46,4.9 -13.26,-2.45 -24,3c-1.76,0.89 -7.94,4.02 -9,10c-0.72,4.08 0.94,7.61 1.38,8.43c0.48,0.92 2.17,4.11 5.62,5.57c1.09,0.46 1.2,0.26 6,1c5.06,0.78 4.17,0.82 6,1c2.02,0.19 4,0.68 6,1c0.54,0.09 1.55,0.25 2,1c0.42,0.71 0.13,1.6 0,2c-0.61,1.93 -0.44,4.05 -1,6c-1.01,3.51 -0.87,8.39 -1,13c-0.29,10.62 -1.82,12.94 -2,20c-0.11,4.19 -0.42,18.97 9,31c1.49,1.9 10.06,12.84 21,12c1.28,-0.1 5.89,-0.45 9,-4c4.48,-5.11 2.13,-12.61 2,-13c-1.5,-4.54 -4.19,-4.73 -8,-10c-4,-5.53 -5.15,-11 -6,-15c-1.67,-7.89 -0.83,-14.05 0,-20c1.22,-8.76 3.02,-10.14 4,-16c0.13,-0.81 0.46,-2.94 2,-5c0,0 0.85,-1.13 2,-2c2.17,-1.64 8.68,-0.49 12.72,-0.42c3.25,0.06 5.06,0.09 7.28,-0.58c1.17,-0.35 8.31,-2.66 10,-9c1.37,-5.14 -1.51,-11.55 -6,-14c-3.8,-2.07 -12.48,-2.26 -15,-3c-1.03,-0.3 -1.9,-0.93 -2,-1c-0.47,-0.34 -0.82,-0.6 -1,-1c-0.56,-1.23 0.89,-2.87 1,-3c0.54,-0.96 1.27,-2.32 2,-4c1.51,-3.46 2.99,-6.85 3,-11c0,-1.91 0.01,-6.19 -3,-10c-3.3,-4.17 -8.02,-4.99 -9.42,-5.24c-0.92,-0.16 -6.37,-1 -11.58,2.24c-1.61,1 -4.15,3 -7,9c-5.75,12.08 -1.75,17.18 -6,21z" class="cls-4" id="lite-t"/>
<path fill="url(#_未命名的渐变_11-5)" stroke-width="0px" d="m362.07715,166.0022c-0.58,0.39 -5.22,3.65 -5,8c0.19,3.92 4.21,6.49 5,7c5.42,3.48 9.31,0.51 25,-1c10.15,-0.98 16.08,-0.47 21,-5c1.89,-1.74 2.72,-3.39 3,-4c2.32,-5.01 -0.29,-10.09 -1,-14c-1.83,-10.01 -12.11,-17.03 -15,-19c-1.39,-0.95 -8.43,-5.63 -19,-7c-4.47,-0.58 -13.46,-1.75 -23,3c-8.94,4.45 -13.31,11.6 -16,16c-5.84,9.56 -6.79,18.48 -7,21c-0.4,4.87 -1.09,14.86 5,25c4.96,8.26 11.87,12.39 15,14c8.24,4.25 15.6,4.12 23,4c9.99,-0.17 16.58,-2.61 23,-5c2,-0.74 5.17,-2.02 8,-5c1.68,-1.77 4.08,-4.29 4,-8c-0.07,-3.36 -2.16,-6.53 -5,-8c-2.95,-1.53 -5.9,-0.81 -9,0c-6.07,1.58 -6.81,4.01 -12,6c-6.49,2.49 -12.52,1.46 -15,1c-3.77,-0.69 -9.81,-1.81 -14,-7c-3.24,-4.01 -3.72,-8.41 -4,-11c-0.26,-2.43 -1.1,-10.07 4,-17c0.43,-0.59 7.21,-9.48 18,-9c1.43,0.06 9.65,0.59 14,7c1.16,1.71 2.94,4.33 2,6c-2.22,3.93 -15.76,-4.29 -25,2z" class="cls-7" id="lite-e"/>
<path fill="url(#_未命名的渐变_240)" stroke-width="0px" d="m502.07715,162.0022l13.33,22l9.67,-15l-8,-20l4,-6l6,2l9,13l7,-11l9,1l3,7l-11,13l17,2l4,8l-5,4l-22,-4l-13.5,16.8l27.5,5.2l11,-14l7,1l3,7l-9,11l19,3l4,7l-4,5l-18,-3l7,14l-4,4l-8,-1l-10,-20l-40,-8l-2,-2l-20,-37l-23,-4l-3,-7l4,-5l16,3l-9,-15l4,-6l7,1l9,16l9,-10l7,2l3,6l-10,14z" class="cls-21" id="logo-snow"/>
<path fill="url(#_未命名的渐变_9)" stroke-width="0px" d="m344.07715,316.0022c-18.61,-18.39 -62.05,-50.01 -50,-71c2.85,-4.5 7.34,-9.45 17,-7c15.19,5.99 14.64,39.18 45,55c8.67,-18.92 10.28,-33.18 10,-43c-0.12,-4.37 -0.74,-12.15 4,-16c0.82,-0.55 5.22,-3.4 10.85,-2.15c3.86,0.85 6.25,3.17 7.15,4.15c0.5,0.6 1.28,1.58 2.09,2.87c8.99,14.33 0.03,32.06 -11.49,69.45c-4.1,13.3 -6.65,21.58 -9.6,33.68c-5.81,23.86 -8.17,40.38 -20,51c-11.03,9.91 -22.32,0.03 -23,-9c-2.89,-15.02 23.13,-56.98 18,-68z" class="cls-27" id="yuki-y"/>
<path fill="url(#_未命名的渐变_9-2)" stroke-width="0px" d="m403.07715,323.0022c-0.54,-0.11 -5.36,-1.02 -9,2c-2.31,1.92 -3.04,4.55 -4,8c-0.74,2.66 -0.91,4.8 -1,6c-0.59,8.25 0,15 0,15c1.18,13.55 1.77,20.33 3,23c1.04,2.27 4.07,8.71 11,13c6.21,3.85 12.31,3.91 21,4c0.81,0 16.83,0.09 21,-4c0.22,-0.21 1.2,-1.26 2,-1c0.93,0.3 0.69,2.05 2,4c0,0 0.63,0.93 2,2c3.54,2.75 12.53,3.41 16,-1c0.62,-0.79 1.75,-2.64 2,-5c0.07,-0.68 0.05,-1.22 0.02,-1.76c-0.13,-2.75 -0.88,-4.83 -1.02,-5.24c-1.31,-3.73 -3.37,-15.69 -4,-23c-0.58,-6.71 0.42,-5.3 0,-19c-0.11,-3.63 -0.12,-9.19 -3,-15c-1.36,-2.75 -2.63,-4.09 -4,-5c-2.95,-1.96 -6.82,-2.22 -10,-1c-0.8,0.31 -3.48,1.34 -5,4c-1.36,2.37 -1.11,4.83 -1,6c0.66,6.98 0.45,14.01 1,21c0.72,9.13 0.57,11.51 -1,14c-1.8,2.86 -4.42,4.2 -6,5c-7.58,3.86 -15,0.82 -17,0c-2.54,-1.04 -3.49,-2.11 -4,-3c-0.74,-1.29 -0.88,-2.78 -1,-4c-0.13,-1.35 -0.05,-2.34 0,-3l0,-16c-0.19,-7.44 -0.28,-11.16 -2,-14c-0.52,-0.85 -3.02,-4.98 -8,-6z" class="cls-22" id="yuki-u"/>
<path fill="url(#_未命名的渐变_9-3)" stroke-width="0px" d="m591.07715,272.0022c-6.65,0.3 -12.55,6.12 -12,13c0.53,6.56 6.7,11.28 13,11c6.65,-0.3 12.55,-6.12 12,-13c-0.53,-6.56 -6.7,-11.28 -13,-11z" class="cls-2" id="yuki-i-1-1"/>
<path fill="url(#_未命名的渐变_9-4)" stroke-width="0px" d="m591.07715,303.0022c-0.8,0.03 -3.41,0.23 -6,2c0,0 -1.73,1.18 -3,3c-3.44,4.92 -2.32,14.54 -2,25c0.14,4.66 0,9.33 0,14c0,11.92 0,17.88 0,18c-0.51,8.41 -1.54,11.05 0,16c0.77,2.5 1.84,5.92 5,8c3.21,2.11 6.76,1.77 8.36,1.62c0.77,-0.07 5.65,-0.54 8.64,-3.62c3.4,-3.49 2.66,-8.56 2,-17c-0.09,-1.11 -1.18,-15.43 -1,-28c0.1,-7.33 -0.76,-14.71 0,-22c0.11,-1.03 0.52,-4.77 -1,-9c-0.64,-1.78 -1.55,-4.19 -4,-6c-2.89,-2.13 -6.05,-2.04 -7,-2z" class="cls-24" id="yuki-i-1"/>
<path fill="url(#_未命名的渐变_9-5)" stroke-width="0px" d="m545.07715,299.0022c-0.26,-0.11 -0.66,-0.27 -1.17,-0.44c0,0 -4.46,-1.53 -9.91,-1c-4.47,0.44 -18.59,11.34 -31.93,31.43c-0.35,-10.53 -0.23,-21.87 0.56,-33.9c0.37,-5.55 0.85,-10.92 1.44,-16.1c0.26,-1.2 0.52,-2.94 0.41,-5.03c-0.09,-1.81 -0.25,-4.96 -2.27,-8.04c-0.56,-0.85 -2.9,-4.42 -7.52,-5.51c-5.61,-1.33 -10.01,2.08 -10.62,2.58c-3.44,2.76 -4.45,6.67 -5,9c-3.6,15.32 -3,27 -3,27c0.42,8.15 1.67,32.57 1,50c-0.22,5.66 0.05,11.33 0,17c-0.05,6.02 -0.13,9.05 1,13c1.45,5.07 4.08,8.75 6,11c1.19,1.11 3.93,3.36 8,4c3.99,0.63 7.79,-0.51 9.84,-1.85c11.06,-7.21 1.65,-39.59 4.55,-40.56c0.39,-0.13 1.19,0.88 2.61,3.4c8.89,11.22 14.99,15.39 30,29c7.7,10.13 23.45,3.59 22,-8c-0.19,-16.59 -39.62,-33.08 -31.91,-39.47c1.89,-2.38 6,-8.21 6.91,-9.53c5.47,-7.8 10.83,-8.44 13,-15c1.34,-4.04 0.69,-6.57 0.43,-7.38c-1.01,-3.15 -3.39,-4.94 -4.43,-5.62l0.01,0.02z" class="cls-28" id="yuki-k"/>
<path fill="url(#_未命名的渐变_4)" stroke-width="0px" d="m79.07715,274.0022c-1.75,-1 -2.61,-0.17 -10,1c-2.22,0.35 -7.11,1.06 -13,3c-1.36,0.45 -2.68,0.93 -3,2c-0.49,1.65 1.56,4.27 4,5c0.32,0.09 0.56,0.13 0.74,0.16c2.19,0.34 3.89,-0.56 5.26,-1.16c1.96,-0.87 3.64,-1.24 7,-2c0,0 4.44,-1 5,0c0.02,0.04 0.04,0.07 0.04,0.07c0.07,0.18 -0.02,0.5 -0.04,2.93c0,0.06 0,0.71 0,2c0,5.01 0.09,5.71 0,7c-0.2,2.74 -0.3,4.11 -1,5c-1.25,1.58 -3.23,1.52 -7,2c-5.62,0.72 -4.41,1.27 -10,2c-7.19,0.94 -9.49,0.07 -11,2c-1.32,1.68 -1.32,4.57 0,6c1.74,1.88 5.46,0.88 9,0c7.35,-1.82 19.55,-4.84 29,-6c4.81,-0.59 12,-1.09 13,-4c0.57,-1.65 0,-3 0,-3c-0.06,-0.14 -0.62,-1.43 -2,-2c-1.19,-0.49 -2.36,-0.18 -3,0c-6.78,1.91 -9.23,2.82 -10,2c-0.45,-0.48 -0.17,-1.39 0,-2c1.77,-6.29 1.22,-12.36 1,-14c-0.23,-1.67 -0.84,-6.18 -4,-8z" class="cls-19" id="lite-jp-1"/>
<path fill="url(#_未命名的渐变_4-2)" stroke-width="0px" d="m75.07715,330.0022c-1.02,0.34 -1.72,1.18 -2,2c-0.31,0.93 -0.06,1.78 0,2c0.38,1.39 0.25,7.26 0,19c-0.12,5.74 -0.36,7.73 0,12c0.21,2.49 0.58,5.11 2,8c0.87,1.76 1.83,3.72 4,5c2.08,1.23 4.09,1.14 7,1c2.69,-0.13 6.51,-0.31 7,-2c0.37,-1.27 -1.18,-3.23 -3,-4c-2.78,-1.18 -4.84,0.97 -7,0c-2.17,-0.97 -2.69,-4.29 -3,-7c-1.07,-9.45 -0.9,-24.04 -1,-30c-0.02,-1.36 -0.12,-5.21 -2,-6c-0.58,-0.25 -1.37,-0.21 -2,0z" class="cls-34" id="liteecho-1"/>
<path fill="url(#_未命名的渐变_72)" stroke-width="0px" d="m263.13715,280.9522c-2.11,0.2 -3.55,1.9 -3.93,2.35c-0.57,0.68 -0.91,1.34 -1.11,1.8c1.09,1.05 6.06,5.64 11.22,4.91c2.33,-0.33 5.1,-1.81 5.34,-2.63c0.03,-0.09 0.03,-0.17 0.03,-0.17c0,-0.29 -0.33,-0.71 -1.89,-1.83c-1.33,-0.96 -2.23,-1.61 -3.3,-2.18c-0.69,-0.37 -1.23,-0.6 -1.68,-0.79c-2.41,-1.04 -3.62,-1.55 -4.68,-1.45l0,-0.01z" class="cls-16" id="yuki-jp-4-1"/>
<path fill="url(#_未命名的渐变_58)" stroke-width="0px" d="m248.07715,265.0022c0.37,-0.49 1.34,-1.8 3,-2c2.33,-0.28 3.93,1.89 4,2c0.33,0.46 0.44,0.81 1,3c0.59,2.3 0.88,3.45 1,4c0.33,1.56 0.71,3.58 1,6c0.65,5.37 0.61,6.78 1,10c0.79,6.47 1.28,10.5 3,15c0.07,0.18 1.23,3.21 1,7c-0.08,1.29 -0.29,2.25 -1,3c-1,1.07 -2.67,1.39 -4,1c-0.58,-0.17 -1.69,-0.65 -3,-3c-1.53,-2.75 -1.78,-5.26 -2,-7c-0.3,-2.39 -0.86,-5.79 -2,-10c-1,-6.33 -2,-12.67 -3,-19c-0.33,-1.67 -0.67,-3.33 -1,-5c-0.1,-0.53 -0.53,-2.95 1,-5z" class="cls-12" id="yuki-jp-4"/>
<path fill="url(#_未命名的渐变_3)" stroke-width="0px" d="m101.07715,341.0022c-1.86,-0.04 -3.59,1.27 -4,3c-0.56,2.38 1.47,5.01 4,5c2.03,-0.01 4,-1.72 4,-4c0,-2.25 -1.92,-3.96 -4,-4z" class="cls-20" id="liteecho-i-1-1"/>
<path fill="url(#_未命名的渐变_3-2)" stroke-width="0px" d="m102.07715,354.0022c-0.61,-0.3 -1.38,-0.31 -2,0c-0.14,0.07 -0.62,0.33 -1,1c-1.18,2.08 -1.01,7.51 -1,8c0.16,5.45 0,9 0,9c-0.08,1.78 -0.22,3.7 1,5c0.21,0.22 0.94,1 2,1c1.08,0 1.81,-0.8 2,-1c1.05,-1.14 1.02,-2.72 1,-4c-0.03,-2.33 0.04,-4.67 0,-7c-0.05,-3.31 0.17,-8.95 -1,-11c-0.12,-0.2 -0.41,-0.71 -1,-1z" class="cls-9" id="liteecho-i-1"/>
<path fill="url(#_未命名的渐变_65)" stroke-width="0px" d="m119.55715,342.6422c-0.52,0.03 -1.3,0.07 -1.92,0.55c-0.25,0.19 -0.76,0.67 -1.1,2.68c-0.15,0.92 -0.29,2.26 -0.14,3.91c-1.17,0.01 -3.52,0.16 -5.32,1.22c-0.38,0.22 -0.77,0.51 -1,1c-0.39,0.85 -0.08,1.91 0.41,2.59c0.74,1.01 1.99,1.19 3.59,1.41c0.85,0.12 1.57,0.12 2.05,0.1c-0.06,0.76 -0.12,1.85 -0.14,3.16c-0.01,1.22 -0.03,2.48 0.14,4.12c0.15,1.49 0.3,2.92 0.96,4.62c0.39,1.03 0.78,1.67 1,2c0.25,0.39 0.74,1.08 1.48,1.76c1.12,1.05 2.67,2.5 4.52,2.24c1.2,-0.17 2.57,-1.06 2.76,-2.24c0.12,-0.76 -0.29,-1.46 -0.55,-1.92c-0.43,-0.76 -0.87,-0.99 -1.51,-1.65c-0.95,-0.97 -1.44,-1.93 -1.78,-2.61c-0.71,-1.41 -0.9,-2.61 -1.1,-3.85c-0.15,-0.97 -0.37,-2.35 -0.14,-4.12c0.09,-0.67 0.22,-1.22 0.32,-1.61c0.56,0 1.35,-0.04 2.29,-0.17c1.54,-0.21 2.32,-0.32 2.88,-0.82c0.8,-0.71 1.27,-2.03 0.83,-3.01c-0.13,-0.3 -0.38,-0.61 -1,-1c-1.52,-0.94 -3.22,-1.02 -4.36,-0.94c0.11,-0.49 0.27,-1.25 0.41,-2.2c0.49,-3.18 0.14,-3.81 -0.14,-4.12c-0.26,-0.3 -0.59,-0.43 -1.24,-0.69c-0.4,-0.16 -1.18,-0.47 -2.2,-0.41z" class="cls-8" id="liteecho-t"/>
<path fill="url(#_未命名的渐变_66)" stroke-width="0px" d="m142.48715,359.4922c-1.24,0 -1.82,0.11 -2.17,0.53c-0.63,0.76 -0.29,2.24 0.31,3.15c1.51,2.31 5.53,2.25 7.8,1.77c1.4,-0.3 4.49,-0.94 5.98,-3.77c1.7,-3.21 -0.04,-6.62 -0.4,-7.27c-1.86,-3.4 -5.47,-4.29 -7.14,-4.7c-3.37,-0.83 -6.16,0.05 -7.27,0.4c-1.07,0.34 -2.81,0.91 -4.53,2.4c-3.45,2.98 -3.92,7.09 -4,8c-0.27,3.05 0.7,5.35 1,6c0.27,0.6 1.17,2.42 3,4c1.04,0.89 2.16,1.53 3,2c0.99,0.56 1.75,0.89 2,1c1.02,0.44 1.88,0.81 3,1c1.17,0.2 2.07,0.1 3,0c0.34,-0.04 1.85,-0.22 4,-1c3.3,-1.2 4.59,-2.54 5,-3c0.47,-0.53 0.68,-0.9 0.71,-1.34c0.06,-1.15 -1.21,-2.31 -2.48,-2.62c-0.51,-0.12 -0.94,-0.08 -1.23,-0.05c-2.01,0.26 -3.04,1.28 -4.31,1.86c-2.59,1.19 -5.84,0.41 -7.69,-0.86c-1.16,-0.8 -1.95,-1.93 -2,-2c-0.26,-0.38 -0.69,-1.07 -1,-2c-0.26,-0.77 -0.95,-2.86 0,-5c0.82,-1.85 2.41,-2.69 3,-3c2.34,-1.23 5.06,-1.05 7,0c0.69,0.37 1.64,0.9 2,2c0.29,0.87 0.26,2.23 -0.52,2.84c-1,0.8 -2.36,-0.34 -6.07,-0.36l0.01,0.02z" class="cls-25" id="liteecho-e"/>
<path fill="url(#_未命名的渐变_68)" stroke-width="0px" d="m197.07715,346.0022c-1.16,0 -4.12,0.04 -7,2c-1.54,1.05 -2.49,2.26 -3,3c-1.62,2.38 -1.87,4.7 -2,6c-0.12,1.26 -0.36,4 1,7c2.14,4.72 6.61,6.52 8,7c1.04,0.36 4.93,1.64 9,0c0.94,-0.38 2.74,-1.13 4,-3c0.45,-0.66 1.99,-2.94 1,-5c-0.54,-1.12 -1.88,-2.25 -3,-2c-1.41,0.32 -1.04,2.48 -3,4c-1.92,1.48 -4.45,1.09 -5,1c-0.54,-0.08 -2.42,-0.41 -4,-2c-2.4,-2.42 -2.04,-5.66 -2,-6c0.07,-0.51 0.43,-3.37 3,-5c2.25,-1.43 5.33,-1.38 7,0c0.93,0.77 0.92,1.55 2,2c1.26,0.52 3.2,0.27 4,-1c0.72,-1.15 0.17,-2.56 0,-3c-0.15,-0.38 -0.69,-1.63 -3,-3c-0.98,-0.58 -3.45,-2.01 -7,-2z" class="cls-29" id="liteecho-c"/>
<path fill="url(#_未命名的渐变_71)" stroke-width="0px" d="m217.07715,329.0022c-0.92,-0.49 -2.08,-0.44 -3,0c-1.8,0.87 -1.99,2.91 -2,3l0,9c0,4 0.04,8 0,12c-0.03,3 0.1,6 0,9c-0.1,3.04 -0.29,5.38 1,8c0.45,0.91 1.42,2.89 3,3c1.63,0.11 2.9,-1.85 3,-2c1.44,-2.28 0.03,-4.1 0,-9c-0.02,-2.85 0,-5.82 2,-8c0.37,-0.4 1.8,-1.97 4,-2c2.44,-0.04 3.91,1.89 4,2c0.92,1.22 1,2.52 1,3c-0.33,1.67 -0.67,3.33 -1,5c-0.41,1.12 -0.91,2.85 -1,5c-0.09,2.3 -0.16,3.99 1,5c1.28,1.11 3.61,1.02 5,0c1.65,-1.21 1.8,-3.61 2,-7c0.08,-1.27 0.04,-2.31 0,-3l0,-8c-0.02,-0.84 -0.16,-2.36 -1,-4c-0.51,-1 -1.73,-2.93 -4,-4c-3.65,-1.72 -7.65,-0.14 -8,0c-1.5,0.62 -2.24,1.36 -3,1c-0.71,-0.34 -0.94,-1.4 -1,-4c-0.12,-5.04 0.61,-6.66 0,-10c-0.56,-3.06 -1.54,-3.76 -2,-4z" class="cls-17" id="liteecho-h"/>
<path fill="url(#_未命名的渐变_58-2)" stroke-width="0px" d="m252.07715,348.0022c-4.27,-0.18 -7.17,2.27 -8,3c-3.05,2.66 -3.79,6 -4,7c-0.18,0.88 -1.21,5.84 2,10c2.08,2.69 4.82,3.62 6,4c1.29,0.42 4.46,1.39 8,0c2.95,-1.16 4.51,-3.31 5,-4c1.43,-2.02 1.81,-3.97 2,-5c0.46,-2.51 0.1,-4.49 0,-5c-0.67,-3.35 -2.52,-5.47 -3,-6c-0.69,-0.76 -3.46,-3.8 -8,-4z" class="cls-33" id="liteecho-o"/>
<path stroke-miterlimit="10" stroke="#fff" fill="#fff" d="m250.57715,354.5022c-2.49,0.24 -3.75,2.55 -4,3c-0.09,0.17 -1.81,3.42 0,6c1.68,2.39 5.6,3.06 8,1c2.1,-1.8 2.13,-4.93 1,-7c-0.84,-1.54 -2.7,-3.22 -5,-3z" class="cls-35" id="liteecho-o-white"/>
<path fill="url(#_未命名的渐变_3-3)" stroke-width="0px" d="m108.07715,274.0022c-0.71,0.63 -1.32,1.81 -1,3c0.38,1.44 1.86,1.95 2,2c0.77,0.25 1.44,0.12 2,0c4.88,-1.04 5.31,-1.31 6,-1c1.18,0.54 1.54,2.08 2,4c0.41,1.72 0.73,3.08 0,4c-0.65,0.81 -2.11,0.89 -5,1c-1.25,0.05 -2.29,0.03 -3,0c-0.53,0.14 -1.96,0.61 -3,2c-0.4,0.54 -1.21,1.61 -1,3c0.24,1.62 1.7,2.78 3,3c0.44,0.08 0.79,0.03 1,0c4.59,-0.65 6.89,-0.98 7,-1c2.55,-0.43 3.84,-0.64 4.45,-0.34c1.26,0.62 1.41,1.86 2.55,5.34c0,0 1.48,4.5 4,10c0.8,1.75 1.53,3.13 2,4c0.16,0.13 2.42,1.9 5,1c0.16,-0.06 2.44,-0.88 3,-3c0.21,-0.78 0.1,-1.42 0,-2c-0.27,-1.54 -0.96,-2.26 -2,-4c-0.12,-0.21 0.03,0.04 -2,-4c-0.67,-1.33 -1.32,-2.67 -2,-4c-1.91,-3.76 -2.3,-4.3 -2,-5c0.2,-0.46 0.49,-0.53 4,-2c1.13,-0.48 2.45,-1.03 4,-2c1.13,-0.71 1.71,-1.21 2,-2c0.06,-0.17 0.35,-1.03 0,-2c-0.48,-1.32 -1.88,-2.03 -3,-2c-0.31,0 -0.54,0.07 -0.66,0.11c-2.2,0.63 -4.77,1.67 -5.34,1.89c-2.21,0.88 -3.31,1.31 -4,1c-0.42,-0.19 -0.77,-0.54 -2,-4c-1.21,-3.42 -1.11,-3.77 -1,-4c0.42,-0.85 1.37,-0.79 6,-2c2.46,-0.64 3.57,-1.02 4,-2c0.3,-0.68 0.14,-1.39 0,-2c-0.09,-0.4 -0.54,-2.17 -2,-3c-1.17,-0.66 -2.4,-0.38 -4,0c-3.65,0.88 -4.88,2.55 -6,2c-0.55,-0.27 -1.15,-1.13 -1,-4c-0.42,-2 -0.76,-3 -1,-3l0,0c-0.76,-1.48 -2.4,-2.29 -4,-2c-1.51,0.27 -2.7,1.47 -3,3c-0.17,2.45 0.41,4.01 1,5c0.57,0.95 1.14,1.36 1,2c-0.24,1.14 -2.45,1.64 -4,2c-2.38,0.55 -3.11,0.2 -4,1z" class="cls-13" id="lite-jp-2"/>
<path fill="url(#_未命名的渐变_5)" stroke-width="0px" d="m153.07715,286.0022c-0.46,1.12 -0.62,2.69 0,4c0.19,0.41 0.79,1.49 2,2c1.22,0.51 2.43,0.16 3,0c5,-1.43 7.51,-2.15 8,-2c4.42,1.34 3.64,20.08 7,22c2.14,1.22 4,1 4,1c1.43,-0.17 3.03,-0.97 3.02,-1c0,0 0,0 -0.02,0c0.27,-0.2 0.65,-0.52 1,-1c1.53,-2.12 0.85,-5.32 -1,-10c-3.3,-8.33 -6.24,-11.07 -5,-13c0.71,-1.1 1.84,-0.46 5.57,-2.36c2.51,-1.27 4.06,-2.06 4.43,-3.64c0.46,-1.98 -1.11,-4.46 -3,-5c-1.01,-0.29 -2.23,0.21 -4.65,1.22c-3.32,1.39 -3.58,2.02 -4.35,1.78c-2.25,-0.7 -0.9,-6.34 -4,-8c-1.22,-0.65 -2.84,-0.54 -4,0c-0.31,0.14 -1.56,0.73 -2,2c-0.24,0.7 -0.14,1.29 0,2c0.51,2.66 2.08,3.57 2,5.99c0,0 0,0 0,0c0.01,0 0.02,0 0.02,0c0.01,0.1 -10.37,-0.01 -12.02,4l0,0.01z" class="cls-5" id="yuki-jp-1"/>
<path fill="url(#_未命名的渐变_69)" stroke-width="0px" d="m230.07715,276.0022c-0.23,-0.2 -0.95,-0.74 -2,-1c-0.58,-0.14 -1.39,-0.34 -2,0c-0.79,0.44 -0.86,1.6 -1,3c0,0 -0.62,6.23 -2,9c-0.43,0.86 -0.97,1.66 -0.97,1.66c-0.12,0.18 -0.24,0.35 -0.35,0.5c-0.2,0.27 -0.42,0.55 -0.68,0.84c-1.47,1.68 -5.93,4.2 -5.93,4.2c-3.23,1.82 -3.66,2.04 -4.07,2.8c-0.61,1.13 -0.9,2.9 0,4c0.8,0.97 2.17,0.99 3,1c0.7,0 3.05,-0.07 7.17,-3.17c1.64,-1.24 3.78,-2.89 5.83,-5.83c3.15,-4.53 3.72,-8.76 4,-11c0.52,-4.2 -0.4,-5.48 -1,-6z" class="cls-31" id="yuki-jp-2-3"/>
<path fill="url(#_未命名的渐变_67)" stroke-width="0px" d="m167.88715,359.1822c-1.13,0.52 -1.6,0.87 -1.74,1.41c-0.25,0.96 0.69,2.15 1.62,2.72c2.35,1.44 5.96,-0.31 7.81,-1.71c1.14,-0.86 3.66,-2.76 3.81,-5.96c0.17,-3.63 -2.85,-5.97 -3.45,-6.41c-3.13,-2.29 -6.78,-1.55 -8.46,-1.22c-3.41,0.68 -5.55,2.66 -6.41,3.45c-0.83,0.76 -2.15,2.02 -3.08,4.1c-1.85,4.16 -0.53,8.09 -0.22,8.94c1.05,2.88 2.91,4.54 3.46,5c0.5,0.42 2.09,1.69 4.42,2.34c1.32,0.37 2.61,0.46 3.57,0.53c1.13,0.08 1.96,0.06 2.24,0.05c1.11,-0.04 2.04,-0.06 3.14,-0.37c1.14,-0.32 1.92,-0.79 2.71,-1.28c0.29,-0.18 1.58,-0.98 3.19,-2.61c2.48,-2.49 3.07,-4.25 3.25,-4.84c0.2,-0.68 0.24,-1.11 0.07,-1.51c-0.43,-1.07 -2.08,-1.58 -3.36,-1.31c-0.51,0.11 -0.88,0.32 -1.13,0.48c-1.71,1.09 -2.2,2.45 -3.11,3.52c-1.84,2.18 -5.11,2.86 -7.32,2.49c-1.39,-0.23 -2.59,-0.92 -2.66,-0.96c-0.4,-0.23 -1.08,-0.67 -1.76,-1.38c-0.56,-0.59 -2.08,-2.19 -2.13,-4.52c-0.04,-2.02 1.04,-3.46 1.44,-3.99c1.59,-2.11 4.14,-3.1 6.33,-2.98c0.79,0.04 1.86,0.12 2.66,0.96c0.63,0.67 1.18,1.91 0.74,2.79c-0.57,1.15 -2.28,0.69 -5.65,2.26l0.02,0.01z" class="cls-14" id="liteecho-e2"/>
</g>
<circle id="svg_6" fill="url(#_未命名的渐变_6)" stroke-width="0px" r="4" cy="301.0022" cx="162.07715" class="cls-23"/>
<ellipse id="svg_7" fill="url(#_未命名的渐变_7)" stroke-width="0px" ry="2.5" rx="2.51" cy="270.5022" cx="183.56715" class="cls-11"/>
<ellipse id="svg_8" fill="url(#_未命名的渐变_7-2)" stroke-width="0px" ry="2.5" rx="2.51" cy="273.5022" cx="176.56715" class="cls-26"/>
<circle id="svg_9" fill="url(#_未命名的渐变_7-3)" stroke-width="0px" r="4" cy="294.3922" cx="185.05715" class="cls-10"/>
<circle id="svg_10" stroke-width="0px" fill="url(#_未命名的渐变_8)" r="4" cy="281.2622" cx="209.49715" class="cls-1"/>
<circle id="svg_11" fill="url(#_未命名的渐变_55)" stroke-width="0px" r="4" cy="275.2622" cx="217.49715" class="cls-6"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,95 +0,0 @@
[
{
"module_name": "liteyukibot-plugin-nonebot",
"project_link": "liteyukibot-plugin-nonebot",
"name": "NoneBot插件",
"desc": "在轻雪中使用NoneBot内置轻雪--NoneBot会话控制器插件为NoneBot开发者提供了更多便捷功能(已内置)",
"author": "snowykami",
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot",
"tags": [
{
"label": "app",
"color": "#aeeaa8"
}
],
"is_official": true,
"type": "application",
"valid": true,
"version": "rolling"
},
{
"module_name": "liteyukibot-plugin-antidislink",
"project_link": "liteyukibot-plugin-antidislink",
"name": "防断联插件",
"desc": "防止你的群友断联化",
"author": "snowykami",
"homepage": "https://github.com/snowykami/liteyukibot-plugin-antidislink",
"tags": [
{
"label": "app",
"color": "#aeeaa8"
},
{
"label": "dislink",
"color": "#d0e9ff"
}
],
"is_official": true,
"type": "application",
"valid": true,
"version": "rolling"
},
{
"module_name": "liteyukibot-plugin-lagrange",
"project_link": "liteyukibot-plugin-lagrange",
"name": "LagrangePlugin",
"desc": "在轻雪中使用lagrange-python",
"author": "snowykami",
"homepage": "https://github.com/snowykami/liteyukibot-plugin-lagrange",
"tags": [
{
"label": "app",
"color": "#aeeaa8"
},
{
"label": "lagrange",
"color": "#ff89f7"
}
],
"is_official": true,
"type": "application",
"valid": true,
"version": "rolling"
},
{
"module_name": "liteyukibot-plugin-htmlrender",
"project_link": "liteyukibot-plugin-htmlrender",
"name": "htmlrender",
"desc": "使用系统中的浏览器对网页进行渲染,便于其他插件使用",
"author": "EillesWan",
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-htmlrender",
"tags": [
{
"label": "ch",
"color": "#4285f4"
},
{
"label": "ro",
"color": "#db4437"
},
{
"label": "mi",
"color": "#f4b400"
},
{
"label": "um",
"color": "#0f9d58"
}
],
"is_official": true,
"type": "application",
"valid": true,
"version": "rolling"
}
]

View File

@ -1,77 +0,0 @@
[
{
"name": "KawaiiStatus",
"author": "SnowyKami",
"description": "可爱的状态卡片仿照koishi的制作",
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip"
},
{
"name": "MiSans字体包",
"author": "SnowyKami",
"description": "小米官方字体MiSans",
"link": "https://cdn.liteyuki.icu/static/lrp/MiSansFonts.zip"
},
{
"name": "MapleMono字体包",
"author": "SnowyKami",
"description": "适用于字母的字体包",
"link": "https://cdn.liteyuki.icu/static/lrp/MapleMonoFonts.zip"
},
{
"name": "野兽先辈主题HomoTheme",
"author": "SnowyKami",
"description": "野兽先辈主题包114514",
"link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip"
},
{
"name": "自定义设备信息",
"author": "SnowyKami",
"description": "自定义服务端的设备信息,自行修改使用",
"link": "https://cdn.liteyuki.icu/static/lrp/custom-device.zip"
},
{
"name": "轻雪傲娇系词库",
"author": "SnowyKami",
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip"
},
{
"name": "轻雪可爱系词库",
"author": "SnowyKami",
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_kawaii.zip"
},
{
"name": "轻雪Kakyo语言包",
"author": "Nanaloveyuki",
"description": "Liteyuki Bot的语言包用于提供多种语言的翻译。",
"link": "https://github.com/Nanaloveyuki/liteyuki-langpack/releases/download/KakyoVer/Kakyo-pack.zip"
},
{
"name": "更多背景模板包",
"author": "snowykami",
"description": "自定义各种卡片的背景",
"link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip"
},
{
"name": "测试资源包",
"desc": "test",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
"homepage": "",
"author": "snowykami"
},
{
"name": "轻雪Kakyo语言包 稳定版",
"description": "Liteyuki Bot的语言包用于提供多种语言的翻译。",
"link": "https://cdn.liteyuki.icu/static/lrp/kakyo.zip",
"homepage": "https://github.com/Nanaloveyuki/liteyuki-langpack",
"author": "Nanaloveyuki"
},
{
"name": "测试资源包2",
"description": "test",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
"homepage": "",
"author": "snowykami"
}
]

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"components/**/*.ts",
],
"exclude": [
"node_modules"
]
}

View File

@ -1,71 +0,0 @@
---
title: 配置
order: 2
---
# 配置
轻雪支持`yaml``json``toml`作为配置文件,取决于你个人的喜好
首次运行后生成`config.yml``config`目录,你可修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers``nickname`字段即可
启动时会加载项目目录下`config.yml/yaml/json/toml``config`目录下的所有配置文件,你可在`config`目录下创建多个配置文件,轻雪会自动合并这些配置文件
## **基础配置项**
```yaml
nonebot:
# Nonebot机器人的配置以前的最外层配置项仍可为Nonebot服务但是部分内容会被覆盖请尽快迁移
command_start: [ "/", "" ] # 指令前缀,若没有""空命令头请开启alconna_use_command_start保证alconna解析正常
host: 127.0.0.1 # 监听地址默认为本机若要接收外部请求请填写0.0.0.0
port: 20216 # 绑定端口
nickname: [ "liteyuki" ] # 机器人昵称列表
superusers: [ "1919810" ] # 超级用户列表
liteyuki:
# 写在外层的配置项将会被覆盖建议迁移到liteyuki下
log_level: "INFO" # 日志等级
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
auto_report: true # 是否自动上报问题给轻雪服务器
auto_update: true # 是否自动更新轻雪每天4点检查更新
plugins: [ ] # 轻雪插件列表
plugin_dirs: [ ] # 轻雪插件目录列表
```
## **其他配置**
以下为默认值,如需自定义请手动添加
```yaml
# 高级NoneBot配置
nonebot:
onebot_access_token: "" # 访问令牌,对公开放时建议设置
default_language: "zh-CN" # 默认语言
alconna_auto_completion: false # alconna是否自动补全指令默认false建议开启
safe_mode: false # 安全模式开启后将不会加载任何第三方NoneBot插件
# 其他Nonebot插件的配置项
custom_config_1: "custom_value1"
custom_config_2: "custom_value2"
# 开发者选项
liteyuki:
allow_update: true # 是否允许更新
debug: false # 轻雪调试开启会自动重载Bot或者资源其他插件自带的调试功能也将开启
dev_mode: false # 开发者模式,开启后将会启动监视者,监视文件变化并自动重载
...
```
## **示例与NoneBot对接的OneBot实现端配置**
生产环境中推荐反向WebSocket
不同的实现端给出的字段可能不同,但是基本上都是一样的,这里给出一个参考值
| 字段 | 参考值 | 说明 |
|-------------|------------------------------------|----------------------------------|
| 协议 | 反向WebSocket | 推荐使用反向ws协议进行通信即轻雪作为服务端 |
| 地址 | ws://127.0.0.1:20216/onebot/v11/ws | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
- 要使用其他通信方式请访问[OneBot Adapter](https://onebot.adapters.nonebot.dev/)获取详细信息
## **其他**
- 轻雪不局限于OneBot适配器你可以使用NoneBot2支持的任何适配器或使用轻雪讯息传递插件

View File

@ -1,52 +0,0 @@
---
title: 答疑
order: 3
---
# 答疑
## **常见问题**
- 设备上Python环境太乱了pip和python不对应怎么办
- 请使用`/path/to/python -m pip install -r requirements.txt`来安装依赖,
然后用`/path/to/python main.py`来启动Bot
其中`/path/to/python`是你要用来运行Bot的可执行文件
- 为什么我启动后机器人没有反应?
- 请检查配置文件的`command_start``superusers`,确认你有权限使用命令并按照正确的命令发送
- 确认命令头没有和`nickname{}`冲突,例如一个命令是`help`,但是`Bot`昵称有一个`help`那么将会被解析为nickname而不是命令
- 更新轻雪失败,报错`InvalidGitRepositoryError`
- 请正确安装`Git`,并使用克隆而非直接下载的方式部署轻雪
- 怎么登录聊天平台例如QQ
- 你有这个问题说明你不是很了解这个项目,本项目不负责实现登录功能,只负责处理和回应消息,登录功能由实现端(协议端)提供,
实现端本身不负责处理响应逻辑将消息按照OneBot标准处理好上报给轻雪
你需要使用Onebot标准的实现端来连接到轻雪并将消息上报给轻雪下面已经列出一些推荐的实现端
- `Playwright`安装失败
- 输入`playwright install`安装浏览器
- 有的插件安装后报错无法启动
- 请先查阅插件文档,确认插件必要配置项完好后,仍然出现问题,请联系插件作者或在安全模式`safe_mode: true`下启动轻雪,在安全模式下你可以使用`npm uninstall`卸载问题插件
- 其他问题
-
加入QQ群[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
## **推荐方案(QQ)**
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core)基于NTQQ的OneBot实现目前Markdown消息支持Lagrange
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot)NTQQ的OneBot插件需要安装NTQQ
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock)基于Lsposed的OneBot11实现
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai),基于`node.js`,可使用`ws-plugin`进行通信
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp)`go`语言实现的OneBot11实现端目前可用性较低
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo),基于 OneBot QQ官方机器人Api Golang 原生实现,需要官方机器人权限
7. 人工实现的`Onebot`协议自己整一个WebSocket客户端看着QQ的消息然后给轻雪传输数据
## **推荐方案(Minecraft)**
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot)我们专门为Minecraft开发的服务器Bot支持OneBotV11标准
使用其他项目连接请先自行查阅文档若有困难请联系对应开发者而不是Liteyuki的开发者
## **鸣谢**
- [Nonebot2](https://nonebot.dev)提供的框架支持
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
- [MiSans](https://hyperos.mi.com/font/zh/)[MapleMono](https://gitee.com/mirrors/Maple-Mono)提供的字体,且遵守了相关字体开源协议

View File

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

View File

@ -1,21 +0,0 @@
---
title: 最佳实践
order: 10
---
# 最佳实践
## 机器人应用
- [LiteyukiBot](https://github.com/LiteyukiStudio/LiteyukiBot):轻雪官方机器人
- [LiteyukiBot-TriM](https://github.com/TriM-Organization/LiteyukiBot-TriM)TriM 官方定制轻雪机器人
- [Liteyuki Marsho](https://git.liteyuki.icu/LiteyukiStudio/marsho-alpha):基于 轻雪 和 github model 的可爱猫娘机器人
## 插件
- [liteyukibot-plugin-nonebot](https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot):轻雪机器人 NoneBot 插件,让轻雪支持 NoneBot
- [nonebot-plugin-liteyukibot](https://github.com/LiteyukiStudio/nonebot-plugin-liteyukibot)NoneBot 插件,让 NoneBot 支持轻雪
## 其他
- [liteyuki starmap](https://starmap.liteyuki.icu):轻雪官方星图,展示轻雪的所有实例及其位置分布
- [TRSS_Liteyuki](https://timerainstarsky.github.io/TRSS_Liteyuki/)TRSS LiteyukiBot 管理脚本
- [litedoc](https://github.com/LiteyukiStudio/litedoc)轻雪API文档生成器
- liteyukibot-api (闭源):轻雪机器人统计接口

View File

@ -1,99 +0,0 @@
---
title: 进程通信
order: 4
---
# **通道通信**
### 简介
轻雪运行在主进程 MainProcess 里,其他插件框架进程是伴随的子进程,因此无法通过内存共享和直接对象传递的方式进行通信,
轻雪提供了一个通道[`Channel`](./api/comm/channel#class-channel-generic-t)用于跨进程通信,
你可以通过[`Channel`](./api/comm/channel#class-channel-generic-t)发送消息给其他进程,也可以监听其他进程的消息。
例如子进程接收到用户信息需要重启机器人,这时可以通过通道对主进程发送消息,主进程接收到消息后重启对应子进程。
### 示例
通道是全双工的,有两种接收模式,但一个通道只能使用一种,即被动模式和主动模式,被动模式由`chan.on_receive()`装饰回调函数实现,主动模式需调用`chan.receive()`实现
- 创建子进程的同时会初始化一个被动通道和一个主动通道,且通道标识为`{process_name}-active``{process_name}-passive`
- 主进程中通过`get_channel`函数获取通道对象
- 子进程中导入单例`active_channel``passive_channel`即可
> 在轻雪插件中(主进程中)
```python
import asyncio
from liteyuki.comm import get_channel, Channel
from liteyuki import get_bot
# get_channel函数获取通道对象参数为调用set_channel时的通道标识
channel_passive = get_channel("nonebot-passive") # 获取被动通道
channel_active = get_channel("nonebot-active") # 获取主动通道
liteyuki_bot = get_bot()
# 注册一个函数在轻雪启动后运行
@liteyuki_bot.on_after_start
async def send_data():
while True:
channel_passive.send("I am liteyuki main process passive")
channel_active.send("I am liteyuki main process active")
await asyncio.sleep(3) # 每3秒发送一次消息
```
> 在子进程中例如NoneBot插件中
```python
from nonebot import get_driver
from liteyuki.comm import active_channel, passive_channel # 子进程中获取通道直接导入进程全局单例即可
from liteyuki.log import logger
driver = get_driver()
# 被动模式,通过装饰器注册一个函数在接收到消息时运行,每次接收到字符串数据时都会运行
@passive_channel.on_receive(filter_func=lambda data: isinstance(data, str))
async def on_passive_receive(data):
logger.info(f"Passive receive: {data}")
# 注册一个函数在NoneBot启动后运行
@driver.on_startup
def on_startup():
while True:
data = active_channel.receive()
logger.info(f"Active receive: {data}")
```
> 启动后控制台输出
```log
0000-00-00 00:00:00 [ℹ️信息] Passive receive: I am liteyuki main process passive
0000-00-00 00:00:00 [ℹ️信息] Active receive: I am liteyuki main process active
0000-00-00 00:00:03 [ℹ️信息] Passive receive: I am liteyuki main process passive
0000-00-00 00:00:03 [ℹ️信息] Active receive: I am liteyuki main process active
...
```
## **共享内存通信**
### 简介
- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信,该模块封装通道实现
- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作
### 示例
> 在任意进程中均可使用
```python
from liteyuki.comm.storage import shared_memory
shared_memory.set("key", "value") # 设置共享内存
value = shared_memory.get("key") # 获取共享内存
```
源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py)

View File

@ -1,71 +0,0 @@
---
title: 开发指南
order: 0
---
<script setup>
import ContributorBar from '../../components/ContributorBar.vue'
</script>
# 开发指南
## 如何开发
插件开发无需阅读本文档,请阅读[插件开发文档](./plugin)和[API 引用](./api/)。
如需往轻雪仓库提交代码,请阅读以下内容:
1. 首先[fork](https://github.com/LiteyukiStudio/LiteyukiBot/fork)一份轻雪仓库到你的个人/组织账户下。
2. 在你的仓库中进行开发。
3. 在你的仓库中创建一个新的分支,将你的代码提交到这个分支。
4. 在你的仓库中创建一个Pull Request将你的分支合并到轻雪仓库的`main`分支。
参与开发默认你已经了解Python语言和轻雪框架的基本使用方法如果是文档相关的开发请确保你了解Markdown语法和基本前端知识。
出现冲突请与仓库维护者联系。
### 建议
- 开发过程中可以使用`mypy`, `flake8`, `black`等工具进行代码检查和格式化。
- 启用开发者模式,可以在`config.yml`中设置`dev_mode: true`,这样可以在控制台看到更多的调试信息。
## 项目结构
- `main.py`: 机器人入口文件
- `liteyuki`: 框架目录liteyukibot
- `src`: 机器人源码目录
- `liteyuki_plugins`: 轻雪插件目录
- `nonebot_plugins`: Nonebot插件目录
- `resources`: 内置资源包目录
- `utils`: 工具目录
- `tests`: 单元测试目录
- `docs`: 文档目录
- `en`: 英文文档
- `zh`: 中文文档
- `docker`: Docker相关内容目录
其中,`liteyuki`框架是包含在`LiteyukiBot`应用中的,所以此项目既是`LiteyukiBot`应用仓库,同时也是`liteyuki`框架的仓库
`src`目录下为应用程序部分,业务逻辑请在此目录下进行开发,`liteyuki_plugins``nonebot_plugins`目录下为插件目录,插件开发请在此目录下进行开发。
## 规范化
- 代码请遵循[`PEP8`](https://pep8.org/)和[`Google Python Style Guide`](https://google.github.io/styleguide/pyguide.html)
- 提交到轻雪仓库的代码,请遵循以下规范:
- 请确保代码是可运行的,没有危害的。
- 请确保代码的类型提示是正确的。
- 请确保注释风格为[`Google Docstring`](https://google.github.io/styleguide/pyguide.html)或[`Liteyuki Docstring`](https://github.com/LiteyukiStudio/litedoc?tab=readme-ov-file#liteyuki-docstring)(推荐)以保证Litedoc能够正确解析并生成文档。
- 字符串请使用双引号`"`,尽量避免使用单引号`'`,但在嵌套字符串时可以使用单引号。
- 若有面向普通用户部分,请确保文档是完善的(每种语言都有对应的文档)。
- commit message请遵循以下规范
- 应在每次完成一个功能或修复一个bug后提交代码不要将多个功能或多个bug的代码混在一起提交。
- 我们使用gitmoji来标记commit的类型`:sparkles:`表示引入新功能,`:bug:`表示修复bug等具体请参考[gitmoji](https://gitmoji.dev/)
- commit message的格式为`<emoji> [type:] <message>`,如`:sparkles: feat: 给event添加新字段`
其中`type`字段为commit类型且**可选**message字段为简短的描述总结来说就是你干了什么。
- 不限制commit message的语言有条件可以使用中英双语。
- 文档请遵循[`Markdown`](https://www.markdownguide.org/)语法并且支持vitepress相关内容
- 修订文档时,每个语言的文档都要修订。
- 请确保文档内的链接是正确的,不要出现无法访问的死链接。
- 请确保**用户文档**是通俗易懂的,**开发文档**是详细的。
## 最后
- 本项目是一个非盈利的开源项目,我们欢迎任何人参与开发,你的贡献将会使轻雪变得更好。
<ContributorBar />

View File

@ -1,72 +0,0 @@
---
title: 轻雪函数
order: 2
---
# **轻雪函数**
轻雪函数 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都会被截停销毁
> [!tip]
> 编写轻雪函数推荐你使用VS Code插件[Liteyuki Function](https://github.com/LiteyukiStudio/lyfunctionTextmate)实现语法高亮

View File

@ -1,80 +0,0 @@
---
title: 轻雪插件开发
order: 3
---
# 简介
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
## 开始
### 创建插件
一个`.py`文件或一个包含`__init__.py`的文件夹即可被识别为插件
首先创建一个文件夹,例如`watchdog_plugin`,并在其中创建一个`__init__.py`文件,即可创建一个插件
`__init__.py`
```python
from liteyuki.plugin import PluginMetadata, PluginType
from .watch_dog import * # 导入逻辑部分
# 定义插件元数据
__plugin_meta__ = PluginMetadata(
name="NoneDog", # 插件名称
version="1.0.0", # 插件版本
description="A simple plugin for nonebot developer", # 插件描述
type=PluginType.SERVICE # 插件类型
)
# 你的插件代码
...
```
### 编写逻辑部分
轻雪主进程不涉及聊天部分,因此插件主要是一些后台任务或者与聊天机器人的通信
以下我们会编写一个简单的插件用于开发NoneBot时进行文件系统变更重载
`watch_dog.py`
```python
import os
from liteyuki.dev import observer # 导入文件系统观察器
from liteyuki import get_bot, logger # 导入轻雪Bot和日志
from watchdog.events import FileSystemEvent # 导入文件系统事件
liteyuki = get_bot() # 获取唯一的轻雪Bot实例
exclude_extensions = (".pyc", ".pyo") # 排除的文件扩展名
# 用observer的on_file_system_event装饰器监听文件系统事件
@observer.on_file_system_event(
directories=("src/nonebot_plugins",),
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path) and os.path.isfile(event.src_path)
)
def restart_nonebot_process(event: FileSystemEvent):
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
liteyuki.restart_process("nonebot") # 调用重启进程方法
```
### 加载插件
#### 方法1
- 在配置文件中的`liteyuki.plugins`中添加你的插件路径,例如`watchdog_plugin`,重启轻雪即可加载插件。
#### 方法2
- 使用开发工具快速运行插件,无需手动创建实例
- 创建入口文件,例如`main.py`,并在其中写入以下代码
```python
from liteyuki.dev.plugin import run_plugins
run_plugins("watchdog_plugin")
```
然后运行`python main.py`即可启动插件
启用插件后我们在src/nonebot_plugins下创建一个文件例如`test.py`并在其中写入一些代码保存后轻雪会自动重载NoneBot进程

View File

@ -1,51 +0,0 @@
---
title: 资源包开发
order: 1
---
# 简介
资源包,亦可根据用途称为主题包、字体包、语言包等,它允许你一定程度上自定义轻雪的外观,并且不用修改源代码
- [资源/主题商店](../store/resource)提供了一些资源包供你选择,你也可以自己制作资源包
- 资源包的制作很简单,如果你接触过`Minecraft`的资源包,那么你能够很快就上手,仅需按照原有路径进行文件替换即可,讲起打包成一个新的资源包。
- 部分内容制作需要一点点前端基础,例如`html``css`
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
- 欢迎各位投稿资源包到轻雪资源商店
请注意主题包中的html渲染使用Js来规定数据的渲染位置请确保您所编写的html代码能被Bot解析否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码以避免出现无法预料的问题。
---
## 加载资源包
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
```shell
main.py
resources
└─resource_pack_1
├─metadata.yml
├─templates
└───...
└─resource_pack_2
├─metadata.yml
└─...
```
- 你自己制作的资源包也应该遵循这个规则,并且应该在`metadata.yml`中填写一些信息
- 若没有`metadata.yml`文件,则该文件夹不会被识别为资源包
```yaml
name: "资源包名称"
version: "1.0.0"
description: "资源包描述"
# 你可以自定义一些信息,但请保证以上三个字段
...
```
- 资源包加载遵循一个优先级即后加载的资源包会覆盖前面的资源包例如你在A包中定义了一个`index.html`文件B包也定义了一个`index.html`文件那么加载B包后A包中的`index.html`文件会被覆盖
- 对于不同资源包的不同文件是可以相对引用的例如你在A中定义了`templates/index.html`在B中定义了`templates/style.css`可以在A的`index.html`中用`./style.css`相对路径引用B中的css
> [!tip]
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包

View File

@ -1,63 +0,0 @@
---
layout: home
hero:
name: "轻雪机器人"
tagline: "一个综合性的聊天机器人应用及管理框架"
actions:
- theme: brand
text: 🚀 快速开始
link: ./deploy/install
- theme: alt
text: 📖 使用手册
link: ./usage/basic
- theme: alt
text: 📦 扩展
link: ./store/resource
- theme: alt
text: 🛠️ 开发
link: ./dev/guide
image:
light: ./liteyuki.svg
dark: ./liteyuki-dark.svg
alt: Liteyuki Logo
features:
- title: 生态良好
icon: 🛠️
details: 基于轻雪框架原生支持任意Python Bot框架和应用程序
link: ./dev/api
- title: 插件管理
icon: 🧩
details: 使用包管理器,便捷管理插件及资源包
link: ./store/plugin
- title: 自由定制
icon: 🎨
details: 使用资源包对外观进行完全自定义
link: ./store/resource
- title: 国际化
icon: 🌐
details: 通过资源包支持多种语言
link: https://baike.baidu.com/item/i18n/6771940
- title: 简易使用
icon: ⚙️
details: 无需繁琐前期过程,开箱即用
link: ./deploy/config
- title: 超高性能
icon: 🚀
details: 500个插件2秒内启动
- title: 滚动更新
icon: ☁️
details: 让你的机器人保持最新提交
- title: 开源项目
icon: 📄
details: 项目遵循LSO LICENCE开源欢迎各位的贡献
link: https://github.com/LiteyukiStudio/LiteyukiStudioOpensourceLICENSE
---

View File

@ -1,6 +0,0 @@
---
title: 插件商店
order: 2
---
<PluginStore />

View File

@ -1,6 +0,0 @@
---
title: 资源商店
order: 1
---
<ResStore />

View File

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

View File

@ -1,122 +0,0 @@
---
title: 基础插件
order: 1
---
# 基础插件
---
> [!tip]
> **参数**`<param>`为必填参数,`[option]`为可选参数。
>
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。
## **轻雪命令`liteyuki_command`**
| 命令 | 说明 | 权限 | 举例 | 可用参数 |
| :----------------------------------------: | :---------------------------------------------------------------------------------------------: | :----------------------------------------: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------: |
| `reload-liteyuki` | 重载轻雪 | 超级用户 | ❌ | ❌ |
| `update-liteyuki` | 更新轻雪 | 超级用户 | ❌ | ❌ |
| `liteecho` | 查看当前bot 版本 | 超级用户 | ❌ | ❌ |
| `status` | 查看统计信息和状态 | 超级用户 | ❌ | ❌ |
| `config set <key> value` | 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的 | 超级用户 | `config set name 'liteyuki-bot'` | `<key>`: 若存在则覆盖, 若不存在则创建于`config.yml` ; `value`: yml格式的所有合法内容 |
| `config get [key] ` | 查询配置项不带key返回配置项列表推荐私聊使用 | 超级用户 | `config get name` | `<key>`: 若存在则返回, 若不存在则返回空 |
| `switch-image-mode ` | 在普通图片和Markdown大图之间切换该功能需要commit:505468b及以后的Lagrange.OneBot默认普通图片 | 超级用户 | `switch-image-mode` | ❌ |
| `/api api_name [args] ` | 调用机器人API | 超级用户 | `/api get_group_member_list group_id=1234567` | `<args>`: 参数列表, 格式为onebot v11协议api, 可用%20代替空格 |
| `/function function_name [args] [kwargs] ` | 调用机器人函数(`.lyfunction`语法) | 超级用户 | `/function send_group_msg group_id=1234567 message='hello'` | `<args>``<kwargs>`: 参数列表, api格式为onebot v11协议api |
| group enable/disable [group_id] | 在群聊启用/停用机器人group_id仅超级用户可用 | 超级用户,群聊仅群主、管理员、超级用户可用 | `group enable 1145141919810` | `<group_id>`: 群号 |
| liteyuki-docs | 查看轻雪文档 | 所有人 | ❌ | ❌ |
---
### **命令别名**
| 命令 | 别名 |
| :---------------: | :----------------------------------: |
| status | 状态 |
| reload-liteyuki | 重启轻雪 |
| update-liteyuki | 更新轻雪 |
| reload-resources | 重载资源 |
| config | 配置, `set` 设置 / `get` 查询 |
| switch-image-mode | 切换图片模式 |
| liteyuki-docs | 轻雪文档 |
| group | 群聊, `enable` 启用 / `disable` 停用 |
---
## **插件/包管理器 `liteyuki_pacman`**
- 插件管理
| 命令 | 说明 | 权限 |
| :-----------------------------------------------------: | :----------------------------------------: | :----------------------------------------------: |
| `npm update` | 更新插件商店索引 | 超级用户 |
| `npm install <plugin_name>` | 安装插件 | 超级用户 |
| `npm uninstall <plugin_name>` | 卸载插件 | 超级用户 |
| `npm search <keywords...>` | 通过关键词搜索插件 | 超级用户 |
| `npm enable-global/disable-global <plugin_name>` | 全局启用/停用插件 | 超级用户 |
| `npm enable/disable <plugin_name> [--group <group_id>]` | 当前会话启用/停用插件 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
| `npm list [page] [num]` | 列出所有插件 page为页数num为每页显示数量 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
| `help <plugin_name>` | 查看插件帮助 | 所有人 |
- 资源包管理
| 命令 | 说明 | 权限 |
| :----------------------: | :------------------------------------------: | :------: |
| `rpm list [page] [num]` | 列出所有资源包 page为页数num为每页显示数量 | 超级用户 |
| `rpm load <pack_name>` | 加载资源包 | 超级用户 |
| `rpm unload <pack_name>` | 卸载资源包 | 超级用户 |
| `rpm change <pack_name>` | 修改优先级 | 超级用户 |
| `rpm reload` | 重载所有资源包 | 超级用户 |
### 命令别名
| 命令 | 别名 |
| :--------------: | :------: |
| `npm` | 插件管理 |
| `update` | 更新 |
| `install` | 安装 |
| `uninstall` | 卸载 |
| `search` | 搜索 |
| `enable` | 启用 |
| `disable` | 停用 |
| `enable-global` | 全局启用 |
| `disable-global` | 全局停用 |
| `rpm` | 资源包 |
| `load` | 加载 |
| `unload` | 卸载 |
| `change` | 更改 |
| `reload` | 重载 |
| `list` | 列表 |
| `help` | 帮助 |
> [!warning]
> 受限于NoneBot2钩子函数的依赖注入参数插件停用只能阻断传入响应对于主动推送的插件不生效请阅读插件主页的说明。
>
---
## **用户管理`liteyuki_user`**
| 命令 | 说明 | 权限 |
| :-------------------------: | :----------------------------: | :----: |
| `profile` | 查看用户信息菜单 | 所有人 |
| `profile set <key> [value]` | 设置用户信息或打开属性设置菜单 | 所有人 |
| `profile get <key>` | 获取用户信息 | 所有人 |
###命令别名
| 命令 | 别名 |
| :-------: | :------: |
| `profile` | 个人信息 |
| `set` | 设置 |
| `get` | 查询 |

View File

@ -1,68 +0,0 @@
---
title: 功能命令
order: 2
---
# 功能插件命令
### **轻雪天气`liteyuki_weather`**
查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
配置项
```yaml
weather_key: "" # 和风天气的天气key会自动判断key版本
```
命令
```shell
weather <keywords...> # Keywords为城市名支持中英文
```
查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
```shell
bind-city <keywords...> # Keywords为城市名支持中英文
```
绑定查询城市,个人全局生效
#### 命令别名
| 命令 | 别名 |
| :-------: | :------- |
| weather | 天气 |
| bind-city | 绑定城市 |
---
### **统计信息`liteyuki_statistics`**
统计信息
命令
```shell
statistic message --duration <duration> --period <period> --group [current|group_id] --bot [current|bot_id]
```
功能: 用于统计Bot接收到的消息, 统计周期为`period`, 统计时间范围为`duration`
| 参数 | 格式 |
| :------: | :------------------------------------------------------------: |
| duration | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
| period | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
| group | `current` (当前群聊) 或 `group_id` (QQ群号) |
| bot | `current` (当前Bot) 或 `bot_id` |
#### 命令别名
| 命令 | 别名 |
| :----------: | :---: |
| `statistic` | stat |
| `message` | m |
| `--duration` | -d |
| --period` | -p |
| `--group` | -g |
| `--bot` | -b |
| `current` | c |

View File

@ -1,31 +0,0 @@
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

View File

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

View File

@ -1,54 +0,0 @@
"""
---
title: liteyuki API
order: 100
---
此模块为liteyuki的框架整体
This module is the overall framework of liteyuki.
"""
from liteyuki.bot import (
LiteyukiBot,
get_bot,
get_config,
get_config_with_compat
)
from liteyuki.comm import (
Channel,
Event
)
from liteyuki.plugin import (
load_plugin,
load_plugins
)
from liteyuki.log import (
init_log,
logger
)
__all__ = [
"LiteyukiBot",
"get_bot",
"get_config",
"get_config_with_compat",
"Channel",
"Event",
"load_plugin",
"load_plugins",
"init_log",
"logger",
]
__version__ = "6.3.10" # 测试版本号
# 6.3.10
# 新增`on_command`装饰器
# 6.3.9
# 更改了on语法
# 6.3.8
# 1. 初步添加对聊天的支持
# 2. 优化了通道的性能

View File

@ -1,294 +0,0 @@
import asyncio
import atexit
import os
import platform
import signal
import sys
import threading
import time
from typing import Any, Optional
from liteyuki.bot.lifespan import LIFESPAN_FUNC, Lifespan, PROCESS_LIFESPAN_FUNC
from liteyuki.comm.channel import get_channel
from liteyuki.core.manager import ProcessManager
from liteyuki.log import init_log, logger
from liteyuki.plugin import load_plugin
from liteyuki.utils import IS_MAIN_PROCESS
# new version
from liteyuki.core.manager import sub_process_manager
__all__ = [
"LiteyukiBot",
"get_bot",
"get_config",
"get_config_with_compat",
]
class LiteyukiBot:
def __init__(self, **kwargs) -> None:
"""
初始化轻雪实例
Args:
**kwargs: 配置
"""
"""常规操作"""
print_logo()
global _BOT_INSTANCE
_BOT_INSTANCE = self # 引用
"""配置"""
self.config: dict[str, Any] = kwargs
"""初始化"""
self.init(**self.config) # 初始化
logger.info("Liteyuki is initializing...")
"""生命周期管理"""
self.lifespan = Lifespan()
self.process_manager: ProcessManager = ProcessManager(lifespan=self.lifespan)
"""事件循环"""
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.stop_event = threading.Event()
self.call_restart_count = 0
"""加载插件加载器"""
load_plugin("liteyuki.plugins.plugin_loader") # 加载轻雪插件
async def _run(self):
"""
启动逻辑
"""
await self.lifespan.before_start() # 启动前钩子
sub_process_manager.start_all()
await self.lifespan.after_start() # 启动后钩子
await self.keep_alive()
def run(self):
"""
外部启动接口
"""
self.process_manager.start_all()
try:
asyncio.run(self._run())
except KeyboardInterrupt:
logger.opt(colors=True).info("<y>Liteyuki is stopping...</y>")
self.stop()
logger.opt(colors=True).info("<y>Liteyuki is stopped...</y>")
async def keep_alive(self):
"""
保持轻雪运行
"""
logger.info("Liteyuki is keeping alive...")
try:
while not self.stop_event.is_set():
await asyncio.sleep(0.1)
except Exception:
logger.info("Liteyuki is exiting...")
self.stop()
def restart(self, delay: int = 0):
"""
重启轻雪本体
Args:
delay ([`int`](https%3A//docs.python.org/3/library/functions.html#int), optional): 延迟重启时间. Defaults to 0.
"""
if self.call_restart_count < 1:
executable = sys.executable
args = sys.argv
logger.info("Restarting LiteyukiBot...")
time.sleep(delay)
if platform.system() == "Windows":
cmd = "start"
elif platform.system() == "Linux":
cmd = "nohup"
elif platform.system() == "Darwin":
cmd = "open"
else:
cmd = "nohup"
self.process_manager.terminate_all()
# 进程退出后重启
threading.Thread(
target=os.system,
args=(f"{cmd} {executable} {' '.join(args)}",),
daemon=True,
).start()
sys.exit(0)
self.call_restart_count += 1
def restart_process(self, name: Optional[str] = None):
"""
停止轻雪
Args:
name ([`Optional`](https%3A//docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)]): 进程名. Defaults to None.
Returns:
"""
if name is not None:
chan_active = get_channel(f"{name}-active")
chan_active.send(1)
else:
for process_name in self.process_manager.processes:
chan_active = get_channel(f"{process_name}-active")
chan_active.send(1)
def init(self, *args, **kwargs):
"""
初始化轻雪, 自动调用
Args:
*args: 参数
**kwargs: 关键字参数
"""
self.init_logger()
def init_logger(self):
"""
初始化日志
"""
init_log(config=self.config)
def stop(self):
"""
停止轻雪
"""
self.process_manager.terminate_all()
self.stop_event.set()
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""
注册启动前的函数
Args:
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
"""
return self.lifespan.on_before_start(func)
def on_after_start(self, func: LIFESPAN_FUNC):
"""
注册启动后的函数
Args:
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
"""
return self.lifespan.on_after_start(func)
def on_after_shutdown(self, func: LIFESPAN_FUNC):
"""
注册停止后的函数:未实现
Args:
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
"""
return self.lifespan.on_after_shutdown(func)
def on_before_process_shutdown(self, func: PROCESS_LIFESPAN_FUNC):
"""
注册进程停止前的函数,为子进程停止时调用
Args:
func ([`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func)): 生命周期函数
Returns:
[`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func): 生命周期函数
"""
return self.lifespan.on_before_process_shutdown(func)
def on_before_process_restart(
self, func: PROCESS_LIFESPAN_FUNC
) -> PROCESS_LIFESPAN_FUNC:
"""
注册进程重启前的函数,为子进程重启时调用
Args:
func ([`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func)): 生命周期函数
Returns:
[`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func): 生命周期函数
"""
return self.lifespan.on_before_process_restart(func)
def on_after_restart(self, func: LIFESPAN_FUNC):
"""
注册重启后的函数:未实现
Args:
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
"""
return self.lifespan.on_after_restart(func)
_BOT_INSTANCE: LiteyukiBot | None = None
def get_bot() -> LiteyukiBot:
"""
获取轻雪实例
Returns:
[`LiteyukiBot`](#class-liteyukibot): 轻雪实例
"""
if IS_MAIN_PROCESS:
if _BOT_INSTANCE is None:
raise RuntimeError("Liteyuki instance not initialized.")
return _BOT_INSTANCE
else:
raise RuntimeError("Can't get bot instance in sub process.")
def get_config(key: str, default: Any = None) -> Any:
"""
获取配置
Args:
key ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 配置键
default ([`Any`](https%3A//docs.python.org/3/library/functions.html#any), optional): 默认值. Defaults to None.
Returns:
[`Any`](https%3A//docs.python.org/3/library/functions.html#any): 配置值
"""
return get_bot().config.get(key, default)
def get_config_with_compat(
key: str, compat_keys: tuple[str], default: Any = None
) -> Any:
"""
获取配置,兼容旧版本
Args:
key ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 配置键
compat_keys ([`tuple`](https%3A//docs.python.org/3/library/stdtypes.html#tuple)[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 兼容键
default ([`Any`](https%3A//docs.python.org/3/library/functions.html#any), optional): 默认值. Defaults to None.
Returns:
[`Any`](https%3A//docs.python.org/3/library/functions.html#any): 配置值
"""
if key in get_bot().config:
return get_bot().config[key]
for compat_key in compat_keys:
if compat_key in get_bot().config:
logger.warning(
f'Config key "{compat_key}" will be deprecated, use "{key}" instead.'
)
return get_bot().config[compat_key]
return default
def print_logo():
"""@litedoc-hide"""
print(
"\033[34m"
+ r"""
__ ______ ________ ________ __ __ __ __ __ __ ______
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
"""
+ "\033[0m"
)

View File

@ -1,159 +0,0 @@
# -*- 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
"""
import asyncio
from typing import Any, Awaitable, Callable, TypeAlias, Sequence
from liteyuki.log import logger
from liteyuki.utils import is_coroutine_callable, async_wrapper
SYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Any] # 同步生命周期函数
ASYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Awaitable[Any]] # 异步生命周期函数
LIFESPAN_FUNC: TypeAlias = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC # 生命周期函数
SYNC_PROCESS_LIFESPAN_FUNC: TypeAlias = Callable[[str], Any] # 同步进程生命周期函数
ASYNC_PROCESS_LIFESPAN_FUNC: TypeAlias = Callable[[str], Awaitable[Any]] # 异步进程生命周期函数
PROCESS_LIFESPAN_FUNC: TypeAlias = SYNC_PROCESS_LIFESPAN_FUNC | ASYNC_PROCESS_LIFESPAN_FUNC # 进程函数
class Lifespan:
def __init__(self) -> None:
"""
轻雪生命周期管理,启动、停止、重启
"""
self.life_flag: int = 0
self._before_start_funcs: list[LIFESPAN_FUNC] = []
self._after_start_funcs: list[LIFESPAN_FUNC] = []
self._before_process_shutdown_funcs: list[PROCESS_LIFESPAN_FUNC] = []
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
self._before_process_restart_funcs: list[PROCESS_LIFESPAN_FUNC] = []
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
@staticmethod
async def run_funcs(funcs: Sequence[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None:
"""
并发运行异步函数
Args:
funcs ([`Sequence`](https%3A//docs.python.org/3/library/typing.html#typing.Sequence)[[`ASYNC_LIFESPAN_FUNC`](#var-lifespan-func) | [`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)]): 函数列表
Returns:
"""
tasks = [func(*args, **kwargs) if is_coroutine_callable(func) else async_wrapper(func)(*args, **kwargs) for func in funcs]
await asyncio.gather(*tasks)
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""
注册启动时的函数
Args:
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
"""
self._before_start_funcs.append(func)
return func
def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""
注册启动时的函数
Args:
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
"""
self._after_start_funcs.append(func)
return func
def on_before_process_shutdown(self, func: PROCESS_LIFESPAN_FUNC) -> PROCESS_LIFESPAN_FUNC:
"""
注册进程停止前的函数
Args:
func ([`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)): 进程生命周期函数
Returns:
[`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func): 进程生命周期函数
"""
self._before_process_shutdown_funcs.append(func)
return func
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""
注册停止后的函数
Args:
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
"""
self._after_shutdown_funcs.append(func)
return func
def on_before_process_restart(self, func: PROCESS_LIFESPAN_FUNC) -> PROCESS_LIFESPAN_FUNC:
"""
注册进程重启前的函数
Args:
func ([`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)): 进程生命周期函数
Returns:
[`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func): 进程生命周期函数
"""
self._before_process_restart_funcs.append(func)
return func
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""
注册重启后的函数
Args:
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
Returns:
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
"""
self._after_restart_funcs.append(func)
return func
async def before_start(self) -> None:
"""
启动前钩子
"""
logger.debug("Running before_start functions")
await self.run_funcs(self._before_start_funcs)
async def after_start(self) -> None:
"""
启动后钩子
"""
logger.debug("Running after_start functions")
await self.run_funcs(self._after_start_funcs)
async def before_process_shutdown(self, *args, **kwargs) -> None:
"""
停止前钩子
"""
logger.debug("Running before_shutdown functions")
await self.run_funcs(self._before_process_shutdown_funcs, *args, **kwargs)
async def after_shutdown(self) -> None:
"""
停止后钩子 未实现
"""
logger.debug("Running after_shutdown functions")
await self.run_funcs(self._after_shutdown_funcs)
async def before_process_restart(self, *args, **kwargs) -> None:
"""
重启前钩子
"""
logger.debug("Running before_restart functions")
await self.run_funcs(self._before_process_restart_funcs, *args, **kwargs)
async def after_restart(self) -> None:
"""
重启后钩子 未实现
"""
logger.debug("Running after_restart functions")
await self.run_funcs(self._after_restart_funcs)

View File

@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
"""
该模块用于轻雪主进程和Nonebot子进程之间的通信
依赖关系
event -> _
storage -> channel_
rpc -> channel_, storage
"""
from liteyuki.comm.channel import (
Channel,
get_channel,
set_channel,
set_channels,
get_channels,
active_channel,
passive_channel
)
from liteyuki.comm.event import Event
__all__ = [
"Channel",
"Event",
"get_channel",
"set_channel",
"set_channels",
"get_channels",
"active_channel",
"passive_channel"
]
from liteyuki.utils import IS_MAIN_PROCESS
# 第一次引用必定为赋值
_ref_count = 0
if not IS_MAIN_PROCESS:
if (active_channel is None or passive_channel is None) and _ref_count > 0:
raise RuntimeError("Error: Channel not initialized in sub process")
_ref_count += 1

View File

@ -1,332 +0,0 @@
# -*- coding: utf-8 -*-
"""
本模块定义了一个通用的通道类,用于进程间通信
"""
import asyncio
from multiprocessing import Pipe
from typing import (
Any,
Callable,
Coroutine,
Generic,
Optional,
TypeAlias,
TypeVar,
get_args,
)
from liteyuki.log import logger
from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable
T = TypeVar("T")
SYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[T], Any] # 同步接收函数
ASYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[
[T], Coroutine[Any, Any, Any]
] # 异步接收函数
ON_RECEIVE_FUNC: TypeAlias = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC # 接收函数
SYNC_FILTER_FUNC: TypeAlias = Callable[[T], bool] # 同步过滤函数
ASYNC_FILTER_FUNC: TypeAlias = Callable[[T], Coroutine[Any, Any, bool]] # 异步过滤函数
FILTER_FUNC: TypeAlias = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC # 过滤函数
_func_id: int = 0
_channel: dict[str, "Channel"] = {}
_callback_funcs: dict[int, ON_RECEIVE_FUNC] = {}
class Channel(Generic[T]):
"""
通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
"""
def __init__(self, name: str, type_check: Optional[bool] = None):
"""
初始化通道
Args:
name: 通道ID
type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭
"""
self.conn_send, self.conn_recv = Pipe()
self._conn_send_inner, self._conn_recv_inner = (
Pipe()
) # 内部通道,用于子进程通信
self._closed = False
self._on_main_receive_func_ids: list[int] = []
self._on_sub_receive_func_ids: list[int] = []
self.name: str = name
self.is_receive_loop_running = False
if type_check is None:
# 若传入泛型则默认开启类型检查
type_check = self._get_generic_type() is not None
elif type_check:
if self._get_generic_type() is None:
raise TypeError("Type hint is required for enforcing type check.")
self.type_check = type_check
if name in _channel:
raise ValueError(f"Channel {name} already exists")
if IS_MAIN_PROCESS:
if name in _channel:
raise ValueError(f"Channel {name} already exists")
_channel[name] = self
logger.debug(f"Channel {name} initialized in main process")
else:
logger.debug(
f"Channel {name} initialized in sub process, should manually set in main process"
)
def _get_generic_type(self) -> Optional[type]:
"""
获取通道传递泛型类型
Returns:
Optional[type]: 泛型类型
"""
if hasattr(self, "__orig_class__"):
return get_args(self.__orig_class__)[0]
return None
def _validate_structure(self, data: Any, structure: type) -> bool:
"""
验证数据结构
Args:
data: 数据
structure: 结构
Returns:
bool: 是否通过验证
"""
if isinstance(structure, type):
return isinstance(data, structure)
elif isinstance(structure, tuple):
if not isinstance(data, tuple) or len(data) != len(structure):
return False
return all(self._validate_structure(d, s) for d, s in zip(data, structure))
elif isinstance(structure, list):
if not isinstance(data, list):
return False
return all(self._validate_structure(d, structure[0]) for d in data)
elif isinstance(structure, dict):
if not isinstance(data, dict):
return False
return all(
k in data and self._validate_structure(data[k], structure[k])
for k in structure
)
return False
def __str__(self):
return f"Channel({self.name})"
def send(self, data: T):
"""
发送数据,发送函数为同步函数,没有异步的必要
Args:
data (T): 数据
"""
if self.type_check:
_type = self._get_generic_type()
if _type is not None and not self._validate_structure(data, _type):
raise TypeError(
f"Data must be an instance of {_type}, {type(data)} found"
)
if self._closed:
raise RuntimeError("Cannot send to a closed channel")
self.conn_send.send(data)
def receive(self) -> T:
"""
同步接收数据,会阻塞线程
Returns:
T: 数据
"""
if self._closed:
raise RuntimeError("Cannot receive from a closed channel")
while True:
data = self.conn_recv.recv()
return data
async def async_receive(self) -> T:
"""
异步接收数据,会挂起等待
Returns:
T: 数据
"""
loop = asyncio.get_running_loop()
data = await loop.run_in_executor(None, self.receive)
return data
def on_receive(
self, filter_func: Optional[FILTER_FUNC] = None
) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]:
"""
接收数据并执行函数
Args:
filter_func ([`Optional`](https%3A//docs.python.org/3/library/typing.html#typing.Optional)[[`FILTER_FUNC`](#var-FILTER_FUNC)], optional): 过滤函数. Defaults to None.
Returns:
Callable[[Callable[[T], Any]], Callable[[T], Any]]: 装饰器
"""
if not IS_MAIN_PROCESS:
raise RuntimeError("on_receive can only be used in main process")
def decorator(func: Callable[[T], Any]) -> Callable[[T], Any]:
global _func_id
async def wrapper(data: T) -> Any:
if filter_func is not None:
if is_coroutine_callable(filter_func):
if not (await filter_func(data)): # type: ignore
return
else:
if not filter_func(data):
return
if is_coroutine_callable(func):
return await func(data)
else:
return func(data)
_callback_funcs[_func_id] = wrapper
if IS_MAIN_PROCESS:
self._on_main_receive_func_ids.append(_func_id)
else:
self._on_sub_receive_func_ids.append(_func_id)
_func_id += 1
return func
return decorator
async def _run_on_receive_funcs(self, data: Any):
"""
运行接收函数
Args:
data: 数据
"""
if IS_MAIN_PROCESS:
[
asyncio.create_task(_callback_funcs[func_id](data))
for func_id in self._on_main_receive_func_ids
]
else:
[
asyncio.create_task(_callback_funcs[func_id](data))
for func_id in self._on_sub_receive_func_ids
]
"""子进程可用的主动和被动通道"""
active_channel: Channel = Channel(name="active_channel") # 主动通道
passive_channel: Channel = Channel(name="passive_channel") # 被动通道
publish_channel: Channel[tuple[str, dict[str, Any]]] = Channel(
name="publish_channel"
) # 发布通道
"""通道传递通道,主进程创建单例,子进程初始化时实例化"""
channel_deliver_active_channel: Channel[Channel[Any]] # 主动通道传递通道
channel_deliver_passive_channel: Channel[tuple[str, dict[str, Any]]] # 被动通道传递通道
if IS_MAIN_PROCESS:
channel_deliver_active_channel = Channel(
name="channel_deliver_active_channel"
) # 主动通道传递通道
channel_deliver_passive_channel = Channel(
name="channel_deliver_passive_channel"
) # 被动通道传递通道
@channel_deliver_passive_channel.on_receive(
filter_func=lambda data: data[0] == "set_channel"
)
def on_set_channel(data: tuple[str, dict[str, Any]]):
name, channel = data[1]["name"], data[1]["channel_"]
set_channel(name, channel)
@channel_deliver_passive_channel.on_receive(
filter_func=lambda data: data[0] == "get_channel"
)
def on_get_channel(data: tuple[str, dict[str, Any]]):
name, recv_chan = data[1]["name"], data[1]["recv_chan"]
recv_chan.send(get_channel(name))
@channel_deliver_passive_channel.on_receive(
filter_func=lambda data: data[0] == "get_channels"
)
def on_get_channels(data: tuple[str, dict[str, Any]]):
recv_chan = data[1]["recv_chan"]
recv_chan.send(get_channels())
def set_channel(name: str, channel: "Channel"):
"""
设置通道实例
Args:
name ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 通道名称
channel ([`Channel`](#class-channel-generic-t)): 通道实例
"""
if not isinstance(channel, Channel):
raise TypeError(
f"channel_ must be an instance of Channel, {type(channel)} found"
)
if IS_MAIN_PROCESS:
if name in _channel:
raise ValueError(f"Channel {name} already exists")
_channel[name] = channel
else:
# 请求主进程设置通道
channel_deliver_passive_channel.send(
(
"set_channel",
{
"name": name,
"channel_": channel,
},
)
)
def set_channels(channels: dict[str, "Channel"]):
"""
设置通道实例
Args:
channels ([`dict`](https%3A//docs.python.org/3/library/stdtypes.html#dict)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str), [`Channel`](#class-channel-generic-t)]): 通道实例
"""
for name, channel in channels.items():
set_channel(name, channel)
def get_channel(name: str) -> "Channel":
"""
获取通道实例
Args:
name ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 通道名称
Returns:
[`Channel`](#class-channel-generic-t): 通道实例
"""
if IS_MAIN_PROCESS:
return _channel[name]
else:
recv_chan = Channel[Channel[Any]]("recv_chan")
channel_deliver_passive_channel.send(
("get_channel", {"name": name, "recv_chan": recv_chan})
)
return recv_chan.receive()
def get_channels() -> dict[str, "Channel"]:
"""
获取通道实例们
Returns:
[`dict`](https%3A//docs.python.org/3/library/stdtypes.html#dict)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str), [`Channel`](#class-channel-generic-t)]: 通道实例
"""
if IS_MAIN_PROCESS:
return _channel
else:
recv_chan = Channel[dict[str, Channel[Any]]]("recv_chan")
channel_deliver_passive_channel.send(("get_channels", {"recv_chan": recv_chan}))
return recv_chan.receive()

View File

@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
"""
本模块用于轻雪主进程和子进程之间的通信的事件类
"""
from typing import Any
class Event:
"""
事件类
"""
def __init__(self, name: str, data: dict[str, Any]):
self.name = name
self.data = data

View File

@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
"""
基于socket的通道
"""
class SocksChannel:
"""
通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
"""
def __init__(self, name: str):
"""
初始化通道
Args:
name: 通道ID
"""
self._name = name
self._conn_send = None
self._conn_recv = None
self._closed = False
def send(self, data):
"""
发送数据
Args:
data: 数据
"""
pass
def receive(self):
"""
接收数据
Returns:
data: 数据
"""
pass
def close(self):
"""
关闭通道
"""
pass

View File

@ -1,271 +0,0 @@
# -*- coding: utf-8 -*-
"""
共享内存模块。类似于redis但是更加轻量级并且线程安全
"""
import asyncio
import threading
from typing import Any, Callable, Optional
from liteyuki.comm import channel
from liteyuki.comm.channel import ASYNC_ON_RECEIVE_FUNC, Channel, ON_RECEIVE_FUNC
from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine_in_thread
if IS_MAIN_PROCESS:
_locks = {}
_on_main_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore
"""主进程订阅者接收函数"""
_on_sub_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore
"""子进程订阅者接收函数"""
def _get_lock(key) -> threading.Lock:
"""
获取锁
"""
if IS_MAIN_PROCESS:
if key not in _locks:
_locks[key] = threading.Lock()
return _locks[key]
else:
raise RuntimeError("Cannot get lock in sub process.")
class KeyValueStore:
def __init__(self):
self._store = {}
self.active_chan = Channel[tuple[str, Optional[dict[str, Any]]]](name="shared_memory-active")
self.passive_chan = Channel[tuple[str, Optional[dict[str, Any]]]](name="shared_memory-passive")
self.publish_channel = Channel[tuple[str, Any]](name="shared_memory-publish")
self.is_main_receive_loop_running = False
self.is_sub_receive_loop_running = False
def set(self, key: str, value: Any) -> None:
"""
设置键值对
Args:
key: 键
value: 值
"""
if IS_MAIN_PROCESS:
lock = _get_lock(key)
with lock:
self._store[key] = value
else:
# 向主进程发送请求拿取
self.passive_chan.send(
(
"set",
{
"key" : key,
"value": value
}
)
)
def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
"""
获取键值对
Args:
key: 键
default: 默认值
Returns:
Any: 值
"""
if IS_MAIN_PROCESS:
lock = _get_lock(key)
with lock:
return self._store.get(key, default)
else:
recv_chan = Channel[Optional[Any]]("recv_chan")
self.passive_chan.send(
(
"get",
{
"key" : key,
"default" : default,
"recv_chan": recv_chan
}
)
)
return recv_chan.receive()
def delete(self, key: str, ignore_key_error: bool = True) -> None:
"""
删除键值对
Args:
key: 键
ignore_key_error: 是否忽略键不存在的错误
Returns:
"""
if IS_MAIN_PROCESS:
lock = _get_lock(key)
with lock:
if key in self._store:
try:
del self._store[key]
del _locks[key]
except KeyError as e:
if not ignore_key_error:
raise e
else:
# 向主进程发送请求删除
self.passive_chan.send(
(
"delete",
{
"key": key
}
)
)
def get_all(self) -> dict[str, Any]:
"""
获取所有键值对
Returns:
dict[str, Any]: 键值对
"""
if IS_MAIN_PROCESS:
return self._store
else:
recv_chan = Channel[dict[str, Any]]("recv_chan")
self.passive_chan.send(
(
"get_all",
{
"recv_chan": recv_chan
}
)
)
return recv_chan.receive()
def publish(self, channel_: str, data: Any) -> None:
"""
发布消息
Args:
channel_: 频道
data: 数据
Returns:
"""
self.active_chan.send(
(
"publish",
{
"channel": channel_,
"data" : data
}
)
)
def on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
"""
订阅者接收消息时的回调
Args:
channel_: 频道
Returns:
装饰器
"""
if not IS_MAIN_PROCESS:
raise RuntimeError("Cannot subscribe in sub process.")
def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC:
async def wrapper(data: Any):
if is_coroutine_callable(func):
await func(data)
else:
func(data)
if IS_MAIN_PROCESS:
if channel_ not in _on_main_subscriber_receive_funcs:
_on_main_subscriber_receive_funcs[channel_] = []
_on_main_subscriber_receive_funcs[channel_].append(wrapper)
else:
if channel_ not in _on_sub_subscriber_receive_funcs:
_on_sub_subscriber_receive_funcs[channel_] = []
_on_sub_subscriber_receive_funcs[channel_].append(wrapper)
return wrapper
return decorator
@staticmethod
async def run_subscriber_receive_funcs(channel_: str, data: Any):
"""
运行订阅者接收函数
Args:
channel_: 频道
data: 数据
"""
[asyncio.create_task(func(data)) for func in _on_main_subscriber_receive_funcs[channel_]]
async def start_receive_loop(self):
"""
启动发布订阅接收器循环,在主进程中运行,若有子进程订阅则推送给子进程
"""
if not IS_MAIN_PROCESS:
raise RuntimeError("Cannot start receive loop in sub process.")
while True:
data = await self.active_chan.async_receive()
if data[0] == "publish":
# 运行主进程订阅函数
await self.run_subscriber_receive_funcs(data[1]["channel"], data[1]["data"])
# 推送给子进程
self.publish_channel.send(data)
class GlobalKeyValueStore:
_instance = None
_lock = threading.Lock()
@classmethod
def get_instance(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = KeyValueStore()
return cls._instance
shared_memory: KeyValueStore = GlobalKeyValueStore.get_instance() # 共享内存对象
# 全局单例访问点
if IS_MAIN_PROCESS:
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "get")
def on_get(data: tuple[str, dict[str, Any]]):
key = data[1]["key"]
default = data[1]["default"]
recv_chan = data[1]["recv_chan"]
recv_chan.send(shared_memory.get(key, default))
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "set")
def on_set(data: tuple[str, dict[str, Any]]):
key = data[1]["key"]
value = data[1]["value"]
shared_memory.set(key, value)
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "delete")
def on_delete(data: tuple[str, dict[str, Any]]):
key = data[1]["key"]
shared_memory.delete(key)
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "get_all")
def on_get_all(data: tuple[str, dict[str, Any]]):
recv_chan = data[1]["recv_chan"]
recv_chan.send(shared_memory.get_all())
_ref_count = 0 # import 引用计数, 防止获取空指针
if not IS_MAIN_PROCESS:
if (shared_memory is None) and _ref_count > 1:
raise RuntimeError("Shared memory not initialized.")
_ref_count += 1

View File

@ -1,132 +0,0 @@
"""
该模块用于常用配置文件的加载
多配置文件编写原则:
1. 尽量不要冲突: 一个键不要多次出现
2. 分工明确: 每个配置文件给一个或一类服务提供配置
3. 扁平化编写: 配置文件尽量扁平化,不要出现过多的嵌套
4. 注意冲突时的优先级: 项目目录下的配置文件优先级高于config目录下的配置文件
5. 请不要将需要动态加载的内容写入配置文件,你应该使用其他储存方式
"""
import os
import json
import copy
import toml
import yaml
from typing import Any
from liteyuki.log import logger
_SUPPORTED_CONFIG_FORMATS = (".yaml", ".yml", ".json", ".toml")
def flat_config(config: dict[str, Any]) -> dict[str, Any]:
"""
扁平化配置文件
{a:{b:{c:1}}} -> {"a.b.c": 1}
Args:
config: 配置项目
Returns:
扁平化后的配置文件,但也包含原有的键值对
"""
new_config = copy.deepcopy(config)
for key, value in config.items():
if isinstance(value, dict):
for k, v in flat_config(value).items():
new_config[f"{key}.{k}"] = v
return new_config
def load_from_yaml(file_: str) -> dict[str, Any]:
"""
Load config from yaml file
"""
logger.debug(f"Loading YAML config from {file_}")
config = yaml.safe_load(open(file_, "r", encoding="utf-8"))
return flat_config(config if config is not None else {})
def load_from_json(file_: str) -> dict[str, Any]:
"""
Load config from json file
"""
logger.debug(f"Loading JSON config from {file_}")
config = json.load(open(file_, "r", encoding="utf-8"))
return flat_config(config if config is not None else {})
def load_from_toml(file_: str) -> dict[str, Any]:
"""
Load config from toml file
"""
logger.debug(f"Loading TOML config from {file_}")
config = toml.load(open(file_, "r", encoding="utf-8"))
return flat_config(config if config is not None else {})
def load_from_files(*files: str, no_warning: bool = False) -> dict[str, Any]:
"""
从指定文件加载配置项,会自动识别文件格式
默认执行扁平化选项
"""
config = {}
for file in files:
if os.path.exists(file):
if file.endswith((".yaml", "yml")):
config.update(load_from_yaml(file))
elif file.endswith(".json"):
config.update(load_from_json(file))
elif file.endswith(".toml"):
config.update(load_from_toml(file))
else:
if not no_warning:
logger.warning(f"Unsupported config file format: {file}")
else:
if not no_warning:
logger.warning(f"Config file not found: {file}")
return config
def load_configs_from_dirs(
*directories: str, no_waring: bool = False
) -> dict[str, Any]:
"""
从目录下加载配置文件,不递归
按照读取文件的优先级反向覆盖
默认执行扁平化选项
"""
config = {}
for directory in directories:
if not os.path.exists(directory):
if not no_waring:
logger.warning(f"Directory not found: {directory}")
continue
for file in os.listdir(directory):
if file.endswith(_SUPPORTED_CONFIG_FORMATS):
config.update(
load_from_files(os.path.join(directory, file), no_warning=no_waring)
)
return config
def load_config_in_default(no_waring: bool = False) -> dict[str, Any]:
"""
从一个标准的轻雪项目加载配置文件
项目目录下的config.*和config目录下的所有配置文件
项目目录下的配置文件优先
"""
config = load_configs_from_dirs("config", no_waring=no_waring)
config.update(
load_from_files(
"config.yaml",
"config.toml",
"config.json",
"config.yml",
no_warning=no_waring,
)
)
return config

View File

@ -1,2 +0,0 @@
from .manager import *

View File

@ -1,272 +0,0 @@
# -*- 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 multiprocessing
import threading
from multiprocessing import Process
from typing import Any, Callable, TYPE_CHECKING, TypeAlias
from croterline.context import Context
from croterline.process import SubProcess, ProcessFuncType
from liteyuki.log import logger
from liteyuki.utils import IS_MAIN_PROCESS
if TYPE_CHECKING:
from liteyuki.bot.lifespan import Lifespan
from liteyuki.comm.storage import KeyValueStore
from liteyuki.comm import Channel
if IS_MAIN_PROCESS:
from liteyuki.comm.channel import get_channel, publish_channel, get_channels
from liteyuki.comm.storage import shared_memory
from liteyuki.comm.channel import (
channel_deliver_active_channel,
channel_deliver_passive_channel,
)
else:
from liteyuki.comm import channel
from liteyuki.comm import storage
TARGET_FUNC: TypeAlias = Callable[..., Any]
TIMEOUT = 10
__all__ = ["ProcessManager", "sub_process_manager"]
multiprocessing.set_start_method("spawn", force=True)
class ChannelDeliver:
def __init__(
self,
active: Channel[Any],
passive: Channel[Any],
channel_deliver_active: Channel[Channel[Any]],
channel_deliver_passive: Channel[tuple[str, dict]],
publish: Channel[tuple[str, Any]],
):
self.active = active
self.passive = passive
self.channel_deliver_active = channel_deliver_active
self.channel_deliver_passive = channel_deliver_passive
self.publish = publish
# 函数处理一些跨进程通道的
def _delivery_channel_wrapper(
func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyValueStore", *args, **kwargs
):
"""
子进程入口函数
处理一些操作
"""
# 给子进程设置通道
if IS_MAIN_PROCESS:
raise RuntimeError("Function should only be called in a sub process.")
channel.active_channel = cd.active # 子进程主动通道
channel.passive_channel = cd.passive # 子进程被动通道
channel.channel_deliver_active_channel = (
cd.channel_deliver_active
) # 子进程通道传递主动通道
channel.channel_deliver_passive_channel = (
cd.channel_deliver_passive
) # 子进程通道传递被动通道
channel.publish_channel = cd.publish # 子进程发布通道
# 给子进程创建共享内存实例
storage.shared_memory = sm
func(*args, **kwargs)
class ProcessManager:
"""
进程管理器
"""
def __init__(self, lifespan: "Lifespan"):
self.lifespan = lifespan
self.targets: dict[str, tuple[Callable, tuple, dict]] = {}
self.processes: dict[str, Process] = {}
def _run_process(self, name: str):
"""
开启后自动监控进程并添加到进程字典中会阻塞请创建task
Args:
name:
Returns:
"""
if name not in self.targets:
raise KeyError(f"Process {name} not found.")
chan_active = get_channel(f"{name}-active")
def _start_process():
process = Process(
target=self.targets[name][0],
args=self.targets[name][1],
kwargs=self.targets[name][2],
daemon=True,
)
self.processes[name] = process
process.start()
# 启动进程并监听信号
_start_process()
while True:
data = chan_active.receive()
if data == 0:
# 停止
logger.info(f"Stopping process {name}")
self.terminate(name)
break
elif data == 1:
# 重启
logger.info(f"Restarting process {name}")
self.terminate(name)
_start_process()
continue
else:
logger.warning("Unknown data received, ignored.")
def start_all(self):
"""
对外启动方法启动所有进程创建asyncio task
"""
# [asyncio.create_task(self._run_process(name)) for name in self.targets]
for name in self.targets:
logger.debug(f"Starting process {name}")
threading.Thread(
target=self._run_process, args=(name,), daemon=True
).start()
def add_target(self, name: str, target: TARGET_FUNC, args: tuple = (), kwargs=None):
"""
添加进程
Args:
name: 进程名,用于获取和唯一标识
target: 进程函数
args: 进程函数参数
kwargs: 进程函数关键字参数通常会默认传入chan_active和chan_passive
"""
if kwargs is None:
kwargs = {}
chan_active: Channel = Channel(name=f"{name}-active")
chan_passive: Channel = Channel(name=f"{name}-passive")
channel_deliver = ChannelDeliver(
active=chan_active,
passive=chan_passive,
channel_deliver_active=channel_deliver_active_channel,
channel_deliver_passive=channel_deliver_passive_channel,
publish=publish_channel,
)
self.targets[name] = (
_delivery_channel_wrapper,
(target, channel_deliver, shared_memory, *args),
kwargs,
)
# 主进程通道
def join_all(self):
for name, process in self.targets:
process.join()
def terminate(self, name: str):
"""
终止进程并从进程字典中删除
Args:
name:
Returns:
"""
if name not in self.processes:
logger.warning(f"Process {name} not found.")
return
process = self.processes[name]
process.terminate()
process.join(TIMEOUT)
if process.is_alive():
process.kill()
logger.success(f"Process {name} terminated.")
def terminate_all(self):
for name in self.targets:
self.terminate(name)
def is_process_alive(self, name: str) -> bool:
"""
检查进程是否存活
Args:
name:
Returns:
"""
if name not in self.targets:
logger.warning(f"Process {name} not found.")
return self.processes[name].is_alive()
# new version
class _SubProcessManager:
def __init__(self):
self.processes: dict[str, SubProcess] = {}
def new_process(
self, name: str, *args, **kwargs
) -> Callable[[ProcessFuncType], None]:
def decorator(func: ProcessFuncType):
self.processes[name] = SubProcess(name, func, *args, **kwargs)
return decorator
def add(self, name: str, func: ProcessFuncType, *args, **kwargs):
"""
添加子进程
Args:
func: 子进程函数
name: 子进程名称
args: 子进程函数参数
kwargs: 子进程函数关键字参数
Returns:
"""
self.processes[name] = SubProcess(name, func, *args, **kwargs)
def start(self, name: str):
"""
启动指定子进程
Args:
name: 子进程名称
Returns:
"""
if name not in self.processes:
raise KeyError(f"Process {name} not found.")
self.processes[name].start()
def start_all(self):
"""
启动所有子进程
"""
for name, process in self.processes.items():
process.start()
logger.debug(f"Starting process {name}")
sub_process_manager = _SubProcessManager()

View File

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
"""
该模块用于存放一些开发工具
"""

View File

@ -1,90 +0,0 @@
"""
此模块用于注册观察者函数使用watchdog监控文件变化并重启bot
启用该模块需要在配置文件中设置`dev_mode`为True
"""
import time
from typing import Callable, TypeAlias
from watchdog.events import FileSystemEvent, FileSystemEventHandler
from watchdog.observers import Observer
from liteyuki import get_bot, get_config_with_compat, logger
liteyuki_bot = get_bot()
CALLBACK_FUNC: TypeAlias = Callable[[FileSystemEvent], None] # 位置1为FileSystemEvent
FILTER_FUNC: TypeAlias = Callable[[FileSystemEvent], bool] # 位置1为FileSystemEvent
observer = Observer()
def debounce(wait):
"""
防抖函数
"""
def decorator(func):
def wrapper(*args, **kwargs):
nonlocal last_call_time
current_time = time.time()
if (current_time - last_call_time) > wait:
last_call_time = current_time
return func(*args, **kwargs)
last_call_time = None
return wrapper
return decorator
if get_config_with_compat("liteyuki.reload", ("dev_mode",), False):
logger.debug("Liteyuki Reload enabled, watching for file changes...")
observer.start()
class CodeModifiedHandler(FileSystemEventHandler):
"""
Handler for code file changes
"""
@debounce(1)
def on_modified(self, event):
raise NotImplementedError("on_modified must be implemented")
def on_created(self, event):
self.on_modified(event)
def on_deleted(self, event):
self.on_modified(event)
def on_moved(self, event):
self.on_modified(event)
def on_any_event(self, event):
self.on_modified(event)
def on_file_system_event(directories: tuple[str], recursive: bool = True, event_filter: FILTER_FUNC = None) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]:
"""
注册文件系统变化监听器
Args:
directories: 监听目录们
recursive: 是否递归监听子目录
event_filter: 事件过滤器, 返回True则执行回调函数
Returns:
装饰器,装饰一个函数在接收到数据后执行
"""
def decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC:
def wrapper(event: FileSystemEvent):
if event_filter is not None and not event_filter(event):
return
func(event)
code_modified_handler = CodeModifiedHandler()
code_modified_handler.on_modified = wrapper
for directory in directories:
observer.schedule(code_modified_handler, directory, recursive=recursive)
return func
return decorator

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/18 上午5:04
@Author : snowykami
@Email : snowykami@outlook.com
@File : plugin.py
@Software: PyCharm
"""
from pathlib import Path
from liteyuki.bot import LiteyukiBot
from liteyuki.config import load_config_in_default
def run_plugins(*module_path: str | Path):
"""
运行插件无需手动初始化bot
Args:
module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名
"""
cfg = load_config_in_default()
plugins = cfg.get("liteyuki.plugins", [])
plugins.extend(module_path)
cfg["liteyuki.plugins"] = plugins
bot = LiteyukiBot(**cfg)
bot.run()

View File

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

View File

@ -1,63 +0,0 @@
# -*- 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
logger = loguru.logger
# 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
def init_log(config: dict):
"""
在语言加载完成后执行
Returns:
"""
logger.remove()
logger.add(
sys.stdout,
level=0,
diagnose=False,
format=get_format(config.get("log_level", "INFO")),
)
show_icon = config.get("log_icon", True)
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")
init_log(config={})

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/19 下午10:44
@Author : snowykami
@Email : snowykami@outlook.com
@File : __init__.py.py
@Software: PyCharm
"""

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