mirror of
https://github.com/LiteyukiStudio/LiteyukiBot.git
synced 2025-05-22 23:01:17 +00:00
🚀 v7
This commit is contained in:
parent
fc017c8255
commit
26c9cb7e35
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
59
.github/ISSUE_TEMPLATE/resource_publish_en.yml
vendored
59
.github/ISSUE_TEMPLATE/resource_publish_en.yml
vendored
@ -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
|
59
.github/ISSUE_TEMPLATE/resource_publish_zh.yml
vendored
59
.github/ISSUE_TEMPLATE/resource_publish_zh.yml
vendored
@ -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
|
53
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
53
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
@ -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`的输出,以及其他相关信息,以及你的建议
|
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@ -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"
|
43
.github/workflows/build-image.yml
vendored
43
.github/workflows/build-image.yml
vendored
@ -1,43 +0,0 @@
|
||||
|
||||
name: Docker Image Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: docker-build
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_USERNAME }}
|
||||
password: ${{ secrets.GHCR_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ghcr.io/liteyukistudio/liteyukibot:latest
|
||||
|
||||
- name: Log out from GitHub Container Registry
|
||||
run: docker logout ghcr.io
|
66
.github/workflows/deploy-docs-liteyukicloud.yml
vendored
66
.github/workflows/deploy-docs-liteyukicloud.yml
vendored
@ -1,66 +0,0 @@
|
||||
name: Deploy VitePress site to Liteyuki PaaS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: write
|
||||
statuses: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
MELI_SITE: "800a7c09-dd9c-4ba7-a43f-af9bc69808c4"
|
||||
|
||||
jobs:
|
||||
# 构建工作
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 如果未启用 lastUpdated,则不需要
|
||||
- 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: "发布"
|
||||
run: |
|
||||
npx -p "@getmeli/cli" meli upload docs/.vitepress/dist \
|
||||
--url "https://pages.liteyuki.icu" \
|
||||
--site "$MELI_SITE" \
|
||||
--token "$MELI_TOKEN" \
|
||||
--release "$GITHUB_SHA"
|
||||
env:
|
||||
MELI_TOKEN: ${{ secrets.MELI_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
51
.github/workflows/issue_handler.yml
vendored
51
.github/workflows/issue_handler.yml
vendored
@ -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 }}
|
30
.github/workflows/pre-commit.yml
vendored
30
.github/workflows/pre-commit.yml
vendored
@ -1,30 +0,0 @@
|
||||
name: Pre-commit checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13'] # 添加你想要测试的 Python 版本
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }} # 使用矩阵中的 Python 版本
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install pdm
|
||||
python -m pip install pre-commit
|
||||
pdm config python.use_venv false
|
||||
pdm install --no-lock
|
||||
pre-commit install
|
||||
|
||||
- name: Run pre-commit
|
||||
run: pre-commit run --all-files
|
21
.github/workflows/pypi-publish.yml
vendored
21
.github/workflows/pypi-publish.yml
vendored
@ -1,21 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pdm-project/setup-pdm@v3
|
||||
|
||||
- name: Publish package distributions to PyPI
|
||||
run: pdm publish
|
74
.gitignore
vendored
74
.gitignore
vendored
@ -1,74 +0,0 @@
|
||||
.venv/
|
||||
.idea/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
venv/
|
||||
|
||||
node_modules/
|
||||
data/
|
||||
db/
|
||||
/resources/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.pyw
|
||||
/plugins/
|
||||
|
||||
#config
|
||||
/config/
|
||||
!config/default.yml
|
||||
_config.yml
|
||||
config.yml
|
||||
config.example.yml
|
||||
|
||||
# vuepress
|
||||
|
||||
# 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
|
||||
.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
|
||||
|
||||
# python toolchain
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
|
||||
# pdm
|
||||
__pypackages__/
|
||||
pdm.lock
|
@ -1,26 +0,0 @@
|
||||
fail_fast: true
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 24.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--config=./pyproject.toml]
|
||||
|
||||
- repo: https://github.com/timothycrosley/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
args: ["--profile", "black"]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.13.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
17
Dockerfile
17
Dockerfile
@ -1,17 +0,0 @@
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/python:3.10-slim-bullseye
|
||||
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
RUN apt-get update && apt-get install -y git
|
||||
|
||||
WORKDIR /liteyukibot
|
||||
|
||||
COPY . /liteyukibot
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
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 libpango-1.0-0 libcairo2
|
||||
|
||||
EXPOSE 20216
|
||||
|
||||
CMD ["python", "main.py"]
|
31
LICENSE
31
LICENSE
@ -1,31 +0,0 @@
|
||||
LSO license
|
||||
LiteyukiStudio Opensource license
|
||||
|
||||
---
|
||||
|
||||
版权所有 © 2024 Snowykami
|
||||
|
||||
---
|
||||
|
||||
免费向任何获得副本的人或组织授予以相同许可为基础的权利
|
||||
|
||||
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件的副本
|
||||
|
||||
本软件及相关文档文件(以下简称"本软件")在相同方式许可为基础, 以开源的形式发布于互联网抑或其他媒体介质平台
|
||||
|
||||
任何人都有权利获取副本并以上述方式获取许可传播和/或使用
|
||||
|
||||
但获取副本时仍需注意:
|
||||
|
||||
- 上述版权声明和本许可声明应包含在本软件的副本中
|
||||
- 使用本软件及其副本时仍需保持与原有形式相同
|
||||
|
||||
- 在使用时仍需将本软件的副本以相同许可公开表现:
|
||||
- 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利
|
||||
|
||||
---
|
||||
|
||||
该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示:
|
||||
包括但不限于适销性保证, 适用于特定目的非侵权
|
||||
|
||||
在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付
|
70
README.md
70
README.md
@ -1,70 +0,0 @@
|
||||
<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]][liteyuki-link]
|
||||
<h2><a href="https://bot.liteyuki.icu"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">6</span></a></h2>
|
||||
<h4> <span style="color: #a2d8f4">✨ 轻量,高效,易于扩展✨</span></h4>
|
||||
|
||||
[![][Liteyuki6.0]][liteyuki-link]
|
||||
[![][Python3.10+]][python-link]
|
||||
[![][Usage]][usage-link]
|
||||
[![][Repo]][repo-link]
|
||||
[![][Github]][github-link]
|
||||
[![][LiteyukiLab]][liteyukilab-link]
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 关于
|
||||
|
||||
访问[轻雪6.0](https://bot.liteyuki.icu)主页获取更多信息
|
||||
|
||||
## 特点及优势
|
||||
|
||||
- 原生支持与任意`Python`Bot框架互联,有良好的生态支持
|
||||
- 开箱即用,无需复杂配置
|
||||
- 集成包管理器,支持一键安装插件
|
||||
- 自定义主题支持,满足审美需求
|
||||
- 国际化支持,支持多种语言
|
||||
- 高性能,500插件2s内启动
|
||||
|
||||
## 服务及支持(敬请期待)
|
||||
- 提供Liteyuki Cloud官方的容器化托管服务,无需担心服务器问题
|
||||
|
||||
**👇所有内容请访问👇**: [bot.liteyuki.icu](https://bot.liteyuki.icu)
|
||||
|
||||
## 参考及鸣谢
|
||||
- [nonebot-plugin-uninfo](https://github.com/RF-Tar-Railt/nonebot-plugin-uninfo)为会话部分用户信息提供了参考
|
||||
- [nonebot-plugin-alconna](https://github.com/nonebot/plugin-alconna/)为消息部分提供了参考
|
||||
|
||||
## 其他
|
||||
- 本仓库是一个monorepo,包含了框架,文档,测试,内置资源包,内置插件,预设配置等
|
||||
|
||||
|
||||
[Liteyuki6.0]: https://img.shields.io/badge/Liteyuki-6.0-blue?style=for-the-badge
|
||||
|
||||
[Python3.10+]: https://img.shields.io/badge/Python-3.10+-blue?style=for-the-badge
|
||||
|
||||
[Usage]: https://img.shields.io/badge/主页-文档-blue?style=for-the-badge
|
||||
|
||||
[Repo]: https://img.shields.io/badge/官方托管-仓库-blue?style=for-the-badge
|
||||
|
||||
[Github]: https://img.shields.io/badge/Github-仓库-blue?style=for-the-badge
|
||||
|
||||
[LiteyukiLab]: https://img.shields.io/badge/轻雪社区-官方-blue?style=for-the-badge
|
||||
|
||||
|
||||
|
||||
[python-link]:https://www.python.org/
|
||||
|
||||
[usage-link]:https://bot.liteyuki.icu/
|
||||
|
||||
[liteyuki-link]:https://bot.liteyuki.icu/
|
||||
|
||||
[repo-link]:https://git.liteyuki.icu/bot/app
|
||||
|
||||
[github-link]:https://github.com/LiteyukiStudio/LiteyukiBot
|
||||
|
||||
[liteyukilab-link]:https://lab.liteyuki.icu/@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.icu%2fstatic%2fsvg%2flylogo-full.svg
|
@ -1,10 +0,0 @@
|
||||
nonebot:
|
||||
host: 0.0.0.0
|
||||
port: 20216
|
||||
command_start: ["", "/"]
|
||||
nickname: [ "liteyuki" ]
|
||||
default_language: zh
|
||||
driver: ~fastapi+~httpx+~websockets
|
||||
alconna_use_command_start: true
|
||||
gotify_token: "empty token"
|
||||
|
@ -1,12 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
image: git.liteyuki.icu/bot/app:latest
|
||||
ports:
|
||||
- "20216:20216"
|
||||
environment:
|
||||
- TZ=Asia/Chongqing
|
||||
volumes:
|
||||
- .:/liteyukibot
|
||||
command: [ "python", "main.py" ]
|
@ -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
|
@ -1,138 +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;
|
||||
}
|
||||
|
||||
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",
|
||||
},
|
||||
],
|
||||
["script", { src: "https://cdn.liteyuki.icu/js/liteyuki_footer.js" }],
|
||||
],
|
||||
rewrites: {
|
||||
[`${defaultLocale}/:rest*`]: ":rest*",
|
||||
},
|
||||
cleanUrls: false,
|
||||
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/bot/app",
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
svg: '<svg t="1736700504329" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14158" width="200" height="200"><path d="M944.355556 142.222222c-17.066667-22.755556-45.511111-34.133333-79.644445-34.133333-28.444444 0-68.266667 5.688889-108.088889 22.755555h-5.688889c17.066667 11.377778 34.133333 28.444444 51.2 39.822223 56.888889-17.066667 91.022222-11.377778 96.711111 0 11.377778 11.377778 5.688889 45.511111-22.755555 91.022222 11.377778 17.066667 17.066667 34.133333 28.444444 51.2 0 0 0 5.688889 5.688889 5.688889 22.755556-34.133333 34.133333-62.577778 45.511111-91.022222 11.377778-28.444444 5.688889-62.577778-11.377777-85.333334z" p-id="14159" fill="#a2d8f4"></path><path d="M267.377778 512a45.511111 45.511111 0 1 0 91.022222 0 45.511111 45.511111 0 1 0-91.022222 0Z" p-id="14160" fill="#a2d8f4"></path><path d="M625.777778 614.4c-113.777778 85.333333-227.555556 153.6-324.266667 193.422222-11.377778 5.688889-17.066667 5.688889-28.444444 11.377778 22.755556 17.066667 51.2 34.133333 79.644444 45.511111 51.2 22.755556 108.088889 34.133333 164.977778 34.133333s113.777778-11.377778 164.977778-34.133333c51.2-22.755556 96.711111-51.2 136.533333-91.022222 39.822222-39.822222 68.266667-85.333333 91.022222-130.844445 22.755556-51.2 34.133333-108.088889 34.133334-159.288888 0-51.2-11.377778-102.4-28.444445-153.6-5.688889 5.688889-11.377778 17.066667-17.066667 22.755555-68.266667 79.644444-164.977778 176.355556-273.066666 261.688889zM813.511111 187.733333c-5.688889-5.688889-11.377778-5.688889-11.377778-11.377777-17.066667-17.066667-34.133333-28.444444-51.2-39.822223-22.755556-11.377778-45.511111-28.444444-68.266666-34.133333-56.888889-28.444444-108.088889-39.822222-164.977778-39.822222s-113.777778 11.377778-164.977778 34.133333c-51.2 22.755556-96.711111 51.2-136.533333 91.022222-39.822222 34.133333-68.266667 79.644444-91.022222 130.844445-22.755556 51.2-34.133333 108.088889-34.133334 159.288889 0 51.2 11.377778 96.711111 22.755556 142.222222-22.755556 34.133333-39.822222 68.266667-51.2 96.711111-11.377778 39.822222-5.688889 68.266667 11.377778 91.022222 17.066667 22.755556 45.511111 34.133333 79.644444 34.133334h11.377778c28.444444 0 62.577778-11.377778 96.711111-22.755556-17.066667-11.377778-34.133333-28.444444-51.2-39.822222-51.2 11.377778-85.333333 11.377778-96.711111 0 0-17.066667 5.688889-45.511111 34.133333-96.711111 17.066667 34.133333 39.822222 62.577778 68.266667 91.022222h5.688889c17.066667-5.688889 39.822222-11.377778 62.577777-17.066667 91.022222-34.133333 204.8-102.4 307.2-187.733333 108.088889-85.333333 199.111111-170.666667 256-250.311111l34.133334-51.2c-22.755556-28.444444-39.822222-56.888889-68.266667-79.644445z m-500.622222 420.977778c-56.888889 0-102.4-45.511111-102.4-102.4s45.511111-102.4 102.4-102.4S409.6 455.111111 409.6 512c0 51.2-45.511111 96.711111-96.711111 96.711111z" p-id="14161" fill="#a2d8f4"></path></svg>',
|
||||
},
|
||||
link: "https://lab.liteyuki.icu/@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 },
|
||||
},
|
||||
});
|
@ -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: 'Page is deployed at <a href="https://meli.liteyuki.icu" target="_blank">Liteyuki Meli</a> and accelerated by <a href="https://cdn.liteyuki.icu" target="_blank">Liteyukiflare</a><br>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',
|
||||
},
|
||||
})
|
@ -1,6 +0,0 @@
|
||||
import {defineConfig} from "vitepress";
|
||||
|
||||
import {common} from './common'
|
||||
export default defineConfig({
|
||||
...common,
|
||||
})
|
@ -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'
|
||||
}
|
@ -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://meli.liteyuki.icu" target="_blank">Liteyuki Meli</a> 由 <a href="https://cdn.liteyuki.icu" target="_blank">Liteyukiflare</a> 提供加速服务<br>文档由 <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: '深色模式',
|
||||
},
|
||||
})
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -1,10 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -1,11 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,4 +0,0 @@
|
||||
export const platformBaseURL = "https://github.com/"
|
||||
export const repoPath = "LiteyukiStudio/LiteyukiBot"
|
||||
|
||||
export const repoURL = `${platformBaseURL}${repoPath}`
|
@ -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;
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
@ -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
|
@ -1,30 +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
|
||||
|
||||
- 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?
|
||||
- Some plugins provide the ability to log in to specific platforms, for example, using the NoneBot plugin to log in to supported adapter platforms
|
||||
|
||||
- `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=)
|
@ -1,62 +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 Repo
|
||||
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
|
||||
|
||||
# Change directory
|
||||
cd LiteyukiBot
|
||||
|
||||
# Create virtual environment
|
||||
python -m venv venv
|
||||
|
||||
# Activate virtual environment
|
||||
.\venv\Scripts\activate # Windows
|
||||
source venv/bin/activate # Linux
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run Liteyuki
|
||||
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**
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/liteyukistudio/liteyukibot:latest # Nightly build
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$(pwd)` <br>
|
||||
> If you have modified the port number, please replace `20216:20216` with your port number
|
||||
|
||||
|
||||
## **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)
|
@ -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
|
@ -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)
|
@ -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 />
|
@ -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)实现语法高亮
|
@ -1,80 +0,0 @@
|
||||
---
|
||||
title: 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进程
|
@ -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]
|
||||
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包
|
@ -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
|
||||
---
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: Plugin Store
|
||||
order: 2
|
||||
---
|
||||
|
||||
<PluginStore />
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: Resource Store
|
||||
order: 1
|
||||
---
|
||||
|
||||
<ResStore />
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: 用户协议
|
||||
order: 3
|
||||
---
|
||||
# 用户协议
|
||||
|
||||
1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息
|
||||
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
|
||||
- 运行环境的设备信息:CPU,内存,系统信息及Python信息
|
||||
- 插件信息(不含插件数据)
|
||||
- 部分异常信息,
|
||||
- 会话负载信息(不含隐私部分)
|
||||
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`
|
||||
3. 本项目不会收集用户的任何隐私信息,但请注意甄别第三方插件的安全性
|
||||
4. 使用此项目代表你已经同意以上协议
|
@ -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` | 查询 |
|
||||
|
||||
|
@ -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 |
|
@ -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"
|
||||
}
|
||||
}
|
2120
docs/pnpm-lock.yaml
generated
2120
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 |
@ -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 |
@ -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 |
@ -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"
|
||||
}
|
||||
]
|
@ -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"
|
||||
}
|
||||
]
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": [
|
||||
"components/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: 适配器
|
||||
order: 3
|
||||
---
|
||||
|
||||
## **示例:与NoneBot对接的OneBot实现端配置**
|
||||
|
||||
生产环境中推荐反向WebSocket
|
||||
不同的实现端给出的字段可能不同,但是基本上都是一样的,这里给出一个参考值
|
||||
|
||||
| 字段 | 参考值 | 说明 |
|
||||
|----------------|--------------------------------|----------------------------------------------------------------------|
|
||||
| 协议 | 反向 WebSocket | 推荐使用反向 WebSocket 协议进行通信,即轻雪作为服务端运行。 |
|
||||
| 地址 | `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支持的任何适配器](https://github.com/nonebot/)(链接指向nonebot的github主页)或使用轻雪讯息传递插件
|
||||
|
||||
例如:
|
||||
- [Console Adapter](https://github.com/nonebot/adapter-console)
|
||||
- [Mirai Adapter](https://github.com/nonebot/adapter-mirai)
|
||||
等
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: 配置
|
||||
order: 2
|
||||
---
|
||||
# 配置
|
||||
|
||||
## 轻雪配置文件说明
|
||||
|
||||
轻雪支持 `.yaml`、`.json` 和 `.toml` 三种文件类型作为配置文件, 您可以根据个人喜好选择使用
|
||||
|
||||
### 配置文件生成
|
||||
|
||||
首次运行轻雪后,会生成以下文件和目录结构:
|
||||
|
||||
```
|
||||
./
|
||||
├── config.yml # 默认生成的配置文件
|
||||
└── config/ # 配置目录,可包含多个配置文件
|
||||
└── file1.yaml # 示例配置文件1
|
||||
└── file2.json # 示例配置文件2
|
||||
└── file3.toml # 示例配置文件3
|
||||
```
|
||||
|
||||
|
||||
### 配置文件修改
|
||||
|
||||
您可以修改生成的 `config.yml` 文件或在 `config` 目录下添加新的配置文件
|
||||
|
||||
绝大多数情况下,您只需要修改以下字段:
|
||||
|
||||
- `superusers`:超级用户列表
|
||||
- `nickname`:昵称配置
|
||||
|
||||
修改完成后,重启轻雪以应用新的配置
|
||||
|
||||
### 配置文件加载
|
||||
|
||||
启动轻雪时,会加载项目目录下以下文件和 `config` 目录下的所有配置文件:
|
||||
|
||||
- `config.yml`
|
||||
- `config.yaml`
|
||||
- `config.json`
|
||||
- `config.toml`
|
||||
|
||||
轻雪会自动合并 `config` 目录下的所有配置文件
|
||||
|
||||
这意味着您可以在 `config` 目录下创建多个配置文件, 轻雪会将它们的内容合并为一个完整的配置
|
||||
|
||||
## 示例配置文件
|
||||
|
||||
### **基础配置项**
|
||||
|
||||
> 请注意, 文档中的配置代码是被拆分的, 实际上为一个配置文件(`config.yml`),此处仅为方便阅读拆分
|
||||
|
||||
**Nonebot配置**
|
||||
```yaml
|
||||
nonebot:
|
||||
# Nonebot机器人的配置,6.3.10版本后,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配置**
|
||||
```yaml
|
||||
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"
|
||||
```
|
||||
|
||||
```yaml
|
||||
# 开发者选项
|
||||
liteyuki:
|
||||
allow_update: true # 是否允许更新
|
||||
debug: false # 轻雪调试,开启会自动重载Bot或者资源,其他插件自带的调试功能也将开启
|
||||
dev_mode: false # 开发者模式,开启后将会启动监视者,监视文件变化并自动重载
|
||||
# ...其他配置项...
|
||||
```
|
@ -1,58 +0,0 @@
|
||||
---
|
||||
title: 答疑
|
||||
order: 4
|
||||
---
|
||||
# 答疑
|
||||
|
||||
## 常见问题
|
||||
|
||||
以下是一些常见问题的解答和解决方案:
|
||||
|
||||
### **设备上 Python 环境太乱了,pip 和 python 不对应怎么办?**
|
||||
|
||||
请使用以下命令来安装依赖, 并启动 Bot:
|
||||
|
||||
```bash
|
||||
/path/to/python -m pip install -r requirements.txt
|
||||
/path/to/python main.py
|
||||
```
|
||||
|
||||
其中 `/path/to/python` 是你要用来运行 Bot 的 Python 可执行文件的路径
|
||||
|
||||
### **更新轻雪失败,报错 `InvalidGitRepositoryError`**
|
||||
|
||||
请确保正确安装了 `Git` , 并使用克隆而非直接下载的方式部署轻雪
|
||||
|
||||
如果你已经正确安装了 `Git` 但仍然遇到问题,请尝试使用手动下载的方式来更新轻雪(这对于一些特殊的网络环境有效)
|
||||
|
||||
### **怎么对接聊天平台?**
|
||||
|
||||
Bot 部分插件提供了对接特定平台的能力, 例如, 使用 NoneBot 插件可以对接支持的[适配器平台](https://bot.liteyuki.icu/deploy/adapter.html)
|
||||
|
||||
### **`Playwright` 安装失败**
|
||||
|
||||
运行以下命令来安装浏览器:
|
||||
|
||||
```bash
|
||||
playwright install
|
||||
```
|
||||
|
||||
### **有的插件安装后报错无法启动**
|
||||
|
||||
请先查阅插件文档,确认插件必要配置项完好, 如果问题仍然存在,请联系插件作者或在安全模式下启动轻雪:
|
||||
|
||||
```yaml
|
||||
safe_mode: true
|
||||
```
|
||||
|
||||
在安全模式下,你可以使用以下命令卸载问题插件:
|
||||
|
||||
```bash
|
||||
npm uninstall <plugin-name>
|
||||
```
|
||||
|
||||
## 其他问题
|
||||
|
||||
如果以上解答无法解决您的问题,欢迎加入我们的 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) 进行进一步的交流和讨论
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
---
|
||||
title: 安装
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 安装
|
||||
|
||||
## **常规部署**
|
||||
|
||||
### 安装基本环境
|
||||
|
||||
**Git 版本管理工具**
|
||||
[Git](https://git-scm.com/download/)
|
||||
|
||||
**Python3.10+**
|
||||
[Python3.10+](https://www.python.org/downloads/release/python-31010/)
|
||||
|
||||
|
||||
### 克隆项目到本地,轻雪使用Git进行版本管理,该步骤为必要项
|
||||
|
||||
`git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
|
||||
> 若你不能访问Github,可以使用Liteyuki镜像:`https://git.liteyuki.icu/bot/app`
|
||||
|
||||
### 切换到Bot目录下
|
||||
|
||||
`cd LiteyukiBot`
|
||||
|
||||
> 如果存在文件夹缺失报错, 尝试使用绝对路径进行路径跳转
|
||||
|
||||
### 创建虚拟环境
|
||||
|
||||
`python -m venv venv`
|
||||
|
||||
> 如果出现相关虚拟环境错误, 建议查询 [Python Venv](https://docs.python.org/3/library/venv.html)
|
||||
|
||||
|
||||
### 激活虚拟环境
|
||||
|
||||
Windows:
|
||||
`.\venv\Scripts\activate`
|
||||
|
||||
> 出现`'activate' 不是内部或外部命令,也不是可运行的程序或批处理文件。`错误, 请使用完整路径激活虚拟环境
|
||||
|
||||
Linux:
|
||||
`source venv/bin/activate`
|
||||
|
||||
> 如果出现`source: command not found`, 请使用完整路径激活虚拟环境
|
||||
|
||||
|
||||
### 安装依赖
|
||||
|
||||
`pip install -r requirements.txt`
|
||||
|
||||
> 若出现`ERROR: Could not open requirements file: [Errno 2] No such file or directory`错误, 请检查是否在Bot目录下执行命令
|
||||
|
||||
> 若出现`ERROR: Could not build wheels for xx, which is required to install pyproject.toml-based projects`错误,请尝试使用手动命令安装相关依赖(虚拟环境下执行`pip install xx`,例如`pip install Pillow`)
|
||||
|
||||
### 启动Bot
|
||||
|
||||
`python main.py`
|
||||
|
||||
> 若出现`ModuleNotFoundError: No module named 'xx'`错误, 请尝试重新执行安装依赖步骤
|
||||
|
||||
|
||||
## **使用Docker构建**
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/liteyukistudio/liteyukibot:latest # 每夜版镜像
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br>
|
||||
> 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号
|
||||
|
||||
## **装置要求**
|
||||
|
||||
- Windows系统版本推荐最低版本: `Windows 10 21H2` / `Windows Server 2019+`
|
||||
>版本低于`Windows 10 21H2` 可能无法正常使用Bot, 建议升级系统版本或使用Docker部署
|
||||
|
||||
- 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)
|
@ -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 (闭源):轻雪机器人统计接口
|
@ -1,97 +0,0 @@
|
||||
---
|
||||
title: 进程通信
|
||||
order: 4
|
||||
---
|
||||
|
||||
# **通道通信**
|
||||
|
||||
### 简介
|
||||
|
||||
轻雪运行在主进程 MainProcess 里,有部分实现插件为了一些功能在子进程中运行,这样两个进程上下文是不会互相干扰的,因此无法通过共享内存和直接对象传递进行通信。
|
||||
|
||||
例如子进程接收到用户信息需要重启机器人,这时可以通过通道对主进程发送消息,主进程接收到消息后重启对应子进程。
|
||||
|
||||
### 示例
|
||||
|
||||
通道是全双工的,有两种接收模式,但一个通道只能使用一种,即被动模式和主动模式,被动模式由`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)
|
@ -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 />
|
@ -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)实现语法高亮
|
@ -1,90 +0,0 @@
|
||||
---
|
||||
title: 插件开发
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 简介
|
||||
|
||||
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
|
||||
|
||||
插件大致可分为应用(Application)、实现(Implementation)及服务(Service)等几种类型,大部分情况下一个插件通常承担着多个责任,可按需调整
|
||||
|
||||
应用:从总线通道接收到消息后进行处理,响应,以实现某些功能,例如`echo`
|
||||
|
||||
实现:对接特定平台,把平台的消息转换为轻雪消息格式传入总线通道
|
||||
|
||||
服务:提供一系列对外的ipc/rpc/http等服务,供其他插件调用
|
||||
|
||||
我们鼓励使用`magicoca`进行对象传递来进行插件间通信而不是依赖关系,这样可以避免很多潜在的问题。
|
||||
|
||||
## 开始
|
||||
|
||||
### 创建插件
|
||||
|
||||
一个`.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进程
|
@ -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]
|
||||
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包
|
@ -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 LICENSE开源,欢迎各位的贡献
|
||||
link: https://github.com/LiteyukiStudio/LiteyukiStudioOpensourceLICENSE
|
||||
---
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: 插件商店
|
||||
order: 2
|
||||
---
|
||||
|
||||
<PluginStore />
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: 资源商店
|
||||
order: 1
|
||||
---
|
||||
|
||||
<ResStore />
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: 用户协议
|
||||
order: 3
|
||||
---
|
||||
# 用户协议
|
||||
|
||||
1. 本项目遵循`LSO`协议,你可以自由使用,修改,分发,但是请保留原作者信息
|
||||
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
|
||||
- 运行环境的设备信息:CPU,内存,系统信息及Python信息
|
||||
- 插件信息(不含插件数据)
|
||||
- 部分异常信息,
|
||||
- 会话负载信息(不含隐私部分)
|
||||
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`
|
||||
3. 本项目不会收集用户的任何隐私信息,但请注意甄别第三方插件的安全性
|
||||
4. 使用此项目代表你已经同意以上协议
|
@ -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` | 查询 |
|
||||
|
||||
|
@ -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 |
|
@ -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
|
@ -1,57 +0,0 @@
|
||||
# 开源声明
|
||||
|
||||
MIT-LSO-AGC许可(cn)
|
||||
本声明推荐Markdown格式阅读
|
||||
|
||||
## 版权声明
|
||||
|
||||
版权所有(c) 2024 Snowykami
|
||||
|
||||
## 开源许可声明
|
||||
|
||||
现授予任何人以同仓库许可的开源,包含:
|
||||
本软件或本库和相关文档文件(以下简称“软件”)
|
||||
|
||||
在本软件或本库中不受限制,包括但不限于**非侵犯MIT-LSO-AGC许可**的:
|
||||
- 使用
|
||||
- 复制
|
||||
- 修改
|
||||
- 合并
|
||||
- 发布
|
||||
- 分发
|
||||
- 再许可和/或受到许可的销售本软件的副本
|
||||
|
||||
并要求或强烈建议使用本软件或库时:
|
||||
- 将上述版权声明和本许可声明包含在所有本软件或本库的副本或主要部分
|
||||
|
||||
---
|
||||
|
||||
本软件或本库按“原样”提供,不提供任何形式的、明确或隐含的,包括但不限于适销性保证,适合特定用途和非侵权性
|
||||
|
||||
## 责任声明
|
||||
|
||||
在任何情况下都不得在使用本软件或库时,在本协议许可的情况下,以损害或其他责任追责,无论是在合同诉讼、侵权行为或其他情况下,抑或是由以下原因引起:
|
||||
由于或与本软件有关,或是使用,或其他交易
|
||||
都与本软件,库作者无责任关系,非作者使用本软件或本库并造成任何损失的行为与作者无关
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 搬运声明
|
||||
|
||||
在前提开源许可的基础上:
|
||||
|
||||
**未经允许不得**搬运至商业性平台**并且**进行**付费行为或疑似未得到许可的交易行为**
|
||||
|
||||
**不得**在搬运本仓库至**其他私有/公有开源镜像代码托管网站**时未经允许修改**任何**关于**原库的内容**
|
||||
|
||||
### 包括但不限于:
|
||||
|
||||
- 将`github`修改为`gitcode`
|
||||
- 篡改外链网址
|
||||
- 修改贡献者名单
|
||||
- 未经原作者允许私自占有仓库并声明为自己所有
|
||||
- 未经允许修改库原先所有的开源协议许可
|
||||
等
|
||||
|
||||
---
|
@ -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. 优化了通道的性能
|
@ -1,286 +0,0 @@
|
||||
import asyncio
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
from magicoca import Chan
|
||||
|
||||
from liteyuki.bot.lifespan import LIFESPAN_FUNC, Lifespan, PROCESS_LIFESPAN_FUNC
|
||||
from liteyuki.comm.channel import get_channel
|
||||
from liteyuki.core.manager import ProcessManager
|
||||
# new version
|
||||
from liteyuki.core.manager import sub_process_manager
|
||||
from liteyuki.log import init_log, logger
|
||||
from liteyuki.plugin import load_plugin
|
||||
from liteyuki.session import message_handler_thread
|
||||
from liteyuki.utils import IS_MAIN_PROCESS
|
||||
|
||||
__all__ = [
|
||||
"LiteyukiBot",
|
||||
"get_bot",
|
||||
"get_config",
|
||||
"get_config_with_compat",
|
||||
]
|
||||
|
||||
|
||||
class LiteyukiBot:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
"""
|
||||
初始化轻雪实例
|
||||
Args:
|
||||
**kwargs: 配置
|
||||
"""
|
||||
"""总通道"""
|
||||
self.i_chan = Chan[Any]() # 外部输入通道
|
||||
self.o_chan = Chan[Any]() # 外部输出通道
|
||||
|
||||
"""常规操作"""
|
||||
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() # 启动后钩子
|
||||
message_handler_thread([_.ctx.sub_chan for _ in sub_process_manager.processes.values()])
|
||||
|
||||
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>")
|
||||
|
||||
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"
|
||||
)
|
@ -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)
|
@ -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
|
@ -1,327 +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
|
||||
|
||||
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()
|
@ -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
|
@ -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
|
@ -1,179 +0,0 @@
|
||||
"""
|
||||
该模块用于常用配置文件的加载
|
||||
多配置文件编写原则:
|
||||
1. 尽量不要冲突: 一个键不要多次出现
|
||||
2. 分工明确: 每个配置文件给一个或一类服务提供配置
|
||||
3. 扁平化编写: 配置文件尽量扁平化,不要出现过多的嵌套
|
||||
4. 注意冲突时的优先级: 项目目录下的配置文件优先级高于config目录下的配置文件
|
||||
5. 请不要将需要动态加载的内容写入配置文件,你应该使用其他储存方式
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import copy
|
||||
import toml # type: ignore
|
||||
import yaml # type: ignore
|
||||
|
||||
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目录下的所有配置文件
|
||||
项目目录下的配置文件优先
|
||||
Args:
|
||||
no_waring: 是否关闭警告
|
||||
"""
|
||||
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
|
||||
|
||||
# new config loader
|
||||
class Loader:
|
||||
def __init__(self):
|
||||
self.config = {}
|
||||
|
||||
def load_from_yaml(self, fp: str) -> "Loader":
|
||||
"""从yaml文件加载配置
|
||||
Args:
|
||||
fp
|
||||
"""
|
||||
with open(fp, 'r') as file:
|
||||
self.config.update(yaml.safe_load(file))
|
||||
return self
|
||||
|
||||
def load_from_toml(self, fp: str) -> "Loader":
|
||||
"""从toml文件加载配置"""
|
||||
with open(fp, 'r') as file:
|
||||
self.config.update(toml.load(file))
|
||||
return self
|
||||
|
||||
def load_from_json(self, fp: str) -> "Loader":
|
||||
"""从json文件加载配置"""
|
||||
with open(fp, 'r') as file:
|
||||
self.config.update(json.load(file))
|
||||
return self
|
||||
|
||||
def load_from_env(self, prefix: str = "") -> "Loader":
|
||||
"""从环境变量加载配置"""
|
||||
for key, value in os.environ.items():
|
||||
if key.startswith(prefix):
|
||||
self.config[key[len(prefix):]] = value
|
||||
return self
|
||||
|
||||
def merge(self, loader: "Loader") -> "Loader":
|
||||
"""合并两个Loader键值对树"""
|
||||
self.config.update(loader.config)
|
||||
return self
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""获取配置值"""
|
||||
return self.config.get(key, default)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Loader(config={self.config})"
|
@ -1,2 +0,0 @@
|
||||
from .manager import *
|
||||
|
@ -1,297 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2025 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:
|
||||
"""
|
||||
子进程管理器
|
||||
若要子进程间通信,请先在子进程A中发送通信事件给主进程,包含当前进程信息及上下文信息,主进程再将信息发送给子进程B,子进程B再根据信息进行操作
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.processes: dict[str, SubProcess] = {}
|
||||
|
||||
|
||||
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}")
|
||||
|
||||
def terminate(self, name: str):
|
||||
"""
|
||||
终止指定子进程
|
||||
Args:
|
||||
name: 子进程名称
|
||||
Returns:
|
||||
"""
|
||||
if name not in self.processes:
|
||||
raise KeyError(f"Process {name} not found.")
|
||||
self.processes[name].terminate()
|
||||
|
||||
def terminate_all(self):
|
||||
"""
|
||||
终止所有子进程
|
||||
"""
|
||||
for name, process in self.processes.items():
|
||||
process.terminate()
|
||||
logger.debug(f"Terminating process {name}")
|
||||
|
||||
def get_process(self, name: str) -> SubProcess | None:
|
||||
"""
|
||||
获取指定子进程
|
||||
Args:
|
||||
name: 子进程名称
|
||||
Returns:
|
||||
"""
|
||||
return self.processes.get(name, None)
|
||||
|
||||
|
||||
sub_process_manager = _SubProcessManager()
|
@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
该模块用于存放一些开发工具
|
||||
"""
|
@ -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
|
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2025 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()
|
@ -1,10 +0,0 @@
|
||||
"""exception模块包含了liteyuki运行中的所有错误
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class LiteyukiException(BaseException):
|
||||
"""Liteyuki的异常基类。"""
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
@ -1,75 +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.bind()
|
||||
|
||||
debug_format: str = (
|
||||
"<c>{time:YYYY-MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}{level}]</lvl> "
|
||||
"<c><{name}.{module}.{function}:{line}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
# 默认日志格式
|
||||
default_format: str = (
|
||||
"<c>{time:MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}{level}]</lvl> "
|
||||
"<c><{name}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
def get_format(level: str) -> str:
|
||||
"""
|
||||
获取日志格式
|
||||
Args:
|
||||
level: 日志等级
|
||||
|
||||
Returns: 日志格式
|
||||
|
||||
"""
|
||||
# DEBUG日志格式
|
||||
|
||||
if level == "DEBUG":
|
||||
return debug_format
|
||||
else:
|
||||
return default_format
|
||||
|
||||
|
||||
def init_log(config: dict):
|
||||
"""
|
||||
在语言加载完成后执行
|
||||
Args:
|
||||
config: 配置
|
||||
"""
|
||||
global logger
|
||||
level = config.get("log_level", "DEBUG")
|
||||
logger.remove()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
level=level,
|
||||
diagnose=False,
|
||||
format=get_format(level),
|
||||
)
|
||||
show_icon = config.get("log_icon", True)
|
||||
logger.level("DEBUG", color="<blue>", icon=f"{'🐛' if show_icon else ''}")
|
||||
logger.level("INFO", color="<normal>", icon=f"{'ℹ️' if show_icon else ''}")
|
||||
logger.level("SUCCESS", color="<green>", icon=f"{'✅' if show_icon else ''}")
|
||||
logger.level("WARNING", color="<yellow>", icon=f"{'⚠️' if show_icon else ''}")
|
||||
logger.level("ERROR", color="<red>", icon=f"{'⭕' if show_icon else ''}")
|
||||
logger.level("CRITICAL", color="<red>", icon=f"{'❌' if show_icon else ''}")
|
||||
logger.level("TRACE", color="<cyan>", icon=f"{'🔍' if show_icon else ''}")
|
||||
|
||||
logger.bind()
|
||||
|
||||
init_log(config={"log_level": "DEBUG", "log_icon": True})
|
@ -1,19 +0,0 @@
|
||||
from liteyuki.plugin.model import Plugin, PluginMetadata, PluginType
|
||||
from liteyuki.plugin.load import load_plugin, load_plugins, _plugins
|
||||
|
||||
__all__ = [
|
||||
"PluginMetadata",
|
||||
"Plugin",
|
||||
"PluginType",
|
||||
"load_plugin",
|
||||
"load_plugins",
|
||||
]
|
||||
|
||||
|
||||
def get_loaded_plugins() -> dict[str, Plugin]:
|
||||
"""
|
||||
获取已加载的插件
|
||||
Returns:
|
||||
dict[str, Plugin]: 插件字典
|
||||
"""
|
||||
return _plugins
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user