1
0
forked from bot/app

112 Commits

Author SHA1 Message Date
20ad8dc53f 🐛 hotfix: from mypy import 2024-10-19 21:15:37 +08:00
de9c91d8bd 🐛 hotfix: from mypy import 2024-10-19 21:12:55 +08:00
6b64a0c379 🐛 hotfix: from mypy import 2024-10-19 21:10:18 +08:00
f117da7ff3 🎨 更新轻雪依赖版本 2024-10-14 20:57:30 +08:00
f548a07230 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 20:51:37 +08:00
e2e53c21fa 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 01:03:06 +08:00
3eaf23a56b 📝 文档删除常规部署,强制使用虚拟环境 2024-10-14 01:02:57 +08:00
4a5dd1f727 🐛 修复一些细节小问题 2024-10-14 00:57:33 +08:00
c2cb416b4e 🐛 hotfix: ubl 2024-10-13 17:44:24 +08:00
5cd528d5e9 🐛 hotfix: ubl 2024-10-13 17:44:17 +08:00
980fca650b 🐛 hotfix: ubl 2024-10-13 13:44:07 +08:00
9c525141f6 分离magicocacroterline 2024-10-13 02:56:29 +08:00
3d218a0e8d Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/liteyuki_plugins/nonebot/__init__.py
#	src/liteyuki_plugins/nonebot/nb_utils/adapter_manager/__init__.py
#	src/liteyuki_plugins/nonebot/nb_utils/adapter_manager/onebot.py
#	src/liteyuki_plugins/nonebot/nb_utils/adapter_manager/satori.py
#	src/liteyuki_plugins/nonebot/nb_utils/driver_manager/__init__.py
#	src/liteyuki_plugins/nonebot/nb_utils/driver_manager/auto_set_env.py
#	src/liteyuki_plugins/nonebot/nb_utils/driver_manager/defines.py
2024-10-13 02:55:04 +08:00
db385f597b 分离magicocacroterline 2024-10-13 02:54:47 +08:00
98a9d6413a 分离magicocacroterline 2024-10-13 02:51:33 +08:00
a77f97fd4b 🐛 hotfix: 移除状态卡片Linux下非储存分区 2024-09-29 22:57:04 +08:00
e6ea1b700f 🐛 hotfix: 天气拉取异常 2024-09-29 22:52:34 +08:00
596f4d06ea 🐛 bug: 修复状态卡片显示问题 2024-09-29 22:35:24 +08:00
8e3d3b3b5d Merge remote-tracking branch 'origin/main' 2024-09-27 00:50:05 +08:00
a34ad87e01 todo: add todo file 2024-09-27 00:49:54 +08:00
6c4c7f34cd 📝 Update resource_publish_en.yml 2024-09-22 01:56:00 +08:00
0c859957b4 🐛 docs: 添加发布插件表单 2024-09-22 01:48:53 +08:00
fbb9ed82ee 🐛 docs: 添加发布插件表单 2024-09-22 01:42:03 +08:00
b469c9420e 🐛 docs: 添加发布插件表单 2024-09-22 01:38:44 +08:00
aa4d930cc4 🐛 docs: 添加发布插件表单 2024-09-21 23:16:37 +08:00
76be748160 :docs: Update resource_publish_zh.yml 2024-09-21 22:36:04 +08:00
a9dd37b8a5 📝 Update resource_publish_zh.yml 2024-09-21 22:32:47 +08:00
5900d621f2 Create resource_publish_zh.yml 2024-09-21 22:28:58 +08:00
7442a3651b Create config.yml 2024-09-21 22:28:13 +08:00
413f438689 Rename resource_publish.md to resource_publish.yml 2024-09-21 22:21:20 +08:00
1fc4999b09 :docs: add resource_publish to resource_publish.md 2024-09-21 22:20:43 +08:00
975446a096 Create resource_publish 2024-09-21 22:19:34 +08:00
98cdd2f4b8 🐛 docs: 商店点击主页改为新窗口,修复插件商店仅轻雪不显示的问题 2024-09-21 03:22:40 +08:00
c0beec0429 🐛 docs: 商店点击主页改为新窗口,修复插件商店仅轻雪不显示的问题 2024-09-21 03:16:32 +08:00
614d78b3fa 🐛 docs: 商店点击主页改为新窗口,修复插件商店仅轻雪不显示的问题 2024-09-21 03:12:32 +08:00
24b0f345e4 📦 docs: 主页添加开发按钮 2024-09-21 02:51:59 +08:00
0ae10aa1b2 Merge remote-tracking branch 'origin/main' 2024-09-21 02:49:11 +08:00
9fe7478840 📦 docs: 修复文档404 2024-09-21 02:49:01 +08:00
liteyuki-flow[bot]
efca0bc7b3 📦 发布资源: 测试资源包2 2024-09-18 01:41:24 +08:00
50c5e99b98 📦 docs: 修改发布按钮样式 2024-09-18 01:38:13 +08:00
7415efcc90 📦 docs: 修改发布按钮样式 2024-09-18 01:32:12 +08:00
5bb4584e6a Merge remote-tracking branch 'origin/main' 2024-09-18 01:31:13 +08:00
795a6f3f76 📦 docs: 修改发布按钮样式 2024-09-18 01:31:03 +08:00
liteyuki-flow[bot]
fa74e08514 📦 发布资源: 测试资源包2 2024-09-18 01:30:26 +08:00
e6cf6e0c68 📦 docs: 修改发布按钮样式 2024-09-18 01:25:43 +08:00
6789c16773 📦 docs: 修改发布按钮样式 2024-09-18 01:24:32 +08:00
cdea0f8563 📦 docs: 修改发布按钮样式 2024-09-18 01:23:21 +08:00
9df55671ac 📦 docs: 修改发布按钮样式 2024-09-18 01:22:08 +08:00
d96c6f13c1 📦 docs: 修改发布按钮样式 2024-09-18 01:05:23 +08:00
bce1bf8704 Merge remote-tracking branch 'origin/main' 2024-09-18 01:04:47 +08:00
8eb626b8da 📦 docs: 修改发布按钮样式 2024-09-18 01:04:37 +08:00
liteyuki-flow[bot]
e6505d335b 📦 发布资源: 测试资源包2 2024-09-18 00:59:47 +08:00
c8cb341afb Merge remote-tracking branch 'origin/main' 2024-09-17 16:30:30 +08:00
e99cb88b13 📦 docs: 修改发布按钮样式 2024-09-17 16:30:19 +08:00
liteyuki-flow[bot]
78c3e299d0 📦 发布资源: 轻雪Kakyo语言包 托管版 2024-09-17 16:27:24 +08:00
23338437e9 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:23:07 +08:00
f95899aebd 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:21:45 +08:00
5df10c66b6 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:21:18 +08:00
811d1594cd 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:19:47 +08:00
c162208638 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:16:29 +08:00
679d6597d8 Merge remote-tracking branch 'origin/main' 2024-09-17 16:14:18 +08:00
f402799f28 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:14:08 +08:00
liteyuki-flow[bot]
60542d7426 📦 发布资源: 轻雪Kakyo语言包 稳定版 2024-09-17 16:06:05 +08:00
db1fb58717 📦 docs: 资源商店新增发布资源功能 2024-09-17 16:01:19 +08:00
7d5675ec97 Merge remote-tracking branch 'origin/main' 2024-09-17 15:58:56 +08:00
d8c50752f7 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:58:45 +08:00
liteyuki-flow[bot]
c674b837bb 📦 发布资源: 测试资源包 2024-09-17 15:53:47 +08:00
d867996072 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:53:17 +08:00
7ef36c6933 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:52:32 +08:00
982aae4dbf 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:50:10 +08:00
b5d3c6aaa8 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:48:02 +08:00
5537bc32df 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:45:43 +08:00
5c0c723c5d 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:33:32 +08:00
0ed3b307d9 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:31:59 +08:00
53a603d4ee 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:26:21 +08:00
fbf906bea7 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:16:54 +08:00
a87e8bc3e8 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:04:19 +08:00
a16a67dbc9 📦 docs: 资源商店新增发布资源功能 2024-09-17 15:03:48 +08:00
4c2231feb5 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:59:49 +08:00
3932dd60da 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:57:27 +08:00
3c6380cb82 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:55:05 +08:00
2612f99f35 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:52:26 +08:00
0b4b9a6241 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:48:48 +08:00
2d100885ee 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:46:06 +08:00
cb335720b7 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:44:43 +08:00
dc8ad30b84 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:42:22 +08:00
09e00652c3 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:40:54 +08:00
b5b15c82f8 📦 docs: 资源商店新增发布资源功能 2024-09-17 14:37:45 +08:00
72e71124b8 📝 docs: 资源商店新增发布资源功能 2024-09-17 01:35:26 +08:00
d2be2acc95 📝 docs: add egg 2024-09-15 00:20:22 +08:00
d95614e960 📝 使用复数代替单数visitor 2024-09-10 08:17:24 +08:00
dad9482d7a 📝 update readme 2024-09-07 04:42:45 +08:00
fff5d09ad9 📝 docs: remove docstring from src of docs 2024-09-06 20:05:29 +08:00
e6ffd1fcc0 📝 docs: remove docstring from src of docs 2024-09-06 19:57:53 +08:00
a97747b7c4 Merge remote-tracking branch 'origin/main' 2024-09-06 19:50:15 +08:00
1921dcd023 📝 docs: remove docstring from src of docs 2024-09-06 19:50:01 +08:00
18af1d00bd 📝 update i18n.ts 2024-09-05 11:45:12 +08:00
30cdc1da23 Merge pull request #73 from ElapsingDreams/main
📝更新轻雪文档url
2024-09-04 20:45:32 +08:00
ElapsingDreams
bb84958ce4 📝更新轻雪文档url
https://bot.liteyuki.icu/usage/  -->  https://bot.liteyuki.icu/
2024-09-04 20:40:31 +08:00
ElapsingDreams
44de3fd00a 📝更新轻雪文档url
https://bot.liteyuki.icu/usage/  -->  https://bot.liteyuki.icu/usage/basic.html
2024-09-04 20:34:13 +08:00
39b1920532 📝 docs: 修改侧边栏选项 2024-09-04 19:15:11 +08:00
aa591ec29e 📝 更新开发规范 2024-09-04 19:10:32 +08:00
310c3f065d 📝 更新开发规范 2024-09-04 03:23:18 +08:00
2311ef82c3 🐛 fix: logo display error 2024-09-04 02:57:21 +08:00
b4b931fc95 📝 更新开发规范 2024-09-04 02:43:44 +08:00
d1b887fcaa 📝https://github.com/jooy2/vitepress-sidebar/issues/170#event-14113011263一起更新 2024-09-03 22:26:58 +08:00
5a2990770c 📝 新增gitea数据 2024-09-02 21:28:08 +08:00
1d0f0a2539 📝 新增gitea数据 2024-09-02 21:27:41 +08:00
dbc4d83b08 📝 新增gitea数据 2024-09-02 21:27:28 +08:00
da905d21bd 📝 新增gitea数据 2024-09-02 21:26:11 +08:00
7d91079500 🚀 workflows: 新增gitea工作流 2024-09-02 17:08:29 +08:00
81a006a308 🐛 [docs]: 增加访客记录 2024-09-02 11:20:53 +08:00
154 changed files with 6493 additions and 4541 deletions

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

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

View File

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

View File

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

View File

@@ -1,44 +0,0 @@
# 问题反馈
## **请确保**
- 已认真阅读[文档]("https://bot.liteyuki.icu"),该问题不是文档提及的或你自己操作不当造成的
- 你的问题是在最新版本的代码上测试的
- 请勿重复提交相同或类似的issue
## **描述问题**
请在此简单描述问题
## **如何复现**
请阐述一下如何重现这个问题
### 预期
描述你期望发生的事情
### 实际
描述实际发生的事情
## **日志或截图**
```
日志内容
```
## **设备信息**
- **系统**: [例如 Ubuntu 22.04]
- **CPU**: [例如 Intel i7-7700K]
- **内存**: [例如 16GB]
- **Python**: [例如CPython 3.10.7]
**补充内容**
可选,推荐提供`pip freeze`的输出,以及其他相关信息,以及你的建议

View File

@@ -40,8 +40,8 @@ jobs:
- name: Setup API markdown - name: Setup API markdown
run: |- run: |-
python -m pip install litedoc python -m pip install litedoc
litedoc liteyuki -o docs/zh/dev/api -l zh-Hans -cd class -fd func -md func -vd var -cs -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/ 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 -cs -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 - name: 安装 pnpm
uses: pnpm/action-setup@v2 uses: pnpm/action-setup@v2

51
.github/workflows/issue_handler.yml vendored Normal file
View File

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

View File

@@ -1,8 +1,9 @@
name: Publish name: Publish
on: on:
release: push:
types: [published] tags:
- 'v*'
jobs: jobs:
pypi-publish: pypi-publish:

View File

@@ -11,10 +11,9 @@
[![][Python3.10+]][python-link] [![][Python3.10+]][python-link]
[![][Usage]][usage-link] [![][Usage]][usage-link]
- 基于[Nonebot2](https://github.com/nonebot/nonebot2),有良好的生态支持 - 原生支持与任意`Python`Bot框架互联,有良好的生态支持
- 开箱即用,无需复杂配置 - 开箱即用,无需复杂配置
- 集成包管理器,支持一键安装插件 - 集成包管理器,支持一键安装插件
- 支持OneBot标准通信但不限于此
- 自定义主题支持,满足审美需求 - 自定义主题支持,满足审美需求
- 国际化支持,支持多种语言 - 国际化支持,支持多种语言
- 高性能500插件2s内启动 - 高性能500插件2s内启动
@@ -24,8 +23,11 @@
</div> </div>
### 感谢 ### 感谢
- [NoneBot2](https://nonebot.dev)提供的框架支持 - 所有贡献者们
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
### 参考及鸣谢
- [nonebot-plugin-uninfo](https://github.com/RF-Tar-Railt/nonebot-plugin-uninfo)为会话部分用户信息提供了参考
- [nonebot-plugin-alconna](https://github.com/nonebot/plugin-alconna/)为消息部分提供了参考
[OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge [OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge

View File

@@ -2,7 +2,6 @@
import {defineConfig} from 'vitepress' import {defineConfig} from 'vitepress'
import {generateSidebar} from 'vitepress-sidebar'; import {generateSidebar} from 'vitepress-sidebar';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import {zh} from "./zh"; import {zh} from "./zh";
import {en} from "./en"; import {en} from "./en";
@@ -11,7 +10,6 @@ const commonSidebarOptions = {
collapsed: true, collapsed: true,
convertSameNameSubFileToGroupIndexPage: true, convertSameNameSubFileToGroupIndexPage: true,
useTitleFromFrontmatter: true, useTitleFromFrontmatter: true,
useTitleFromFileHeading: true,
useFolderTitleFromIndexFile: true, useFolderTitleFromIndexFile: true,
useFolderLinkFromIndexFile: true, useFolderLinkFromIndexFile: true,
includeFolderIndexFile: true, includeFolderIndexFile: true,
@@ -73,7 +71,13 @@ export const common = defineConfig({
] ]
), ),
socialLinks: [ socialLinks: [
{icon: 'github', link: 'https://github.com/LiteyukiStudio/LiteyukiBot'} {icon: 'github', link: 'https://github.com/LiteyukiStudio/LiteyukiBot'},
{
icon: {
svg: '<svg t="1725391346807" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5067" width="256" height="256"><path d="M1004.692673 466.396616l-447.094409-447.073929c-25.743103-25.763582-67.501405-25.763582-93.264987 0l-103.873521 103.873521 78.171378 78.171378c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928 0 14.827372-3.358686 28.856031-9.359266 41.389666l127.97824 127.97824c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928s-42.98709 96.00928-96.00928 96.00928-96.00928-42.98709-96.00928-96.00928c0-14.827372 3.358686-28.856031 9.359266-41.389666l-127.97824-127.97824c-3.051489 1.454065-6.184898 2.744293-9.379746 3.870681l0 266.97461c37.273227 13.188988 63.99936 48.721433 63.99936 90.520695 0 53.02219-42.98709 96.00928-96.00928 96.00928s-96.00928-42.98709-96.00928-96.00928c0-41.799262 26.726133-77.331707 63.99936-90.520695l0-266.97461c-37.273227-13.188988-63.99936-48.721433-63.99936-90.520695 0-14.827372 3.358686-28.856031 9.359266-41.389666l-78.171378-78.171378-295.892081 295.871601c-25.743103 25.784062-25.743103 67.542365 0 93.285467l447.114889 447.073929c25.743103 25.743103 67.480925 25.743103 93.264987 0l445.00547-445.00547c25.763582-25.763582 25.763582-67.542365 0-93.285467z" fill="#a2d8f4" p-id="5068"></path></svg>'
},
link: "https://git.liteyuki.icu/LiteyukiStudio/LiteyukiBot"
}
], ],
search: { search: {
provider: 'local', provider: 'local',

View File

@@ -1,5 +1,5 @@
import DefaultTheme from 'vitepress/theme' import DefaultTheme from 'vitepress/theme'
import './liteyuki.css' import './liteyuki.scss'
import StatsBar from '../../components/StatsBar.vue' import StatsBar from '../../components/StatsBar.vue'
import PluginStore from '../../components/PluginStore.vue' import PluginStore from '../../components/PluginStore.vue'

View File

@@ -1,95 +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;
}
.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;
}

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
</div> </div>
<div class="item-bar"> <div class="item-bar">
<!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id--> <!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
<a :href=props.item.homepage class="btn"> <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"> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
<path fill="currentColor" <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"/> 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"/>

View File

@@ -3,6 +3,7 @@ import {computed, ref} from 'vue'
import ItemCard from './PluginItemCard.vue' import ItemCard from './PluginItemCard.vue'
import ToggleSwitch from "./ToggleSwitch.vue"; import ToggleSwitch from "./ToggleSwitch.vue";
import {getTextRef} from "./scripts/i18n"; import {getTextRef} from "./scripts/i18n";
import pluginsJson from "../public/plugins.json"
let showLiteyukiPluginOnly = ref(false) let showLiteyukiPluginOnly = ref(false)
let filteredItems = computed(() => { let filteredItems = computed(() => {
@@ -23,16 +24,11 @@ let filteredItems = computed(() => {
// 插件商店Nonebot // 插件商店Nonebot
let items = ref([]) let items = ref([])
let search = ref('') let search = ref('')
// 从官方拉取 // 从轻雪官方拉取,添加轻雪插件属性
fetch("/plugins.json") items.value = pluginsJson
.then(response => response.json()) items.value.forEach(item => {
.then(data => { item.is_liteyuki_plugin = true
data.forEach(item => { })
item.is_liteyuki_plugin = true
})
items.value = data
})
.catch(error => console.error(error))
//追加 //追加
fetch('https://registry.nonebot.dev/plugins.json') fetch('https://registry.nonebot.dev/plugins.json')
@@ -57,6 +53,11 @@ fetch('https://registry.nonebot.dev/plugins.json')
<ToggleSwitch v-model:modelValue="showLiteyukiPluginOnly"/> <ToggleSwitch v-model:modelValue="showLiteyukiPluginOnly"/>
{{ getTextRef('liteyukiOnly') }} {{ getTextRef('liteyukiOnly') }}
</div> </div>
<!-- 按钮们-->
<!-- <div class="tab">-->
<!-- <button @click="open"-->
<!-- </div>-->
<div class="items"> <div class="items">
<!-- 使用filteredItems来布局商品 --> <!-- 使用filteredItems来布局商品 -->
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/> <ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>

View File

@@ -4,7 +4,7 @@
<div class="item-description">{{ props.item.description }}</div> <div class="item-description">{{ props.item.description }}</div>
<div class="item-bar"> <div class="item-bar">
<!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id--> <!-- 三个可点击svg一个github一个下载一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
<a :href=props.item.link class=""> <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"> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
<path fill="currentColor" <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"/> 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"/>

View File

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

View File

@@ -1,8 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref} from 'vue' import {computed, ref} from 'vue'
import ItemCard from './ResItemCard.vue' import ItemCard from './ResItemCard.vue'
import * as url from "node:url"; import ResPubWindow from "./ResPubWindow.vue";
import {getTextRef} from "./scripts/i18n"; 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加载插件 // 从public/assets/resources.json加载插件
let filteredItems = computed(() => { let filteredItems = computed(() => {
@@ -18,24 +23,68 @@ let filteredItems = computed(() => {
// 插件商店Nonebot // 插件商店Nonebot
let items = ref([]) let items = ref([])
let search = ref('') let search = ref('')
fetch("/resources.json") items.value = resourcesJson
.then(response => response.json())
.then(data => {
items.value = data
})
.catch(error => console.error(error))
// 列表倒序 // 列表倒序
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> </script>
<template> <template>
<div class="market"> <div class="market">
<h1>{{ getTextRef('resourceStore') }}</h1> <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="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"> <div class="items">
<!-- 使用filteredItems来布局商品 --> <!-- 使用filteredItems来布局商品 -->
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/> <ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
</div> </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> </div>
</template> </template>
@@ -51,4 +100,6 @@ h1 {
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 10px; gap: 10px;
} }
</style> </style>

View File

@@ -13,55 +13,55 @@ const dataSections = {
total: { total: {
name: 'total', name: 'total',
color: '#00a6ff', color: '#00a6ff',
value: ref(2005), value: ref(-1),
link: StarMapUrl link: StarMapUrl
}, },
online: { online: {
name: 'online', name: 'online',
color: '#7eff7e', color: '#7eff7e',
value: ref(1145), value: ref(-1),
link: StarMapUrl link: StarMapUrl
}, },
stars: { stars: {
name: 'stars', name: 'stars',
color: '#ffcc00', color: '#ffcc00',
value: ref(1234), value: ref(-1),
link: `${RepoUrl}/stargazers` link: `${RepoUrl}/stargazers`
}, },
forks: { forks: {
name: 'forks', name: 'forks',
color: '#ff6600', color: '#ff6600',
value: ref(9420), value: ref(-1),
link: `${RepoUrl}/forks` link: `${RepoUrl}/forks`
}, },
issues: { issues: {
name: 'issues', name: 'issues',
color: '#ff0000', color: '#ff0000',
value: ref(1145), value: ref(-1),
link: `${RepoUrl}/issues` link: `${RepoUrl}/issues`
}, },
prs: { prs: {
name: 'prs', name: 'prs',
color: '#f15df1', color: '#f15df1',
value: ref(6543), value: ref(-1),
link: `${RepoUrl}/pulls` link: `${RepoUrl}/pulls`
}, },
plugins: { plugins: {
name: 'plugins', name: 'plugins',
color: '#a766ff', color: '#a766ff',
value: ref(1763), value: ref(-1),
link: './store/plugin' link: './store/plugin'
}, },
resources: { resources: {
name: 'resources', name: 'resources',
color: '#5a54fa', color: '#5a54fa',
value: ref(6789), value: ref(-1),
link: './store/resource' link: './store/resource'
}, },
visitors: { visitors: {
name: 'visitors', name: 'visitors',
color: '#00a6ff', color: '#00a6ff',
value: ref(1234), value: ref(-1),
link: RepoUrl link: RepoUrl
}, },
} }
@@ -109,7 +109,20 @@ onBeforeRouteUpdate(() => {
updateRefData(); updateRefData();
}); });
console.log(
" _ _ _ _ _ ____ _ \n" +
" | | (_) | | | (_) _ \\ | | \n" +
" | | _| |_ ___ _ _ _ _| | ___| |_) | ___ | |_ \n" +
" | | | | __/ _ \\ | | | | | | |/ / | _ < / _ \\| __|\n" +
" | |____| | || __/ |_| | |_| | <| | |_) | (_) | |_ \n" +
" |______|_|\\__\\___|\\__, |\\__,_|_|\\_\\_|____/ \\___/ \\__|\n" +
" __/ | \n" +
" |___/ "
)
console.log(
getTextRef('easterEgg')
)
</script> </script>
@@ -133,7 +146,8 @@ onBeforeRouteUpdate(() => {
</div> </div>
</div> </div>
<div class="starmap"> <div class="starmap">
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px" class="gamma"></iframe> <iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px" class="gamma">
</iframe>
</div> </div>
</div> </div>
</div> </div>
@@ -267,7 +281,7 @@ onBeforeRouteUpdate(() => {
.stats-info { .stats-info {
width: 40%; width: 40%;
margin: 30px; margin: 10px 30px 30px 30px;
} }
.starmap { .starmap {

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ const i18nData = {
forks: 'Forks', forks: 'Forks',
issues: 'Issues', issues: 'Issues',
prs: 'Pull Requests', prs: 'Pull Requests',
visitors: 'Visitor', visitors: 'Visitors',
size: 'Size', size: 'Size',
plugins: 'Plugins', plugins: 'Plugins',
resources: 'Resources', resources: 'Resources',
@@ -22,6 +22,25 @@ const i18nData = {
liteyukiOnly: 'Liteyuki Only', liteyukiOnly: 'Liteyuki Only',
search: 'Search', search: 'Search',
resourceStore: 'Resources Store', 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: { zh: {
stats: '统计信息', stats: '统计信息',
@@ -30,9 +49,9 @@ const i18nData = {
total: '实例', total: '实例',
fetching: '获取中', fetching: '获取中',
stars: '星星', stars: '星星',
forks: '叉', forks: '叉',
issues: '开启议题', issues: '议题',
prs: '合并请求', prs: '拉取请求',
visitors: '访客', visitors: '访客',
size: '大小', size: '大小',
plugins: '插件', plugins: '插件',
@@ -43,6 +62,25 @@ const i18nData = {
liteyukiOnly: '仅轻雪', liteyukiOnly: '仅轻雪',
search: '搜索', search: '搜索',
resourceStore: '资源商店', resourceStore: '资源商店',
thx_contributors: '感谢以下贡献者!',
easterEgg: '恭喜你发现了彩蛋!',
publishPlugin: '发布插件',
publishRes: '发布资源',
closeButtonText: '关闭',
submitButtonText: '提交',
resName: '名称',
resDesc: '描述',
resAuthor: '作者',
resLink: '下载链接',
resHomepage: '主页',
resNameText: '示例:可爱风格主题',
resDescText: '示例:一个可爱风格和配色的主题',
resAuthorText: '通常为github用户名示例yanyongyu',
resLinkText: '直接下载链接通常为zip包链接',
resHomepageText: '可选可为git平台仓库名',
} }
} }
@@ -53,7 +91,7 @@ function getText(lang: string, key: string): string {
return i18nData[lang][key]; return i18nData[lang][key];
} }
function formatLang(lang: string): string { export function formatLang(lang: string): string {
if (lang.includes('-')) { if (lang.includes('-')) {
return lang.split('-')[0]; return lang.split('-')[0];
} }
@@ -70,5 +108,5 @@ export function updateRefData() {
export function getTextRef(key: string): any { export function getTextRef(key: string): any {
const lang = formatLang(useData().site.value.lang); const lang = formatLang(useData().site.value.lang);
refData[key] = getText(lang, key); refData[key] = getText(lang, key);
return refData[key] return refData[key] || key;
} }

View File

@@ -2,6 +2,7 @@
export const OWNER = "LiteyukiStudio" export const OWNER = "LiteyukiStudio"
export const REPO = "LiteyukiBot" export const REPO = "LiteyukiBot"
const githubAPIUrl = "https://api.github.com" const githubAPIUrl = "https://api.github.com"
const giteaAPIUrl = "https://git.liteyuki.icu/api/v1"
const onlineFetchUrl = "https://api.liteyuki.icu/online"; const onlineFetchUrl = "https://api.liteyuki.icu/online";
const totalFetchUrl = "https://api.liteyuki.icu/count"; const totalFetchUrl = "https://api.liteyuki.icu/count";
const visitRecordUrl = "https://api.liteyuki.icu/visit"; const visitRecordUrl = "https://api.liteyuki.icu/visit";
@@ -32,28 +33,34 @@ interface StatsApi {
export type {GithubStats}; export type {GithubStats};
// 实现接口 async function getGiteaStats() {
export const statsApi: StatsApi = { try {
getTotal: async () => { const url = `${giteaAPIUrl}/repos/${OWNER}/${REPO}`;
try { console.log(url);
const res = await fetch(totalFetchUrl); const res = await fetch(url);
const data = await res.json(); const data = await res.json();
return data.register; return {
} catch (e) { stars: data.stars_count,
return -1; forks: data.forks_count,
} watchers: data.watchers_count,
}, issues: 0,
getOnline: async () => { prs: 0,
try { size: data.size,
const res = await fetch(onlineFetchUrl); };
const data = await res.json(); } catch (e) {
return data.online; return {
} catch (e) { stars: -1,
return -1; forks: -1,
} watchers: -1,
}, issues: -1,
getGithubStats: async () => { prs: -1,
try { size: -1,
};
}
}
async function getGithubStats() {
try {
const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`); const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`);
const data = await res.json(); const data = await res.json();
return { return {
@@ -74,7 +81,43 @@ export const statsApi: StatsApi = {
size: -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 () => { getPluginNum: async () => {
try { try {
const res = await fetch('/plugins.json'); const res = await fetch('/plugins.json');

View File

@@ -9,13 +9,23 @@ order: 1
1. Install [`Git`](https://git-scm.com/download/) and [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) Environment. 1. Install [`Git`](https://git-scm.com/download/) and [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) Environment.
```bash ```bash
# Clone the project # Clone Repo
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1 git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
# change directory
# Change directory
cd LiteyukiBot cd LiteyukiBot
# install dependencies
# 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 pip install -r requirements.txt
# start the bot!
# Run Liteyuki
python main.py python main.py
``` ```
@@ -37,9 +47,6 @@ python main.py
> If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$&#40;pwd&#41;` <br> > If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$&#40;pwd&#41;` <br>
> If you have modified the port number, please replace `20216:20216` with your port number > If you have modified the port number, please replace `20216:20216` with your port number
## **Use TRSS Script**
[TRSS_Liteyuki Management Script](https://timerainstarsky.github.io/TRSS_Liteyuki/), which provides a more convenient way to manage LiteyukiBot, recommended to use `Arch Linux`
## **Device Requirements** ## **Device Requirements**
- Windows system version minimum `Windows10+`/`Windows Server 2019+` - Windows system version minimum `Windows10+`/`Windows Server 2019+`

View File

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

View File

@@ -1,5 +1,5 @@
--- ---
title: 进程通信 title: Process Communication
order: 4 order: 4
--- ---

View File

@@ -2,6 +2,12 @@
title: Development Guide title: Development Guide
order: 0 order: 0
--- ---
<script setup>
import ContributorBar from '../../components/ContributorBar.vue'
</script>
# Development Guide # Development Guide
## How to Develop ## How to Develop
@@ -52,8 +58,8 @@ The `src` directory is the application part, please develop business logic in th
- commit message should follow the following guidelines: - 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. - 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. - 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> [module]: <message>`, such as `:sparkles: [liteyuki.event]: add new feature`, - The format of the commit message is `<emoji> [type:] <message>`, such as `:sparkles: feat: add new field to event`,
where the module field is the affected part, such as `liteyuki.message`, `docs.en.guide.md`, etc., not much is required, but please fill in as much as possible; the message field is a brief description, in summary, what you did. 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. - 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: - The documentation should follow [`Markdown`](https://www.markdownguide.org/) syntax and support vitepress-related content:
- Revise the documentation for each language when editing. - Revise the documentation for each language when editing.
@@ -62,3 +68,6 @@ The `src` directory is the application part, please develop business logic in th
## Finally ## Finally
- This project is a non-profit open-source project, and we welcome anyone to participate in development. Your contributions will make Liteyuki better. - This project is a non-profit open-source project, and we welcome anyone to participate in development. Your contributions will make Liteyuki better.
<ContributorBar />

View File

@@ -1,5 +1,5 @@
--- ---
title: 轻雪函数 title: Liteyuki Function
order: 2 order: 2
--- ---

View File

@@ -1,5 +1,5 @@
--- ---
title: 轻雪插件开发 title: Liteyuki Plugin
order: 3 order: 3
--- ---

View File

@@ -1,5 +1,5 @@
--- ---
title: 资源包开发 title: Resource Pack
order: 1 order: 1
--- ---

View File

@@ -14,16 +14,19 @@ hero:
- theme: alt - theme: alt
text: 📦 Extensions text: 📦 Extensions
link: ./store/ link: ./store/
- theme: alt
text: 🛠️ Development
link: ./dev/guide
image: image:
light: ./liteyuki.svg light: /liteyuki.svg
dark: ./liteyuki-dark.svg dark: /liteyuki-dark.svg
alt: Liteyuki Logo alt: Liteyuki Logo
features: features:
- title: Ecological Diversity - title: Ecological Diversity
icon: 🛠️ icon: 🛠️
details: Based on the liteyuki framework, compatible with any Python bot framework and application details: Based on the liteyuki framework, compatible with any Python bot framework and application
link: ./dev/api/api link: ./dev/api
- title: Pluggable - title: Pluggable
icon: 🧩 icon: 🧩

View File

@@ -1,8 +1,9 @@
{ {
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^4.0.0", "@intlify/unplugin-vue-i18n": "^4.0.0",
"sass-embedded": "^1.78.0",
"vitepress": "^1.3.4", "vitepress": "^1.3.4",
"vitepress-sidebar": "^1.25.0" "vitepress-sidebar": "^1.25.3"
}, },
"scripts": { "scripts": {
"docs:dev": "vitepress dev --host", "docs:dev": "vitepress dev --host",

2252
docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -52,5 +52,26 @@
"author": "snowykami", "author": "snowykami",
"description": "自定义各种卡片的背景", "description": "自定义各种卡片的背景",
"link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip" "link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip"
},
{
"name": "测试资源包",
"desc": "test",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
"homepage": "",
"author": "snowykami"
},
{
"name": "轻雪Kakyo语言包 稳定版",
"description": "Liteyuki Bot的语言包用于提供多种语言的翻译。",
"link": "https://cdn.liteyuki.icu/static/lrp/kakyo.zip",
"homepage": "https://github.com/Nanaloveyuki/liteyuki-langpack",
"author": "Nanaloveyuki"
},
{
"name": "测试资源包2",
"description": "test",
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
"homepage": "",
"author": "snowykami"
} }
] ]

View File

@@ -14,7 +14,7 @@ order: 2
```yaml ```yaml
nonebot: nonebot:
# Nonebot机器人的配置以前的最外层配置项仍可为Nonebot服务,但是部分内容会被覆盖,请尽快迁移 # Nonebot机器人的配置6.3.10版本后NoneBot下配置已迁移至nonebot键下不再使用外层配置,但是部分内容会被覆盖,请尽快迁移
command_start: [ "/", "" ] # 指令前缀,若没有""空命令头请开启alconna_use_command_start保证alconna解析正常 command_start: [ "/", "" ] # 指令前缀,若没有""空命令头请开启alconna_use_command_start保证alconna解析正常
host: 127.0.0.1 # 监听地址默认为本机若要接收外部请求请填写0.0.0.0 host: 127.0.0.1 # 监听地址默认为本机若要接收外部请求请填写0.0.0.0
port: 20216 # 绑定端口 port: 20216 # 绑定端口

View File

@@ -2,26 +2,35 @@
title: 安装 title: 安装
order: 1 order: 1
--- ---
# 安装 # 安装
## **常规部署** ## **常规部署**
1. 安装 [`Git`](https://git-scm.com/download/) 和 [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) 环境 1. 安装 [`Git`](https://git-scm.com/download/) 和 [
`Python3.10+`](https://www.python.org/downloads/release/python-31010/) 环境
```bash ```bash
# 克隆项目到本地轻雪使用Git进行版本管理该步骤为必要项 # 克隆项目到本地轻雪使用Git进行版本管理该步骤为必要项
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1 git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1 # 若你不能访问Github可以使用Liteyuki镜像https://git.liteyuki.icu/LiteyukiStudio/LiteyukiBot
# 切换到Bot目录下 # 切换到Bot目录下
cd LiteyukiBot cd LiteyukiBot
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
.\venv\Scripts\activate # Windows
source venv/bin/activate # Linux
# 安装依赖 # 安装依赖
pip install -r requirements.txt pip install -r requirements.txt
# 启动Bot # 启动Bot
python main.py python main.py
``` ```
> [!tip]
> 推荐使用虚拟环境来运行轻雪,以避免依赖冲突,你可以使用`python -m venv .venv`来创建虚拟环境,然后使用`.venv\Scripts\activate`来激活虚拟环境Linux下使用`source .venv/bin/activate`激活)
## **使用Docker构建** ## **使用Docker构建**
1. 安装 [`Docker`](https://docs.docker.com/get-docker/) 1. 安装 [`Docker`](https://docs.docker.com/get-docker/)
@@ -34,10 +43,6 @@ python main.py
> Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br> > Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br>
> 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号 > 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号
## **使用TRSS Scripts部署**
[TRSS_Liteyuki轻雪机器人管理脚本](https://timerainstarsky.github.io/TRSS_Liteyuki/)该功能由TRSS提供支持不是LiteyukiBot官方提供的功能推荐使用`Arch Linux`
## **装置要求** ## **装置要求**
- Windows系统版本最低`Windows10+`/`Windows Server 2019+` - Windows系统版本最低`Windows10+`/`Windows Server 2019+`
@@ -47,7 +52,8 @@ python main.py
- 硬盘: 至少`1GB`空间 - 硬盘: 至少`1GB`空间
> [!warning] > [!warning]
> 如果装置上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`为你的Python可执行文件路径 > 如果装置上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`
> 为你的Python可执行文件路径
> [!warning] > [!warning]
> 轻雪的更新功能依赖Git如果你没有安装Git直接下载源代码运行你将无法使用更新功能 > 轻雪的更新功能依赖Git如果你没有安装Git直接下载源代码运行你将无法使用更新功能

View File

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

View File

@@ -2,6 +2,13 @@
title: 开发指南 title: 开发指南
order: 0 order: 0
--- ---
<script setup>
import ContributorBar from '../../components/ContributorBar.vue'
</script>
# 开发指南 # 开发指南
## 如何开发 ## 如何开发
@@ -49,8 +56,8 @@ order: 0
- commit message请遵循以下规范 - commit message请遵循以下规范
- 应在每次完成一个功能或修复一个bug后提交代码不要将多个功能或多个bug的代码混在一起提交。 - 应在每次完成一个功能或修复一个bug后提交代码不要将多个功能或多个bug的代码混在一起提交。
- 我们使用gitmoji来标记commit的类型`:sparkles:`表示引入新功能,`:bug:`表示修复bug等具体请参考[gitmoji](https://gitmoji.dev/) - 我们使用gitmoji来标记commit的类型`:sparkles:`表示引入新功能,`:bug:`表示修复bug等具体请参考[gitmoji](https://gitmoji.dev/)
- commit message的格式为`<emoji> [module]: <message>`,如`:sparkles: [liteyuki.event]: add new feature` - commit message的格式为`<emoji> [type:] <message>`,如`:sparkles: feat: 给event添加新字段`
其中module字段为受影响部分例如`liteyuki.message``docs.en.guide.md`等,不作过多要求,但请尽量填写;message字段为简短的描述总结来说就是你干了什么。 其中`type`字段为commit类型且**可选**message字段为简短的描述总结来说就是你干了什么。
- 不限制commit message的语言有条件可以使用中英双语。 - 不限制commit message的语言有条件可以使用中英双语。
- 文档请遵循[`Markdown`](https://www.markdownguide.org/)语法并且支持vitepress相关内容 - 文档请遵循[`Markdown`](https://www.markdownguide.org/)语法并且支持vitepress相关内容
- 修订文档时,每个语言的文档都要修订。 - 修订文档时,每个语言的文档都要修订。
@@ -59,3 +66,6 @@ order: 0
## 最后 ## 最后
- 本项目是一个非盈利的开源项目,我们欢迎任何人参与开发,你的贡献将会使轻雪变得更好。 - 本项目是一个非盈利的开源项目,我们欢迎任何人参与开发,你的贡献将会使轻雪变得更好。
<ContributorBar />

View File

@@ -14,6 +14,9 @@ hero:
- theme: alt - theme: alt
text: 📦 扩展 text: 📦 扩展
link: ./store/resource link: ./store/resource
- theme: alt
text: 🛠️ 开发
link: ./dev/guide
image: image:
light: ./liteyuki.svg light: ./liteyuki.svg
dark: ./liteyuki-dark.svg dark: ./liteyuki-dark.svg
@@ -23,7 +26,7 @@ features:
- title: 生态良好 - title: 生态良好
icon: 🛠️ icon: 🛠️
details: 基于轻雪框架原生支持任意Python Bot框架和应用程序 details: 基于轻雪框架原生支持任意Python Bot框架和应用程序
link: ./dev/api/api link: ./dev/api
- title: 插件管理 - title: 插件管理
icon: 🧩 icon: 🧩

View File

@@ -1,25 +1,28 @@
import asyncio import asyncio
import atexit
import os import os
import platform import platform
import signal
import sys import sys
import threading import threading
import time import time
from typing import Any, Optional from typing import Any, Optional
from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan, PROCESS_LIFESPAN_FUNC) from magicoca import Chan
from liteyuki.bot.lifespan import LIFESPAN_FUNC, Lifespan, PROCESS_LIFESPAN_FUNC
from liteyuki.comm.channel import get_channel from liteyuki.comm.channel import get_channel
from liteyuki.core.manager import ProcessManager 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.log import init_log, logger
from liteyuki.plugin import load_plugin from liteyuki.plugin import load_plugin
from liteyuki.session import message_handler_thread
from liteyuki.utils import IS_MAIN_PROCESS from liteyuki.utils import IS_MAIN_PROCESS
__all__ = [ __all__ = [
"LiteyukiBot", "LiteyukiBot",
"get_bot", "get_bot",
"get_config", "get_config",
"get_config_with_compat", "get_config_with_compat",
] ]
@@ -30,6 +33,10 @@ class LiteyukiBot:
Args: Args:
**kwargs: 配置 **kwargs: 配置
""" """
"""总通道"""
self.i_chan = Chan[Any]() # 外部输入通道
self.o_chan = Chan[Any]() # 外部输出通道
"""常规操作""" """常规操作"""
print_logo() print_logo()
global _BOT_INSTANCE global _BOT_INSTANCE
@@ -60,8 +67,9 @@ class LiteyukiBot:
启动逻辑 启动逻辑
""" """
await self.lifespan.before_start() # 启动前钩子 await self.lifespan.before_start() # 启动前钩子
sub_process_manager.start_all()
await self.lifespan.after_start() # 启动后钩子 await self.lifespan.after_start() # 启动后钩子
await self.keep_alive() message_handler_thread([_.ctx.sub_chan for _ in sub_process_manager.processes.values()])
def run(self): def run(self):
""" """
@@ -73,19 +81,7 @@ class LiteyukiBot:
except KeyboardInterrupt: except KeyboardInterrupt:
logger.opt(colors=True).info("<y>Liteyuki is stopping...</y>") logger.opt(colors=True).info("<y>Liteyuki is stopping...</y>")
self.stop() self.stop()
logger.opt(colors=True).info("<y>Liteyuki is stopped...</y>") logger.opt(colors=True).info("<y>Liteyuki is stopped !</y>")
async def keep_alive(self):
"""
保持轻雪运行
"""
logger.info("Liteyuki is keeping alive...")
try:
while not self.stop_event.is_set():
await asyncio.sleep(0.1)
except Exception:
logger.info("Liteyuki is exiting...")
self.stop()
def restart(self, delay: int = 0): def restart(self, delay: int = 0):
""" """
@@ -108,7 +104,11 @@ class LiteyukiBot:
cmd = "nohup" cmd = "nohup"
self.process_manager.terminate_all() self.process_manager.terminate_all()
# 进程退出后重启 # 进程退出后重启
threading.Thread(target=os.system, args=(f"{cmd} {executable} {' '.join(args)}",), daemon=True).start() threading.Thread(
target=os.system,
args=(f"{cmd} {executable} {' '.join(args)}",),
daemon=True,
).start()
sys.exit(0) sys.exit(0)
self.call_restart_count += 1 self.call_restart_count += 1
@@ -189,7 +189,9 @@ class LiteyukiBot:
""" """
return self.lifespan.on_before_process_shutdown(func) return self.lifespan.on_before_process_shutdown(func)
def on_before_process_restart(self, func: PROCESS_LIFESPAN_FUNC) -> PROCESS_LIFESPAN_FUNC: def on_before_process_restart(
self, func: PROCESS_LIFESPAN_FUNC
) -> PROCESS_LIFESPAN_FUNC:
""" """
注册进程重启前的函数,为子进程重启时调用 注册进程重启前的函数,为子进程重启时调用
Args: Args:
@@ -211,7 +213,7 @@ class LiteyukiBot:
return self.lifespan.on_after_restart(func) return self.lifespan.on_after_restart(func)
_BOT_INSTANCE: LiteyukiBot _BOT_INSTANCE: LiteyukiBot | None = None
def get_bot() -> LiteyukiBot: def get_bot() -> LiteyukiBot:
@@ -241,7 +243,9 @@ def get_config(key: str, default: Any = None) -> Any:
return get_bot().config.get(key, default) return get_bot().config.get(key, default)
def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any = None) -> Any: def get_config_with_compat(
key: str, compat_keys: tuple[str], default: Any = None
) -> Any:
""" """
获取配置,兼容旧版本 获取配置,兼容旧版本
Args: Args:
@@ -256,14 +260,18 @@ def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any = Non
return get_bot().config[key] return get_bot().config[key]
for compat_key in compat_keys: for compat_key in compat_keys:
if compat_key in get_bot().config: if compat_key in get_bot().config:
logger.warning(f"Config key \"{compat_key}\" will be deprecated, use \"{key}\" instead.") logger.warning(
f'Config key "{compat_key}" will be deprecated, use "{key}" instead.'
)
return get_bot().config[compat_key] return get_bot().config[compat_key]
return default return default
def print_logo(): def print_logo():
"""@litedoc-hide""" """@litedoc-hide"""
print("\033[34m" + r""" print(
"\033[34m"
+ r"""
__ ______ ________ ________ __ __ __ __ __ __ ______ __ ______ ________ ________ __ __ __ __ __ __ ______
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ | / | / |/ |/ |/ \ / |/ | / |/ | / |/ |
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/ $$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
@@ -273,4 +281,6 @@ def print_logo():
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_ $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ | $$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
""" + "\033[0m") """
+ "\033[0m"
)

View File

@@ -4,20 +4,31 @@
""" """
import asyncio import asyncio
from multiprocessing import Pipe from multiprocessing import Pipe
from typing import Any, Callable, Coroutine, Generic, Optional, TypeAlias, TypeVar, get_args from typing import (
Any,
Callable,
Coroutine,
Generic,
Optional,
TypeAlias,
TypeVar,
get_args,
)
from liteyuki.log import logger from liteyuki.log import logger
from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable
T = TypeVar("T") T = TypeVar("T")
SYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[T], Any] # 同步接收函数 SYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[T], Any] # 同步接收函数
ASYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[T], Coroutine[Any, Any, Any]] # 异步接收函数 ASYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[
ON_RECEIVE_FUNC: TypeAlias = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC # 接收函数 [T], Coroutine[Any, Any, Any]
] # 异步接收函数
ON_RECEIVE_FUNC: TypeAlias = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC # 接收函数
SYNC_FILTER_FUNC: TypeAlias = Callable[[T], bool] # 同步过滤函数 SYNC_FILTER_FUNC: TypeAlias = Callable[[T], bool] # 同步过滤函数
ASYNC_FILTER_FUNC: TypeAlias = Callable[[T], Coroutine[Any, Any, bool]] # 异步过滤函数 ASYNC_FILTER_FUNC: TypeAlias = Callable[[T], Coroutine[Any, Any, bool]] # 异步过滤函数
FILTER_FUNC: TypeAlias = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC # 过滤函数 FILTER_FUNC: TypeAlias = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC # 过滤函数
_func_id: int = 0 _func_id: int = 0
_channel: dict[str, "Channel"] = {} _channel: dict[str, "Channel"] = {}
@@ -39,7 +50,9 @@ class Channel(Generic[T]):
""" """
self.conn_send, self.conn_recv = Pipe() self.conn_send, self.conn_recv = Pipe()
self._conn_send_inner, self._conn_recv_inner = Pipe() # 内部通道,用于子进程通信 self._conn_send_inner, self._conn_recv_inner = (
Pipe()
) # 内部通道,用于子进程通信
self._closed = False self._closed = False
self._on_main_receive_func_ids: list[int] = [] self._on_main_receive_func_ids: list[int] = []
self._on_sub_receive_func_ids: list[int] = [] self._on_sub_receive_func_ids: list[int] = []
@@ -64,7 +77,9 @@ class Channel(Generic[T]):
_channel[name] = self _channel[name] = self
logger.debug(f"Channel {name} initialized in main process") logger.debug(f"Channel {name} initialized in main process")
else: else:
logger.debug(f"Channel {name} initialized in sub process, should manually set in main process") logger.debug(
f"Channel {name} initialized in sub process, should manually set in main process"
)
def _get_generic_type(self) -> Optional[type]: def _get_generic_type(self) -> Optional[type]:
""" """
@@ -72,7 +87,7 @@ class Channel(Generic[T]):
Returns: Returns:
Optional[type]: 泛型类型 Optional[type]: 泛型类型
""" """
if hasattr(self, '__orig_class__'): if hasattr(self, "__orig_class__"):
return get_args(self.__orig_class__)[0] return get_args(self.__orig_class__)[0]
return None return None
@@ -98,7 +113,10 @@ class Channel(Generic[T]):
elif isinstance(structure, dict): elif isinstance(structure, dict):
if not isinstance(data, dict): if not isinstance(data, dict):
return False return False
return all(k in data and self._validate_structure(data[k], structure[k]) for k in structure) return all(
k in data and self._validate_structure(data[k], structure[k])
for k in structure
)
return False return False
def __str__(self): def __str__(self):
@@ -113,10 +131,12 @@ class Channel(Generic[T]):
if self.type_check: if self.type_check:
_type = self._get_generic_type() _type = self._get_generic_type()
if _type is not None and not self._validate_structure(data, _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") raise TypeError(
f"Data must be an instance of {_type}, {type(data)} found"
)
if self._closed: if self._closed:
raise RuntimeError("Cannot send to a closed channel_") raise RuntimeError("Cannot send to a closed channel")
self.conn_send.send(data) self.conn_send.send(data)
def receive(self) -> T: def receive(self) -> T:
@@ -126,7 +146,7 @@ class Channel(Generic[T]):
T: 数据 T: 数据
""" """
if self._closed: if self._closed:
raise RuntimeError("Cannot receive from a closed channel_") raise RuntimeError("Cannot receive from a closed channel")
while True: while True:
data = self.conn_recv.recv() data = self.conn_recv.recv()
@@ -142,7 +162,9 @@ class Channel(Generic[T]):
data = await loop.run_in_executor(None, self.receive) data = await loop.run_in_executor(None, self.receive)
return data return data
def on_receive(self, filter_func: Optional[FILTER_FUNC] = None) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]: def on_receive(
self, filter_func: Optional[FILTER_FUNC] = None
) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]:
""" """
接收数据并执行函数 接收数据并执行函数
Args: Args:
@@ -187,37 +209,52 @@ class Channel(Generic[T]):
data: 数据 data: 数据
""" """
if IS_MAIN_PROCESS: if IS_MAIN_PROCESS:
[asyncio.create_task(_callback_funcs[func_id](data)) for func_id in self._on_main_receive_func_ids] [
asyncio.create_task(_callback_funcs[func_id](data))
for func_id in self._on_main_receive_func_ids
]
else: else:
[asyncio.create_task(_callback_funcs[func_id](data)) for func_id in self._on_sub_receive_func_ids] [
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") # 主动通道 active_channel: Channel = Channel(name="active_channel") # 主动通道
passive_channel: Channel = Channel(name="passive_channel") # 被动通道 passive_channel: Channel = Channel(name="passive_channel") # 被动通道
publish_channel: Channel[tuple[str, dict[str, Any]]] = Channel(name="publish_channel") # 发布通道 publish_channel: Channel[tuple[str, dict[str, Any]]] = Channel(
name="publish_channel"
) # 发布通道
"""通道传递通道,主进程创建单例,子进程初始化时实例化""" """通道传递通道,主进程创建单例,子进程初始化时实例化"""
channel_deliver_active_channel: Channel[Channel[Any]] # 主动通道传递通道 channel_deliver_active_channel: Channel[Channel[Any]] # 主动通道传递通道
channel_deliver_passive_channel: Channel[tuple[str, dict[str, Any]]] # 被动通道传递通道 channel_deliver_passive_channel: Channel[tuple[str, dict[str, Any]]] # 被动通道传递通道
if IS_MAIN_PROCESS: if IS_MAIN_PROCESS:
channel_deliver_active_channel = Channel(name="channel_deliver_active_channel") # 主动通道传递通道 channel_deliver_active_channel = Channel(
channel_deliver_passive_channel = Channel(name="channel_deliver_passive_channel") # 被动通道传递通道 name="channel_deliver_active_channel"
) # 主动通道传递通道
channel_deliver_passive_channel = Channel(
name="channel_deliver_passive_channel"
) # 被动通道传递通道
@channel_deliver_passive_channel.on_receive(
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == "set_channel") filter_func=lambda data: data[0] == "set_channel"
)
def on_set_channel(data: tuple[str, dict[str, Any]]): def on_set_channel(data: tuple[str, dict[str, Any]]):
name, channel = data[1]["name"], data[1]["channel_"] name, channel = data[1]["name"], data[1]["channel_"]
set_channel(name, channel) set_channel(name, channel)
@channel_deliver_passive_channel.on_receive(
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == "get_channel") filter_func=lambda data: data[0] == "get_channel"
)
def on_get_channel(data: tuple[str, dict[str, Any]]): def on_get_channel(data: tuple[str, dict[str, Any]]):
name, recv_chan = data[1]["name"], data[1]["recv_chan"] name, recv_chan = data[1]["name"], data[1]["recv_chan"]
recv_chan.send(get_channel(name)) recv_chan.send(get_channel(name))
@channel_deliver_passive_channel.on_receive(
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == "get_channels") filter_func=lambda data: data[0] == "get_channels"
)
def on_get_channels(data: tuple[str, dict[str, Any]]): def on_get_channels(data: tuple[str, dict[str, Any]]):
recv_chan = data[1]["recv_chan"] recv_chan = data[1]["recv_chan"]
recv_chan.send(get_channels()) recv_chan.send(get_channels())
@@ -231,7 +268,9 @@ def set_channel(name: str, channel: "Channel"):
channel ([`Channel`](#class-channel-generic-t)): 通道实例 channel ([`Channel`](#class-channel-generic-t)): 通道实例
""" """
if not isinstance(channel, Channel): if not isinstance(channel, Channel):
raise TypeError(f"channel_ must be an instance of Channel, {type(channel)} found") raise TypeError(
f"channel_ must be an instance of Channel, {type(channel)} found"
)
if IS_MAIN_PROCESS: if IS_MAIN_PROCESS:
if name in _channel: if name in _channel:
@@ -241,10 +280,11 @@ def set_channel(name: str, channel: "Channel"):
# 请求主进程设置通道 # 请求主进程设置通道
channel_deliver_passive_channel.send( channel_deliver_passive_channel.send(
( (
"set_channel", { "set_channel",
"name" : name, {
"channel_": channel, "name": name,
} "channel_": channel,
},
) )
) )
@@ -273,13 +313,7 @@ def get_channel(name: str) -> "Channel":
else: else:
recv_chan = Channel[Channel[Any]]("recv_chan") recv_chan = Channel[Channel[Any]]("recv_chan")
channel_deliver_passive_channel.send( channel_deliver_passive_channel.send(
( ("get_channel", {"name": name, "recv_chan": recv_chan})
"get_channel",
{
"name" : name,
"recv_chan": recv_chan
}
)
) )
return recv_chan.receive() return recv_chan.receive()
@@ -294,12 +328,5 @@ def get_channels() -> dict[str, "Channel"]:
return _channel return _channel
else: else:
recv_chan = Channel[dict[str, Channel[Any]]]("recv_chan") recv_chan = Channel[dict[str, Channel[Any]]]("recv_chan")
channel_deliver_passive_channel.send( channel_deliver_passive_channel.send(("get_channels", {"recv_chan": recv_chan}))
(
"get_channels",
{
"recv_chan": recv_chan
}
)
)
return recv_chan.receive() return recv_chan.receive()

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
本模块用于实现RPC(基于IPC)通信
"""
from typing import TypeAlias, Callable, Any
from liteyuki.comm.channel import Channel
ON_CALLING_FUNC: TypeAlias = Callable[[tuple, dict], Any]
class RPC:
"""
RPC类
"""
def __init__(self, on_calling: ON_CALLING_FUNC) -> None:
self.on_calling = on_calling
def call(self, args: tuple, kwargs: dict) -> Any:
"""
调用
"""
# 获取self.calling函数名
return self.on_calling(args, kwargs)

View File

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

View File

@@ -14,6 +14,9 @@ import threading
from multiprocessing import Process from multiprocessing import Process
from typing import Any, Callable, TYPE_CHECKING, TypeAlias 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.log import logger
from liteyuki.utils import IS_MAIN_PROCESS from liteyuki.utils import IS_MAIN_PROCESS
@@ -26,7 +29,10 @@ from liteyuki.comm import Channel
if IS_MAIN_PROCESS: if IS_MAIN_PROCESS:
from liteyuki.comm.channel import get_channel, publish_channel, get_channels from liteyuki.comm.channel import get_channel, publish_channel, get_channels
from liteyuki.comm.storage import shared_memory from liteyuki.comm.storage import shared_memory
from liteyuki.comm.channel import channel_deliver_active_channel, channel_deliver_passive_channel from liteyuki.comm.channel import (
channel_deliver_active_channel,
channel_deliver_passive_channel,
)
else: else:
from liteyuki.comm import channel from liteyuki.comm import channel
from liteyuki.comm import storage from liteyuki.comm import storage
@@ -34,20 +40,18 @@ else:
TARGET_FUNC: TypeAlias = Callable[..., Any] TARGET_FUNC: TypeAlias = Callable[..., Any]
TIMEOUT = 10 TIMEOUT = 10
__all__ = [ __all__ = ["ProcessManager", "sub_process_manager"]
"ProcessManager"
]
multiprocessing.set_start_method("spawn", force=True) multiprocessing.set_start_method("spawn", force=True)
class ChannelDeliver: class ChannelDeliver:
def __init__( def __init__(
self, self,
active: Channel[Any], active: Channel[Any],
passive: Channel[Any], passive: Channel[Any],
channel_deliver_active: Channel[Channel[Any]], channel_deliver_active: Channel[Channel[Any]],
channel_deliver_passive: Channel[tuple[str, dict]], channel_deliver_passive: Channel[tuple[str, dict]],
publish: Channel[tuple[str, Any]], publish: Channel[tuple[str, Any]],
): ):
self.active = active self.active = active
self.passive = passive self.passive = passive
@@ -57,7 +61,9 @@ class ChannelDeliver:
# 函数处理一些跨进程通道的 # 函数处理一些跨进程通道的
def _delivery_channel_wrapper(func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyValueStore", *args, **kwargs): def _delivery_channel_wrapper(
func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyValueStore", *args, **kwargs
):
""" """
子进程入口函数 子进程入口函数
处理一些操作 处理一些操作
@@ -68,8 +74,12 @@ def _delivery_channel_wrapper(func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyVal
channel.active_channel = cd.active # 子进程主动通道 channel.active_channel = cd.active # 子进程主动通道
channel.passive_channel = cd.passive # 子进程被动通道 channel.passive_channel = cd.passive # 子进程被动通道
channel.channel_deliver_active_channel = cd.channel_deliver_active # 子进程通道传递主动通道 channel.channel_deliver_active_channel = (
channel.channel_deliver_passive_channel = cd.channel_deliver_passive # 子进程通道传递被动通道 cd.channel_deliver_active
) # 子进程通道传递主动通道
channel.channel_deliver_passive_channel = (
cd.channel_deliver_passive
) # 子进程通道传递被动通道
channel.publish_channel = cd.publish # 子进程发布通道 channel.publish_channel = cd.publish # 子进程发布通道
# 给子进程创建共享内存实例 # 给子进程创建共享内存实例
@@ -102,8 +112,12 @@ class ProcessManager:
chan_active = get_channel(f"{name}-active") chan_active = get_channel(f"{name}-active")
def _start_process(): def _start_process():
process = Process(target=self.targets[name][0], args=self.targets[name][1], process = Process(
kwargs=self.targets[name][2], daemon=True) target=self.targets[name][0],
args=self.targets[name][1],
kwargs=self.targets[name][2],
daemon=True,
)
self.processes[name] = process self.processes[name] = process
process.start() process.start()
@@ -133,7 +147,9 @@ class ProcessManager:
for name in self.targets: for name in self.targets:
logger.debug(f"Starting process {name}") logger.debug(f"Starting process {name}")
threading.Thread(target=self._run_process, args=(name, ), daemon=True).start() threading.Thread(
target=self._run_process, args=(name,), daemon=True
).start()
def add_target(self, name: str, target: TARGET_FUNC, args: tuple = (), kwargs=None): def add_target(self, name: str, target: TARGET_FUNC, args: tuple = (), kwargs=None):
""" """
@@ -154,10 +170,14 @@ class ProcessManager:
passive=chan_passive, passive=chan_passive,
channel_deliver_active=channel_deliver_active_channel, channel_deliver_active=channel_deliver_active_channel,
channel_deliver_passive=channel_deliver_passive_channel, channel_deliver_passive=channel_deliver_passive_channel,
publish=publish_channel publish=publish_channel,
) )
self.targets[name] = (_delivery_channel_wrapper, (target, channel_deliver, shared_memory, *args), kwargs) self.targets[name] = (
_delivery_channel_wrapper,
(target, channel_deliver, shared_memory, *args),
kwargs,
)
# 主进程通道 # 主进程通道
def join_all(self): def join_all(self):
@@ -199,3 +219,79 @@ class ProcessManager:
if name not in self.targets: if name not in self.targets:
logger.warning(f"Process {name} not found.") logger.warning(f"Process {name} not found.")
return self.processes[name].is_alive() 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()

View File

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

View File

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

View File

@@ -60,7 +60,6 @@ def load_plugin(module_path: str | Path) -> Optional[Plugin]:
f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type
) )
else: else:
logger.opt(colors=True).warning( logger.opt(colors=True).warning(
f'The metadata of Liteyuki plugin "{module.__name__}" is not specified, use empty.' f'The metadata of Liteyuki plugin "{module.__name__}" is not specified, use empty.'
) )

View File

@@ -9,9 +9,9 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Software: PyCharm @Software: PyCharm
""" """
from liteyuki.message.on import on_startswith from liteyuki.session.on import on_startswith
from liteyuki.message.event import MessageEvent from liteyuki.session.event import MessageEvent
from liteyuki.message.rule import is_su_rule from liteyuki.session.rule import is_su_rule
@on_startswith(["liteecho"], rule=is_su_rule).handle() @on_startswith(["liteecho"], rule=is_su_rule).handle()

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
"""
该模块参考并引用了nonebot-plugin-alconna的消息段定义
"""
from typing import Any
from typing import Iterable
from magicoca import Chan, select
def message_handler_thread(i_chans: Iterable[Chan[Any]]):
"""
Args:
i_chans: 多路输入管道组
Returns:
"""
for msg in select(*i_chans):
print("Recv from anybot", msg)

View File

@@ -11,7 +11,6 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
from typing import Any, Optional from typing import Any, Optional
from liteyuki import Channel from liteyuki import Channel
from liteyuki.comm.storage import shared_memory
class MessageEvent: class MessageEvent:

View File

@@ -11,8 +11,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
import traceback import traceback
from typing import Any, TypeAlias, Callable, Coroutine from typing import Any, TypeAlias, Callable, Coroutine
from liteyuki.message.event import MessageEvent from liteyuki.session.event import MessageEvent
from liteyuki.message.rule import Rule from liteyuki.session.rule import Rule
EventHandler: TypeAlias = Callable[[MessageEvent], Coroutine[None, None, Any]] EventHandler: TypeAlias = Callable[[MessageEvent], Coroutine[None, None, Any]]

View File

@@ -0,0 +1,51 @@
from pydantic import BaseModel
class User(BaseModel):
"""
用户信息
Attributes:
id: 用户ID
name: 用户名
nick: 用户昵称
avatar: 用户头像图链接
"""
id: str
name: str | None
nick: str | None
avatar: str | None
class Scene(BaseModel):
"""
场景信息
Attributes:
id: 场景ID
type: 场景类型
name: 场景名
avatar: 场景头像图链接
parent: 父场景
"""
id: str
type: str
name: str | None
avatar: str | None
parent: "Scene | None"
class Session(BaseModel):
"""
会话信息
Attributes:
self_id: 机器人ID
adapter: 适配器ID
scope: 会话范围
scene: 场景信息
user: 用户信息
member: 成员信息,仅频道及群聊有效
operator: 操作者信息,仅频道及群聊有效
"""
self_id: str
adapter: str
scope: str
scene: Scene
user: User
member: "Member | None"
operator: "Member | None"

View File

@@ -11,33 +11,13 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
from queue import Queue from queue import Queue
from liteyuki.comm.storage import shared_memory from liteyuki.session.event import MessageEvent
from liteyuki.log import logger from liteyuki.session.matcher import Matcher
from liteyuki.message.event import MessageEvent from liteyuki.session.rule import Rule, empty_rule
from liteyuki.message.matcher import Matcher
from liteyuki.message.rule import Rule, empty_rule
_matcher_list: list[Matcher] = [] _matcher_list: list[Matcher] = []
_queue: Queue = Queue() _queue: Queue = Queue()
@shared_memory.on_subscriber_receive("event_to_liteyuki")
async def _(event: MessageEvent):
print("AA")
current_priority = -1
for i, matcher in enumerate(_matcher_list):
logger.info(f"Running matcher {matcher} for event: {event}")
await matcher.run(event)
# 同优先级不阻断,不同优先级阻断
if current_priority != matcher.priority:
current_priority = matcher.priority
if matcher.block:
break
else:
logger.info(f"No matcher matched for event: {event}")
print("BB")
def add_matcher(matcher: Matcher): def add_matcher(matcher: Matcher):
for i, m in enumerate(_matcher_list): for i, m in enumerate(_matcher_list):
if m.priority < matcher.priority: if m.priority < matcher.priority:

View File

@@ -11,7 +11,7 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
import inspect import inspect
from typing import Optional, TypeAlias, Callable, Coroutine from typing import Optional, TypeAlias, Callable, Coroutine
from liteyuki.message.event import MessageEvent from liteyuki.session.event import MessageEvent
from liteyuki import get_config from liteyuki import get_config
_superusers: list[str] = get_config("liteyuki.superusers", []) _superusers: list[str] = get_config("liteyuki.superusers", [])

View File

@@ -0,0 +1,3 @@
"""
Module docs
"""

60
liteyuki_flow/__main__.py Normal file
View File

@@ -0,0 +1,60 @@
"""
Module docs
"""
import os
from github import Github
from argparse import ArgumentParser
from liteyuki_flow.const import PLUGIN_PREFIX, RESOURCE_PREFIX
from liteyuki_flow.typ import err, nil # type: ignore
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("--handle", action="store_true") # 处理issue
parser.add_argument("-p", "--parse", action="store_true") # 解析markdown文件
parser.add_argument("-i", "--input", type=str, help="Path to the markdown file.")
args = parser.parse_args()
if args.handle:
print("Starting the issue handler module...")
ISSUE_NUMBER = os.getenv("ISSUE_NUMBER")
REPOSITORY = os.getenv("REPOSITORY")
ACT_TYPE = os.getenv("ACT_TYPE") # opened, edited, closed, reopened
if ISSUE_NUMBER is None or REPOSITORY is None or ACT_TYPE is None:
raise ValueError("Issue number, repository and action type are required.")
g = Github(os.getenv("GITHUB_TOKEN"))
repo = g.get_repo(REPOSITORY)
issue = g.get_repo(REPOSITORY).get_issue(int(ISSUE_NUMBER))
# 审资源
if issue.title.strip().startswith(RESOURCE_PREFIX):
from liteyuki_flow.resource_handler import handle_resource # type: ignore
handle_resource(github=g, issue=issue, repo=repo, act_type=ACT_TYPE)
# 审插件
elif issue.title.strip().startswith(PLUGIN_PREFIX):
from liteyuki_flow.plugin_handler import handle_plugin # type: ignore
pass
else:
print("No handler found for the issue.")
elif args.parse:
print("Starting the markdown parser module...")
from liteyuki_flow.markdown_parser import MarkdownParser # type: ignore
if args.input is None:
raise ValueError("Input file is required.")
with open(args.input, "r", encoding="utf-8") as f:
content = f.read()
md_parser = MarkdownParser(content) # type: ignore
err = md_parser.parse_front_matters() # type: ignore
if err != nil:
print(f"Err: {err}")
for k, v in md_parser.front_matters.content.items():
print(f"{k}: {v}")
else:
print("No module specified.")

19
liteyuki_flow/const.py Normal file
View File

@@ -0,0 +1,19 @@
"""
Module docs
"""
OPENED = "opened"
EDITED = "edited"
CLOSED = "closed"
REOPENED = "reopened"
RESOURCE_PREFIX = "Resource: "
PLUGIN_PREFIX = "Plugin: "
PLUGIN_JSON = "docs/public/plugins.json"
RESOURCE_JSON = "docs/public/resources.json"
edit_content_tip = "若要修改请编辑这段front matter不要编辑正文/If you want to modify, please edit the front matter, do not edit the body"
edit_tip = "如需修改请直接编辑issue请不要新建issue我会自动检查"
bot_id = "liteyuki-flow"

View File

@@ -0,0 +1,146 @@
"""
从markdown提取插件/资源信息
"""
from typing import Any
from github.Issue import Issue
from liteyuki_flow.typ import Nil, err, nil # type: ignore
# # xxx
class Header:
def __init__(self, level: int, content: str):
self.level = level
self.content = content
def __str__(self):
return f'Header({self.level}, {self.content})'
def __repr__(self):
return self.__str__()
# - xxx
class List:
def __init__(self, level: int, content: str):
self.level = level
self.content = content
def __str__(self):
return f'List({self.level}, {self.content})'
def __repr__(self):
return self.__str__()
class FrontMatter:
def __init__(self, content: dict[str, str]):
self.content = content
def __setitem__(self, key: str, value: str):
self.content[key] = value
def get(self, key, default=None) -> Any:
return self.content.get(key, default)
def __str__(self):
return "\n".join([f'{k}: {v}' for k, v in self.content.items()])
class MarkdownParser:
def __init__(self, content: str):
self.content = content
self.content_lines = content.split('\n')
self.front_matters: FrontMatter = FrontMatter({})
self._content_list: list[Any] = [self.front_matters]
self.lineno = 0
self._parsed = False
def parse_front_matters(self) -> err:
if self.content_lines[self.lineno].strip() != '---':
return ValueError('Invalid front matter')
while self.lineno < len(self.content_lines):
self._next_line()
line = self.content_lines[self.lineno]
if line.strip() == '---':
break
if line.strip().startswith('#'):
# fm注释
continue
try:
key, value = line.split(':', 1)
except ValueError:
return Exception(f'Invalid front matter: {line}')
self.front_matters[key.strip()] = value.strip()
return nil
def build_front_matters(self) -> str:
return "---\n" + str(self.front_matters) + "\n---"
def _parse_content(self) -> tuple[list[Any], err]:
content: list[Any] = []
while self.lineno < len(self.content_lines):
item, e = self._parse_line()
if e != nil:
return nil, e
content.append(item)
return content, nil
def _parse_line(self) -> tuple[Any, err]:
line = self.content_lines[self.lineno]
if line.startswith('#'):
# 计算start有几个#
start = 0
while line[start] == '#':
start += 1
return Header(start, line[start:].strip()), nil
elif line.startswith('-'):
start = 0
while line[start] == '-':
start += 1
return List(start, line[start:].strip()), nil
# 处理<!--注释 continue
elif line.strip().startswith('<!--'):
while not line.strip().endswith('-->'):
self._next_line()
line = self.content_lines[self.lineno]
return None, nil
# 处理[//]: # (注释) continue
elif line.strip().startswith('[//]: #'):
self._next_line()
return None, nil
self._next_line()
return nil, ValueError(f'Invalid line: {line}')
def _next_line(self):
self.lineno += 1
def parse(self) -> tuple[list[Any] | Nil, err]:
if self._parsed:
return self._content_list, nil
e = self.parse_front_matters()
if e != nil:
return nil, e
ls, e = self._parse_content()
if e != nil:
return nil, e
self._content_list.extend(ls)
self._parsed = True
return self._content_list, nil
# 解析资源发布issue体
def parse_resource_publish_info(issue: Issue) -> dict[str, str]:
parser = MarkdownParser(issue.body)
parser.parse_front_matters()
return parser.front_matters

View File

@@ -0,0 +1,7 @@
"""
Module docs
"""
def plugin_handler():
pass

View File

@@ -0,0 +1,3 @@
PyGithub==2.4.0
requests==2.31.0
pyyaml==6.0.2

View File

@@ -0,0 +1,199 @@
"""
Module docs
"""
import requests # type: ignore
import zipfile
from github import Github, InputGitTreeElement, GitTree
from github.Issue import Issue
from github.Repository import Repository
import json
import yaml
from liteyuki_flow.const import OPENED, EDITED, CLOSED, REOPENED, RESOURCE_JSON, bot_id, edit_tip
from liteyuki_flow.markdown_parser import MarkdownParser
from liteyuki_flow.typ import err, nil
user_agent = "liteyuki-flow"
headers = {
"User-Agent": user_agent
}
def push_check_result(issue: Issue, result: str):
cid = None
for cm in issue.get_comments():
if cm.body.startswith("检查结果") and cm.user.login == bot_id:
cid = cm.id
break
if cid is not None:
issue.get_comment(cid).edit("检查结果: " + result)
else:
issue.create_comment("检查结果: " + result)
def push_publish_result(issue: Issue, result: str):
cid = None
for cm in issue.get_comments():
if cm.body.startswith("发布结果") and cm.user.login == bot_id:
cid = cm.id
break
if cid is not None:
issue.get_comment(cid).edit("发布结果: " + result)
else:
issue.create_comment("发布结果: " + result)
# opened: 创建新的资源包,预审核
# edited: 编辑资源包信息,需重新审核
# closed: 审核通过修改json并提交
# reopened: 重新打开,无操作
def on_first_open(github: Github, issue: Issue, repo: Repository):
issue.create_comment("已收到资源包发布请求,我会马上开始预检. " + edit_tip)
push_check_result(issue, "请等待")
issue.add_to_labels("Resource")
# opened | edited
def pre_check(github: Github, issue: Issue, repo: Repository) -> err:
parser = MarkdownParser(issue.body)
parser.parse_front_matters()
name = parser.front_matters.get("name")
desc = parser.front_matters.get("desc")
link = parser.front_matters.get("link")
homepage = parser.front_matters.get("homepage") # optional
author = parser.front_matters.get("author")
if not all((name, desc, link, author)):
push_check_result(issue, "❌ name, desc, link, homepage 及 author 为必填字段.")
return ValueError("name, desc, link, homepage 及 author 为必填字段.")
# 下载并解析资源包
r = requests.get(link, headers=headers)
if r.status_code != 200:
push_check_result(issue, "❌ 下载失败.")
return ValueError("下载失败.")
try:
with open(f"{name}.zip", "wb") as f:
f.write(r.content)
# 解压
with zipfile.ZipFile(f"{name}.zip", "r") as z:
z.extractall(f"{name}")
# 检测包内metadata.yml文件
data = yaml.load(open(f"{name}/metadata.yml"), Loader=yaml.SafeLoader)
except Exception as e:
push_check_result(issue, "❌ 解析资源包失败可能是格式问题或metadata.yml不存在: " + str(e))
return e
# 检测必要字段 namedescriptionversion
if not all((data.get("name"), data.get("description"), data.get("version"))):
push_check_result(issue, "❌ 元数据中缺少必要字段 name, description 或 version.")
return ValueError("元数据中缺少必要字段 name, description 或 version.")
# 不检测重复资源包,因为资源包可能有多个版本
# 检测通过编辑原issue
metadata_markdown = f"**名称**: {data.get('name')}\n**描述**: {data.get('description')}\n**版本**: {data.get('version')}\n"
for k, v in data.items():
if k not in ("name", "description", "version"):
metadata_markdown += f"**{k}**: {v}\n"
new_issue_body = f"---\nname: {name}\ndesc: {desc}\nlink: {link}\nhomepage: {homepage}\nauthor: {author}\n---\n"
publish_info = f"## 发布信息\n"
publish_info += f"**名称**: {name}\n"
publish_info += f"**描述**: {desc}\n"
publish_info += f"**作者**: {author}\n"
publish_info += f"**主页**: {homepage}\n"
publish_info += f"**下载**: {link}\n"
# 遍历其他字段
for k, v in data.items():
if k not in ("name", "description", "version"):
new_issue_body += f"**{k}**: {v}\n"
issue.edit(title=f"Resource: {name}")
issue.add_to_labels("pre-checked")
push_check_result(issue, f"✅ 预检查通过,等待管理员人工审核\n{publish_info}\n## 元数据\n{metadata_markdown}")
return nil
# closed
def add_resource(github: Github, issue: Issue, repo: Repository) -> err:
# 检测关闭时是否有管理员发布的通过评论
try:
if "pre-checked" not in [l.name for l in issue.labels]:
issue.edit(state="open")
push_publish_result(issue, "❌ 请先通过预检查。")
return ValueError("请先进行预检查。")
# 检测评论
for cm in issue.get_comments():
if cm.body.startswith(("通过", "pass",)):
# 检测用户是否是管理员
if cm.user.login not in [u.login for u in repo.get_collaborators()]:
issue.edit(state="open")
push_publish_result(issue, "❌ 你不是仓库管理员,无法发布资源包。")
return ValueError("你不是仓库管理员,无法发布资源包。")
break
else:
issue.edit(state="open")
push_publish_result(issue, "❌ 管理员未审核。")
return ValueError("管理员未审核。")
parser = MarkdownParser(issue.body)
parser.parse_front_matters()
name = parser.front_matters.get("name")
desc = parser.front_matters.get("desc")
link = parser.front_matters.get("link")
homepage = parser.front_matters.get("homepage") # optional
author = parser.front_matters.get("author")
# 编辑仓库内的json文件
resources = json.load(open(RESOURCE_JSON))
resources.append({
"name": name,
"description": desc,
"link": link,
"homepage": homepage,
"author": author
})
ref = repo.get_git_ref("heads/main")
tree = repo.create_git_tree(
base_tree=repo.get_git_commit(ref.object.sha).tree,
tree=[
InputGitTreeElement(
path=RESOURCE_JSON,
mode="100644",
type="blob",
content=json.dumps(resources, indent=4, ensure_ascii=False)
)
]
)
commit = repo.create_git_commit(
message=f":package: 发布资源: {name}",
tree=tree,
parents=[repo.get_git_commit(ref.object.sha)]
)
ref.edit(commit.sha)
if "pre-checked" in [l.name for l in issue.labels]:
issue.remove_from_labels("pre-checked")
issue.add_to_labels("published")
push_publish_result(issue, f"✅ 资源包 {name} 已发布!商店页面稍后就会更新。")
return nil
except Exception as e:
issue.edit(state="open")
push_publish_result(issue, f"❌ 发布失败: {str(e)}")
return e
def handle_resource(github: Github, issue: Issue, repo: Repository, act_type: str):
if act_type in (OPENED, EDITED):
if act_type == OPENED:
on_first_open(github, issue, repo)
pre_check(github, issue, repo)
elif act_type == CLOSED:
e = add_resource(github, issue, repo)
if e != nil:
print(f"Error: {e}")
else:
print("No operation found for the issue: ", act_type)

26
liteyuki_flow/ts.md Normal file
View File

@@ -0,0 +1,26 @@
---
# 请修改此处内容/Please modify the content here
# 不要修改正文中的内容/Do not modify the content in the body
name: PluginName
desc: 插件描述
author: 作者名
link: 下载链接
homepage: 主页链接
---
<!-- 以下字段自动生成,请勿编辑 -->
# Plugin: PluginName
## 插件信息
- **包名**: PluginName
- **描述**: 插件描述
- **作者**: AuthorName
- **主页**: https://a.b.c
## 资源包信息
- **名称**: PluginName
- **描述**: 插件描述
- **作者**: AuthorName
- **主页**: https://a.b.c
- **下载**: https://a.b.c

20
liteyuki_flow/typ.py Normal file
View File

@@ -0,0 +1,20 @@
"""
Module docs
"""
from typing import TypeAlias
class Nil():
def __eq__(self, other):
if isinstance(other, Nil):
return True
return other is None
# 不等于
def __ne__(self, other):
return not self.__eq__(other)
nil = Nil()
err: TypeAlias = Exception | Nil

515
pdm.lock generated Normal file
View File

@@ -0,0 +1,515 @@
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default", "dev"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:0ae69881170a4850760711fe7edf87f03fb6f0349f9d3008276d6c9ac3fcf83b"
[[metadata.targets]]
requires_python = ">=3.10"
[[package]]
name = "annotated-types"
version = "0.7.0"
requires_python = ">=3.8"
summary = "Reusable constraint types to use with typing.Annotated"
groups = ["default"]
dependencies = [
"typing-extensions>=4.0.0; python_version < \"3.9\"",
]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
[[package]]
name = "black"
version = "24.10.0"
requires_python = ">=3.9"
summary = "The uncompromising code formatter."
groups = ["dev"]
dependencies = [
"click>=8.0.0",
"mypy-extensions>=0.4.3",
"packaging>=22.0",
"pathspec>=0.9.0",
"platformdirs>=2",
"tomli>=1.1.0; python_version < \"3.11\"",
"typing-extensions>=4.0.1; python_version < \"3.11\"",
]
files = [
{file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"},
{file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"},
{file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"},
{file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"},
{file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"},
{file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"},
{file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"},
{file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"},
{file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"},
{file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"},
{file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"},
{file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"},
{file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"},
{file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"},
{file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"},
{file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"},
{file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"},
{file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"},
]
[[package]]
name = "click"
version = "8.1.7"
requires_python = ">=3.7"
summary = "Composable command line interface toolkit"
groups = ["dev"]
dependencies = [
"colorama; platform_system == \"Windows\"",
"importlib-metadata; python_version < \"3.8\"",
]
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]
[[package]]
name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
groups = ["default", "dev"]
marker = "sys_platform == \"win32\" or platform_system == \"Windows\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "croterline"
version = "1.0.7"
requires_python = ">=3.10"
summary = "Default template for PDM package"
groups = ["default"]
dependencies = [
"magicoca>=1.0.1",
]
files = [
{file = "croterline-1.0.7-py3-none-any.whl", hash = "sha256:45d2c65b58edf9e723428f5e84a84f54c4b5dae49dbe60c2bb7988ce31243ea4"},
{file = "croterline-1.0.7.tar.gz", hash = "sha256:9a72fb81786529ee23960cfe1c88edaf97a3270b5261b6675fad3413a09e7eb6"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
requires_python = ">=3.7"
summary = "Backport of PEP 654 (exception groups)"
groups = ["dev"]
marker = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
requires_python = ">=3.7"
summary = "brain-dead simple config-ini parsing"
groups = ["dev"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "loguru"
version = "0.7.2"
requires_python = ">=3.5"
summary = "Python logging made (stupidly) simple"
groups = ["default"]
dependencies = [
"aiocontextvars>=0.2.0; python_version < \"3.7\"",
"colorama>=0.3.4; sys_platform == \"win32\"",
"win32-setctime>=1.0.0; sys_platform == \"win32\"",
]
files = [
{file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
{file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
]
[[package]]
name = "magicoca"
version = "1.0.5"
requires_python = ">=3.10"
summary = "A communication library for Python"
groups = ["default"]
files = [
{file = "magicoca-1.0.5-py3-none-any.whl", hash = "sha256:e11a88dcd71fdb779ddf646d3071249f198623756ec8b4ddc3b92f65f68570d3"},
{file = "magicoca-1.0.5.tar.gz", hash = "sha256:764a53be7cb170b1261d85f1db757cd98c584b8340d0bb3096dc7327266bb259"},
]
[[package]]
name = "mypy"
version = "1.12.0"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["dev"]
dependencies = [
"mypy-extensions>=1.0.0",
"tomli>=1.1.0; python_version < \"3.11\"",
"typing-extensions>=4.6.0",
]
files = [
{file = "mypy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4397081e620dc4dc18e2f124d5e1d2c288194c2c08df6bdb1db31c38cd1fe1ed"},
{file = "mypy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:684a9c508a283f324804fea3f0effeb7858eb03f85c4402a967d187f64562469"},
{file = "mypy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cabe4cda2fa5eca7ac94854c6c37039324baaa428ecbf4de4567279e9810f9e"},
{file = "mypy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:060a07b10e999ac9e7fa249ce2bdcfa9183ca2b70756f3bce9df7a92f78a3c0a"},
{file = "mypy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0eff042d7257f39ba4ca06641d110ca7d2ad98c9c1fb52200fe6b1c865d360ff"},
{file = "mypy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b86de37a0da945f6d48cf110d5206c5ed514b1ca2614d7ad652d4bf099c7de7"},
{file = "mypy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20c7c5ce0c1be0b0aea628374e6cf68b420bcc772d85c3c974f675b88e3e6e57"},
{file = "mypy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a64ee25f05fc2d3d8474985c58042b6759100a475f8237da1f4faf7fcd7e6309"},
{file = "mypy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faca7ab947c9f457a08dcb8d9a8664fd438080e002b0fa3e41b0535335edcf7f"},
{file = "mypy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:5bc81701d52cc8767005fdd2a08c19980de9ec61a25dbd2a937dfb1338a826f9"},
{file = "mypy-1.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8462655b6694feb1c99e433ea905d46c478041a8b8f0c33f1dab00ae881b2164"},
{file = "mypy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:923ea66d282d8af9e0f9c21ffc6653643abb95b658c3a8a32dca1eff09c06475"},
{file = "mypy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ebf9e796521f99d61864ed89d1fb2926d9ab6a5fab421e457cd9c7e4dd65aa9"},
{file = "mypy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e478601cc3e3fa9d6734d255a59c7a2e5c2934da4378f3dd1e3411ea8a248642"},
{file = "mypy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:c72861b7139a4f738344faa0e150834467521a3fba42dc98264e5aa9507dd601"},
{file = "mypy-1.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52b9e1492e47e1790360a43755fa04101a7ac72287b1a53ce817f35899ba0521"},
{file = "mypy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48d3e37dd7d9403e38fa86c46191de72705166d40b8c9f91a3de77350daa0893"},
{file = "mypy-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f106db5ccb60681b622ac768455743ee0e6a857724d648c9629a9bd2ac3f721"},
{file = "mypy-1.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:233e11b3f73ee1f10efada2e6da0f555b2f3a5316e9d8a4a1224acc10e7181d3"},
{file = "mypy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ae8959c21abcf9d73aa6c74a313c45c0b5a188752bf37dace564e29f06e9c1b"},
{file = "mypy-1.12.0-py3-none-any.whl", hash = "sha256:fd313226af375d52e1e36c383f39bf3836e1f192801116b31b090dfcd3ec5266"},
{file = "mypy-1.12.0.tar.gz", hash = "sha256:65a22d87e757ccd95cbbf6f7e181e6caa87128255eb2b6be901bb71b26d8a99d"},
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
groups = ["dev"]
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[[package]]
name = "packaging"
version = "24.1"
requires_python = ">=3.8"
summary = "Core utilities for Python packages"
groups = ["dev"]
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "pathspec"
version = "0.12.1"
requires_python = ">=3.8"
summary = "Utility library for gitignore style pattern matching of file paths."
groups = ["dev"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[[package]]
name = "pdm-backend"
version = "2.4.2"
requires_python = ">=3.8"
summary = "The build backend used by PDM that supports latest packaging standards"
groups = ["default"]
dependencies = [
"importlib-metadata>=3.6; python_version < \"3.10\"",
]
files = [
{file = "pdm_backend-2.4.2-py3-none-any.whl", hash = "sha256:8537a3273b19d6448eb07a4a1a92dedc0b60935344a037729ada7be33b5f71ad"},
{file = "pdm_backend-2.4.2.tar.gz", hash = "sha256:1f833e527ae172f34b4b84e2fcf1f65859a2a5ca746e496d8313b3ea6539969f"},
]
[[package]]
name = "platformdirs"
version = "4.3.6"
requires_python = ">=3.8"
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
groups = ["dev"]
files = [
{file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
{file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
]
[[package]]
name = "pluggy"
version = "1.5.0"
requires_python = ">=3.8"
summary = "plugin and hook calling mechanisms for python"
groups = ["dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[[package]]
name = "pydantic"
version = "2.9.2"
requires_python = ">=3.8"
summary = "Data validation using Python type hints"
groups = ["default"]
dependencies = [
"annotated-types>=0.6.0",
"pydantic-core==2.23.4",
"typing-extensions>=4.12.2; python_version >= \"3.13\"",
"typing-extensions>=4.6.1; python_version < \"3.13\"",
]
files = [
{file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
{file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
]
[[package]]
name = "pydantic-core"
version = "2.23.4"
requires_python = ">=3.8"
summary = "Core functionality for Pydantic validation and serialization"
groups = ["default"]
dependencies = [
"typing-extensions!=4.7.0,>=4.6.0",
]
files = [
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
{file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
{file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
{file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
{file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
{file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
{file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
{file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
{file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
{file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
]
[[package]]
name = "pytest"
version = "8.3.3"
requires_python = ">=3.8"
summary = "pytest: simple powerful testing with Python"
groups = ["dev"]
dependencies = [
"colorama; sys_platform == \"win32\"",
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
"iniconfig",
"packaging",
"pluggy<2,>=1.5",
"tomli>=1; python_version < \"3.11\"",
]
files = [
{file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
{file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
]
[[package]]
name = "pyyaml"
version = "6.0.2"
requires_python = ">=3.8"
summary = "YAML parser and emitter for Python"
groups = ["default"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "toml"
version = "0.10.2"
requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
summary = "Python Library for Tom's Obvious, Minimal Language"
groups = ["default"]
files = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
[[package]]
name = "tomli"
version = "2.0.2"
requires_python = ">=3.8"
summary = "A lil' TOML parser"
groups = ["dev"]
marker = "python_version < \"3.11\""
files = [
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["default", "dev"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
name = "uv"
version = "0.4.24"
requires_python = ">=3.8"
summary = "An extremely fast Python package and project manager, written in Rust."
groups = ["dev"]
files = [
{file = "uv-0.4.24-py3-none-linux_armv6l.whl", hash = "sha256:bbc24b232c5e874741d863c5bec2257533db86f91381f1a101872028a0502ec9"},
{file = "uv-0.4.24-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8d467d4c4746127b2121d6f67686957a2b5431935d26767aa02fa4516694293"},
{file = "uv-0.4.24-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7d076875e9fa4d8cda44d3e51c9b47efc578db830535c62f25884772bfa265bc"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c40f75df1f2c45a7f67fcc69d80231760f6a017b7c8e889a16e21348651a34d7"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b459913d8ba6edba2c4b299e87fccfbd7fca4b2e2abe5fd4fa0da56147e19fc8"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e3ce0350e74b3dba6854789dd253faeab2fdf8e84f2671b68573070bb40ff17"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d274f7ddc013697fb52962632bc7e77889a6ec87d2cd12316d218686cfece3d4"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ef6914a7294ac7df5bd15b21652cbe61d1c12a0f29a94d178dce6192f858092"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d8e5f66a8756d4908121cb59189e6f9992fdbd0f9c26a5a30a069b94f8acab3"},
{file = "uv-0.4.24-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a3ea6780e3451c81ce1635656abcd8a47e43f1b0f02542c433b4b6dd459df8e"},
{file = "uv-0.4.24-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:beaff8fdaad3bcd781a8d28b60843b8d1cd2a04229847dc314c1bb7e0bb39ca2"},
{file = "uv-0.4.24-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:70a76cb5b8a459d6f6931becf2b5689599382c2512341d566ce335b8304c44e8"},
{file = "uv-0.4.24-py3-none-musllinux_1_1_i686.whl", hash = "sha256:29c514752873c1be259afd82b975e528ec6783564a306fd24deee0cccb2dc566"},
{file = "uv-0.4.24-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:c03a411f1b86ce7de25d6271d90358ba2d33e87b4922dc5378c4c07674909363"},
{file = "uv-0.4.24-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a03bc4b2ca2236eece97fffb8b5605b7a2248cd8a4b9a9c67955ad08756a1ceb"},
{file = "uv-0.4.24-py3-none-win32.whl", hash = "sha256:a97c347af12deb687c09fed82dc829efd6e5fbc4d76a38e98b2eaa2b065e4cfe"},
{file = "uv-0.4.24-py3-none-win_amd64.whl", hash = "sha256:ec0570f5e2e4dbfd83a89e9a55d5f033050d749f684bd0e7d4c327fd49f89b12"},
{file = "uv-0.4.24.tar.gz", hash = "sha256:f71a00f10cfa15b4f4f0184a67da19f35c48683bba9bb49cebe9c206f1b2bc1f"},
]
[[package]]
name = "watchdog"
version = "5.0.3"
requires_python = ">=3.9"
summary = "Filesystem events monitoring"
groups = ["default"]
files = [
{file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:85527b882f3facda0579bce9d743ff7f10c3e1e0db0a0d0e28170a7d0e5ce2ea"},
{file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:53adf73dcdc0ef04f7735066b4a57a4cd3e49ef135daae41d77395f0b5b692cb"},
{file = "watchdog-5.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e25adddab85f674acac303cf1f5835951345a56c5f7f582987d266679979c75b"},
{file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f01f4a3565a387080dc49bdd1fefe4ecc77f894991b88ef927edbfa45eb10818"},
{file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91b522adc25614cdeaf91f7897800b82c13b4b8ac68a42ca959f992f6990c490"},
{file = "watchdog-5.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d52db5beb5e476e6853da2e2d24dbbbed6797b449c8bf7ea118a4ee0d2c9040e"},
{file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d11b07c64f63f49876e0ab8042ae034674c8653bfcdaa8c4b32e71cfff87e8"},
{file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:349c9488e1d85d0a58e8cb14222d2c51cbc801ce11ac3936ab4c3af986536926"},
{file = "watchdog-5.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:53a3f10b62c2d569e260f96e8d966463dec1a50fa4f1b22aec69e3f91025060e"},
{file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:950f531ec6e03696a2414b6308f5c6ff9dab7821a768c9d5788b1314e9a46ca7"},
{file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae6deb336cba5d71476caa029ceb6e88047fc1dc74b62b7c4012639c0b563906"},
{file = "watchdog-5.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1021223c08ba8d2d38d71ec1704496471ffd7be42cfb26b87cd5059323a389a1"},
{file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:90a67d7857adb1d985aca232cc9905dd5bc4803ed85cfcdcfcf707e52049eda7"},
{file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:720ef9d3a4f9ca575a780af283c8fd3a0674b307651c1976714745090da5a9e8"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dd021efa85970bd4824acacbb922066159d0f9e546389a4743d56919b6758b91"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_armv7l.whl", hash = "sha256:78864cc8f23dbee55be34cc1494632a7ba30263951b5b2e8fc8286b95845f82c"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_i686.whl", hash = "sha256:1e9679245e3ea6498494b3028b90c7b25dbb2abe65c7d07423ecfc2d6218ff7c"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64.whl", hash = "sha256:9413384f26b5d050b6978e6fcd0c1e7f0539be7a4f1a885061473c5deaa57221"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:294b7a598974b8e2c6123d19ef15de9abcd282b0fbbdbc4d23dfa812959a9e05"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:26dd201857d702bdf9d78c273cafcab5871dd29343748524695cecffa44a8d97"},
{file = "watchdog-5.0.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:0f9332243355643d567697c3e3fa07330a1d1abf981611654a1f2bf2175612b7"},
{file = "watchdog-5.0.3-py3-none-win32.whl", hash = "sha256:c66f80ee5b602a9c7ab66e3c9f36026590a0902db3aea414d59a2f55188c1f49"},
{file = "watchdog-5.0.3-py3-none-win_amd64.whl", hash = "sha256:f00b4cf737f568be9665563347a910f8bdc76f88c2970121c86243c8cfdf90e9"},
{file = "watchdog-5.0.3-py3-none-win_ia64.whl", hash = "sha256:49f4d36cb315c25ea0d946e018c01bb028048023b9e103d3d3943f58e109dd45"},
{file = "watchdog-5.0.3.tar.gz", hash = "sha256:108f42a7f0345042a854d4d0ad0834b741d421330d5f575b81cb27b883500176"},
]
[[package]]
name = "win32-setctime"
version = "1.1.0"
requires_python = ">=3.5"
summary = "A small Python utility to set file creation time on Windows"
groups = ["default"]
marker = "sys_platform == \"win32\""
files = [
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]

View File

@@ -10,17 +10,18 @@ readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
authors = [ authors = [
{ name = "snowykami", email = "snowykami@outlook.com" }, { name = "snowykami", email = "snowykami@outlook.com" },
{ name = "LiteyukiStudio", email = "studio@liteyuki.icu" },
] ]
license = { text = "MIT&LSO" } license = { text = "MIT&LSO" }
dependencies = [ dependencies = [
"loguru~=0.7.2", "loguru~=0.7.2",
"pydantic==2.8.2", "pydantic>=2.9.2",
"PyYAML==6.0.2", "PyYAML>=6.0.2",
"toml==0.10.2", "toml>=0.10.2",
"watchdog==4.0.1", "watchdog>=4.0.1",
"pdm-backend==2.3.3" "pdm-backend>=2.3.3",
"magicoca>=1.0.5",
"croterline~=1.0.5",
] ]
[project.urls] [project.urls]
@@ -38,5 +39,14 @@ includes = ["liteyuki/", "LICENSE", "README.md"]
excludes = ["tests/", "docs/", "src/"] excludes = ["tests/", "docs/", "src/"]
[tool.pdm.version] [tool.pdm.version]
source = "file" source = "scm"
path = "liteyuki/__init__.py" tag_filter = "v*"
tag_regex = '^v(?:\D*)?(?P<version>([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|c|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$)$'
[tool.pdm.dev-dependencies]
dev = [
"pytest>=8.3.3",
"black>=24.10.0",
"uv>=0.4.20",
"mypy>=1.11.2",
]

View File

@@ -1,3 +1,4 @@
# app dependencies
aiohttp>=3.9.3 aiohttp>=3.9.3
aiofiles>=23.2.1 aiofiles>=23.2.1
colored>=2.2.4 colored>=2.2.4
@@ -26,3 +27,11 @@ importlib_metadata>=7.0.2
watchdog>=4.0.0 watchdog>=4.0.0
jieba>=0.42.1 jieba>=0.42.1
python-dotenv>=1.0.1 python-dotenv>=1.0.1
loguru~=0.7.2
pydantic~=2.9.2
pip~=23.2.1
fastapi~=0.115.0
# liteyuki dependencies
croterline>=1.0.7
magicoca>=1.0.5

View File

@@ -9,8 +9,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Software: PyCharm @Software: PyCharm
""" """
from liteyuki.plugin import PluginMetadata, PluginType from liteyuki.plugin import PluginMetadata, PluginType
from liteyuki.message.on import on_message from liteyuki.session.on import on_message
from liteyuki.message.event import MessageEvent from liteyuki.session.event import MessageEvent
__plugin_meta__ = PluginMetadata( __plugin_meta__ = PluginMetadata(
name="你好轻雪", name="你好轻雪",

View File

@@ -1,53 +0,0 @@
# -*- coding: utf-8 -*-
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@Time : 2024/8/11 下午5:24
@Author : snowykami
@Email : snowykami@outlook.com
@File : __init__.py.py
@Software: PyCharm
"""
import nonebot
from liteyuki.utils import IS_MAIN_PROCESS
from liteyuki.plugin import PluginMetadata, PluginType
from .nb_utils import adapter_manager, driver_manager # type: ignore
from liteyuki.log import logger
__plugin_meta__ = PluginMetadata(
name="NoneBot2启动器",
type=PluginType.APPLICATION,
)
def nb_run(*args, **kwargs):
"""
初始化NoneBot并运行在子进程
Args:
**kwargs:
Returns:
"""
# 给子进程传递通道对象
kwargs.update(kwargs.get("nonebot", {})) # nonebot配置优先
nonebot.init(**kwargs)
driver_manager.init(config=kwargs)
adapter_manager.init(kwargs)
adapter_manager.register()
try:
# nonebot.load_plugin("nonebot-plugin-lnpm") # 尝试加载轻雪NoneBot插件加载器Nonebot插件
nonebot.load_plugin("src.liteyuki_main") # 尝试加载轻雪主插件Nonebot插件
except Exception as e:
pass
nonebot.run()
if IS_MAIN_PROCESS:
from liteyuki import get_bot
from .dev_reloader import *
liteyuki = get_bot()
liteyuki.process_manager.add_target(name="nonebot", target=nb_run, args=(), kwargs=liteyuki.config)

View File

@@ -0,0 +1,33 @@
import os.path
from pathlib import Path
import nonebot
from croterline.utils import IsMainProcess
from liteyuki.core import sub_process_manager
from liteyuki.plugin import PluginMetadata, PluginType
__plugin_meta__ = PluginMetadata(
name="NoneBot2启动器",
type=PluginType.APPLICATION,
)
def nb_run(*args, **kwargs):
nonebot.init(**kwargs)
from .nb_utils import driver_manager, adapter_manager
driver_manager.init(config=kwargs)
adapter_manager.init(kwargs)
adapter_manager.register()
nonebot.load_plugin(Path(os.path.dirname(__file__)) / "np_main")
nonebot.run()
if IsMainProcess:
from .dev_reloader import *
bot = get_bot()
sub_process_manager.add(
name="nonebot", func=nb_run, **bot.config.get("nonebot", {})
)

View File

@@ -10,15 +10,17 @@ from liteyuki.utils import IS_MAIN_PROCESS
from watchdog.events import FileSystemEvent from watchdog.events import FileSystemEvent
liteyuki = get_bot() bot = get_bot()
exclude_extensions = (".pyc", ".pyo") exclude_extensions = (".pyc", ".pyo")
@observer.on_file_system_event( @observer.on_file_system_event(
directories=("src/nonebot_plugins",), 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) 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): def restart_nonebot_process(event: FileSystemEvent):
logger.debug(f"File {event.src_path} changed, reloading nonebot...") logger.debug(f"File {event.src_path} changed, reloading nonebot...")
liteyuki.restart_process("nonebot") bot.restart_process("nonebot")

View File

@@ -10,7 +10,7 @@ from .common import MessageEventModel, msg_db
from src.utils.base.language import Language from src.utils.base.language import Language
from src.utils.base.resource import get_path from src.utils.base.resource import get_path
from src.utils.message.string_tool import convert_seconds_to_time from src.utils.message.string_tool import convert_seconds_to_time
from ...utils.external.logo import get_group_icon, get_user_icon from src.utils.external.logo import get_group_icon, get_user_icon
async def count_msg_by_bot_id(bot_id: str) -> int: async def count_msg_by_bot_id(bot_id: str) -> int:

View File

@@ -64,6 +64,8 @@ data
- percent: float - percent: float
- total: int - total: int
""" """
# status_card_cache = {} # lang -> bytes # status_card_cache = {} # lang -> bytes
@@ -90,11 +92,11 @@ data
# 获取状态卡片 # 获取状态卡片
# bot_id 参数已经是bot参数的一部分了不需要保留但为了“兼容性”…… # bot_id 参数已经是bot参数的一部分了不需要保留但为了“兼容性”……
async def generate_status_card( async def generate_status_card(
bot: dict, bot: dict,
hardware: dict, hardware: dict,
liteyuki: dict, liteyuki: dict,
lang="zh-CN", lang="zh-CN",
bot_id="0", bot_id="0",
) -> bytes: ) -> bytes:
return await template2image( return await template2image(
get_path("templates/status.html", abs_path=True), get_path("templates/status.html", abs_path=True),
@@ -269,7 +271,9 @@ async def get_hardware_data() -> dict:
for disk in psutil.disk_partitions(all=True): for disk in psutil.disk_partitions(all=True):
try: try:
disk_usage = psutil.disk_usage(disk.mountpoint) disk_usage = psutil.disk_usage(disk.mountpoint)
if disk_usage.total == 0: if disk_usage.total == 0 or disk.mountpoint.startswith(
("/var", "/boot", "/run", "/proc", "/sys", "/dev", "/tmp", "/snap")
):
continue # 虚拟磁盘 continue # 虚拟磁盘
result["disk"].append( result["disk"].append(
{ {
@@ -297,7 +301,7 @@ async def get_liteyuki_data() -> dict:
"python": f"{platform.python_implementation()} {platform.python_version()}", "python": f"{platform.python_implementation()} {platform.python_version()}",
"system": f"{platform.system()} {platform.release()}", "system": f"{platform.system()} {platform.release()}",
"runtime": time.time() "runtime": time.time()
- temp_data.data.get("start_time", time.time()), # 运行时间秒数 - temp_data.data.get("start_time", time.time()), # 运行时间秒数
"bots": len(nonebot.get_bots()), "bots": len(nonebot.get_bots()),
} }
return result return result

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