📝 文档初步大迁移 vuepress
-> vitepress
4
.github/workflows/deploy-docs.yml
vendored
@ -41,8 +41,8 @@ jobs:
|
||||
- name: 生成API markdown
|
||||
run: |-
|
||||
python -m pip install litedoc
|
||||
litedoc liteyuki -o docs/dev/api -l zh-Hans -t vuepress -cd class -fd def -md def -vd var -cs -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/ -f lastUpdated=false
|
||||
litedoc liteyuki -o docs/en/dev/api -l en -t vuepress -cd class -fd def -md def -vd var -cs -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/ -f lastUpdated=false
|
||||
litedoc liteyuki -o docs/zh/dev/api -l zh-Hans -cd class -fd def -md def -vd var -cs -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
|
||||
litedoc liteyuki -o docs/en/dev/api -l en -cd class -fd def -md def -vd var -cs -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
|
||||
|
||||
|
||||
- name: 构建文档
|
||||
|
9
.gitignore
vendored
@ -17,7 +17,7 @@ __pycache__/
|
||||
/plugins/
|
||||
|
||||
#config
|
||||
config/
|
||||
/config/
|
||||
!config/default.yml
|
||||
_config.yml
|
||||
config.yml
|
||||
@ -54,4 +54,9 @@ dist
|
||||
doc
|
||||
|
||||
mkdoc2.py
|
||||
result.json
|
||||
result.json
|
||||
|
||||
# litedoc
|
||||
docs/zh/dev/api
|
||||
docs/en/dev/api
|
||||
mkdoc.bat
|
5
docs/.gitignore
vendored
@ -1,5 +0,0 @@
|
||||
|
||||
node_modules/
|
||||
./.vuepress/.cache/
|
||||
./.vuepress/.temp/
|
||||
./.vuepress/dist/
|
4
docs/.vitepress/theme/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './liteyuki.css'
|
||||
|
||||
export default DefaultTheme
|
9
docs/.vitepress/theme/liteyuki.css
Normal file
@ -0,0 +1,9 @@
|
||||
: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);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import {defineClientConfig} from "vuepress/client";
|
||||
|
||||
import resourceStoreComp from "./components/ResStore.vue";
|
||||
import pluginStoreComp from "./components/PluginStore.vue";
|
||||
import dashComp from "./components/Dash.vue";
|
||||
import homeComp from "./components/Home.vue";
|
||||
import geoComp from "./components/Geo.vue";
|
||||
|
||||
// import ElementPlus from 'element-plus';
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance: ({app, router, siteData}) => {
|
||||
app.component("homeComp", homeComp);
|
||||
app.component("dashComp", dashComp);
|
||||
app.component("resourceStoreComp", resourceStoreComp);
|
||||
app.component("pluginStoreComp", pluginStoreComp);
|
||||
app.component("geoComp", geoComp);
|
||||
// app.use(ElementPlus);
|
||||
},
|
||||
});
|
||||
|
@ -1,38 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
|
||||
let total = ref(0);
|
||||
let online = ref(0);
|
||||
fetch("https://api.liteyuki.icu/count")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
total.value = data.register;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
fetch("https://api.liteyuki.icu/online")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
online.value = data.online;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="info-box">
|
||||
<h1>Dashboard</h1>
|
||||
<div class="info">
|
||||
<div class="info-item">
|
||||
<h2>Total</h2>
|
||||
<p>{{ total }}</p>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<h2>Online</h2>
|
||||
<p>{{ online }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,20 +0,0 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<h2>地理分布</h2>
|
||||
<p>数据来源于Liteyuki API</p>
|
||||
</div>
|
||||
<div id="main-chart" style="width: 100%; height: 600px;"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#main-chart {
|
||||
width: 100px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
@ -1,10 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,152 +0,0 @@
|
||||
<template>
|
||||
<div class="item-card">
|
||||
<div class="item-name">{{ props.item.name }}</div>
|
||||
<div class="item-description">{{ props.item.desc }}</div>
|
||||
<div class="tags">
|
||||
<span class="tag" v-for="tag in props.item.tags" :key="tag" :style=getTagStyle(tag.color)>{{ tag.label }}</span>
|
||||
</div>
|
||||
<div class="item-bar">
|
||||
<!-- 三个可点击svg,一个github,一个下载,一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
|
||||
<a :href=props.item.homepage class="btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
|
||||
<path fill="currentColor"
|
||||
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- <button class="copy-btn btn"><div @click="copyToClipboard">安装</div></button> 点击后把安装命令写入剪贴板-->
|
||||
<button class="btn copy-btn" @click="copyToClipboard">复制安装命令</button>
|
||||
|
||||
<div class="btn">
|
||||
<a class="author-info" :href="`https://github.com/${props.item.author }`">
|
||||
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
|
||||
<div class="author-name">{{ props.item.author }}</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- 复制键,复制安装命令,npm install props.item.module_name-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps, onMounted} from 'vue'
|
||||
import Clipboard from 'clipboard'
|
||||
// 复制安装命令按钮
|
||||
|
||||
// 构建复制成功和失败的提示
|
||||
const props = defineProps({
|
||||
item: Object
|
||||
})
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const clipboard = new Clipboard('.copy-btn', {
|
||||
text: () => `npm install ${props.item.module_name}`
|
||||
})
|
||||
clipboard.on('success', () => {
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getTagStyle = (backgroundColor: string) => {
|
||||
// 将颜色值转换为 RGB 格式
|
||||
const rgb = backgroundColor.replace(/^#/, '');
|
||||
const [r, g, b] = rgb.match(/.{2}/g).map(x => parseInt(x, 16));
|
||||
|
||||
// 计算亮度
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
|
||||
// 根据亮度决定文字颜色
|
||||
return {
|
||||
backgroundColor: backgroundColor,
|
||||
color: brightness > 128 ? '#000' : '#fff'
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 复制到剪贴板的函数
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-card {
|
||||
position: relative;
|
||||
border-radius: 15px;
|
||||
background-color: #00000011;
|
||||
height: 160px;
|
||||
padding: 16px;
|
||||
margin: 10px;
|
||||
box-sizing: border-box;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #00000000;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
color: #111;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item-description {
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: $themeColor;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.item-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
color: #00000055;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 2px 5px;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
@ -1,88 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './PluginItemCard.vue'
|
||||
import ToggleSwitch from "./ToggleSwitch.vue";
|
||||
|
||||
let showLiteyukiPluginOnly = ref(false)
|
||||
let filteredItems = computed(() => {
|
||||
let filtered = items.value
|
||||
if (search.value) {
|
||||
filtered = filtered.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.desc.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.module_name.toLowerCase().includes(search.value.toLowerCase())
|
||||
)
|
||||
}
|
||||
if (showLiteyukiPluginOnly.value) {
|
||||
filtered = filtered.filter(item => item.is_liteyuki_plugin)
|
||||
}
|
||||
return filtered
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
// 从官方拉取
|
||||
fetch("/assets/plugins.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(item => {
|
||||
item.is_liteyuki_plugin = true
|
||||
})
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
|
||||
//追加
|
||||
fetch('https://registry.nonebot.dev/plugins.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 遍历data的每一项,把is_official设为false
|
||||
data.forEach(item => {
|
||||
item.is_official = false
|
||||
})
|
||||
items.value = items.value.concat(data)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>插件商店</h1>
|
||||
<p>内容来自轻雪商店及<a href="https://nonebot.dev/store/plugins">NoneBot插件商店</a>,轻雪通过nonebot插件实现兼容NoneBot,在此仅作引用,具体请访问NoneBot插件商店</p>
|
||||
<!-- 搜索框-->
|
||||
<div class="search-box-div">
|
||||
<input class="item-search-box" type="text" placeholder="搜索插件" v-model="search"/>
|
||||
<ToggleSwitch v-model:modelValue="showLiteyukiPluginOnly" />仅轻雪插件
|
||||
</div>
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-box-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.search-box-div input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<div class="item-card">
|
||||
<div class="item-name">{{ props.item.name }}</div>
|
||||
<div class="item-description">{{ props.item.description }}</div>
|
||||
<div class="item-bar">
|
||||
<!-- 三个可点击svg,一个github,一个下载,一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
|
||||
<a :href=props.item.link class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
|
||||
<path fill="currentColor"
|
||||
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div><a class="author-info" :href="`https://github.com/${props.item.author }`">
|
||||
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
|
||||
<div class="author-name">{{ props.item.author }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
item: Object
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.item-card {
|
||||
position: relative;
|
||||
border-radius: 15px;
|
||||
background-color: #00000011;
|
||||
height: 160px;
|
||||
padding: 16px;
|
||||
margin: 10px;
|
||||
box-sizing: border-box;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.item-card:hover {
|
||||
border: 2px solid $themeColor;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
color: $themeColor;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item-description {
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: $themeColor;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.item-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
color: #00000055;
|
||||
}
|
||||
</style>
|
@ -1,53 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './ResItemCard.vue'
|
||||
import * as url from "node:url";
|
||||
|
||||
// 从public/assets/resources.json加载插件
|
||||
let filteredItems = computed(() => {
|
||||
if (!search.value) {
|
||||
return items.value.reverse()
|
||||
}
|
||||
return items.value.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.description.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase())
|
||||
).reverse()
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
fetch("/assets/resources.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
items.value = data
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
// 列表倒序
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>主题/资源商店</h1>
|
||||
<div class="search-box-div"><input class="item-search-box" type="text" placeholder="搜索资源" v-model="search" /></div>
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<label class="switch">
|
||||
<input type="checkbox" :checked="modelValue" @change="updateValue($event.target.checked)">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const updateValue = (value: boolean) => {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: .4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px); /* 调整为 26px 以确保对齐 */
|
||||
}
|
||||
</style>
|
@ -1,47 +0,0 @@
|
||||
import {defineUserConfig} from "vuepress";
|
||||
import theme from "./theme.js";
|
||||
import viteBundler from "@vuepress/bundler-vite";
|
||||
|
||||
export default defineUserConfig({
|
||||
base: "/",
|
||||
|
||||
locales: {
|
||||
"/": {
|
||||
// 设置正在使用的语言
|
||||
lang: "zh-CN",
|
||||
title: "LiteyukiBot 轻雪机器人",
|
||||
description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人",
|
||||
head: [
|
||||
["script", {"src": "/js/zh/get_data.js", "type": "module"}],
|
||||
]
|
||||
},
|
||||
"/en/": {
|
||||
// 设置正在使用的语言
|
||||
lang: "en-US",
|
||||
title: "LiteyukiBot",
|
||||
description: "LiteyukiBot | An OneBot Standard ChatBot ",
|
||||
head: [
|
||||
["script", {"src": "/js/en/get_data.js", "type": "module"}],
|
||||
]
|
||||
},
|
||||
},
|
||||
head: [
|
||||
// 设置 favor.ico,.vuepress/public 下
|
||||
["script", {src: "/js/style.js", "type": "module"}],
|
||||
['link', {rel: 'icon', href: 'https://cdn.liteyuki.icu/favicon.ico'},],
|
||||
|
||||
['link', {rel: 'stylesheet', href: 'https://cdn.bootcdn.net/ajax/libs/firacode/6.2.0/fira_code.min.css'}],
|
||||
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no",
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
theme,
|
||||
// 和 PWA 一起启用
|
||||
// shouldPrefetch: false,
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import {navbar} from "vuepress-theme-hope";
|
||||
|
||||
export const enNavbarConfig = navbar([
|
||||
"/en/",
|
||||
{
|
||||
text: "Deploy",
|
||||
link: "/en/deploy/",
|
||||
prefix: "deploy/",
|
||||
},
|
||||
{
|
||||
text: "Usage",
|
||||
link: "/en/usage/",
|
||||
prefix: "usage/",
|
||||
},
|
||||
{
|
||||
text: "Extensions",
|
||||
link: "/en/store/",
|
||||
prefix: "store/",
|
||||
},
|
||||
{
|
||||
text: "Contribute",
|
||||
link: "/en/dev/",
|
||||
prefix: "dev/",
|
||||
},
|
||||
]);
|
@ -1,2 +0,0 @@
|
||||
export * from "./zh.js"
|
||||
export * from "./en.js"
|
@ -1,26 +0,0 @@
|
||||
|
||||
import {navbar} from "vuepress-theme-hope";
|
||||
|
||||
export const zhNavbarConfig = navbar([
|
||||
"/",
|
||||
{
|
||||
text: "安装及部署",
|
||||
link: "/deploy/",
|
||||
prefix: "deploy/",
|
||||
},
|
||||
{
|
||||
text: "使用及功能",
|
||||
link: "/usage/",
|
||||
prefix: "usage/",
|
||||
},
|
||||
{
|
||||
text: "资源及插件",
|
||||
link: "/store/",
|
||||
prefix: "store/",
|
||||
},
|
||||
{
|
||||
text: "开发及贡献",
|
||||
link: "/dev/",
|
||||
prefix: "dev/",
|
||||
},
|
||||
]);
|
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 9.8 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path fill="#FDD7AD" d="M512 0 335.448 88.272l-70.616 35.312-70.624 35.312-176.552 88.28v529.648L512 1024l494.344-247.176V247.176z"/><path fill="#CBB292" d="m759.176 370.76-70.624 35.304-494.344-247.168 70.624-35.312zM512 494.344V1024L17.656 776.824V247.176z"/><path fill="#7F6E5D" d="M1006.344 247.168v529.656L512 1024V494.344l176.552-88.28v70.624l141.24-70.624v-70.616z"/><path fill="#7F5B53" d="M829.792 335.448v70.624L688.56 476.68v-70.624z"/><path fill="#CBB292" d="m829.792 335.448-70.624 35.312-494.344-247.176 70.624-35.312z"/><path fill="#2C3E50" d="m682.52 550.32 157.032-78.512a17.656 17.656 0 0 1 25.552 15.792v9.32a52.96 52.96 0 0 1-29.28 47.376L678.8 622.8a17.656 17.656 0 0 1-25.552-15.792v-9.312a52.96 52.96 0 0 1 29.28-47.376z"/></svg>
|
Before Width: | Height: | Size: 854 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024"><defs><linearGradient id="a" x1="522.593" x2="522.593" y1="-70.302" y2="-335.937" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#fe5d5a" stop-opacity=".1"/><stop offset=".908" stop-color="#ef1220" stop-opacity=".5"/></linearGradient><linearGradient id="b" x1="107.12" x2="935.038" y1="-373.67" y2="-373.67" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#ff5e59"/><stop offset="1" stop-color="#f01422"/></linearGradient><linearGradient id="c" x1="519.405" x2="519.405" y1="-195.547" y2="-726.816" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#ffe2e2"/><stop offset=".888" stop-color="#ff8e8e"/></linearGradient><linearGradient id="d" x1="191.5" x2="483.9" y1="-564.9" y2="-564.9" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#e92700" stop-opacity=".3"/><stop offset=".013" stop-color="#ef1220" stop-opacity=".2"/></linearGradient><linearGradient id="e" x1="403.502" x2="253.121" y1="-847.32" y2="-586.853" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#ff5e59"/><stop offset=".201" stop-color="#f01422"/></linearGradient><linearGradient id="f" x1="330.485" x2="330.485" y1="-801.787" y2="-625.789" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#ff5e59"/><stop offset=".201" stop-color="#f01422"/></linearGradient><linearGradient id="g" x1="397.351" x2="256.845" y1="-647.231" y2="-890.596" gradientUnits="userSpaceOnUse" spreadMethod="pad"><stop offset="0" stop-color="#ffa6a6"/><stop offset=".908" stop-color="#ff6b5d"/></linearGradient></defs><path fill="url(#a)" d="M501.2 662.3 327.6 763.8c-13.9 8.1-14.2 28.1-.5 36.7l179.1 97.7c10.9 5.9 24.1 5.9 34.9-.1l177-97.9c13.6-8.5 13.4-28.3-.3-36.5l-168.4-101c-14.8-9-33.3-9.1-48.2-.4Z"/><path fill="#f63037" d="m110.2 525.7-3.1 77.6 57.5 18.5L184 519.4Z"/><path fill="url(#b)" d="m476.6 363.5-328 154.6c-21 42.7-55.4 65.4-35.5 103.5 4.2 8 9.4 14.4 15.4 18.1l358.2 195.5c21.8 11.9 48.1 11.8 69.8-.2l354-195.8c27.2-16.9 34.8-90.3 7.3-106.8L573 364.1c-29.7-17.8-66.6-18-96.4-.6Z"/><path fill="url(#c)" d="M476.6 298.7 129.4 501.6c-27.8 16.3-28.4 56.3-1 73.3l358.2 195.5c21.8 11.9 48.1 11.8 69.8-.2l354-195.8c27.2-16.9 26.9-56.6-.6-73.1L573 299.3c-29.7-17.8-66.6-18-96.4-.6Z"/><path fill="#ff8989" fill-opacity=".31" d="m481.2 387.8 39.4 123.4c1.1 3.4 4 6 7.6 6.6l173.4 30.4-33-118.3c-.9-3.3-3.6-5.8-7-6.5l-180.4-35.6ZM327 499.2l40.4 101.1L496.7 525c2.5-1.5 3.7-4.5 2.7-7.3l-36-106.8-127.6 65c-8.6 4.3-12.4 14.4-8.8 23.3ZM523.8 540.5l-140.3 77.2L567.2 659c3.2.7 6.6.1 9.3-1.6l134.6-85-174.7-33.8c-4.3-1-8.7-.3-12.6 1.9Z"/><path fill="url(#d)" d="M483.9 406.1c0 35.46-65.46 64.2-146.2 64.2s-146.2-28.74-146.2-64.2c0-35.46 65.46-64.2 146.2-64.2s146.2 28.74 146.2 64.2Z"/><path fill="url(#e)" d="m254.2 188.4-123 83.1c-1.8 1.3-2.6 3.6-1.8 5.7l39.1 110.6c.6 1.7 2 2.9 3.8 3.2l221.8 40.5c1.3.3 2.7-.1 3.7-.8l131.7-93.6c1.9-1.4 2.6-3.9 1.7-6.1l-49.4-107c-.6-1.5-2.1-2.6-3.7-2.8l-220.3-33.5c-1.3-.2-2.6.1-3.6.7Z"/><path fill="url(#f)" d="m528.6 274.5 3 59.1-205 65.6-177.2-72.7-20-49.2 1.9-54.1Z"/><path fill="url(#g)" d="m250.6 138-112.3 76c-6 4.1-8.5 11.7-6.1 18.5l34.2 96.6c1.9 5.4 6.6 9.3 12.1 10.4l211 38.5c4.3.7 8.6-.2 12.1-2.7l120.5-85.5c6.3-4.4 8.4-12.7 5.3-19.7l-43.1-93.5c-2.2-4.9-6.8-8.3-12.1-9.1L262 135.6c-4-.7-8 .2-11.4 2.4Z"/><path fill="#fff" d="m419.8 252.8-79-11-29-57.7c-3.8-7.6-13.2-10.7-20.8-6.9-7.6 3.8-10.7 13.2-6.9 20.8l26.6 52.9-61.8 42.2c-7.1 4.8-8.9 14.5-4.1 21.5 3 4.4 7.9 6.8 12.8 6.8 3 0 6-.9 8.7-2.7l68-46.4 81.1 11.2c.7.1 1.4.1 2.1.1 7.6 0 14.3-5.6 15.3-13.4 1.4-8.4-4.5-16.2-13-17.4Z"/></svg>
|
Before Width: | Height: | Size: 3.6 KiB |
@ -1 +0,0 @@
|
||||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
Before Width: | Height: | Size: 963 B |
@ -1 +0,0 @@
|
||||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 960 B |
Before Width: | Height: | Size: 9.0 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1536 1024"><path fill="#1296db" d="M1425.067.256H110.933A110.933 110.933 0 0 0 0 110.848v723.627a110.933 110.933 0 0 0 110.933 110.933h1314.39c61.269 0 110.933-49.75 110.677-110.677V110.848A110.933 110.933 0 0 0 1425.067.256z" class="selected" data-spm-anchor-id="a313x.7781069.0.i4"/><path fill="#FFF" d="M664.747 723.797V435.883L517.12 620.373l-147.456-184.49v288l-148.053-67.158V221.781h147.626l147.627 184.576 147.541-184.576h147.627v565.76z"/><path d="M1024 0h426.667A85.333 85.333 0 0 1 1536 85.333v768a85.333 85.333 0 0 1-85.333 85.334H1024V0z" opacity=".1"/><path fill="#FFF" d="m1256.96 731.307-170.667-216.491h113.75V304.64h113.749v210.176h113.835z" opacity=".5"/></svg>
|
Before Width: | Height: | Size: 771 B |
@ -1,95 +0,0 @@
|
||||
[
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-nonebot",
|
||||
"project_link": "liteyukibot-plugin-nonebot",
|
||||
"name": "NoneBot插件",
|
||||
"desc": "在轻雪中使用NoneBot,内置轻雪--NoneBot会话控制器插件,为NoneBot开发者提供了更多便捷功能(已内置)",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-antidislink",
|
||||
"project_link": "liteyukibot-plugin-antidislink",
|
||||
"name": "防断联插件",
|
||||
"desc": "防止你的群友断联化",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/snowykami/liteyukibot-plugin-antidislink",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
},
|
||||
{
|
||||
"label": "dislink",
|
||||
"color": "#d0e9ff"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-lagrange",
|
||||
"project_link": "liteyukibot-plugin-lagrange",
|
||||
"name": "LagrangePlugin",
|
||||
"desc": "在轻雪中使用lagrange-python!",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/snowykami/liteyukibot-plugin-lagrange",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
},
|
||||
{
|
||||
"label": "lagrange",
|
||||
"color": "#ff89f7"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-htmlrender",
|
||||
"project_link": "liteyukibot-plugin-htmlrender",
|
||||
"name": "htmlrender",
|
||||
"desc": "使用系统中的浏览器对网页进行渲染,便于其他插件使用",
|
||||
"author": "EillesWan",
|
||||
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-htmlrender",
|
||||
"tags": [
|
||||
{
|
||||
"label": "ch",
|
||||
"color": "#4285f4"
|
||||
},
|
||||
{
|
||||
"label": "ro",
|
||||
"color": "#db4437"
|
||||
},
|
||||
{
|
||||
"label": "mi",
|
||||
"color": "#f4b400"
|
||||
},
|
||||
{
|
||||
"label": "um",
|
||||
"color": "#0f9d58"
|
||||
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
}
|
||||
]
|
@ -1,56 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "KawaiiStatus",
|
||||
"author": "SnowyKami",
|
||||
"description": "可爱的状态卡片,仿照koishi的制作",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip"
|
||||
},
|
||||
{
|
||||
"name": "MiSans字体包",
|
||||
"author": "SnowyKami",
|
||||
"description": "小米官方字体MiSans",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/MiSansFonts.zip"
|
||||
},
|
||||
{
|
||||
"name": "MapleMono字体包",
|
||||
"author": "SnowyKami",
|
||||
"description": "适用于字母的字体包",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/MapleMonoFonts.zip"
|
||||
},
|
||||
{
|
||||
"name": "野兽先辈主题HomoTheme",
|
||||
"author": "SnowyKami",
|
||||
"description": "野兽先辈主题包,114514!",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip"
|
||||
},
|
||||
{
|
||||
"name": "自定义设备信息",
|
||||
"author": "SnowyKami",
|
||||
"description": "自定义服务端的设备信息,自行修改使用",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/custom-device.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪傲娇系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪可爱系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_kawaii.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪Kakyo语言包",
|
||||
"author": "Nanaloveyuki",
|
||||
"description": "Liteyuki Bot的语言包,用于提供多种语言的翻译。",
|
||||
"link": "https://github.com/Nanaloveyuki/liteyuki-langpack/releases/download/KakyoVer/Kakyo-pack.zip"
|
||||
},
|
||||
{
|
||||
"name": "更多背景模板包",
|
||||
"author": "snowykami",
|
||||
"description": "自定义各种卡片的背景",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip"
|
||||
}
|
||||
]
|
@ -1,291 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1366 768">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: url(#_未命名的渐变_8);
|
||||
}
|
||||
|
||||
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9, .cls-10, .cls-11, .cls-12, .cls-13, .cls-14, .cls-15, .cls-16, .cls-17, .cls-18, .cls-19, .cls-20, .cls-21, .cls-22, .cls-23, .cls-24, .cls-25, .cls-26, .cls-27, .cls-28, .cls-29, .cls-30, .cls-31, .cls-32, .cls-33, .cls-34 {
|
||||
stroke-width: 0px;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: url(#_未命名的渐变_9-3);
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: url(#_未命名的渐变_11);
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: url(#_未命名的渐变_11-4);
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: url(#_未命名的渐变_5);
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: url(#_未命名的渐变_55);
|
||||
}
|
||||
|
||||
.cls-7 {
|
||||
fill: url(#_未命名的渐变_11-5);
|
||||
}
|
||||
|
||||
.cls-8 {
|
||||
fill: url(#_未命名的渐变_65);
|
||||
}
|
||||
|
||||
.cls-9 {
|
||||
fill: url(#_未命名的渐变_3-2);
|
||||
}
|
||||
|
||||
.cls-10 {
|
||||
fill: url(#_未命名的渐变_7-3);
|
||||
}
|
||||
|
||||
.cls-11 {
|
||||
fill: url(#_未命名的渐变_7);
|
||||
}
|
||||
|
||||
.cls-12 {
|
||||
fill: url(#_未命名的渐变_58);
|
||||
}
|
||||
|
||||
.cls-13 {
|
||||
fill: url(#_未命名的渐变_3-3);
|
||||
}
|
||||
|
||||
.cls-14 {
|
||||
fill: url(#_未命名的渐变_67);
|
||||
}
|
||||
|
||||
.cls-15 {
|
||||
fill: url(#_未命名的渐变_11-2);
|
||||
}
|
||||
|
||||
.cls-16 {
|
||||
fill: url(#_未命名的渐变_72);
|
||||
}
|
||||
|
||||
.cls-17 {
|
||||
fill: url(#_未命名的渐变_71);
|
||||
}
|
||||
|
||||
.cls-18, .cls-35 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.cls-19 {
|
||||
fill: url(#_未命名的渐变_4);
|
||||
}
|
||||
|
||||
.cls-20 {
|
||||
fill: url(#_未命名的渐变_3);
|
||||
}
|
||||
|
||||
.cls-21 {
|
||||
fill: url(#_未命名的渐变_240);
|
||||
}
|
||||
|
||||
.cls-22 {
|
||||
fill: url(#_未命名的渐变_9-2);
|
||||
}
|
||||
|
||||
.cls-23 {
|
||||
fill: url(#_未命名的渐变_6);
|
||||
}
|
||||
|
||||
.cls-24 {
|
||||
fill: url(#_未命名的渐变_9-4);
|
||||
}
|
||||
|
||||
.cls-25 {
|
||||
fill: url(#_未命名的渐变_66);
|
||||
}
|
||||
|
||||
.cls-26 {
|
||||
fill: url(#_未命名的渐变_7-2);
|
||||
}
|
||||
|
||||
.cls-35 {
|
||||
stroke: #fff;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.cls-27 {
|
||||
fill: url(#_未命名的渐变_9);
|
||||
}
|
||||
|
||||
.cls-28 {
|
||||
fill: url(#_未命名的渐变_9-5);
|
||||
}
|
||||
|
||||
.cls-29 {
|
||||
fill: url(#_未命名的渐变_68);
|
||||
}
|
||||
|
||||
.cls-30 {
|
||||
fill: url(#_未命名的渐变_11-3);
|
||||
}
|
||||
|
||||
.cls-31 {
|
||||
fill: url(#_未命名的渐变_69);
|
||||
}
|
||||
|
||||
.cls-32 {
|
||||
fill: #9cf;
|
||||
}
|
||||
|
||||
.cls-33 {
|
||||
fill: url(#_未命名的渐变_58-2);
|
||||
}
|
||||
|
||||
.cls-34 {
|
||||
fill: url(#_未命名的渐变_4-2);
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="_未命名的渐变_11" data-name="未命名的渐变 11" x1="445.89" y1="235.02" x2="528.25" y2="235.02" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#73fff1"/>
|
||||
<stop offset="1" stop-color="#5cefff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_11-2" data-name="未命名的渐变 11" x1="549.98" y1="292.99" x2="584.05" y2="292.99" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-3" data-name="未命名的渐变 11" x1="553" y1="212" x2="585" y2="212" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-4" data-name="未命名的渐变 11" x1="609.82" y1="260.3" x2="698.34" y2="260.3" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_11-5" data-name="未命名的渐变 11" x1="702.75" y1="282.24" x2="784.94" y2="282.24" xlink:href="#_未命名的渐变_11"/>
|
||||
<linearGradient id="_未命名的渐变_240" data-name="未命名的渐变 240" x1="835" y1="294" x2="960" y2="294" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#eadeff"/>
|
||||
<stop offset=".28" stop-color="#e0efff"/>
|
||||
<stop offset=".43" stop-color="#d9ecff"/>
|
||||
<stop offset=".59" stop-color="#aedbff"/>
|
||||
<stop offset="1" stop-color="#00d7ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_9" data-name="未命名的渐变 9" x1="664.92" y1="424.34" x2="766.76" y2="424.34" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#2ebbff"/>
|
||||
<stop offset="1" stop-color="#006bff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_9-2" data-name="未命名的渐变 9" x1="761.74" y1="467.72" x2="842.05" y2="467.72" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-3" data-name="未命名的渐变 9" x1="951.96" y1="394" x2="977.04" y2="394" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-4" data-name="未命名的渐变 9" x1="952.19" y1="456.86" x2="977.56" y2="456.86" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_9-5" data-name="未命名的渐变 9" x1="848.96" y1="437.15" x2="934.09" y2="437.15" xlink:href="#_未命名的渐变_9"/>
|
||||
<linearGradient id="_未命名的渐变_4" data-name="未命名的渐变 4" x1="418.01" y1="402.81" x2="470.25" y2="402.81" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#6445ff"/>
|
||||
<stop offset="1" stop-color="#5c86ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_4-2" data-name="未命名的渐变 4" x1="445.81" y1="464.45" x2="466.06" y2="464.45" xlink:href="#_未命名的渐变_4"/>
|
||||
<linearGradient id="_未命名的渐变_72" data-name="未命名的渐变 72" x1="634.1" y1="393.61" x2="649.62" y2="390.81" gradientTransform="translate(-320.36 273.4) rotate(-11.09) scale(1.44 .77)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#c4aeff"/>
|
||||
<stop offset=".43" stop-color="#ce9fff"/>
|
||||
<stop offset=".55" stop-color="#af8eff"/>
|
||||
<stop offset=".72" stop-color="#7d74ff"/>
|
||||
<stop offset=".9" stop-color="#6333ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_58" data-name="未命名的渐变 58" x1="620.28" y1="400.01" x2="636.02" y2="397.18" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#b1ccff"/>
|
||||
<stop offset=".34" stop-color="#b8c0ff"/>
|
||||
<stop offset=".94" stop-color="#cba2ff"/>
|
||||
<stop offset="1" stop-color="#ce9fff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_3" data-name="未命名的渐变 3" x1="469.91" y1="455" x2="478" y2="455" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#5c86ff"/>
|
||||
<stop offset="1" stop-color="#64a2ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_3-2" data-name="未命名的渐变 3" x1="470.95" y1="475.89" x2="477.01" y2="475.89" xlink:href="#_未命名的渐变_3"/>
|
||||
<linearGradient id="_未命名的渐变_65" data-name="未命名的渐变 65" x1="485.09" y1="467.16" x2="502.74" y2="469.91" gradientTransform="translate(-109.75 153.43) rotate(-15.93)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".11" stop-color="#64a2ff"/>
|
||||
<stop offset=".28" stop-color="#62a6ff"/>
|
||||
<stop offset="1" stop-color="#5cb5ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_66" data-name="未命名的渐变 66" x1="506.01" y1="471.79" x2="532.2" y2="471.17" gradientTransform="translate(117.47 -103.02) rotate(12.81)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#5cb5ff"/>
|
||||
<stop offset="1" stop-color="#74e0ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_68" data-name="未命名的渐变 68" x1="559.81" y1="469.17" x2="583.24" y2="468.62" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#9cd1ff"/>
|
||||
<stop offset="1" stop-color="#bae0ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_71" data-name="未命名的渐变 71" x1="584.93" y1="460.83" x2="609.05" y2="460.83" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a6d3ff"/>
|
||||
<stop offset=".7" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_58-2" data-name="未命名的渐变 58" x1="605.79" y1="473.79" x2="643.63" y2="466.98" xlink:href="#_未命名的渐变_58"/>
|
||||
<linearGradient id="_未命名的渐变_3-3" data-name="未命名的渐变 3" x1="479.91" y1="397.6" x2="514.17" y2="397.6" xlink:href="#_未命名的渐变_3"/>
|
||||
<linearGradient id="_未命名的渐变_5" data-name="未命名的渐变 5" x1="525.59" y1="402.28" x2="558.08" y2="402.28" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64a2ff"/>
|
||||
<stop offset="1" stop-color="#5cc9ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_69" data-name="未命名的渐变 69" x1="583.43" y1="398.41" x2="604.15" y2="398.41" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#84c9ff"/>
|
||||
<stop offset=".42" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_67" data-name="未命名的渐变 67" x1="531.83" y1="464.83" x2="558.02" y2="464.22" gradientTransform="translate(-86.67 132.78) rotate(-12.37)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#74e0ff"/>
|
||||
<stop offset=".56" stop-color="#8dd6ff"/>
|
||||
<stop offset="1" stop-color="#9cd1ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_6" data-name="未命名的渐变 6" x1="531" y1="411" x2="539" y2="411" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64a2ff"/>
|
||||
<stop offset="1" stop-color="#5cbfff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_7" data-name="未命名的渐变 7" x1="553.98" y1="380.5" x2="559" y2="380.5" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#64ccff"/>
|
||||
<stop offset="1" stop-color="#84c9ff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_7-2" data-name="未命名的渐变 7" x1="546.98" y1="383.5" x2="552" y2="383.5" xlink:href="#_未命名的渐变_7"/>
|
||||
<linearGradient id="_未命名的渐变_7-3" data-name="未命名的渐变 7" y1="404.39" x2="561.98" y2="404.39" xlink:href="#_未命名的渐变_7"/>
|
||||
<linearGradient id="_未命名的渐变_8" data-name="未命名的渐变 8" x1="578.42" y1="391.26" x2="586.42" y2="391.26" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".01" stop-color="#84c9ff"/>
|
||||
<stop offset="1" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="_未命名的渐变_55" data-name="未命名的渐变 55" x1="623.35" y1="385.26" x2="623.75" y2="385.26" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#b1c9ff"/>
|
||||
<stop offset=".42" stop-color="#b1ccff"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="_图层_3" data-name="图层 3">
|
||||
<path class="cls-32" d="m447,485c11.04,8.56,30.35.82,33,0,14.37-4.43,15.38-15.33,26-15,10.44.33,13.16,10.98,24,10,6.48-.59,7.04-4.53,16-6,8-1.31,8.66,1.66,17,1,12.76-1.01,15.73-8.33,24-6,8.42,4.47,11.58,6.51,22,5,0,0,11.3-1.75,32-10,1.42-9.07,4.72,6.12,11.99,12,10.1,9.09,19.95-.4,31,8,10.86,8.26,13.5,26.66,13,31-1.65,4.39,6.37,10.16,6,10-4.41-.77-20.72-10.82-25-11-6.8-1.26-15.53-2.26-24-5-17.1-5.24-15.17-10.82-34-10-7.53.04-6.69-.47-12,1-2.54-.23-9.74.73-13-1-18.29-11.87-9.94,11.78-36,3-9.47,1.86-14.72.65-18.02-1.12-2.21-1.19-4.61-5.33-6.74-4.61-.57.19-.74.58-2.24,1.74-1.2.93-2.27,1.58-3,2-.91.36-1.91.7-3,1-2.93.8-5.59,1.02-7.75,1.02-.74,0-1.87-.01-3.25-.02-3.97,0-3.61.03-4,0-2.87-.21-3.75-.93-7-1-.42,0-.77,0-1,0h-6c-3,.22-5.39.65-7,1-1.72.37-3.36.82-10,3-3.07,1.01-4.59,1.59-6,2-1.81.53-5.13,1.49-9,2-5.07.67-9,.3-12,0-6.54-.65-11.56-2.18-14-3-4.6-1.55-26.12-13.37-20-10,0,0,0,0,0,0-.03-.02-20.88-13.37-28-32-1.09-2.85-2-6-2-6-.52-1.8-1.44-5.03-2-9-.44-3.12-1.34-9.53,0-10,1.69-.59,6.23,8.51,7.27,10.48,5.15,9.76,18.12,22.54,52.73,35.52Z"/>
|
||||
<path class="cls-32" d="m633,394c-13.63,7.43-2.93,57.29,2,57,1.82-.11,1.13-6.97,8-16,3-3.94,3.81-3.37,5-6,.88-3.8,2.06-11.66-2-19-.61-1.11-1.29-2.11-2-3-.31-.36-.8-1.04-1-2-.06-.32-.19-.91,0-1,.26-.13.82.87,2,2,.84.81,1.71,1.61,3,2,1.06.32,1.94.17,3,0,1.38-.23,2.37-.69,3-1,1.31-.64,2.25-1.09,3-2,1.84-2.22,1.14-5.41,1-6-.33-1.36-.69-1.29-2-4-.87-1.8-.57-1.55-2-5-.49-1.19-1-2.79-2-6-.12-.39-.37-1.22,0-2,0,0,.29-.6,1-1,1.98-1.11,11.6.55,15,1,2.66.35,13.92,1.61,15.83,2.95.42.3.8.64,1.17,1.05,2.43,2.73.93,4.31,3,9,3.83,8.69,13.16,12.57,19,15,5.09,2.12,8.54,2.5,13,3,6.22.69,7.53-.22,8-1,.74-1.23-.27-2.7-1-5-1.53-4.78-.3-8.09,0-12,.51-6.64-1.77-13.74-4-17-9.26-13.54-41.29,4.77-71-2-1.28-.29-9.89-2.3-12,1-1.87,2.94,2.99,7.55,1,12-1.49,3.33-5.5,3.63-8,5Z"/>
|
||||
<path class="cls-32" d="m683,228c-22.42,19.49,74.7,203.11,32,245-9.08,8.91-26.9,13.94-28,28-.65,8.33,4.78,18.3,13,23,13.88,7.94,28.83,2.05,33-1,2.68-1.96,4-4,4-4,1.44-2.22,1.56-3.91,3-8,.76-2.17,1.5-3.9,2-5,.43-1.13.95-2.86,1-5,.04-1.88-.32-2.29-.25-4.13.04-1.01.22-5.22,2.25-5.87.27-.09,1.3-.35,3,1-.17-.41-.33-.87-.21-.95.24-.16,1.16,1.42,2.21,1.95,2.87,1.44,7.56-.07,8-2-10.79-5.85-14.4-6.84-15-6-.79,1.11,3.49,5.56,3,6-.37.33-3.05-1.93-3-2,.05-.07,3.08,1.97,4,3,.08.09.44.51,1,1,.23.2.43.36.55.46.1.12.26.31.45.54,2.9,3.62,4.2,7.01,4.62,8.05.92,2.28,2.67,5.77,6.38,10.95,15.05,16.54,34.83,19.18,54,9,16.33,3.81,7.85,10.54,33,5,25.9-4.5,50.39,1.98,49-25-1.26-10.66-5.86-19.02,0-10,8.68,14.17,21.69,39.51,43,27,6.44-4.99,5.9-9.29,8-16,1.22-1.48,4.53-4.38,6-4,.01.08.16,1-.41,1.5-.6.53-1.8.43-2.59-.5,2.97,10.52,12.31,17.02,21,16,7.93-.94,13.74-10.13,14.27-11.06.54-.96.98-1.97.98-1.97s.41-.95.75-1.97c1.19-3.64,1.46-7.4,1.46-7.4.88-11.86.98-15.27.98-15.27.35-11.02.5-9.78.67-17.66.19-8.34-.04-7.03.13-13.62,0,0,.55-21.79-3.25-26.05-.48-.54-1-1-1-1-1.02-.91-1.79-1.28-3-2,0,0-1-.67-3-2-1-.67-2.08-1.22-3-2-1.25-1.06-1.85-1.94-3-2-.39-.02-.52.07-1,0-1.2-.18-1.99-1.02-2-1-.02.03.5.41,3,2,.1-.62,1.27-2.67,2-3,5.54-4.98,5.58-5.38,8-10,6.12-15.03-2.92-19.21-14-26-1.23-1.16-1.87-.9-3-1-.59-2.25-2.17-3.18-3-6-1.01-2.71-2.29-10.67-2-12,.57-5.41,2.39-8.84,4-11,2.1-2.82,3.85-3.49,6-7,1.22-2,2.84-4.65,3-8,.25-5.38-3.39-9.63-8-15-2.47-2.87-5.06-5.17-6-6-2.42-2.13-4.22-3.42-5-4-5.14-3.8-8.31-10.94-9.7-14.15-5.32-12.26-7.9-23.86-8.3-32.85-1.17-26.5-38.54-51.77-63-35-7.04,5.65-2.81,10.14-11,18-10.42,10-22.57,7.8-34,13-5.71,3.43-10.18.22-16-2-9.95-3.09-18.5-13.48-24-22-1.52-1.57-3.98-3.73-7.79-5.64-30.14-4.8-47.97-7.45-51.21-7.36-.78.02-4.27.18-7.57-1.74-7.85-9.54-30.21,6.06-40.43,13.74Z"/>
|
||||
<path class="cls-32" d="m667,174c-4.66.36-8.11,1.64-10.79,3.57-13.35,9.62-7.67,24.47-21.35,37.56-3.88,3.71-4.86,2.87-33.86,12.87-.06.02-2.46.85-4,0-.23-.13-.75-.41-1-1-.23-.54-.15-1.16-.12-1.33.02-.16.09-.51.12-.67h0c1.24-1.71,8.11-11.55,4.97-21.02-3.76-11.34-20.93-19.28-39.97-12.98-12.11,2.27-15.97.27-17.33-1.85-1.94-3.02.84-6.86,5.33-19.16,3.86-10.58,5.89-16.13,6-22,.1-5.49-1.43-13.73-10-24-23.21-14.8-47-13.43-56-2-1.46,1.85-5.64,7.92-4,20-3.3,4.02-9.31,1.14-20,4-10.63,1.96-20.69,9.46-13,23,5.63,9.91,94.53,145.36,192.09,115.49,50.17-15.36,72.52-65.2,72.91-66.49.03-.1.51-1.68,0-2-.15-.1-.3-.03-1,0h-3c-1.55,0-3.44-.77-4-1-.5-.2-1.8-.75-5-3-1.04-.73-2.42-1.73-4-3-3.1-.72-4.4-1.96-5-3-.3-.52-.38-.9-1-2-1.21-2.14-1.61-1.92-2-3-.34-.93-.04-1.09,0-5,0-.35,0-.06,0-2,0-3.6.02-3.67,0-4-.12-1.85-.9-3.16-2-5-2.63-4.41-6.25-6.57-7-7-1.67-.96-2.99-1.38-5-2-2.56-.79-6.28-1.69-11-2Z"/>
|
||||
</g>
|
||||
<g id="_背景" data-name="背景">
|
||||
<path class="cls-18" d="m572,182c-16.42-3.08-26.21,8.1-31,4-7.63-6.54,18.33-33.97,9-55-6.88-15.51-30.12-21.13-45-21-3.33.03-16.49.44-31,9-30.76,18.15-37.62,55.27-56,109-20.03,58.55-43.71,77.3-45,119-.4,13.04.88,31.37,1,33,2.91,40.64,9.7,47.97,12,64,.27,1.85.98,7.34,5,12,.83.96,1.59,1.64,2.01,2,.47.48,1.15,1.18,1.99,2,10.84,10.63,27.33,20.18,27.33,20.18,17.89,10.12,26.84,15.18,35.66,13.82,3.19-.49,6.88-1.68,16.62-3.27,3.46-.56,6.46-.79,12.39-1.73,3.95-.63,7.36-.89,10-1,3.64-.15,6.36-.03,7,0,4.66.22,8.01.84,9,1,3.81.61,10.14.32,21-4,3.96.13,4.67,2.49,9,4,4.59,1.6,5.73-.37,15,0,16.7,2.72,12.4-.95,25-4,5.48-.56,5.78,1.97,14,3,9.86,2.45,36.35-8.51,48,1,6.44,5.27,9.1,4.24,16,6,7.05,3.78,15.85,4.35,23,8,18.2,11.17,34.14,20.96,51,2,5.59-6.98,3.83-12.43,8-14,7.13-2.81,31.59,30.21,58,23,15.36-6.57,13.31-4.51,28,1,5.68,1.04,23.32-3.84,33-4,12.6-.87,15.51.92,21-2,11.5-6.32,8.35-12.82,9-21,6.52,3,17.82,31.94,43,18,6.28-4.14,7.41-10.27,11-10,3.81.29,3.43,7.28,9,11,7.22,4.82,20.84,1.79,26-6,1.88-2.84,1.95-5.28,2-16,.06-12.86,2.5-34.29,1-57-.27-4.14-.62-6.12-2-8-1.81-2.47-4.68-3.81-7.73-8.57-.11-.18-.21-.33-.27-.43,0-.08-.02-.19-.02-.33.03-1.6,1.77-2.75,2.36-3.17,1.19-.85,2.8-2.4,4.66-5.5,10.77-33.99-34.41-21.61-20-59,3.12-6.89,6.55-6.37,8-12,2.32-9.04-4.5-18.26-8-23-5.99-8.1-8.37-6.45-17-17-2.93-3.58-6.84-8.36-9.5-15.5-2.32-6.24-2.48-11.05-2.5-12.5,0-.42,0-.77,0-1,.13-4.73-.28-14.34-6-24-8.66-14.64-25.27-22.26-40-22-22.75.41-39.01,19.59-41,22-4.52,5.48-5.53,9.19-11,12-8.17,4.19-19.82,3.24-29-1-1.04-.48-1.78-.88-2-1-13.03-7.1-12.62-18.04-23-25-.78-.52-2.86-1.85-9.5-3.93-18.42-5.77-40.67-7.51-55.5-8.07-1.93.07-4.76-.05-8-1-3.67-1.08-6.36-2.77-8-4-5.34-3.78-6.82-2.84-9-5-3.59-3.56.14-10.39-3-17-2.84-5.97-10.15-7.96-14-9-2.47-.67-11.2-2.68-20,2-1.88,1-6.08,3.3-9,8-2.37,3.82-1.56,5.46-4,14-2.08,7.28-3.18,10.97-6,13-1.17.85-5.1,3.27-13,1-3.07-.18-7.45,0-12,2-8.86,3.88-12,11.82-14,11-2.79-1.15,5.52-15.64,0-28-5.58-12.5-22.22-15.85-23-16Z"/>
|
||||
<g id="_主体" data-name="主体">
|
||||
<path id="lite-l" class="cls-3" d="m516,141c.03-1.1-.07-2.97-1-5-.89-1.93-2.11-3.1-2.75-3.7-.4-.38-1.57-1.42-3.37-2.29-1.11-.53-2.12-.83-2.88-1.01-.48,0-1.16,0-1.98.04-1.4.09-4.59.32-7.8,1.81-3.86,1.79-6.04,4.59-7.21,6.15-1.03,1.36-.93,1.77-4,8-1.08,2.19-1.79,3.57-2,4-5.9,11.79-12,35-12,35-7.65,29.08-7.7,25.76-12,44-2.8,11.87-4.71,21.49-6,28-.61,3.07-2.21,11.25-4,22,0,0-1.57,9.87-3,30-.1,1.34-.46,7.05,1,14,.85,4.05,3,8,3,8h0c2.28,2.98,4.52,4.83,5.99,5.89,11.06,8,23.32,4.35,57.01,4.11,3.06-.02,5.53-.01,7,0,1.02-.28,2.51-.84,4-2,.52-.41,1.27-1.06,2-2,.45-.58,1.59-1.91,2-4,.21-1.08.43-2.87,0-5-.36-1.17-.7-2.18-1-3-.3-.83-.53-1.39-1-2-.36-.46-.73-.79-1-1-.24-.23-.76-.76-1-1-1.73-1.73-1.87-1.87-2-2-1.25-1.2-2.82-1.65-4-2-5.84-1.71-23-2-23-2-4.12-.07-10.14-.62-17.53-2.99,1.73-17.19,5.56-49.39,13.31-81.46,4.95-20.51,4.35-18.09,8.22-30.55,5.85-18.81,8.77-28.22,11-34,0,0,3.24-8.41,4.63-15.69.44-2.32.84-4.8.84-4.8.18-1.17.35-2.34.53-3.5Z"/>
|
||||
<path id="lite-i-1" class="cls-15" d="m569,240c4.11.31,6.91,2.96,8,4,6.83,6.48,6.93,17.68,7,26,0,.06,0,.03,0,7,0,14.32,0,21.48,0,22,.04,13.27.66,35.34-4,42-1.29,1.84-3,3-3,3-2.97,2.01-6.17,1.99-8,2-1.45,0-7.74-.12-13-5-6.34-5.87-6.06-13.87-6-15,0-.07,0-9.93,0-10,0-38.04,0-37.85,0-38-.02-1.29-.01-3.53,0-8,.03-9.9.15-12.06,1-15,1.28-4.42,3.15-6.95,4-8,1.1-1.36,2.19-2.34,3-3,.7-.58,1.7-1.32,3-2,.98-.52,4.28-2.28,8-2Z"/>
|
||||
<path id="lite-i-1-1" class="cls-30" d="m566.78,196.07c8.85-.96,18.71,7.52,18.2,17.09-.5,9.28-10.62,16.63-19.33,14.46-6.93-1.73-13.7-9.65-12.51-18.4,1.06-7.79,7.93-12.52,13.65-13.14Z"/>
|
||||
<path id="lite-t" class="cls-4" d="m643,219c-5.46,4.9-13.26-2.45-24,3-1.76.89-7.94,4.02-9,10-.72,4.08.94,7.61,1.38,8.43.48.92,2.17,4.11,5.62,5.57,1.09.46,1.2.26,6,1,5.06.78,4.17.82,6,1,2.02.19,4,.68,6,1,.54.09,1.55.25,2,1,.42.71.13,1.6,0,2-.61,1.93-.44,4.05-1,6-1.01,3.51-.87,8.39-1,13-.29,10.62-1.82,12.94-2,20-.11,4.19-.42,18.97,9,31,1.49,1.9,10.06,12.84,21,12,1.28-.1,5.89-.45,9-4,4.48-5.11,2.13-12.61,2-13-1.5-4.54-4.19-4.73-8-10-4-5.53-5.15-11-6-15-1.67-7.89-.83-14.05,0-20,1.22-8.76,3.02-10.14,4-16,.13-.81.46-2.94,2-5,0,0,.85-1.13,2-2,2.17-1.64,8.68-.49,12.72-.42,3.25.06,5.06.09,7.28-.58,1.17-.35,8.31-2.66,10-9,1.37-5.14-1.51-11.55-6-14-3.8-2.07-12.48-2.26-15-3-1.03-.3-1.9-.93-2-1-.47-.34-.82-.6-1-1-.56-1.23.89-2.87,1-3,.54-.96,1.27-2.32,2-4,1.51-3.46,2.99-6.85,3-11,0-1.91.01-6.19-3-10-3.3-4.17-8.02-4.99-9.42-5.24-.92-.16-6.37-1-11.58,2.24-1.61,1-4.15,3-7,9-5.75,12.08-1.75,17.18-6,21Z"/>
|
||||
<path id="lite-e" class="cls-7" d="m735,276c-.58.39-5.22,3.65-5,8,.19,3.92,4.21,6.49,5,7,5.42,3.48,9.31.51,25-1,10.15-.98,16.08-.47,21-5,1.89-1.74,2.72-3.39,3-4,2.32-5.01-.29-10.09-1-14-1.83-10.01-12.11-17.03-15-19-1.39-.95-8.43-5.63-19-7-4.47-.58-13.46-1.75-23,3-8.94,4.45-13.31,11.6-16,16-5.84,9.56-6.79,18.48-7,21-.4,4.87-1.09,14.86,5,25,4.96,8.26,11.87,12.39,15,14,8.24,4.25,15.6,4.12,23,4,9.99-.17,16.58-2.61,23-5,2-.74,5.17-2.02,8-5,1.68-1.77,4.08-4.29,4-8-.07-3.36-2.16-6.53-5-8-2.95-1.53-5.9-.81-9,0-6.07,1.58-6.81,4.01-12,6-6.49,2.49-12.52,1.46-15,1-3.77-.69-9.81-1.81-14-7-3.24-4.01-3.72-8.41-4-11-.26-2.43-1.1-10.07,4-17,.43-.59,7.21-9.48,18-9,1.43.06,9.65.59,14,7,1.16,1.71,2.94,4.33,2,6-2.22,3.93-15.76-4.29-25,2Z"/>
|
||||
<path id="logo-snow" class="cls-21" d="m875,272l13.33,22,9.67-15-8-20,4-6,6,2,9,13,7-11,9,1,3,7-11,13,17,2,4,8-5,4-22-4-13.5,16.8,27.5,5.2,11-14,7,1,3,7-9,11,19,3,4,7-4,5-18-3,7,14-4,4-8-1-10-20-40-8-2-2-20-37-23-4-3-7,4-5,16,3-9-15,4-6,7,1,9,16,9-10,7,2,3,6-10,14Z"/>
|
||||
<path id="yuki-y" class="cls-27" d="m717,426c-18.61-18.39-62.05-50.01-50-71,2.85-4.5,7.34-9.45,17-7,15.19,5.99,14.64,39.18,45,55,8.67-18.92,10.28-33.18,10-43-.12-4.37-.74-12.15,4-16,.82-.55,5.22-3.4,10.85-2.15,3.86.85,6.25,3.17,7.15,4.15.5.6,1.28,1.58,2.09,2.87,8.99,14.33.03,32.06-11.49,69.45-4.1,13.3-6.65,21.58-9.6,33.68-5.81,23.86-8.17,40.38-20,51-11.03,9.91-22.32.03-23-9-2.89-15.02,23.13-56.98,18-68Z"/>
|
||||
<path id="yuki-u" class="cls-22" d="m776,433c-.54-.11-5.36-1.02-9,2-2.31,1.92-3.04,4.55-4,8-.74,2.66-.91,4.8-1,6-.59,8.25,0,15,0,15,1.18,13.55,1.77,20.33,3,23,1.04,2.27,4.07,8.71,11,13,6.21,3.85,12.31,3.91,21,4,.81,0,16.83.09,21-4,.22-.21,1.2-1.26,2-1,.93.3.69,2.05,2,4,0,0,.63.93,2,2,3.54,2.75,12.53,3.41,16-1,.62-.79,1.75-2.64,2-5,.07-.68.05-1.22.02-1.76-.13-2.75-.88-4.83-1.02-5.24-1.31-3.73-3.37-15.69-4-23-.58-6.71.42-5.3,0-19-.11-3.63-.12-9.19-3-15-1.36-2.75-2.63-4.09-4-5-2.95-1.96-6.82-2.22-10-1-.8.31-3.48,1.34-5,4-1.36,2.37-1.11,4.83-1,6,.66,6.98.45,14.01,1,21,.72,9.13.57,11.51-1,14-1.8,2.86-4.42,4.2-6,5-7.58,3.86-15,.82-17,0-2.54-1.04-3.49-2.11-4-3-.74-1.29-.88-2.78-1-4-.13-1.35-.05-2.34,0-3v-16c-.19-7.44-.28-11.16-2-14-.52-.85-3.02-4.98-8-6Z"/>
|
||||
<path id="yuki-i-1-1" class="cls-2" d="m964,382c-6.65.3-12.55,6.12-12,13,.53,6.56,6.7,11.28,13,11,6.65-.3,12.55-6.12,12-13-.53-6.56-6.7-11.28-13-11Z"/>
|
||||
<path id="yuki-i-1" class="cls-24" d="m964,413c-.8.03-3.41.23-6,2,0,0-1.73,1.18-3,3-3.44,4.92-2.32,14.54-2,25,.14,4.66,0,9.33,0,14,0,11.92,0,17.88,0,18-.51,8.41-1.54,11.05,0,16,.77,2.5,1.84,5.92,5,8,3.21,2.11,6.76,1.77,8.36,1.62.77-.07,5.65-.54,8.64-3.62,3.4-3.49,2.66-8.56,2-17-.09-1.11-1.18-15.43-1-28,.1-7.33-.76-14.71,0-22,.11-1.03.52-4.77-1-9-.64-1.78-1.55-4.19-4-6-2.89-2.13-6.05-2.04-7-2Z"/>
|
||||
<path id="yuki-k" class="cls-28" d="m918,409c-.26-.11-.66-.27-1.17-.44,0,0-4.46-1.53-9.91-1-4.47.44-18.59,11.34-31.93,31.43-.35-10.53-.23-21.87.56-33.9.37-5.55.85-10.92,1.44-16.1.26-1.2.52-2.94.41-5.03-.09-1.81-.25-4.96-2.27-8.04-.56-.85-2.9-4.42-7.52-5.51-5.61-1.33-10.01,2.08-10.62,2.58-3.44,2.76-4.45,6.67-5,9-3.6,15.32-3,27-3,27,.42,8.15,1.67,32.57,1,50-.22,5.66.05,11.33,0,17-.05,6.02-.13,9.05,1,13,1.45,5.07,4.08,8.75,6,11,1.19,1.11,3.93,3.36,8,4,3.99.63,7.79-.51,9.84-1.85,11.06-7.21,1.65-39.59,4.55-40.56.39-.13,1.19.88,2.61,3.4,8.89,11.22,14.99,15.39,30,29,7.7,10.13,23.45,3.59,22-8-.19-16.59-39.62-33.08-31.91-39.47,1.89-2.38,6-8.21,6.91-9.53,5.47-7.8,10.83-8.44,13-15,1.34-4.04.69-6.57.43-7.38-1.01-3.15-3.39-4.94-4.43-5.62Z"/>
|
||||
<path id="lite-jp-1" class="cls-19" d="m452,384c-1.75-1-2.61-.17-10,1-2.22.35-7.11,1.06-13,3-1.36.45-2.68.93-3,2-.49,1.65,1.56,4.27,4,5,.32.09.56.13.74.16,2.19.34,3.89-.56,5.26-1.16,1.96-.87,3.64-1.24,7-2,0,0,4.44-1,5,0,.02.04.04.07.04.07.07.18-.02.5-.04,2.93,0,.06,0,.71,0,2,0,5.01.09,5.71,0,7-.2,2.74-.3,4.11-1,5-1.25,1.58-3.23,1.52-7,2-5.62.72-4.41,1.27-10,2-7.19.94-9.49.07-11,2-1.32,1.68-1.32,4.57,0,6,1.74,1.88,5.46.88,9,0,7.35-1.82,19.55-4.84,29-6,4.81-.59,12-1.09,13-4,.57-1.65,0-3,0-3-.06-.14-.62-1.43-2-2-1.19-.49-2.36-.18-3,0-6.78,1.91-9.23,2.82-10,2-.45-.48-.17-1.39,0-2,1.77-6.29,1.22-12.36,1-14-.23-1.67-.84-6.18-4-8Z"/>
|
||||
<path id="liteecho-1" class="cls-34" d="m448,440c-1.02.34-1.72,1.18-2,2-.31.93-.06,1.78,0,2,.38,1.39.25,7.26,0,19-.12,5.74-.36,7.73,0,12,.21,2.49.58,5.11,2,8,.87,1.76,1.83,3.72,4,5,2.08,1.23,4.09,1.14,7,1,2.69-.13,6.51-.31,7-2,.37-1.27-1.18-3.23-3-4-2.78-1.18-4.84.97-7,0-2.17-.97-2.69-4.29-3-7-1.07-9.45-.9-24.04-1-30-.02-1.36-.12-5.21-2-6-.58-.25-1.37-.21-2,0Z"/>
|
||||
<path id="yuki-jp-4-1" class="cls-16" d="m636.06,390.95c-2.11.2-3.55,1.9-3.93,2.35-.57.68-.91,1.34-1.11,1.8,1.09,1.05,6.06,5.64,11.22,4.91,2.33-.33,5.1-1.81,5.34-2.63.03-.09.03-.17.03-.17,0-.29-.33-.71-1.89-1.83-1.33-.96-2.23-1.61-3.3-2.18-.69-.37-1.23-.6-1.68-.79-2.41-1.04-3.62-1.55-4.68-1.45Z"/>
|
||||
<path id="yuki-jp-4" class="cls-12" d="m621,375c.37-.49,1.34-1.8,3-2,2.33-.28,3.93,1.89,4,2,.33.46.44.81,1,3,.59,2.3.88,3.45,1,4,.33,1.56.71,3.58,1,6,.65,5.37.61,6.78,1,10,.79,6.47,1.28,10.5,3,15,.07.18,1.23,3.21,1,7-.08,1.29-.29,2.25-1,3-1,1.07-2.67,1.39-4,1-.58-.17-1.69-.65-3-3-1.53-2.75-1.78-5.26-2-7-.3-2.39-.86-5.79-2-10-1-6.33-2-12.67-3-19-.33-1.67-.67-3.33-1-5-.1-.53-.53-2.95,1-5Z"/>
|
||||
<path id="liteecho-i-1-1" class="cls-20" d="m474,451c-1.86-.04-3.59,1.27-4,3-.56,2.38,1.47,5.01,4,5,2.03-.01,4-1.72,4-4,0-2.25-1.92-3.96-4-4Z"/>
|
||||
<path id="liteecho-i-1" class="cls-9" d="m475,464c-.61-.3-1.38-.31-2,0-.14.07-.62.33-1,1-1.18,2.08-1.01,7.51-1,8,.16,5.45,0,9,0,9-.08,1.78-.22,3.7,1,5,.21.22.94,1,2,1,1.08,0,1.81-.8,2-1,1.05-1.14,1.02-2.72,1-4-.03-2.33.04-4.67,0-7-.05-3.31.17-8.95-1-11-.12-.2-.41-.71-1-1Z"/>
|
||||
<path id="liteecho-t" class="cls-8" d="m492.48,452.64c-.52.03-1.3.07-1.92.55-.25.19-.76.67-1.1,2.68-.15.92-.29,2.26-.14,3.91-1.17.01-3.52.16-5.32,1.22-.38.22-.77.51-1,1-.39.85-.08,1.91.41,2.59.74,1.01,1.99,1.19,3.59,1.41.85.12,1.57.12,2.05.1-.06.76-.12,1.85-.14,3.16-.01,1.22-.03,2.48.14,4.12.15,1.49.3,2.92.96,4.62.39,1.03.78,1.67,1,2,.25.39.74,1.08,1.48,1.76,1.12,1.05,2.67,2.5,4.52,2.24,1.2-.17,2.57-1.06,2.76-2.24.12-.76-.29-1.46-.55-1.92-.43-.76-.87-.99-1.51-1.65-.95-.97-1.44-1.93-1.78-2.61-.71-1.41-.9-2.61-1.1-3.85-.15-.97-.37-2.35-.14-4.12.09-.67.22-1.22.32-1.61.56,0,1.35-.04,2.29-.17,1.54-.21,2.32-.32,2.88-.82.8-.71,1.27-2.03.83-3.01-.13-.3-.38-.61-1-1-1.52-.94-3.22-1.02-4.36-.94.11-.49.27-1.25.41-2.2.49-3.18.14-3.81-.14-4.12-.26-.3-.59-.43-1.24-.69-.4-.16-1.18-.47-2.2-.41Z"/>
|
||||
<path id="liteecho-e" class="cls-25" d="m515.41,469.49c-1.24,0-1.82.11-2.17.53-.63.76-.29,2.24.31,3.15,1.51,2.31,5.53,2.25,7.8,1.77,1.4-.3,4.49-.94,5.98-3.77,1.7-3.21-.04-6.62-.4-7.27-1.86-3.4-5.47-4.29-7.14-4.7-3.37-.83-6.16.05-7.27.4-1.07.34-2.81.91-4.53,2.4-3.45,2.98-3.92,7.09-4,8-.27,3.05.7,5.35,1,6,.27.6,1.17,2.42,3,4,1.04.89,2.16,1.53,3,2,.99.56,1.75.89,2,1,1.02.44,1.88.81,3,1,1.17.2,2.07.1,3,0,.34-.04,1.85-.22,4-1,3.3-1.2,4.59-2.54,5-3,.47-.53.68-.9.71-1.34.06-1.15-1.21-2.31-2.48-2.62-.51-.12-.94-.08-1.23-.05-2.01.26-3.04,1.28-4.31,1.86-2.59,1.19-5.84.41-7.69-.86-1.16-.8-1.95-1.93-2-2-.26-.38-.69-1.07-1-2-.26-.77-.95-2.86,0-5,.82-1.85,2.41-2.69,3-3,2.34-1.23,5.06-1.05,7,0,.69.37,1.64.9,2,2,.29.87.26,2.23-.52,2.84-1,.8-2.36-.34-6.07-.36Z"/>
|
||||
<path id="liteecho-c" class="cls-29" d="m570,456c-1.16,0-4.12.04-7,2-1.54,1.05-2.49,2.26-3,3-1.62,2.38-1.87,4.7-2,6-.12,1.26-.36,4,1,7,2.14,4.72,6.61,6.52,8,7,1.04.36,4.93,1.64,9,0,.94-.38,2.74-1.13,4-3,.45-.66,1.99-2.94,1-5-.54-1.12-1.88-2.25-3-2-1.41.32-1.04,2.48-3,4-1.92,1.48-4.45,1.09-5,1-.54-.08-2.42-.41-4-2-2.4-2.42-2.04-5.66-2-6,.07-.51.43-3.37,3-5,2.25-1.43,5.33-1.38,7,0,.93.77.92,1.55,2,2,1.26.52,3.2.27,4-1,.72-1.15.17-2.56,0-3-.15-.38-.69-1.63-3-3-.98-.58-3.45-2.01-7-2Z"/>
|
||||
<path id="liteecho-h" class="cls-17" d="m590,439c-.92-.49-2.08-.44-3,0-1.8.87-1.99,2.91-2,3v9c0,4,.04,8,0,12-.03,3,.1,6,0,9-.1,3.04-.29,5.38,1,8,.45.91,1.42,2.89,3,3,1.63.11,2.9-1.85,3-2,1.44-2.28.03-4.1,0-9-.02-2.85,0-5.82,2-8,.37-.4,1.8-1.97,4-2,2.44-.04,3.91,1.89,4,2,.92,1.22,1,2.52,1,3-.33,1.67-.67,3.33-1,5-.41,1.12-.91,2.85-1,5-.09,2.3-.16,3.99,1,5,1.28,1.11,3.61,1.02,5,0,1.65-1.21,1.8-3.61,2-7,.08-1.27.04-2.31,0-3v-8c-.02-.84-.16-2.36-1-4-.51-1-1.73-2.93-4-4-3.65-1.72-7.65-.14-8,0-1.5.62-2.24,1.36-3,1-.71-.34-.94-1.4-1-4-.12-5.04.61-6.66,0-10-.56-3.06-1.54-3.76-2-4Z"/>
|
||||
<path id="liteecho-o" class="cls-33" d="m625,458c-4.27-.18-7.17,2.27-8,3-3.05,2.66-3.79,6-4,7-.18.88-1.21,5.84,2,10,2.08,2.69,4.82,3.62,6,4,1.29.42,4.46,1.39,8,0,2.95-1.16,4.51-3.31,5-4,1.43-2.02,1.81-3.97,2-5,.46-2.51.1-4.49,0-5-.67-3.35-2.52-5.47-3-6-.69-.76-3.46-3.8-8-4Z"/>
|
||||
<path id="liteecho-o-white" class="cls-35" d="m623.5,464.5c-2.49.24-3.75,2.55-4,3-.09.17-1.81,3.42,0,6,1.68,2.39,5.6,3.06,8,1,2.1-1.8,2.13-4.93,1-7-.84-1.54-2.7-3.22-5-3Z"/>
|
||||
<path id="lite-jp-2" class="cls-13" d="m481,384c-.71.63-1.32,1.81-1,3,.38,1.44,1.86,1.95,2,2,.77.25,1.44.12,2,0,4.88-1.04,5.31-1.31,6-1,1.18.54,1.54,2.08,2,4,.41,1.72.73,3.08,0,4-.65.81-2.11.89-5,1-1.25.05-2.29.03-3,0-.53.14-1.96.61-3,2-.4.54-1.21,1.61-1,3,.24,1.62,1.7,2.78,3,3,.44.08.79.03,1,0,4.59-.65,6.89-.98,7-1,2.55-.43,3.84-.64,4.45-.34,1.26.62,1.41,1.86,2.55,5.34,0,0,1.48,4.5,4,10,.8,1.75,1.53,3.13,2,4,.16.13,2.42,1.9,5,1,.16-.06,2.44-.88,3-3,.21-.78.1-1.42,0-2-.27-1.54-.96-2.26-2-4-.12-.21.03.04-2-4-.67-1.33-1.32-2.67-2-4-1.91-3.76-2.3-4.3-2-5,.2-.46.49-.53,4-2,1.13-.48,2.45-1.03,4-2,1.13-.71,1.71-1.21,2-2,.06-.17.35-1.03,0-2-.48-1.32-1.88-2.03-3-2-.31,0-.54.07-.66.11-2.2.63-4.77,1.67-5.34,1.89-2.21.88-3.31,1.31-4,1-.42-.19-.77-.54-2-4-1.21-3.42-1.11-3.77-1-4,.42-.85,1.37-.79,6-2,2.46-.64,3.57-1.02,4-2,.3-.68.14-1.39,0-2-.09-.4-.54-2.17-2-3-1.17-.66-2.4-.38-4,0-3.65.88-4.88,2.55-6,2-.55-.27-1.15-1.13-1-4-.42-2-.76-3-1-3h0c-.76-1.48-2.4-2.29-4-2-1.51.27-2.7,1.47-3,3-.17,2.45.41,4.01,1,5,.57.95,1.14,1.36,1,2-.24,1.14-2.45,1.64-4,2-2.38.55-3.11.2-4,1Z"/>
|
||||
<path id="yuki-jp-1" class="cls-5" d="m526,396c-.46,1.12-.62,2.69,0,4,.19.41.79,1.49,2,2,1.22.51,2.43.16,3,0,5-1.43,7.51-2.15,8-2,4.42,1.34,3.64,20.08,7,22,2.14,1.22,4,1,4,1,1.43-.17,3.03-.97,3.02-1,0,0,0,0-.02,0,.27-.2.65-.52,1-1,1.53-2.12.85-5.32-1-10-3.3-8.33-6.24-11.07-5-13,.71-1.1,1.84-.46,5.57-2.36,2.51-1.27,4.06-2.06,4.43-3.64.46-1.98-1.11-4.46-3-5-1.01-.29-2.23.21-4.65,1.22-3.32,1.39-3.58,2.02-4.35,1.78-2.25-.7-.9-6.34-4-8-1.22-.65-2.84-.54-4,0-.31.14-1.56.73-2,2-.24.7-.14,1.29,0,2,.51,2.66,2.08,3.57,2,5.99,0,0,0,0,0,0,.01,0,.02,0,.02,0,.01.1-10.37-.01-12.02,4Z"/>
|
||||
<path id="yuki-jp-2-3" class="cls-31" d="m603,386c-.23-.2-.95-.74-2-1-.58-.14-1.39-.34-2,0-.79.44-.86,1.6-1,3,0,0-.62,6.23-2,9-.43.86-.97,1.66-.97,1.66-.12.18-.24.35-.35.5-.2.27-.42.55-.68.84-1.47,1.68-5.93,4.2-5.93,4.2-3.23,1.82-3.66,2.04-4.07,2.8-.61,1.13-.9,2.9,0,4,.8.97,2.17.99,3,1,.7,0,3.05-.07,7.17-3.17,1.64-1.24,3.78-2.89,5.83-5.83,3.15-4.53,3.72-8.76,4-11,.52-4.2-.4-5.48-1-6Z"/>
|
||||
<path id="liteecho-e2" class="cls-14" d="m540.81,469.18c-1.13.52-1.6.87-1.74,1.41-.25.96.69,2.15,1.62,2.72,2.35,1.44,5.96-.31,7.81-1.71,1.14-.86,3.66-2.76,3.81-5.96.17-3.63-2.85-5.97-3.45-6.41-3.13-2.29-6.78-1.55-8.46-1.22-3.41.68-5.55,2.66-6.41,3.45-.83.76-2.15,2.02-3.08,4.1-1.85,4.16-.53,8.09-.22,8.94,1.05,2.88,2.91,4.54,3.46,5,.5.42,2.09,1.69,4.42,2.34,1.32.37,2.61.46,3.57.53,1.13.08,1.96.06,2.24.05,1.11-.04,2.04-.06,3.14-.37,1.14-.32,1.92-.79,2.71-1.28.29-.18,1.58-.98,3.19-2.61,2.48-2.49,3.07-4.25,3.25-4.84.2-.68.24-1.11.07-1.51-.43-1.07-2.08-1.58-3.36-1.31-.51.11-.88.32-1.13.48-1.71,1.09-2.2,2.45-3.11,3.52-1.84,2.18-5.11,2.86-7.32,2.49-1.39-.23-2.59-.92-2.66-.96-.4-.23-1.08-.67-1.76-1.38-.56-.59-2.08-2.19-2.13-4.52-.04-2.02,1.04-3.46,1.44-3.99,1.59-2.11,4.14-3.1,6.33-2.98.79.04,1.86.12,2.66.96.63.67,1.18,1.91.74,2.79-.57,1.15-2.28.69-5.65,2.26Z"/>
|
||||
</g>
|
||||
<circle class="cls-23" cx="535" cy="411" r="4"/>
|
||||
<ellipse class="cls-11" cx="556.49" cy="380.5" rx="2.51" ry="2.5"/>
|
||||
<ellipse class="cls-26" cx="549.49" cy="383.5" rx="2.51" ry="2.5"/>
|
||||
<circle class="cls-10" cx="557.98" cy="404.39" r="4"/>
|
||||
<circle class="cls-1" cx="582.42" cy="391.26" r="4"/>
|
||||
<circle class="cls-6" cx="590.42" cy="385.26" r="4"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 31 KiB |
@ -1,73 +0,0 @@
|
||||
// 定义全局变量来存储数据
|
||||
|
||||
let globalTotal = 0;
|
||||
|
||||
let globalOnline = 0;
|
||||
|
||||
|
||||
// 从API获取数据并更新全局变量
|
||||
|
||||
function fetchAndUpdateData() {
|
||||
|
||||
Promise.all([
|
||||
|
||||
fetch("https://api.liteyuki.icu/count").then(res => res.json()),
|
||||
|
||||
fetch("https://api.liteyuki.icu/online").then(res => res.json())
|
||||
|
||||
])
|
||||
|
||||
.then(([countRes, onlineRes]) => {
|
||||
|
||||
globalTotal = countRes.register;
|
||||
|
||||
globalOnline = onlineRes.online;
|
||||
|
||||
})
|
||||
|
||||
.catch(err => {
|
||||
|
||||
console.error("Error fetching data:", err);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 更新页面显示,使用全局变量中的数据
|
||||
|
||||
function updatePageDisplay() {
|
||||
|
||||
let countInfo = document.getElementById("count-info");
|
||||
|
||||
if (!countInfo) {
|
||||
|
||||
let info = `<div id="count-info" style="text-align: center; font-size: 20px; font-weight: 500">
|
||||
|
||||
Instances:<span id="total">${globalTotal}</span> Online:<span id="online">${globalOnline}</span></div>`;
|
||||
|
||||
let mainDescription = document.querySelector("#main-description");
|
||||
|
||||
if (mainDescription) {
|
||||
|
||||
mainDescription.insertAdjacentHTML('afterend', info);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 初始调用更新数据
|
||||
|
||||
fetchAndUpdateData();
|
||||
|
||||
updatePageDisplay();
|
||||
|
||||
|
||||
// 设置定时器,分别以不同频率调用更新数据和更新页面的函数
|
||||
|
||||
setInterval(fetchAndUpdateData, 10000); // 每10秒更新一次数据
|
||||
|
||||
setInterval(updatePageDisplay, 1000); // 每1秒更新一次页面显示
|
@ -1,93 +0,0 @@
|
||||
echart = require('echarts');
|
||||
let chart = echarts.init(document.getElementById('main-chart'));
|
||||
const color = ['#9ae5fc', '#dcbf71']; // 自定义图中要用到的颜色
|
||||
console.log("加载图标");
|
||||
// 在地图加载完成后设置所有地区不可选
|
||||
function setAllRegionsUnselectable(geoModel) {
|
||||
const regions = geoModel.get('regions');
|
||||
|
||||
// 遍历所有地区并设置selected为false
|
||||
for (let i = 0; i < regions.length; i++) {
|
||||
const region = regions[i];
|
||||
region.selected = false;
|
||||
}
|
||||
|
||||
// 更新模型以反映更改
|
||||
geoModel.set('regions', regions);
|
||||
|
||||
// 更新图表以显示更改
|
||||
chart.setOption({
|
||||
geo: {
|
||||
regions: regions
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取数据并初始化图表
|
||||
fetch('https://api.liteyuki.icu/distribution')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 构造 ECharts 需要的数据格式
|
||||
const locations = data.locations;
|
||||
const seriesData = locations.map(location => ({
|
||||
value: [location[1], location[0]] // 直接使用经纬度数组
|
||||
}));
|
||||
console.log(seriesData);
|
||||
// 初始化图表选项
|
||||
chart.setOption({
|
||||
backgroundColor: '#454545',
|
||||
title: {
|
||||
text: 'LiteyukiBot分布demo',
|
||||
subtext: 'LiteyukiBot分布',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 20
|
||||
},
|
||||
top: '10px',
|
||||
left: '10px'
|
||||
},
|
||||
geo: {
|
||||
map: 'world',
|
||||
roam: false,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#000',
|
||||
borderType: null, // 设置边界线类型为无
|
||||
borderColor: '#000', // 设置边界线颜色
|
||||
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#000',
|
||||
borderType: null, // 设置边界线类型为无
|
||||
borderColor: '#000', // 设置边界线颜色
|
||||
}
|
||||
},
|
||||
regions: [] // 先保留为空
|
||||
},
|
||||
series: [{
|
||||
// 散点效果
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'geo', // 表示使用的坐标系为地理坐标系
|
||||
symbolSize: 5, // 设置散点的大小为20
|
||||
itemStyle: {
|
||||
color: '#ffeb3b', // 黄色
|
||||
},
|
||||
data: seriesData
|
||||
}],
|
||||
textStyle: {
|
||||
fontSize: 1
|
||||
}
|
||||
});
|
||||
|
||||
// 在地图加载完成后设置所有地区不可选
|
||||
chart.on('ready', function () {
|
||||
const geoModel = chart.getModel().componentModels.geo[0];
|
||||
setAllRegionsUnselectable(geoModel);
|
||||
});
|
||||
|
||||
// 自适应窗口大小变化
|
||||
window.addEventListener("resize", function () {
|
||||
chart.resize();
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('Error fetching data:', error));
|
@ -1,31 +0,0 @@
|
||||
function applyStyle() {
|
||||
// 先检测页面中是否有macos-tab,有则不再添加
|
||||
let tabs = document.body.querySelectorAll('.macos-tab')
|
||||
if (tabs.length > 0) {
|
||||
return
|
||||
}
|
||||
let lineNumbers = document.body.querySelectorAll('[class^="language-"].line-numbers-mode')
|
||||
lineNumbers.forEach((item) => {
|
||||
// 插入现成的html文本
|
||||
let title = item.getAttribute('data-title')
|
||||
let tabStr =
|
||||
"<div class='tab macos-tab' style='display: flex; background-color: #d0e9ff'>" +
|
||||
" <div class='tab-buttons'>" +
|
||||
" <div class='tab-button' style='background-color: #FF5F57'></div>" +
|
||||
" <div class='tab-button' style='background-color: #FFBD2E'></div>" +
|
||||
" <div class='tab-button' style='background-color: #27C93F'></div>" +
|
||||
" </div>" +
|
||||
` <div class='tab-title'>${title}</div>` +
|
||||
" <div style='flex: 1'></div>" +
|
||||
"</div>"
|
||||
// 在代码块前插入选项卡
|
||||
item.insertAdjacentHTML('beforebegin', tabStr);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
applyStyle()
|
||||
// 定时器,每隔1s检查一次
|
||||
setInterval(() => {
|
||||
applyStyle()
|
||||
}, 1000)
|
@ -1,73 +0,0 @@
|
||||
// 定义全局变量来存储数据
|
||||
|
||||
let globalTotal = 0;
|
||||
|
||||
let globalOnline = 0;
|
||||
|
||||
|
||||
// 从API获取数据并更新全局变量
|
||||
|
||||
function fetchAndUpdateData() {
|
||||
|
||||
Promise.all([
|
||||
|
||||
fetch("https://api.liteyuki.icu/count").then(res => res.json()),
|
||||
|
||||
fetch("https://api.liteyuki.icu/online").then(res => res.json())
|
||||
|
||||
])
|
||||
|
||||
.then(([countRes, onlineRes]) => {
|
||||
|
||||
globalTotal = countRes.register;
|
||||
|
||||
globalOnline = onlineRes.online;
|
||||
|
||||
})
|
||||
|
||||
.catch(err => {
|
||||
|
||||
console.error("Error fetching data:", err);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 更新页面显示,使用全局变量中的数据
|
||||
|
||||
function updatePageDisplay() {
|
||||
|
||||
let countInfo = document.getElementById("count-info");
|
||||
|
||||
if (!countInfo) {
|
||||
|
||||
let info = `<div id="count-info" style="text-align: center; font-size: 20px; font-weight: 500">
|
||||
|
||||
全球实例:<span id="total">${globalTotal}</span> 当前在线:<span id="online">${globalOnline}</span></div>`;
|
||||
|
||||
let mainDescription = document.querySelector("#main-description");
|
||||
|
||||
if (mainDescription) {
|
||||
|
||||
mainDescription.insertAdjacentHTML('afterend', info);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 初始调用更新数据
|
||||
|
||||
fetchAndUpdateData();
|
||||
|
||||
updatePageDisplay();
|
||||
|
||||
|
||||
// 设置定时器,分别以不同频率调用更新数据和更新页面的函数
|
||||
|
||||
setInterval(fetchAndUpdateData, 10000); // 每10秒更新一次数据
|
||||
|
||||
setInterval(updatePageDisplay, 1000); // 每1秒更新一次页面显示
|
Before Width: | Height: | Size: 92 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" class="icon" viewBox="0 0 3280.944 2800"><path fill="#41b883" d="M1645.332 601.004h375.675L1081.82 2238.478 142.636 601.004h718.477l220.708 379.704 216.013-379.704z"/><path fill="#41b883" d="M142.636 601.004l939.185 1637.474 939.186-1637.474h-375.675l-563.51 982.484-568.208-982.484z"/><path fill="#35495e" d="M513.188 601.004l568.207 987.23 563.511-987.23h-347.498l-216.013 379.704-220.708-379.704zM1607.792 1311.83l594.678 2.293 187.353-316.325-598.662 2.292zM2198.506 1909.57C2867.436 732.7 2939.502 605.426 2937.874 603.78c-.715-.723 45.303-1.314 102.262-1.314s103.562.428 103.562.951c0 .523-208.57 367.978-463.491 816.567L2216.715 2235.6l-102.1.596-102.102.596z"/><path fill="#41b883" d="M1680.563 2233.328c0-1.34 168.208-298.145 440.375-777.048a4135645.775 4135645.775 0 00337.619-594.19l146.13-257.25 170.746-.04 170.747-.04-5.536 9.741c-3.044 5.358-43.727 77.302-90.407 159.875-85.356 150.992-337.562 595.163-656.602 1156.373l-172 302.559-170.536.588c-93.795.322-170.536.069-170.536-.567z"/><path fill="#35495e" d="M1429.783 1625.351l594.679 2.292 187.353-316.324-598.662 2.292z"/><path fill="#41b883" d="M1524.207 1464.903l608.285 6.877 173.746-320.909h-619.072z"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,10 +0,0 @@
|
||||
import {sidebar} from "vuepress-theme-hope";
|
||||
|
||||
export const enSidebarConfig = sidebar(
|
||||
{
|
||||
"/en/deploy/": "structure",
|
||||
"/en/usage/": "structure",
|
||||
"/en/store/": "structure",
|
||||
"/en/dev/": "structure",
|
||||
}
|
||||
)
|
@ -1,2 +0,0 @@
|
||||
export * from "./zh.js"
|
||||
export * from "./en.js"
|
@ -1,11 +0,0 @@
|
||||
import {sidebar} from "vuepress-theme-hope";
|
||||
|
||||
|
||||
export const zhSidebarConfig = sidebar(
|
||||
{
|
||||
"/deploy/": "structure",
|
||||
"/usage/": "structure",
|
||||
"/store/": "structure",
|
||||
"/dev/": "structure",
|
||||
}
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
// you can change config here
|
||||
$colors: #c0392b, #d35400, #f39c12, #27ae60, #16a085, #2980b9, #8e44ad, #2c3e50,
|
||||
#7f8c8d !default;
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
// place your custom styles here
|
||||
|
||||
:root {
|
||||
--code-window-border-radius: 10px;
|
||||
--button-distance: 8px;
|
||||
}
|
||||
|
||||
#main-title {
|
||||
font-family: ColorTube, "Fira Code", serif;
|
||||
color: #ff0000 !important; /* 你想要的颜色 */
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ColorTube;
|
||||
src: url("/assets/fonts/ColorTube.woff") format("woff")
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Fira Code", monospace !important;
|
||||
}
|
||||
|
||||
.vp-hero-image {
|
||||
overflow: hidden;
|
||||
padding: -50px;
|
||||
}
|
||||
|
||||
#main-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-hope-content pre {
|
||||
overflow: auto;
|
||||
margin: 0 0;
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
line-height: 1.375;
|
||||
}
|
||||
|
||||
// 移除该before
|
||||
.theme-hope-content pre::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.theme-hope-content > div[class*=language-] {
|
||||
margin: 0 0 0 0;
|
||||
// 仅下半部分有圆弧
|
||||
border-radius: 0 0 var(--code-window-border-radius) var(--code-window-border-radius);
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
height: 25px;
|
||||
margin-bottom: 0;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top-left-radius: var(--code-window-border-radius);
|
||||
border-top-right-radius: var(--code-window-border-radius);
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
padding: 7px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
height: 60%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
margin-right: var(--button-distance);
|
||||
border-radius: 50%;
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
border-radius: 100px;
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-box-div {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
width: 80%;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
// you can change colors here
|
||||
$theme-color: #00a6ff;
|
@ -1,203 +0,0 @@
|
||||
import {hopeTheme} from "vuepress-theme-hope";
|
||||
import {enSidebarConfig, zhSidebarConfig} from "./sidebar/index.js";
|
||||
import {enNavbarConfig, zhNavbarConfig} from "./navbar/index.js";
|
||||
|
||||
export default hopeTheme({
|
||||
|
||||
hostname: "https://vuepress-theme-hope-docs-demo.netlify.app",
|
||||
hotReload: true,
|
||||
|
||||
locales: {
|
||||
"/": {
|
||||
navbar: zhNavbarConfig,
|
||||
sidebar: zhSidebarConfig,
|
||||
author: {
|
||||
name: "远野千束",
|
||||
url: "https://sfkm.me",
|
||||
}
|
||||
},
|
||||
"/en/": {
|
||||
navbar: enNavbarConfig,
|
||||
sidebar: enSidebarConfig,
|
||||
author: {
|
||||
name: "SnowyKami",
|
||||
url: "https://sfkm.me",
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
iconAssets: "fontawesome-with-brands",
|
||||
|
||||
logo: "https://cdn.liteyuki.icu/static/img/liteyuki_icon_640.png",
|
||||
|
||||
repo: "https://github.com/LiteyukiStudio/LiteyukiBot",
|
||||
|
||||
docsDir: "docs",
|
||||
|
||||
// 页脚
|
||||
footer: "LiteyukiBot",
|
||||
displayFooter: true,
|
||||
|
||||
// 加密配置
|
||||
encrypt: {
|
||||
config: {
|
||||
"/demo/encrypt.html": ["1234"],
|
||||
},
|
||||
},
|
||||
|
||||
// 多语言配置
|
||||
metaLocales: {
|
||||
editLink: "在 GitHub 上编辑",
|
||||
},
|
||||
|
||||
// 如果想要实时查看任何改变,启用它。注: 这对更新性能有很大负面影响
|
||||
// hotReload: true,
|
||||
|
||||
// 在这里配置主题提供的插件
|
||||
plugins: {
|
||||
search: true,
|
||||
// search: true,
|
||||
comment: {
|
||||
provider: "Giscus",
|
||||
repo: "snowykami/LiteyukiBot",
|
||||
repoId: "R_kgDOHVNKpQ",
|
||||
category: "Announcements",
|
||||
categoryId: "DIC_kwDOHVNKpc4CeWxj",
|
||||
},
|
||||
|
||||
components: {
|
||||
components: ["Badge", "VPCard"],
|
||||
},
|
||||
|
||||
// 此处开启了很多功能用于演示,你应仅保留用到的功能。
|
||||
mdEnhance: {
|
||||
echarts: true,
|
||||
alert: true,
|
||||
align: true,
|
||||
attrs: true,
|
||||
codetabs: true,
|
||||
footnote: true,
|
||||
component: true,
|
||||
demo: true,
|
||||
figure: true,
|
||||
imgLazyload: true,
|
||||
imgSize: true,
|
||||
include: true,
|
||||
mark: true,
|
||||
stylize: [
|
||||
{
|
||||
matcher: "Recommended",
|
||||
replacer: ({tag}) => {
|
||||
if (tag === "em")
|
||||
return {
|
||||
tag: "Badge",
|
||||
attrs: {type: "tip"},
|
||||
content: "Recommended",
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
sub: true,
|
||||
sup: true,
|
||||
tabs: true,
|
||||
vPre: true,
|
||||
|
||||
|
||||
// 在启用之前安装 chart.js
|
||||
// chart: true,
|
||||
|
||||
// insert component easily
|
||||
|
||||
// 在启用之前安装 echarts
|
||||
// echarts: true,
|
||||
|
||||
// 在启用之前安装 flowchart.ts
|
||||
// flowchart: true,
|
||||
|
||||
// gfm requires mathjax-full to provide tex support
|
||||
// gfm: true,
|
||||
|
||||
// 在启用之前安装 katex
|
||||
// katex: true,
|
||||
|
||||
// 在启用之前安装 mathjax-full
|
||||
// mathjax: true,
|
||||
|
||||
// 在启用之前安装 mermaid
|
||||
// mermaid: true,
|
||||
|
||||
// playground: {
|
||||
// presets: ["ts", "vue"],
|
||||
// },
|
||||
|
||||
// 在启用之前安装 reveal.js
|
||||
// revealJs: {
|
||||
// plugins: ["highlight", "math", "search", "notes", "zoom"],
|
||||
// },
|
||||
|
||||
// 在启用之前安装 @vue/repl
|
||||
// vuePlayground: true,
|
||||
|
||||
// install sandpack-vue3 before enabling it
|
||||
// sandpack: true,
|
||||
},
|
||||
|
||||
// 如果你需要 PWA。安装 @vuepress/plugin-pwa 并取消下方注释
|
||||
// pwa: {
|
||||
// favicon: "/favicon.ico",
|
||||
// cacheHTML: true,
|
||||
// cachePic: true,
|
||||
// appendBase: true,
|
||||
// apple: {
|
||||
// icon: "/assets/icon/apple-icon-152.png",
|
||||
// statusBarColor: "black",
|
||||
// },
|
||||
// msTile: {
|
||||
// image: "/assets/icon/ms-icon-144.png",
|
||||
// color: "#ffffff",
|
||||
// },
|
||||
// manifest: {
|
||||
// icons: [
|
||||
// {
|
||||
// src: "/assets/icon/chrome-mask-512.png",
|
||||
// sizes: "512x512",
|
||||
// purpose: "maskable",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// {
|
||||
// src: "/assets/icon/chrome-mask-192.png",
|
||||
// sizes: "192x192",
|
||||
// purpose: "maskable",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// {
|
||||
// src: "/assets/icon/chrome-512.png",
|
||||
// sizes: "512x512",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// {
|
||||
// src: "/assets/icon/chrome-192.png",
|
||||
// sizes: "192x192",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// ],
|
||||
// shortcuts: [
|
||||
// {
|
||||
// name: "Demo",
|
||||
// short_name: "Demo",
|
||||
// url: "/demo/",
|
||||
// icons: [
|
||||
// {
|
||||
// src: "/assets/icon/guide-maskable.png",
|
||||
// sizes: "192x192",
|
||||
// purpose: "maskable",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
},
|
||||
});
|
@ -1,82 +0,0 @@
|
||||
---
|
||||
home: true
|
||||
icon: home
|
||||
title: 首页
|
||||
heroImage: https://cdn.liteyuki.icu/static/svg/lylogo-full.svg
|
||||
heroImageDark: https://cdn.liteyuki.icu/static/svg/lylogo-full-dark.svg
|
||||
bgImage:
|
||||
bgImageDark:
|
||||
bgImageStyle:
|
||||
background-attachment: fixed
|
||||
heroText: LiteyukiBot
|
||||
tagline: LiteyukiBot 轻雪机器人,综合性的机器人应用及管理框架
|
||||
|
||||
actions:
|
||||
- text: 快速部署
|
||||
icon: rocket
|
||||
link: ./deploy/install.html
|
||||
type: primary
|
||||
|
||||
- text: 使用手册
|
||||
icon: book
|
||||
link: ./usage/basic_command.html
|
||||
|
||||
highlights:
|
||||
- header: 简洁至上
|
||||
image: /assets/image/layout.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/2-dark.svg
|
||||
bgImageStyle:
|
||||
background-repeat: repeat
|
||||
background-size: initial
|
||||
features:
|
||||
- title: 支持多种框架
|
||||
icon: robot
|
||||
details: 兼容nonebot,melobot等,拥有良好的生态支持
|
||||
link: https://nonebot.dev/
|
||||
|
||||
- title: 便捷管理
|
||||
icon: plug
|
||||
details: 使用包管理器,便捷管理插件及资源包
|
||||
|
||||
- title: 主题支持
|
||||
icon: paint-brush
|
||||
details: 使用资源包对外观进行完全自定义
|
||||
link: https://bot.liteyuki.icu/usage/resource_pack.html
|
||||
|
||||
- title: 国际化
|
||||
icon: globe
|
||||
details: 通过资源包支持多种语言
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: 简易使用
|
||||
icon: cog
|
||||
details: 无需繁琐前期过程,开箱即用
|
||||
link: https://bot.liteyuki.icu/deployment/config.html
|
||||
|
||||
- title: 超高性能
|
||||
icon: tachometer-alt
|
||||
details: 500个插件,2s内启动
|
||||
|
||||
- title: 滚动更新
|
||||
icon: cloud-download
|
||||
details: 让你的机器人保持最新提交
|
||||
|
||||
- title: 开源项目
|
||||
icon: code
|
||||
details: 项目遵循MIT LICENCE开源,欢迎各位的贡献
|
||||
|
||||
- header: 快速部署
|
||||
image: /assets/image/box.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
|
||||
highlights:
|
||||
- title: 安装 Git 及 Python3.10+
|
||||
- title: 使用 <code>git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1</code> 以克隆项目至本地。
|
||||
details: 如果无法连接到GitHub,可以使用 <code>git clone https://gitee.com/snowykami/LiteyukiBot --depth=1</code>。
|
||||
- title: 使用 <code>cd LiteyukiBot</code> 切换到项目目录。
|
||||
- title: 使用 <code>pip install -r requirements.txt</code> 安装项目依赖。
|
||||
details: 如果你有多个 Python 环境,请使用 <code>pythonx -m pip install -r requirements.txt</code>。
|
||||
- title: 使用 <code>python main.py</code> 启动项目。
|
||||
copyright: © 2021-2024 SnowyKami All Rights Reserved
|
||||
---
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: 项目部署
|
||||
index: false
|
||||
icon: laptop-code
|
||||
category: 部署
|
||||
---
|
||||
|
||||
<Catalog />
|
@ -1,9 +0,0 @@
|
||||
# note
|
||||
开发者选项
|
||||
allow_update: true # 是否允许更新
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方插件
|
||||
dev_mode: false # 开发者模式,开启后将会启动看门狗
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: 开发及贡献
|
||||
index: false
|
||||
icon: laptop-code
|
||||
category: 开发
|
||||
---
|
||||
|
||||
<Catalog />
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki
|
||||
---
|
@ -1,562 +0,0 @@
|
||||
---
|
||||
title: liteyuki.bot
|
||||
---
|
||||
### *func* `get_bot() -> LiteyukiBot`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取轻雪实例
|
||||
|
||||
|
||||
**返回**: LiteyukiBot: 当前的轻雪实例
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_bot() -> LiteyukiBot:
|
||||
"""
|
||||
获取轻雪实例
|
||||
|
||||
Returns:
|
||||
LiteyukiBot: 当前的轻雪实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
if _BOT_INSTANCE is None:
|
||||
raise RuntimeError('Liteyuki instance not initialized.')
|
||||
return _BOT_INSTANCE
|
||||
else:
|
||||
raise RuntimeError("Can't get bot instance in sub process.")
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_config(key: str = None) -> Any`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取配置
|
||||
|
||||
**参数**:
|
||||
> - key: 配置键
|
||||
> - default: 默认值
|
||||
|
||||
**返回**: Any: 配置值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_config(key: str, default: Any=None) -> Any:
|
||||
"""
|
||||
获取配置
|
||||
Args:
|
||||
key: 配置键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
return get_bot().config.get(key, default)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_config_with_compat(key: str = None) -> Any`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取配置,兼容旧版本
|
||||
|
||||
**参数**:
|
||||
> - key: 配置键
|
||||
> - compat_keys: 兼容键
|
||||
> - default: 默认值
|
||||
|
||||
**返回**: Any: 配置值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any=None) -> Any:
|
||||
"""
|
||||
获取配置,兼容旧版本
|
||||
Args:
|
||||
key: 配置键
|
||||
compat_keys: 兼容键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
if key in get_bot().config:
|
||||
return get_bot().config[key]
|
||||
for compat_key in compat_keys:
|
||||
if compat_key in get_bot().config:
|
||||
logger.warning(f'Config key "{compat_key}" will be deprecated, use "{key}" instead.')
|
||||
return get_bot().config[compat_key]
|
||||
return default
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `print_logo()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def print_logo():
|
||||
print('\x1b[34m' + '\n __ ______ ________ ________ __ __ __ __ __ __ ______ \n / | / |/ |/ |/ \\ / |/ | / |/ | / |/ |\n $$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \\ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/ \n $$ | $$ | $$ | $$ |__ $$ \\/$$/ $$ | $$ |$$ |/$$/ $$ | \n $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ | \n $$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \\ $$ | \n $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \\__$$ |$$ |$$ \\ _$$ |_ \n $$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |\n $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ \n ' + '\x1b[0m')
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `LiteyukiBot`
|
||||
### *method* `__init__(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 初始化轻雪实例
|
||||
|
||||
**参数**:
|
||||
> - *args:
|
||||
> - **kwargs: 配置
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
初始化轻雪实例
|
||||
Args:
|
||||
*args:
|
||||
**kwargs: 配置
|
||||
|
||||
"""
|
||||
'常规操作'
|
||||
print_logo()
|
||||
global _BOT_INSTANCE
|
||||
_BOT_INSTANCE = self
|
||||
'配置'
|
||||
self.config: dict[str, Any] = kwargs
|
||||
'初始化'
|
||||
self.init(**self.config)
|
||||
logger.info('Liteyuki is initializing...')
|
||||
'生命周期管理'
|
||||
self.lifespan = Lifespan()
|
||||
self.process_manager: ProcessManager = ProcessManager(lifespan=self.lifespan)
|
||||
'事件循环'
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.stop_event = threading.Event()
|
||||
self.call_restart_count = 0
|
||||
'加载插件加载器'
|
||||
load_plugin('liteyuki.plugins.plugin_loader')
|
||||
'信号处理'
|
||||
signal.signal(signal.SIGINT, self._handle_exit)
|
||||
signal.signal(signal.SIGTERM, self._handle_exit)
|
||||
atexit.register(self.process_manager.terminate_all)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `run(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 启动逻辑
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def run(self):
|
||||
"""
|
||||
启动逻辑
|
||||
"""
|
||||
self.lifespan.before_start()
|
||||
self.process_manager.start_all()
|
||||
self.lifespan.after_start()
|
||||
self.keep_alive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `keep_alive(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 保持轻雪运行
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def keep_alive(self):
|
||||
"""
|
||||
保持轻雪运行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
try:
|
||||
while not self.stop_event.is_set():
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
logger.info('Liteyuki is stopping...')
|
||||
self.stop()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_handle_exit(self, signum, frame)`
|
||||
|
||||
|
||||
|
||||
**说明**: 信号处理
|
||||
|
||||
**参数**:
|
||||
> - signum:
|
||||
> - frame:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _handle_exit(self, signum, frame):
|
||||
"""
|
||||
信号处理
|
||||
Args:
|
||||
signum:
|
||||
frame:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.info('Received signal, stopping all processes.')
|
||||
self.stop()
|
||||
sys.exit(0)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `restart(self, delay: int = 0)`
|
||||
|
||||
|
||||
|
||||
**说明**: 重启轻雪本体
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def restart(self, delay: int=0):
|
||||
"""
|
||||
重启轻雪本体
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if self.call_restart_count < 1:
|
||||
executable = sys.executable
|
||||
args = sys.argv
|
||||
logger.info('Restarting LiteyukiBot...')
|
||||
time.sleep(delay)
|
||||
if platform.system() == 'Windows':
|
||||
cmd = 'start'
|
||||
elif platform.system() == 'Linux':
|
||||
cmd = 'nohup'
|
||||
elif platform.system() == 'Darwin':
|
||||
cmd = 'open'
|
||||
else:
|
||||
cmd = 'nohup'
|
||||
self.process_manager.terminate_all()
|
||||
threading.Thread(target=os.system, args=(f"{cmd} {executable} {' '.join(args)}",)).start()
|
||||
sys.exit(0)
|
||||
self.call_restart_count += 1
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `restart_process(self, name: Optional[str] = None)`
|
||||
|
||||
|
||||
|
||||
**说明**: 停止轻雪
|
||||
|
||||
**参数**:
|
||||
> - name: 进程名称, 默认为None, 所有进程
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def restart_process(self, name: Optional[str]=None):
|
||||
"""
|
||||
停止轻雪
|
||||
Args:
|
||||
name: 进程名称, 默认为None, 所有进程
|
||||
Returns:
|
||||
"""
|
||||
self.lifespan.before_process_shutdown()
|
||||
self.lifespan.before_process_shutdown()
|
||||
if name is not None:
|
||||
chan_active = get_channel(f'{name}-active')
|
||||
chan_active.send(1)
|
||||
else:
|
||||
for process_name in self.process_manager.processes:
|
||||
chan_active = get_channel(f'{process_name}-active')
|
||||
chan_active.send(1)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `init(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 初始化轻雪, 自动调用
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def init(self, *args, **kwargs):
|
||||
"""
|
||||
初始化轻雪, 自动调用
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.init_logger()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `init_logger(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def init_logger(self):
|
||||
init_log(config=self.config)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `stop(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 停止轻雪
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def stop(self):
|
||||
"""
|
||||
停止轻雪
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stop_event.set()
|
||||
self.loop.stop()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_start(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册启动前的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动前的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_start(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_start(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册启动后的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_start(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_shutdown(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册停止后的函数:未实现
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册停止后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_shutdown(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_shutdown(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册进程停止前的函数,为子进程停止时调用
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程停止前的函数,为子进程停止时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_process_shutdown(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_restart(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册进程重启前的函数,为子进程重启时调用
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程重启前的函数,为子进程重启时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_process_restart(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_restart(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册重启后的函数:未实现
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_restart(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_nonebot_init(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册nonebot初始化后的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_nonebot_init(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册nonebot初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_nonebot_init(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_BOT_INSTANCE = NO_DEFAULT`
|
||||
|
||||
- **类型**: `LiteyukiBot`
|
||||
|
@ -1,423 +0,0 @@
|
||||
---
|
||||
title: liteyuki.bot.lifespan
|
||||
---
|
||||
### **class** `Lifespan`
|
||||
### *method* `__init__(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 轻雪生命周期管理,启动、停止、重启
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
轻雪生命周期管理,启动、停止、重启
|
||||
"""
|
||||
self.life_flag: int = 0
|
||||
self._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@staticmethod`
|
||||
### *method* `run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC]) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行函数
|
||||
|
||||
**参数**:
|
||||
> - funcs:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
def run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None:
|
||||
"""
|
||||
运行函数
|
||||
Args:
|
||||
funcs:
|
||||
Returns:
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
tasks = []
|
||||
for func in funcs:
|
||||
if is_coroutine_callable(func):
|
||||
tasks.append(func(*args, **kwargs))
|
||||
else:
|
||||
tasks.append(async_wrapper(func)(*args, **kwargs))
|
||||
loop.run_until_complete(asyncio.gather(*tasks))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册启动时的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_start_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册启动时的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_start_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册停止前的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止前的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_process_shutdown_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册停止后的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
|
||||
"""
|
||||
self._after_shutdown_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册重启时的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_process_restart_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册重启后的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
**返回**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启后的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_restart_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_nonebot_init(self, func)`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册 NoneBot 初始化后的函数
|
||||
|
||||
**参数**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_nonebot_init(self, func):
|
||||
"""
|
||||
注册 NoneBot 初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._after_nonebot_init_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_start(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 启动前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def before_start(self) -> None:
|
||||
"""
|
||||
启动前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_start functions')
|
||||
self.run_funcs(self._before_start_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_start(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 启动后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def after_start(self) -> None:
|
||||
"""
|
||||
启动后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running after_start functions')
|
||||
self.run_funcs(self._after_start_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_process_shutdown(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 停止前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def before_process_shutdown(self) -> None:
|
||||
"""
|
||||
停止前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_shutdown functions')
|
||||
self.run_funcs(self._before_process_shutdown_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_shutdown(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 停止后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def after_shutdown(self) -> None:
|
||||
"""
|
||||
停止后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running after_shutdown functions')
|
||||
self.run_funcs(self._after_shutdown_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_process_restart(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 重启前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def before_process_restart(self) -> None:
|
||||
"""
|
||||
重启前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_restart functions')
|
||||
self.run_funcs(self._before_process_restart_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_restart(self) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 重启后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def after_restart(self) -> None:
|
||||
"""
|
||||
重启后
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.debug('Running after_restart functions')
|
||||
self.run_funcs(self._after_restart_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `SYNC_LIFESPAN_FUNC = Callable[[], Any]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_LIFESPAN_FUNC = Callable[[], Awaitable[Any]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `LIFESPAN_FUNC = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `SYNC_PROCESS_LIFESPAN_FUNC = Callable[[str], Any]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_PROCESS_LIFESPAN_FUNC = Callable[[str], Awaitable[Any]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `PROCESS_LIFESPAN_FUNC = SYNC_PROCESS_LIFESPAN_FUNC | ASYNC_PROCESS_LIFESPAN_FUNC`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.comm
|
||||
---
|
@ -1,555 +0,0 @@
|
||||
---
|
||||
title: liteyuki.comm.channel
|
||||
---
|
||||
### *func* `set_channel()`
|
||||
|
||||
|
||||
|
||||
**说明**: 设置通道实例
|
||||
|
||||
**参数**:
|
||||
> - name: 通道名称
|
||||
> - channel: 通道实例
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def set_channel(name: str, channel: Channel):
|
||||
"""
|
||||
设置通道实例
|
||||
Args:
|
||||
name: 通道名称
|
||||
channel: 通道实例
|
||||
"""
|
||||
if not isinstance(channel, Channel):
|
||||
raise TypeError(f'channel_ must be an instance of Channel, {type(channel)} found')
|
||||
if IS_MAIN_PROCESS:
|
||||
_channel[name] = channel
|
||||
else:
|
||||
channel_deliver_passive_channel.send(('set_channel', {'name': name, 'channel_': channel}))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `set_channels()`
|
||||
|
||||
|
||||
|
||||
**说明**: 设置通道实例
|
||||
|
||||
**参数**:
|
||||
> - channels: 通道名称
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def set_channels(channels: dict[str, Channel]):
|
||||
"""
|
||||
设置通道实例
|
||||
Args:
|
||||
channels: 通道名称
|
||||
"""
|
||||
for name, channel in channels.items():
|
||||
set_channel(name, channel)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_channel() -> Channel`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取通道实例
|
||||
|
||||
**参数**:
|
||||
> - name: 通道名称
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_channel(name: str) -> Channel:
|
||||
"""
|
||||
获取通道实例
|
||||
Args:
|
||||
name: 通道名称
|
||||
Returns:
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return _channel[name]
|
||||
else:
|
||||
recv_chan = Channel[Channel[Any]]('recv_chan')
|
||||
channel_deliver_passive_channel.send(('get_channel', {'name': name, 'recv_chan': recv_chan}))
|
||||
return recv_chan.receive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_channels() -> dict[str, Channel]`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取通道实例
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_channels() -> dict[str, Channel]:
|
||||
"""
|
||||
获取通道实例
|
||||
Returns:
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return _channel
|
||||
else:
|
||||
recv_chan = Channel[dict[str, Channel[Any]]]('recv_chan')
|
||||
channel_deliver_passive_channel.send(('get_channels', {'recv_chan': recv_chan}))
|
||||
return recv_chan.receive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'set_channel')`
|
||||
### *func* `on_set_channel()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'set_channel')
|
||||
def on_set_channel(data: tuple[str, dict[str, Any]]):
|
||||
name, channel = (data[1]['name'], data[1]['channel_'])
|
||||
set_channel(name, channel)
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channel')`
|
||||
### *func* `on_get_channel()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channel')
|
||||
def on_get_channel(data: tuple[str, dict[str, Any]]):
|
||||
name, recv_chan = (data[1]['name'], data[1]['recv_chan'])
|
||||
recv_chan.send(get_channel(name))
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channels')`
|
||||
### *func* `on_get_channels()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channels')
|
||||
def on_get_channels(data: tuple[str, dict[str, Any]]):
|
||||
recv_chan = data[1]['recv_chan']
|
||||
recv_chan.send(get_channels())
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `Channel(Generic[T])`
|
||||
### *method* `__init__(self, _id: str = '', type_check: Optional[bool] = None)`
|
||||
|
||||
|
||||
|
||||
**说明**: 初始化通道
|
||||
|
||||
**参数**:
|
||||
> - _id: 通道ID
|
||||
> - type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, _id: str='', type_check: Optional[bool]=None):
|
||||
"""
|
||||
初始化通道
|
||||
Args:
|
||||
_id: 通道ID
|
||||
type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭
|
||||
"""
|
||||
self.conn_send, self.conn_recv = Pipe()
|
||||
self._closed = False
|
||||
self._on_main_receive_funcs: list[int] = []
|
||||
self._on_sub_receive_funcs: list[int] = []
|
||||
self.name: str = _id
|
||||
self.is_main_receive_loop_running = False
|
||||
self.is_sub_receive_loop_running = False
|
||||
if type_check is None:
|
||||
type_check = self._get_generic_type() is not None
|
||||
elif type_check:
|
||||
if self._get_generic_type() is None:
|
||||
raise TypeError('Type hint is required for enforcing type check.')
|
||||
self.type_check = type_check
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_get_generic_type(self) -> Optional[type]`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取通道传递泛型类型
|
||||
|
||||
|
||||
**返回**: Optional[type]: 泛型类型
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _get_generic_type(self) -> Optional[type]:
|
||||
"""
|
||||
获取通道传递泛型类型
|
||||
|
||||
Returns:
|
||||
Optional[type]: 泛型类型
|
||||
"""
|
||||
if hasattr(self, '__orig_class__'):
|
||||
return get_args(self.__orig_class__)[0]
|
||||
return None
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_validate_structure(self, data: Any, structure: type) -> bool`
|
||||
|
||||
|
||||
|
||||
**说明**: 验证数据结构
|
||||
|
||||
**参数**:
|
||||
> - data: 数据
|
||||
> - structure: 结构
|
||||
|
||||
**返回**: bool: 是否通过验证
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _validate_structure(self, data: Any, structure: type) -> bool:
|
||||
"""
|
||||
验证数据结构
|
||||
Args:
|
||||
data: 数据
|
||||
structure: 结构
|
||||
|
||||
Returns:
|
||||
bool: 是否通过验证
|
||||
"""
|
||||
if isinstance(structure, type):
|
||||
return isinstance(data, structure)
|
||||
elif isinstance(structure, tuple):
|
||||
if not isinstance(data, tuple) or len(data) != len(structure):
|
||||
return False
|
||||
return all((self._validate_structure(d, s) for d, s in zip(data, structure)))
|
||||
elif isinstance(structure, list):
|
||||
if not isinstance(data, list):
|
||||
return False
|
||||
return all((self._validate_structure(d, structure[0]) for d in data))
|
||||
elif isinstance(structure, dict):
|
||||
if not isinstance(data, dict):
|
||||
return False
|
||||
return all((k in data and self._validate_structure(data[k], structure[k]) for k in structure))
|
||||
return False
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `send(self, data: T)`
|
||||
|
||||
|
||||
|
||||
**说明**: 发送数据
|
||||
|
||||
**参数**:
|
||||
> - data: 数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def send(self, data: T):
|
||||
"""
|
||||
发送数据
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
if self.type_check:
|
||||
_type = self._get_generic_type()
|
||||
if _type is not None and (not self._validate_structure(data, _type)):
|
||||
raise TypeError(f'Data must be an instance of {_type}, {type(data)} found')
|
||||
if self._closed:
|
||||
raise RuntimeError('Cannot send to a closed channel_')
|
||||
self.conn_send.send(data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `receive(self) -> T`
|
||||
|
||||
|
||||
|
||||
**说明**: 接收数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def receive(self) -> T:
|
||||
"""
|
||||
接收数据
|
||||
Args:
|
||||
"""
|
||||
if self._closed:
|
||||
raise RuntimeError('Cannot receive from a closed channel_')
|
||||
while True:
|
||||
data = self.conn_recv.recv()
|
||||
return data
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `close(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 关闭通道
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def close(self):
|
||||
"""
|
||||
关闭通道
|
||||
"""
|
||||
self._closed = True
|
||||
self.conn_send.close()
|
||||
self.conn_recv.close()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_receive(self, filter_func: Optional[FILTER_FUNC] = None) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]`
|
||||
|
||||
|
||||
|
||||
**说明**: 接收数据并执行函数
|
||||
|
||||
**参数**:
|
||||
> - filter_func: 过滤函数,为None则不过滤
|
||||
|
||||
**返回**: 装饰器,装饰一个函数在接收到数据后执行
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_receive(self, filter_func: Optional[FILTER_FUNC]=None) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]:
|
||||
"""
|
||||
接收数据并执行函数
|
||||
Args:
|
||||
filter_func: 过滤函数,为None则不过滤
|
||||
Returns:
|
||||
装饰器,装饰一个函数在接收到数据后执行
|
||||
"""
|
||||
if not self.is_sub_receive_loop_running and (not IS_MAIN_PROCESS):
|
||||
threading.Thread(target=self._start_sub_receive_loop, daemon=True).start()
|
||||
if not self.is_main_receive_loop_running and IS_MAIN_PROCESS:
|
||||
threading.Thread(target=self._start_main_receive_loop, daemon=True).start()
|
||||
|
||||
def decorator(func: Callable[[T], Any]) -> Callable[[T], Any]:
|
||||
global _func_id
|
||||
|
||||
async def wrapper(data: T) -> Any:
|
||||
if filter_func is not None:
|
||||
if is_coroutine_callable(filter_func):
|
||||
if not await filter_func(data):
|
||||
return
|
||||
elif not filter_func(data):
|
||||
return
|
||||
if is_coroutine_callable(func):
|
||||
return await func(data)
|
||||
else:
|
||||
return func(data)
|
||||
_callback_funcs[_func_id] = wrapper
|
||||
if IS_MAIN_PROCESS:
|
||||
self._on_main_receive_funcs.append(_func_id)
|
||||
else:
|
||||
self._on_sub_receive_funcs.append(_func_id)
|
||||
_func_id += 1
|
||||
return func
|
||||
return decorator
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_run_on_main_receive_funcs(self, data: Any)`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行接收函数
|
||||
|
||||
**参数**:
|
||||
> - data: 数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _run_on_main_receive_funcs(self, data: Any):
|
||||
"""
|
||||
运行接收函数
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
for func_id in self._on_main_receive_funcs:
|
||||
func = _callback_funcs[func_id]
|
||||
run_coroutine(func(data))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_run_on_sub_receive_funcs(self, data: Any)`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行接收函数
|
||||
|
||||
**参数**:
|
||||
> - data: 数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _run_on_sub_receive_funcs(self, data: Any):
|
||||
"""
|
||||
运行接收函数
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
for func_id in self._on_sub_receive_funcs:
|
||||
func = _callback_funcs[func_id]
|
||||
run_coroutine(func(data))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_start_main_receive_loop(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 开始接收数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _start_main_receive_loop(self):
|
||||
"""
|
||||
开始接收数据
|
||||
"""
|
||||
self.is_main_receive_loop_running = True
|
||||
while not self._closed:
|
||||
data = self.conn_recv.recv()
|
||||
self._run_on_main_receive_funcs(data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_start_sub_receive_loop(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 开始接收数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _start_sub_receive_loop(self):
|
||||
"""
|
||||
开始接收数据
|
||||
"""
|
||||
self.is_sub_receive_loop_running = True
|
||||
while not self._closed:
|
||||
data = self.conn_recv.recv()
|
||||
self._run_on_sub_receive_funcs(data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `SYNC_ON_RECEIVE_FUNC = Callable[[T], Any]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_ON_RECEIVE_FUNC = Callable[[T], Coroutine[Any, Any, Any]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `ON_RECEIVE_FUNC = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `SYNC_FILTER_FUNC = Callable[[T], bool]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_FILTER_FUNC = Callable[[T], Coroutine[Any, Any, bool]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `FILTER_FUNC = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
### ***var*** `_func_id = 0`
|
||||
|
||||
- **类型**: `int`
|
||||
|
||||
### ***var*** `_channel = {}`
|
||||
|
||||
- **类型**: `dict[str, 'Channel']`
|
||||
|
||||
### ***var*** `_callback_funcs = {}`
|
||||
|
||||
- **类型**: `dict[int, ON_RECEIVE_FUNC]`
|
||||
|
||||
### ***var*** `active_channel = None`
|
||||
|
||||
- **类型**: `Optional['Channel']`
|
||||
|
||||
- **说明**: 子进程可用的主动和被动通道
|
||||
|
||||
### ***var*** `passive_channel = None`
|
||||
|
||||
- **类型**: `Optional['Channel']`
|
||||
|
||||
### ***var*** `publish_channel = Channel(_id='publish_channel')`
|
||||
|
||||
- **类型**: `Channel[tuple[str, dict[str, Any]]]`
|
||||
|
||||
### ***var*** `channel_deliver_active_channel = NO_DEFAULT`
|
||||
|
||||
- **类型**: `Channel[Channel[Any]]`
|
||||
|
||||
- **说明**: 通道传递通道,主进程创建单例,子进程初始化时实例化
|
||||
|
||||
### ***var*** `channel_deliver_passive_channel = NO_DEFAULT`
|
||||
|
||||
- **类型**: `Channel[tuple[str, dict[str, Any]]]`
|
||||
|
@ -1,17 +0,0 @@
|
||||
---
|
||||
title: liteyuki.comm.event
|
||||
---
|
||||
### **class** `Event`
|
||||
### *method* `__init__(self, name: str, data: dict[str, Any])`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, name: str, data: dict[str, Any]):
|
||||
self.name = name
|
||||
self.data = data
|
||||
```
|
||||
</details>
|
||||
|
@ -1,449 +0,0 @@
|
||||
---
|
||||
title: liteyuki.comm.storage
|
||||
---
|
||||
### `@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get')`
|
||||
### *func* `on_get()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get')
|
||||
def on_get(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]['key']
|
||||
default = data[1]['default']
|
||||
recv_chan = data[1]['recv_chan']
|
||||
recv_chan.send(shared_memory.get(key, default))
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'set')`
|
||||
### *func* `on_set()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'set')
|
||||
def on_set(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]['key']
|
||||
value = data[1]['value']
|
||||
shared_memory.set(key, value)
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'delete')`
|
||||
### *func* `on_delete()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'delete')
|
||||
def on_delete(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]['key']
|
||||
shared_memory.delete(key)
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get_all')`
|
||||
### *func* `on_get_all()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get_all')
|
||||
def on_get_all(data: tuple[str, dict[str, Any]]):
|
||||
recv_chan = data[1]['recv_chan']
|
||||
recv_chan.send(shared_memory.get_all())
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@channel.publish_channel.on_receive()`
|
||||
### *func* `on_publish()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@channel.publish_channel.on_receive()
|
||||
def on_publish(data: tuple[str, Any]):
|
||||
channel_, data = data
|
||||
shared_memory.run_subscriber_receive_funcs(channel_, data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `Subscriber`
|
||||
### *method* `__init__(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self):
|
||||
self._subscribers = {}
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `receive(self) -> Any`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def receive(self) -> Any:
|
||||
pass
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `unsubscribe(self) -> None`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def unsubscribe(self) -> None:
|
||||
pass
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `KeyValueStore`
|
||||
### *method* `__init__(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
self.active_chan = Channel[tuple[str, Optional[dict[str, Any]]]](_id='shared_memory-active')
|
||||
self.passive_chan = Channel[tuple[str, Optional[dict[str, Any]]]](_id='shared_memory-passive')
|
||||
self.publish_channel = Channel[tuple[str, Any]](_id='shared_memory-publish')
|
||||
self.is_main_receive_loop_running = False
|
||||
self.is_sub_receive_loop_running = False
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `set(self, key: str, value: Any) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 设置键值对
|
||||
|
||||
**参数**:
|
||||
> - key: 键
|
||||
> - value: 值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
设置键值对
|
||||
Args:
|
||||
key: 键
|
||||
value: 值
|
||||
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
self._store[key] = value
|
||||
else:
|
||||
self.passive_chan.send(('set', {'key': key, 'value': value}))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `get(self, key: str, default: Optional[Any] = None) -> Optional[Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取键值对
|
||||
|
||||
**参数**:
|
||||
> - key: 键
|
||||
> - default: 默认值
|
||||
|
||||
**返回**: Any: 值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get(self, key: str, default: Optional[Any]=None) -> Optional[Any]:
|
||||
"""
|
||||
获取键值对
|
||||
Args:
|
||||
key: 键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 值
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
return self._store.get(key, default)
|
||||
else:
|
||||
recv_chan = Channel[Optional[Any]]('recv_chan')
|
||||
self.passive_chan.send(('get', {'key': key, 'default': default, 'recv_chan': recv_chan}))
|
||||
return recv_chan.receive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `delete(self, key: str, ignore_key_error: bool = True) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 删除键值对
|
||||
|
||||
**参数**:
|
||||
> - key: 键
|
||||
> - ignore_key_error: 是否忽略键不存在的错误
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def delete(self, key: str, ignore_key_error: bool=True) -> None:
|
||||
"""
|
||||
删除键值对
|
||||
Args:
|
||||
key: 键
|
||||
ignore_key_error: 是否忽略键不存在的错误
|
||||
|
||||
Returns:
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
if key in self._store:
|
||||
try:
|
||||
del self._store[key]
|
||||
del _locks[key]
|
||||
except KeyError as e:
|
||||
if not ignore_key_error:
|
||||
raise e
|
||||
else:
|
||||
self.passive_chan.send(('delete', {'key': key}))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `get_all(self) -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取所有键值对
|
||||
|
||||
**返回**: dict[str, Any]: 键值对
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_all(self) -> dict[str, Any]:
|
||||
"""
|
||||
获取所有键值对
|
||||
Returns:
|
||||
dict[str, Any]: 键值对
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return self._store
|
||||
else:
|
||||
recv_chan = Channel[dict[str, Any]]('recv_chan')
|
||||
self.passive_chan.send(('get_all', {'recv_chan': recv_chan}))
|
||||
return recv_chan.receive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `publish(self, channel_: str, data: Any) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 发布消息
|
||||
|
||||
**参数**:
|
||||
> - channel_: 频道
|
||||
> - data: 数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def publish(self, channel_: str, data: Any) -> None:
|
||||
"""
|
||||
发布消息
|
||||
Args:
|
||||
channel_: 频道
|
||||
data: 数据
|
||||
|
||||
Returns:
|
||||
"""
|
||||
self.active_chan.send(('publish', {'channel': channel_, 'data': data}))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]`
|
||||
|
||||
|
||||
|
||||
**说明**: 订阅者接收消息时的回调
|
||||
|
||||
**参数**:
|
||||
> - channel_: 频道
|
||||
|
||||
**返回**: 装饰器
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
|
||||
"""
|
||||
订阅者接收消息时的回调
|
||||
Args:
|
||||
channel_: 频道
|
||||
|
||||
Returns:
|
||||
装饰器
|
||||
"""
|
||||
if IS_MAIN_PROCESS and (not self.is_main_receive_loop_running):
|
||||
threading.Thread(target=self._start_receive_loop, daemon=True).start()
|
||||
shared_memory.is_main_receive_loop_running = True
|
||||
elif not IS_MAIN_PROCESS and (not self.is_sub_receive_loop_running):
|
||||
threading.Thread(target=self._start_receive_loop, daemon=True).start()
|
||||
shared_memory.is_sub_receive_loop_running = True
|
||||
|
||||
def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC:
|
||||
|
||||
async def wrapper(data: Any):
|
||||
if is_coroutine_callable(func):
|
||||
await func(data)
|
||||
else:
|
||||
func(data)
|
||||
if IS_MAIN_PROCESS:
|
||||
if channel_ not in _on_main_subscriber_receive_funcs:
|
||||
_on_main_subscriber_receive_funcs[channel_] = []
|
||||
_on_main_subscriber_receive_funcs[channel_].append(wrapper)
|
||||
else:
|
||||
if channel_ not in _on_sub_subscriber_receive_funcs:
|
||||
_on_sub_subscriber_receive_funcs[channel_] = []
|
||||
_on_sub_subscriber_receive_funcs[channel_].append(wrapper)
|
||||
return wrapper
|
||||
return decorator
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@staticmethod`
|
||||
### *method* `run_subscriber_receive_funcs(channel_: str, data: Any)`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行订阅者接收函数
|
||||
|
||||
**参数**:
|
||||
> - channel_: 频道
|
||||
> - data: 数据
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
def run_subscriber_receive_funcs(channel_: str, data: Any):
|
||||
"""
|
||||
运行订阅者接收函数
|
||||
Args:
|
||||
channel_: 频道
|
||||
data: 数据
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
if channel_ in _on_main_subscriber_receive_funcs and _on_main_subscriber_receive_funcs[channel_]:
|
||||
run_coroutine_in_thread(*[func(data) for func in _on_main_subscriber_receive_funcs[channel_]])
|
||||
elif channel_ in _on_sub_subscriber_receive_funcs and _on_sub_subscriber_receive_funcs[channel_]:
|
||||
run_coroutine_in_thread(*[func(data) for func in _on_sub_subscriber_receive_funcs[channel_]])
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_start_receive_loop(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 启动发布订阅接收器循环,在主进程中运行,若有子进程订阅则推送给子进程
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def _start_receive_loop(self):
|
||||
"""
|
||||
启动发布订阅接收器循环,在主进程中运行,若有子进程订阅则推送给子进程
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
while True:
|
||||
data = self.active_chan.receive()
|
||||
if data[0] == 'publish':
|
||||
self.run_subscriber_receive_funcs(data[1]['channel'], data[1]['data'])
|
||||
self.publish_channel.send(data)
|
||||
else:
|
||||
while True:
|
||||
data = self.publish_channel.receive()
|
||||
if data[0] == 'publish':
|
||||
self.run_subscriber_receive_funcs(data[1]['channel'], data[1]['data'])
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `GlobalKeyValueStore`
|
||||
### `@classmethod`
|
||||
### *method* `get_instance(cls)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = KeyValueStore()
|
||||
return cls._instance
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_on_main_subscriber_receive_funcs = {}`
|
||||
|
||||
- **类型**: `dict[str, list[ASYNC_ON_RECEIVE_FUNC]]`
|
||||
|
||||
- **说明**: 主进程订阅者接收函数
|
||||
|
||||
### ***var*** `_on_sub_subscriber_receive_funcs = {}`
|
||||
|
||||
- **类型**: `dict[str, list[ASYNC_ON_RECEIVE_FUNC]]`
|
||||
|
||||
- **说明**: 子进程订阅者接收函数
|
||||
|
||||
### ***var*** `shared_memory = GlobalKeyValueStore.get_instance()`
|
||||
|
||||
- **类型**: `KeyValueStore`
|
||||
|
@ -1,196 +0,0 @@
|
||||
---
|
||||
title: liteyuki.config
|
||||
---
|
||||
### *func* `flat_config() -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 扁平化配置文件
|
||||
|
||||
{a:{b:{c:1}}} -> {"a.b.c": 1}
|
||||
|
||||
**参数**:
|
||||
> - config: 配置项目
|
||||
|
||||
**返回**: 扁平化后的配置文件,但也包含原有的键值对
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def flat_config(config: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
扁平化配置文件
|
||||
|
||||
{a:{b:{c:1}}} -> {"a.b.c": 1}
|
||||
Args:
|
||||
config: 配置项目
|
||||
|
||||
Returns:
|
||||
扁平化后的配置文件,但也包含原有的键值对
|
||||
"""
|
||||
new_config = copy.deepcopy(config)
|
||||
for key, value in config.items():
|
||||
if isinstance(value, dict):
|
||||
for k, v in flat_config(value).items():
|
||||
new_config[f'{key}.{k}'] = v
|
||||
return new_config
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_from_yaml() -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: Load config from yaml file
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_from_yaml(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from yaml file
|
||||
|
||||
"""
|
||||
logger.debug(f'Loading YAML config from {file_}')
|
||||
config = yaml.safe_load(open(file_, 'r', encoding='utf-8'))
|
||||
return flat_config(config if config is not None else {})
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_from_json() -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: Load config from json file
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_from_json(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from json file
|
||||
"""
|
||||
logger.debug(f'Loading JSON config from {file_}')
|
||||
config = json.load(open(file_, 'r', encoding='utf-8'))
|
||||
return flat_config(config if config is not None else {})
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_from_toml() -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: Load config from toml file
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_from_toml(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from toml file
|
||||
"""
|
||||
logger.debug(f'Loading TOML config from {file_}')
|
||||
config = toml.load(open(file_, 'r', encoding='utf-8'))
|
||||
return flat_config(config if config is not None else {})
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_from_files(*, no_warning: bool = False) -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 从指定文件加载配置项,会自动识别文件格式
|
||||
默认执行扁平化选项
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_from_files(*files: str, no_warning: bool=False) -> dict[str, Any]:
|
||||
"""
|
||||
从指定文件加载配置项,会自动识别文件格式
|
||||
默认执行扁平化选项
|
||||
"""
|
||||
config = {}
|
||||
for file in files:
|
||||
if os.path.exists(file):
|
||||
if file.endswith(('.yaml', 'yml')):
|
||||
config.update(load_from_yaml(file))
|
||||
elif file.endswith('.json'):
|
||||
config.update(load_from_json(file))
|
||||
elif file.endswith('.toml'):
|
||||
config.update(load_from_toml(file))
|
||||
elif not no_warning:
|
||||
logger.warning(f'Unsupported config file format: {file}')
|
||||
elif not no_warning:
|
||||
logger.warning(f'Config file not found: {file}')
|
||||
return config
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_configs_from_dirs(*, no_waring: bool = False) -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 从目录下加载配置文件,不递归
|
||||
按照读取文件的优先级反向覆盖
|
||||
默认执行扁平化选项
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_configs_from_dirs(*directories: str, no_waring: bool=False) -> dict[str, Any]:
|
||||
"""
|
||||
从目录下加载配置文件,不递归
|
||||
按照读取文件的优先级反向覆盖
|
||||
默认执行扁平化选项
|
||||
"""
|
||||
config = {}
|
||||
for directory in directories:
|
||||
if not os.path.exists(directory):
|
||||
if not no_waring:
|
||||
logger.warning(f'Directory not found: {directory}')
|
||||
continue
|
||||
for file in os.listdir(directory):
|
||||
if file.endswith(_SUPPORTED_CONFIG_FORMATS):
|
||||
config.update(load_from_files(os.path.join(directory, file), no_warning=no_waring))
|
||||
return config
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_config_in_default(no_waring: bool = False) -> dict[str, Any]`
|
||||
|
||||
|
||||
|
||||
**说明**: 从一个标准的轻雪项目加载配置文件
|
||||
项目目录下的config.*和config目录下的所有配置文件
|
||||
项目目录下的配置文件优先
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_config_in_default(no_waring: bool=False) -> dict[str, Any]:
|
||||
"""
|
||||
从一个标准的轻雪项目加载配置文件
|
||||
项目目录下的config.*和config目录下的所有配置文件
|
||||
项目目录下的配置文件优先
|
||||
"""
|
||||
config = load_configs_from_dirs('config', no_waring=no_waring)
|
||||
config.update(load_from_files('config.yaml', 'config.toml', 'config.json', 'config.yml', no_warning=no_waring))
|
||||
return config
|
||||
```
|
||||
</details>
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.core
|
||||
---
|
@ -1,235 +0,0 @@
|
||||
---
|
||||
title: liteyuki.core.manager
|
||||
---
|
||||
### **class** `ChannelDeliver`
|
||||
### *method* `__init__(self, active: Channel[Any], passive: Channel[Any], channel_deliver_active: Channel[Channel[Any]], channel_deliver_passive: Channel[tuple[str, dict]], publish: Channel[tuple[str, Any]])`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, active: Channel[Any], passive: Channel[Any], channel_deliver_active: Channel[Channel[Any]], channel_deliver_passive: Channel[tuple[str, dict]], publish: Channel[tuple[str, Any]]):
|
||||
self.active = active
|
||||
self.passive = passive
|
||||
self.channel_deliver_active = channel_deliver_active
|
||||
self.channel_deliver_passive = channel_deliver_passive
|
||||
self.publish = publish
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `ProcessManager`
|
||||
### *method* `__init__(self, lifespan: Lifespan)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, lifespan: 'Lifespan'):
|
||||
self.lifespan = lifespan
|
||||
self.targets: dict[str, tuple[Callable, tuple, dict]] = {}
|
||||
self.processes: dict[str, Process] = {}
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `start(self, name: str)`
|
||||
|
||||
|
||||
|
||||
**说明**: 开启后自动监控进程,并添加到进程字典中
|
||||
|
||||
**参数**:
|
||||
> - name:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def start(self, name: str):
|
||||
"""
|
||||
开启后自动监控进程,并添加到进程字典中
|
||||
Args:
|
||||
name:
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.targets:
|
||||
raise KeyError(f'Process {name} not found.')
|
||||
chan_active = get_channel(f'{name}-active')
|
||||
|
||||
def _start_process():
|
||||
process = Process(target=self.targets[name][0], args=self.targets[name][1], kwargs=self.targets[name][2], daemon=True)
|
||||
self.processes[name] = process
|
||||
process.start()
|
||||
_start_process()
|
||||
while True:
|
||||
data = chan_active.receive()
|
||||
if data == 0:
|
||||
logger.info(f'Stopping process {name}')
|
||||
self.lifespan.before_process_shutdown()
|
||||
self.terminate(name)
|
||||
break
|
||||
elif data == 1:
|
||||
logger.info(f'Restarting process {name}')
|
||||
self.lifespan.before_process_shutdown()
|
||||
self.lifespan.before_process_restart()
|
||||
self.terminate(name)
|
||||
_start_process()
|
||||
continue
|
||||
else:
|
||||
logger.warning('Unknown data received, ignored.')
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `start_all(self)`
|
||||
|
||||
|
||||
|
||||
**说明**: 启动所有进程
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def start_all(self):
|
||||
"""
|
||||
启动所有进程
|
||||
"""
|
||||
for name in self.targets:
|
||||
threading.Thread(target=self.start, args=(name,), daemon=True).start()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `add_target(self, name: str, target: TARGET_FUNC, args: tuple = (), kwargs = None)`
|
||||
|
||||
|
||||
|
||||
**说明**: 添加进程
|
||||
|
||||
**参数**:
|
||||
> - name: 进程名,用于获取和唯一标识
|
||||
> - target: 进程函数
|
||||
> - args: 进程函数参数
|
||||
> - kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def add_target(self, name: str, target: TARGET_FUNC, args: tuple=(), kwargs=None):
|
||||
"""
|
||||
添加进程
|
||||
Args:
|
||||
name: 进程名,用于获取和唯一标识
|
||||
target: 进程函数
|
||||
args: 进程函数参数
|
||||
kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive
|
||||
"""
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
chan_active: Channel = Channel(_id=f'{name}-active')
|
||||
chan_passive: Channel = Channel(_id=f'{name}-passive')
|
||||
channel_deliver = ChannelDeliver(active=chan_active, passive=chan_passive, channel_deliver_active=channel_deliver_active_channel, channel_deliver_passive=channel_deliver_passive_channel, publish=publish_channel)
|
||||
self.targets[name] = (_delivery_channel_wrapper, (target, channel_deliver, shared_memory, *args), kwargs)
|
||||
set_channels({f'{name}-active': chan_active, f'{name}-passive': chan_passive})
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `join_all(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def join_all(self):
|
||||
for name, process in self.targets:
|
||||
process.join()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `terminate(self, name: str)`
|
||||
|
||||
|
||||
|
||||
**说明**: 终止进程并从进程字典中删除
|
||||
|
||||
**参数**:
|
||||
> - name:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def terminate(self, name: str):
|
||||
"""
|
||||
终止进程并从进程字典中删除
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.processes:
|
||||
logger.warning(f'Process {name} not found.')
|
||||
return
|
||||
process = self.processes[name]
|
||||
process.terminate()
|
||||
process.join(TIMEOUT)
|
||||
if process.is_alive():
|
||||
process.kill()
|
||||
logger.success(f'Process {name} terminated.')
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `terminate_all(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def terminate_all(self):
|
||||
for name in self.targets:
|
||||
self.terminate(name)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `is_process_alive(self, name: str) -> bool`
|
||||
|
||||
|
||||
|
||||
**说明**: 检查进程是否存活
|
||||
|
||||
**参数**:
|
||||
> - name:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def is_process_alive(self, name: str) -> bool:
|
||||
"""
|
||||
检查进程是否存活
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.targets:
|
||||
logger.warning(f'Process {name} not found.')
|
||||
return self.processes[name].is_alive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `TARGET_FUNC = Callable[..., Any]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.dev
|
||||
---
|
@ -1,152 +0,0 @@
|
||||
---
|
||||
title: liteyuki.dev.observer
|
||||
---
|
||||
### *func* `debounce()`
|
||||
|
||||
|
||||
|
||||
**说明**: 防抖函数
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def debounce(wait):
|
||||
"""
|
||||
防抖函数
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal last_call_time
|
||||
current_time = time.time()
|
||||
if current_time - last_call_time > wait:
|
||||
last_call_time = current_time
|
||||
return func(*args, **kwargs)
|
||||
last_call_time = None
|
||||
return wrapper
|
||||
return decorator
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `on_file_system_event(directories: tuple[str] = True, recursive: bool = None) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]`
|
||||
|
||||
|
||||
|
||||
**说明**: 注册文件系统变化监听器
|
||||
|
||||
**参数**:
|
||||
> - directories: 监听目录们
|
||||
> - recursive: 是否递归监听子目录
|
||||
> - event_filter: 事件过滤器, 返回True则执行回调函数
|
||||
|
||||
**返回**: 装饰器,装饰一个函数在接收到数据后执行
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_file_system_event(directories: tuple[str], recursive: bool=True, event_filter: FILTER_FUNC=None) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]:
|
||||
"""
|
||||
注册文件系统变化监听器
|
||||
Args:
|
||||
directories: 监听目录们
|
||||
recursive: 是否递归监听子目录
|
||||
event_filter: 事件过滤器, 返回True则执行回调函数
|
||||
Returns:
|
||||
装饰器,装饰一个函数在接收到数据后执行
|
||||
"""
|
||||
|
||||
def decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC:
|
||||
|
||||
def wrapper(event: FileSystemEvent):
|
||||
if event_filter is not None and (not event_filter(event)):
|
||||
return
|
||||
func(event)
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
code_modified_handler.on_modified = wrapper
|
||||
for directory in directories:
|
||||
observer.schedule(code_modified_handler, directory, recursive=recursive)
|
||||
return func
|
||||
return decorator
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `CodeModifiedHandler(FileSystemEventHandler)`
|
||||
### `@debounce(1)`
|
||||
### *method* `on_modified(self, event)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@debounce(1)
|
||||
def on_modified(self, event):
|
||||
raise NotImplementedError('on_modified must be implemented')
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_created(self, event)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_created(self, event):
|
||||
self.on_modified(event)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_deleted(self, event)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_deleted(self, event):
|
||||
self.on_modified(event)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_moved(self, event)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_moved(self, event):
|
||||
self.on_modified(event)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_any_event(self, event)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_any_event(self, event):
|
||||
self.on_modified(event)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `CALLBACK_FUNC = Callable[[FileSystemEvent], None]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
- **说明**: 位置1为FileSystemEvent
|
||||
|
||||
### ***var*** `FILTER_FUNC = Callable[[FileSystemEvent], bool]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
- **说明**: 位置1为FileSystemEvent
|
||||
|
@ -1,32 +0,0 @@
|
||||
---
|
||||
title: liteyuki.dev.plugin
|
||||
---
|
||||
### *func* `run_plugins()`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行插件,无需手动初始化bot
|
||||
|
||||
**参数**:
|
||||
> - module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def run_plugins(*module_path: str | Path):
|
||||
"""
|
||||
运行插件,无需手动初始化bot
|
||||
Args:
|
||||
module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名
|
||||
"""
|
||||
cfg = load_config_in_default()
|
||||
plugins = cfg.get('liteyuki.plugins', [])
|
||||
plugins.extend(module_path)
|
||||
cfg['liteyuki.plugins'] = plugins
|
||||
bot = LiteyukiBot(**cfg)
|
||||
bot.run()
|
||||
```
|
||||
</details>
|
||||
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: liteyuki.exception
|
||||
---
|
||||
### **class** `LiteyukiException(BaseException)`
|
@ -1,54 +0,0 @@
|
||||
---
|
||||
title: liteyuki.log
|
||||
---
|
||||
### *func* `get_format() -> str`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_format(level: str) -> str:
|
||||
if level == 'DEBUG':
|
||||
return debug_format
|
||||
else:
|
||||
return default_format
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `init_log()`
|
||||
|
||||
|
||||
|
||||
**说明**: 在语言加载完成后执行
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def init_log(config: dict):
|
||||
"""
|
||||
在语言加载完成后执行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.remove()
|
||||
logger.add(sys.stdout, level=0, diagnose=False, format=get_format(config.get('log_level', 'INFO')))
|
||||
show_icon = config.get('log_icon', True)
|
||||
logger.level('DEBUG', color='<blue>', icon=f"{('🐛' if show_icon else '')}DEBUG")
|
||||
logger.level('INFO', color='<normal>', icon=f"{('ℹ️' if show_icon else '')}INFO")
|
||||
logger.level('SUCCESS', color='<green>', icon=f"{('✅' if show_icon else '')}SUCCESS")
|
||||
logger.level('WARNING', color='<yellow>', icon=f"{('⚠️' if show_icon else '')}WARNING")
|
||||
logger.level('ERROR', color='<red>', icon=f"{('⭕' if show_icon else '')}ERROR")
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `debug_format = '<c>{time:YYYY-MM-DD HH:mm:ss}</c> <lvl>[{level.icon}]</lvl> <c><{name}.{module}.{function}:{line}></c> {message}'`
|
||||
|
||||
- **类型**: `str`
|
||||
|
||||
### ***var*** `default_format = '<c>{time:MM-DD HH:mm:ss}</c> <lvl>[{level.icon}]</lvl> <c><{name}></c> {message}'`
|
||||
|
||||
- **类型**: `str`
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message
|
||||
---
|
@ -1,71 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message.event
|
||||
---
|
||||
### **class** `MessageEvent`
|
||||
### *method* `__init__(self, bot_id: str, message: list[dict[str, Any]] | str, message_type: str, raw_message: str, session_id: str, user_id: str, session_type: str, receive_channel: str, data: Optional[dict[str, Any]] = None)`
|
||||
|
||||
|
||||
|
||||
**说明**: 轻雪抽象消息事件
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, bot_id: str, message: list[dict[str, Any]] | str, message_type: str, raw_message: str, session_id: str, user_id: str, session_type: str, receive_channel: str, data: Optional[dict[str, Any]]=None):
|
||||
"""
|
||||
轻雪抽象消息事件
|
||||
Args:
|
||||
|
||||
bot_id: 机器人ID
|
||||
message: 消息,消息段数组[{type: str, data: dict[str, Any]}]
|
||||
raw_message: 原始消息(通常为纯文本的格式)
|
||||
message_type: 消息类型(private, group, other)
|
||||
|
||||
session_id: 会话ID(私聊通常为用户ID,群聊通常为群ID)
|
||||
session_type: 会话类型(private, group)
|
||||
receive_channel: 接收频道(用于回复消息)
|
||||
|
||||
data: 附加数据
|
||||
"""
|
||||
if data is None:
|
||||
data = {}
|
||||
self.message_type = message_type
|
||||
self.data = data
|
||||
self.bot_id = bot_id
|
||||
self.message = message
|
||||
self.raw_message = raw_message
|
||||
self.session_id = session_id
|
||||
self.session_type = session_type
|
||||
self.user_id = user_id
|
||||
self.receive_channel = receive_channel
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `reply(self, message: str | dict[str, Any])`
|
||||
|
||||
|
||||
|
||||
**说明**: 回复消息
|
||||
|
||||
**参数**:
|
||||
> - message:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def reply(self, message: str | dict[str, Any]):
|
||||
"""
|
||||
回复消息
|
||||
Args:
|
||||
message:
|
||||
Returns:
|
||||
"""
|
||||
reply_event = MessageEvent(message_type=self.session_type, message=message, raw_message='', data={'message': message}, bot_id=self.bot_id, session_id=self.session_id, session_type=self.session_type, receive_channel='_')
|
||||
shared_memory.publish(self.receive_channel, reply_event)
|
||||
```
|
||||
</details>
|
||||
|
@ -1,97 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message.matcher
|
||||
---
|
||||
### **class** `Matcher`
|
||||
### *method* `__init__(self, rule: Rule, priority: int, block: bool)`
|
||||
|
||||
|
||||
|
||||
**说明**: 匹配器
|
||||
|
||||
**参数**:
|
||||
> - rule: 规则
|
||||
> - priority: 优先级 >= 0
|
||||
> - block: 是否阻断后续优先级更低的匹配器
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, rule: Rule, priority: int, block: bool):
|
||||
"""
|
||||
匹配器
|
||||
Args:
|
||||
rule: 规则
|
||||
priority: 优先级 >= 0
|
||||
block: 是否阻断后续优先级更低的匹配器
|
||||
"""
|
||||
self.rule = rule
|
||||
self.priority = priority
|
||||
self.block = block
|
||||
self.handlers: list[EventHandler] = []
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `handle(self) -> Callable[[EventHandler], EventHandler]`
|
||||
|
||||
|
||||
|
||||
**说明**: 添加处理函数,装饰器
|
||||
|
||||
**返回**: 装饰器 handler
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def handle(self) -> Callable[[EventHandler], EventHandler]:
|
||||
"""
|
||||
添加处理函数,装饰器
|
||||
Returns:
|
||||
装饰器 handler
|
||||
"""
|
||||
|
||||
def decorator(handler: EventHandler) -> EventHandler:
|
||||
self.handlers.append(handler)
|
||||
return handler
|
||||
return decorator
|
||||
```
|
||||
</details>
|
||||
|
||||
### *async method* `run(self, event: MessageEvent) -> None`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行处理函数
|
||||
|
||||
**参数**:
|
||||
> - event:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
async def run(self, event: MessageEvent) -> None:
|
||||
"""
|
||||
运行处理函数
|
||||
Args:
|
||||
event:
|
||||
Returns:
|
||||
"""
|
||||
if not await self.rule(event):
|
||||
return
|
||||
for handler in self.handlers:
|
||||
try:
|
||||
await handler(event)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `EventHandler = Callable[[MessageEvent], Coroutine[None, None, Any]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
@ -1,46 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message.on
|
||||
---
|
||||
### *func* `on_message(rule: Rule = empty_rule, priority: int = 0, block: bool = False) -> Matcher`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_message(rule: Rule=empty_rule, priority: int=0, block: bool=False) -> Matcher:
|
||||
matcher = Matcher(rule, priority, block)
|
||||
for i, m in enumerate(_matcher_list):
|
||||
if m.priority < matcher.priority:
|
||||
_matcher_list.insert(i, matcher)
|
||||
break
|
||||
else:
|
||||
_matcher_list.append(matcher)
|
||||
return matcher
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `on_keywords(keywords: list[str] = empty_rule, rule = 0, priority: int = False) -> Matcher`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def on_keywords(keywords: list[str], rule=empty_rule, priority: int=0, block: bool=False) -> Matcher:
|
||||
|
||||
@Rule
|
||||
async def on_keywords_rule(event: MessageEvent):
|
||||
return any((keyword in event.raw_message for keyword in keywords))
|
||||
return on_message(on_keywords_rule & rule, priority, block)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_matcher_list = []`
|
||||
|
||||
- **类型**: `list[Matcher]`
|
||||
|
||||
### ***var*** `_queue = Queue()`
|
||||
|
||||
- **类型**: `Queue`
|
||||
|
@ -1,98 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message.rule
|
||||
---
|
||||
### `@Rule`
|
||||
### *async func* `empty_rule() -> bool`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@Rule
|
||||
async def empty_rule(event: MessageEvent) -> bool:
|
||||
return True
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@Rule`
|
||||
### *async func* `is_su_rule() -> bool`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@Rule
|
||||
async def is_su_rule(event: MessageEvent) -> bool:
|
||||
return str(event.user_id) in _superusers
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `Rule`
|
||||
### *method* `__init__(self, handler: RuleHandlerFunc)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, handler: RuleHandlerFunc):
|
||||
self.handler = handler
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `__or__(self, other: Rule) -> Rule`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __or__(self, other: 'Rule') -> 'Rule':
|
||||
|
||||
async def combined_handler(event: MessageEvent) -> bool:
|
||||
return await self.handler(event) or await other.handler(event)
|
||||
return Rule(combined_handler)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `__and__(self, other: Rule) -> Rule`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __and__(self, other: 'Rule') -> 'Rule':
|
||||
|
||||
async def combined_handler(event: MessageEvent) -> bool:
|
||||
return await self.handler(event) and await other.handler(event)
|
||||
return Rule(combined_handler)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *async method* `__call__(self, event: MessageEvent) -> bool`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
async def __call__(self, event: MessageEvent) -> bool:
|
||||
if self.handler is None:
|
||||
return True
|
||||
return await self.handler(event)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_superusers = get_config('liteyuki.superusers', [])`
|
||||
|
||||
- **类型**: `list[str]`
|
||||
|
||||
### ***var*** `RuleHandlerFunc = Callable[[MessageEvent], Coroutine[None, None, bool]]`
|
||||
|
||||
- **类型**: `TypeAlias`
|
||||
|
||||
- **说明**: 规则函数签名
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.message.session
|
||||
---
|
@ -1,282 +0,0 @@
|
||||
---
|
||||
title: liteyuki.mkdoc
|
||||
---
|
||||
### *func* `get_relative_path() -> str`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取相对路径
|
||||
|
||||
**参数**:
|
||||
> - base_path: 基础路径
|
||||
> - target_path: 目标路径
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_relative_path(base_path: str, target_path: str) -> str:
|
||||
"""
|
||||
获取相对路径
|
||||
Args:
|
||||
base_path: 基础路径
|
||||
target_path: 目标路径
|
||||
"""
|
||||
return os.path.relpath(target_path, base_path)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `write_to_files()`
|
||||
|
||||
|
||||
|
||||
**说明**: 输出文件
|
||||
|
||||
**参数**:
|
||||
> - file_data: 文件数据 相对路径
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def write_to_files(file_data: dict[str, str]):
|
||||
"""
|
||||
输出文件
|
||||
Args:
|
||||
file_data: 文件数据 相对路径
|
||||
"""
|
||||
for rp, data in file_data.items():
|
||||
if not os.path.exists(os.path.dirname(rp)):
|
||||
os.makedirs(os.path.dirname(rp))
|
||||
with open(rp, 'w', encoding='utf-8') as f:
|
||||
f.write(data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_file_list()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_file_list(module_folder: str):
|
||||
file_list = []
|
||||
for root, dirs, files in os.walk(module_folder):
|
||||
for file in files:
|
||||
if file.endswith(('.py', '.pyi')):
|
||||
file_list.append(os.path.join(root, file))
|
||||
return file_list
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_module_info_normal(file_path: str = True) -> ModuleInfo`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取函数和类
|
||||
|
||||
**参数**:
|
||||
> - file_path: Python 文件路径
|
||||
> - ignore_private: 忽略私有函数和类
|
||||
|
||||
**返回**: 模块信息
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_module_info_normal(file_path: str, ignore_private: bool=True) -> ModuleInfo:
|
||||
"""
|
||||
获取函数和类
|
||||
Args:
|
||||
file_path: Python 文件路径
|
||||
ignore_private: 忽略私有函数和类
|
||||
Returns:
|
||||
模块信息
|
||||
"""
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
file_content = file.read()
|
||||
tree = ast.parse(file_content)
|
||||
dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '')
|
||||
module_docstring = ast.get_docstring(tree)
|
||||
module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '')
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
if not any((isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(node))) and (not ignore_private or not node.name.startswith('_')):
|
||||
if node.args.args:
|
||||
first_arg = node.args.args[0]
|
||||
if first_arg.arg in ('self', 'cls'):
|
||||
continue
|
||||
function_docstring = ast.get_docstring(node)
|
||||
func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node))
|
||||
module_info.functions.append(func_info)
|
||||
elif isinstance(node, ast.ClassDef):
|
||||
class_docstring = ast.get_docstring(node)
|
||||
class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases])
|
||||
for class_node in node.body:
|
||||
if isinstance(class_node, ast.FunctionDef) and (not ignore_private or not class_node.name.startswith('_') or class_node.name == '__init__'):
|
||||
method_docstring = ast.get_docstring(class_node)
|
||||
def_type = DefType.METHOD
|
||||
if class_node.decorator_list:
|
||||
if any((isinstance(decorator, ast.Name) and decorator.id == 'staticmethod' for decorator in class_node.decorator_list)):
|
||||
def_type = DefType.STATIC_METHOD
|
||||
elif any((isinstance(decorator, ast.Name) and decorator.id == 'classmethod' for decorator in class_node.decorator_list)):
|
||||
def_type = DefType.CLASS_METHOD
|
||||
elif any((isinstance(decorator, ast.Name) and decorator.id == 'property' for decorator in class_node.decorator_list)):
|
||||
def_type = DefType.PROPERTY
|
||||
class_info.methods.append(FunctionInfo(name=class_node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in class_node.args.args], return_type=ast.unparse(class_node.returns) if class_node.returns else 'None', docstring=method_docstring if method_docstring else '', type=def_type, is_async=isinstance(class_node, ast.AsyncFunctionDef), source_code=ast.unparse(class_node)))
|
||||
elif isinstance(class_node, ast.Assign):
|
||||
for target in class_node.targets:
|
||||
if isinstance(target, ast.Name):
|
||||
class_info.attributes.append(AttributeInfo(name=target.id, type=ast.unparse(class_node.value)))
|
||||
module_info.classes.append(class_info)
|
||||
elif isinstance(node, ast.Assign):
|
||||
if not any((isinstance(parent, (ast.ClassDef, ast.FunctionDef)) for parent in ast.iter_child_nodes(node))):
|
||||
for target in node.targets:
|
||||
if isinstance(target, ast.Name) and (not ignore_private or not target.id.startswith('_')):
|
||||
attr_type = NO_TYPE_HINT
|
||||
if isinstance(node.value, ast.AnnAssign) and node.value.annotation:
|
||||
attr_type = ast.unparse(node.value.annotation)
|
||||
module_info.attributes.append(AttributeInfo(name=target.id, type=attr_type, value=ast.unparse(node.value) if node.value else None))
|
||||
return module_info
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `generate_markdown(module_info: ModuleInfo = None, front_matter = 'zh-CN') -> str`
|
||||
|
||||
|
||||
|
||||
**说明**: 生成模块的Markdown
|
||||
你可在此自定义生成的Markdown格式
|
||||
|
||||
**参数**:
|
||||
> - module_info: 模块信息
|
||||
> - front_matter: 自定义选项title, index, icon, category
|
||||
> - lang: 语言
|
||||
|
||||
**返回**: Markdown 字符串
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def generate_markdown(module_info: ModuleInfo, front_matter=None, lang: str='zh-CN') -> str:
|
||||
"""
|
||||
生成模块的Markdown
|
||||
你可在此自定义生成的Markdown格式
|
||||
Args:
|
||||
module_info: 模块信息
|
||||
front_matter: 自定义选项title, index, icon, category
|
||||
lang: 语言
|
||||
Returns:
|
||||
Markdown 字符串
|
||||
"""
|
||||
content = ''
|
||||
front_matter = '---\n' + '\n'.join([f'{k}: {v}' for k, v in front_matter.items()]) + '\n---\n\n'
|
||||
content += front_matter
|
||||
for func in module_info.functions:
|
||||
args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args]
|
||||
content += f"### ***{('async ' if func.is_async else '')}def*** `{func.name}({', '.join(args_with_type)}) -> {func.return_type}`\n\n"
|
||||
func.docstring = func.docstring.replace('\n', '\n\n')
|
||||
content += f'{func.docstring}\n\n'
|
||||
content += f'<details>\n<summary>源代码</summary>\n\n```python\n{func.source_code}\n```\n</details>\n\n'
|
||||
for cls in module_info.classes:
|
||||
if cls.inherit:
|
||||
inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ''
|
||||
content += f'### ***class*** `{cls.name}{inherit}`\n\n'
|
||||
else:
|
||||
content += f'### ***class*** `{cls.name}`\n\n'
|
||||
cls.docstring = cls.docstring.replace('\n', '\n\n')
|
||||
content += f'{cls.docstring}\n\n'
|
||||
for method in cls.methods:
|
||||
if method.type != DefType.METHOD:
|
||||
args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args]
|
||||
content += f'###   ***@{method.type.value}***\n'
|
||||
else:
|
||||
args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args]
|
||||
content += f"###   ***{('async ' if method.is_async else '')}def*** `{method.name}({', '.join(args_with_type)}) -> {method.return_type}`\n\n"
|
||||
method.docstring = method.docstring.replace('\n', '\n\n')
|
||||
content += f' {method.docstring}\n\n'
|
||||
if lang == 'zh-CN':
|
||||
TEXT_SOURCE_CODE = '源代码'
|
||||
else:
|
||||
TEXT_SOURCE_CODE = 'Source Code'
|
||||
content += f'<details>\n<summary>{TEXT_SOURCE_CODE}</summary>\n\n```python\n{method.source_code}\n```\n</details>\n\n'
|
||||
for attr in cls.attributes:
|
||||
content += f'###   ***attr*** `{attr.name}: {attr.type}`\n\n'
|
||||
for attr in module_info.attributes:
|
||||
if attr.type == NO_TYPE_HINT:
|
||||
content += f'### ***var*** `{attr.name} = {attr.value}`\n\n'
|
||||
else:
|
||||
content += f'### ***var*** `{attr.name}: {attr.type} = {attr.value}`\n\n'
|
||||
attr.docstring = attr.docstring.replace('\n', '\n\n')
|
||||
content += f'{attr.docstring}\n\n'
|
||||
return content
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `generate_docs(module_folder: str = False, output_dir: str = 'zh-CN', with_top: bool = None)`
|
||||
|
||||
|
||||
|
||||
**说明**: 生成文档
|
||||
|
||||
**参数**:
|
||||
> - module_folder: 模块文件夹
|
||||
> - output_dir: 输出文件夹
|
||||
> - with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md
|
||||
> - ignored_paths: 忽略的路径
|
||||
> - lang: 语言
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def generate_docs(module_folder: str, output_dir: str, with_top: bool=False, lang: str='zh-CN', ignored_paths=None):
|
||||
"""
|
||||
生成文档
|
||||
Args:
|
||||
module_folder: 模块文件夹
|
||||
output_dir: 输出文件夹
|
||||
with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md
|
||||
ignored_paths: 忽略的路径
|
||||
lang: 语言
|
||||
"""
|
||||
if ignored_paths is None:
|
||||
ignored_paths = []
|
||||
file_data: dict[str, str] = {}
|
||||
file_list = get_file_list(module_folder)
|
||||
shutil.rmtree(output_dir, ignore_errors=True)
|
||||
os.mkdir(output_dir)
|
||||
replace_data = {'__init__': 'README', '.py': '.md'}
|
||||
for pyfile_path in file_list:
|
||||
if any((ignored_path.replace('\\', '/') in pyfile_path.replace('\\', '/') for ignored_path in ignored_paths)):
|
||||
continue
|
||||
no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path)
|
||||
rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path
|
||||
for rk, rv in replace_data.items():
|
||||
rel_md_path = rel_md_path.replace(rk, rv)
|
||||
abs_md_path = os.path.join(output_dir, rel_md_path)
|
||||
module_info = get_module_info_normal(pyfile_path)
|
||||
if 'README' in abs_md_path:
|
||||
front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'}
|
||||
else:
|
||||
front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'}
|
||||
md_content = generate_markdown(module_info, front_matter)
|
||||
print(f'Generate {pyfile_path} -> {abs_md_path}')
|
||||
file_data[abs_md_path] = md_content
|
||||
write_to_files(file_data)
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `DefType(Enum)`
|
||||
### **class** `FunctionInfo(BaseModel)`
|
||||
### **class** `AttributeInfo(BaseModel)`
|
||||
### **class** `ClassInfo(BaseModel)`
|
||||
### **class** `ModuleInfo(BaseModel)`
|
@ -1,26 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugin
|
||||
---
|
||||
### *func* `get_loaded_plugins() -> dict[str, Plugin]`
|
||||
|
||||
|
||||
|
||||
**说明**: 获取已加载的插件
|
||||
|
||||
**返回**: dict[str, Plugin]: 插件字典
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def get_loaded_plugins() -> dict[str, Plugin]:
|
||||
"""
|
||||
获取已加载的插件
|
||||
Returns:
|
||||
dict[str, Plugin]: 插件字典
|
||||
"""
|
||||
return _plugins
|
||||
```
|
||||
</details>
|
||||
|
@ -1,162 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugin.load
|
||||
---
|
||||
### *func* `load_plugin() -> Optional[Plugin]`
|
||||
|
||||
|
||||
|
||||
**说明**: 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
||||
|
||||
|
||||
**参数**:
|
||||
> - module_path: 插件名称 `path.to.your.plugin`
|
||||
> - 或插件路径 `pathlib.Path(path/to/your/plugin)`:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_plugin(module_path: str | Path) -> Optional[Plugin]:
|
||||
"""加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
||||
|
||||
参数:
|
||||
module_path: 插件名称 `path.to.your.plugin`
|
||||
或插件路径 `pathlib.Path(path/to/your/plugin)`
|
||||
"""
|
||||
module_path = path_to_module_name(Path(module_path)) if isinstance(module_path, Path) else module_path
|
||||
try:
|
||||
module = import_module(module_path)
|
||||
_plugins[module.__name__] = Plugin(name=module.__name__, module=module, module_name=module_path)
|
||||
if module.__dict__.get('__plugin_metadata__', None):
|
||||
metadata: 'PluginMetadata' = module.__dict__['__plugin_metadata__']
|
||||
display_name = module.__name__.split('.')[-1]
|
||||
elif module.__dict__.get('__liteyuki_plugin_meta__', None):
|
||||
metadata: 'PluginMetadata' = module.__dict__['__liteyuki_plugin_meta__']
|
||||
display_name = format_display_name(f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type)
|
||||
elif module.__dict__.get('__plugin_meta__', None):
|
||||
metadata: 'PluginMetadata' = module.__dict__['__plugin_meta__']
|
||||
display_name = format_display_name(f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type)
|
||||
else:
|
||||
logger.opt(colors=True).warning(f'The metadata of Liteyuki plugin "{module.__name__}" is not specified, use empty.')
|
||||
metadata = PluginMetadata(name=module.__name__)
|
||||
display_name = module.__name__.split('.')[-1]
|
||||
_plugins[module.__name__].metadata = metadata
|
||||
logger.opt(colors=True).success(f'Succeeded to load liteyuki plugin "{display_name}"')
|
||||
return _plugins[module.__name__]
|
||||
except Exception as e:
|
||||
logger.opt(colors=True).success(f'Failed to load liteyuki plugin "<r>{module_path}</r>"')
|
||||
traceback.print_exc()
|
||||
return None
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `load_plugins(*, ignore_warning: bool = True) -> set[Plugin]`
|
||||
|
||||
|
||||
|
||||
**说明**: 导入文件夹下多个插件
|
||||
|
||||
|
||||
**参数**:
|
||||
> - plugin_dir: 文件夹路径
|
||||
> - ignore_warning: 是否忽略警告,通常是目录不存在或目录为空
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def load_plugins(*plugin_dir: str, ignore_warning: bool=True) -> set[Plugin]:
|
||||
"""导入文件夹下多个插件
|
||||
|
||||
参数:
|
||||
plugin_dir: 文件夹路径
|
||||
ignore_warning: 是否忽略警告,通常是目录不存在或目录为空
|
||||
"""
|
||||
plugins = set()
|
||||
for dir_path in plugin_dir:
|
||||
if not os.path.exists(dir_path):
|
||||
if not ignore_warning:
|
||||
logger.warning(f"Plugins dir '{dir_path}' does not exist.")
|
||||
continue
|
||||
if not os.listdir(dir_path):
|
||||
if not ignore_warning:
|
||||
logger.warning(f"Plugins dir '{dir_path}' is empty.")
|
||||
continue
|
||||
if not os.path.isdir(dir_path):
|
||||
if not ignore_warning:
|
||||
logger.warning(f"Plugins dir '{dir_path}' is not a directory.")
|
||||
continue
|
||||
for f in os.listdir(dir_path):
|
||||
path = Path(os.path.join(dir_path, f))
|
||||
module_name = None
|
||||
if os.path.isfile(path) and f.endswith('.py') and (f != '__init__.py'):
|
||||
module_name = f'{path_to_module_name(Path(dir_path))}.{f[:-3]}'
|
||||
elif os.path.isdir(path) and os.path.exists(os.path.join(path, '__init__.py')):
|
||||
module_name = path_to_module_name(path)
|
||||
if module_name:
|
||||
load_plugin(module_name)
|
||||
if _plugins.get(module_name):
|
||||
plugins.add(_plugins[module_name])
|
||||
return plugins
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `format_display_name() -> str`
|
||||
|
||||
|
||||
|
||||
**说明**: 设置插件名称颜色,根据不同类型插件设置颜色
|
||||
|
||||
**参数**:
|
||||
> - display_name: 插件名称
|
||||
> - plugin_type: 插件类型
|
||||
|
||||
**返回**: str: 设置后的插件名称 <y>name</y>
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def format_display_name(display_name: str, plugin_type: PluginType) -> str:
|
||||
"""
|
||||
设置插件名称颜色,根据不同类型插件设置颜色
|
||||
Args:
|
||||
display_name: 插件名称
|
||||
plugin_type: 插件类型
|
||||
|
||||
Returns:
|
||||
str: 设置后的插件名称 <y>name</y>
|
||||
"""
|
||||
color = 'y'
|
||||
match plugin_type:
|
||||
case PluginType.APPLICATION:
|
||||
color = 'm'
|
||||
case PluginType.TEST:
|
||||
color = 'g'
|
||||
case PluginType.MODULE:
|
||||
color = 'e'
|
||||
case PluginType.SERVICE:
|
||||
color = 'c'
|
||||
return f'<{color}>{display_name} [{plugin_type.name}]</{color}>'
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_plugins = {}`
|
||||
|
||||
- **类型**: `dict[str, Plugin]`
|
||||
|
||||
### ***var*** `metadata = module.__dict__['__plugin_metadata__']`
|
||||
|
||||
- **类型**: `'PluginMetadata'`
|
||||
|
||||
### ***var*** `metadata = module.__dict__['__liteyuki_plugin_meta__']`
|
||||
|
||||
- **类型**: `'PluginMetadata'`
|
||||
|
||||
### ***var*** `metadata = module.__dict__['__plugin_meta__']`
|
||||
|
||||
- **类型**: `'PluginMetadata'`
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugin.manager
|
||||
---
|
@ -1,18 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugin.model
|
||||
---
|
||||
### **class** `PluginType(Enum)`
|
||||
### **class** `PluginMetadata(BaseModel)`
|
||||
### **class** `Plugin(BaseModel)`
|
||||
### *method* `__hash__(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def __hash__(self):
|
||||
return hash(self.module_name)
|
||||
```
|
||||
</details>
|
||||
|
@ -1,17 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugins.liteecho
|
||||
---
|
||||
### `@on_startswith(['liteecho'], rule=is_su_rule).handle()`
|
||||
### *async func* `liteecho()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
@on_startswith(['liteecho'], rule=is_su_rule).handle()
|
||||
async def liteecho(event: MessageEvent):
|
||||
event.reply(event.raw_message.strip()[8:].strip())
|
||||
```
|
||||
</details>
|
||||
|
@ -1,25 +0,0 @@
|
||||
---
|
||||
title: liteyuki.plugins.plugin_loader
|
||||
---
|
||||
### *func* `default_plugins_loader()`
|
||||
|
||||
|
||||
|
||||
**说明**: 默认插件加载器,应在初始化时调用
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def default_plugins_loader():
|
||||
"""
|
||||
默认插件加载器,应在初始化时调用
|
||||
"""
|
||||
for plugin in get_config('liteyuki.plugins', []):
|
||||
load_plugin(plugin)
|
||||
for plugin_dir in get_config('liteyuki.plugin_dirs', ['src/liteyuki_plugins']):
|
||||
load_plugins(plugin_dir)
|
||||
```
|
||||
</details>
|
||||
|
@ -1,168 +0,0 @@
|
||||
---
|
||||
title: liteyuki.utils
|
||||
---
|
||||
### *func* `is_coroutine_callable() -> bool`
|
||||
|
||||
|
||||
|
||||
**说明**: 判断是否为协程可调用对象
|
||||
|
||||
**参数**:
|
||||
> - call: 可调用对象
|
||||
|
||||
**返回**: bool: 是否为协程可调用对象
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def is_coroutine_callable(call: Callable[..., Any]) -> bool:
|
||||
"""
|
||||
判断是否为协程可调用对象
|
||||
Args:
|
||||
call: 可调用对象
|
||||
Returns:
|
||||
bool: 是否为协程可调用对象
|
||||
"""
|
||||
if inspect.isroutine(call):
|
||||
return inspect.iscoroutinefunction(call)
|
||||
if inspect.isclass(call):
|
||||
return False
|
||||
func_ = getattr(call, '__call__', None)
|
||||
return inspect.iscoroutinefunction(func_)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `run_coroutine()`
|
||||
|
||||
|
||||
|
||||
**说明**: 运行协程
|
||||
|
||||
**参数**:
|
||||
> - coro:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def run_coroutine(*coro: Coroutine):
|
||||
"""
|
||||
运行协程
|
||||
Args:
|
||||
coro:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
for c in coro:
|
||||
asyncio.ensure_future(c)
|
||||
else:
|
||||
for c in coro:
|
||||
loop.run_until_complete(c)
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(asyncio.gather(*coro))
|
||||
loop.close()
|
||||
except Exception as e:
|
||||
logger.error(f'Exception occurred: {e}')
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `run_coroutine_in_thread()`
|
||||
|
||||
|
||||
|
||||
**说明**: 在新线程中运行协程
|
||||
|
||||
**参数**:
|
||||
> - coro:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def run_coroutine_in_thread(*coro: Coroutine):
|
||||
"""
|
||||
在新线程中运行协程
|
||||
Args:
|
||||
coro:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
threading.Thread(target=run_coroutine, args=coro, daemon=True).start()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `path_to_module_name() -> str`
|
||||
|
||||
|
||||
|
||||
**说明**: 转换路径为模块名
|
||||
|
||||
**参数**:
|
||||
> - path: 路径a/b/c/d -> a.b.c.d
|
||||
|
||||
**返回**: str: 模块名
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def path_to_module_name(path: Path) -> str:
|
||||
"""
|
||||
转换路径为模块名
|
||||
Args:
|
||||
path: 路径a/b/c/d -> a.b.c.d
|
||||
Returns:
|
||||
str: 模块名
|
||||
"""
|
||||
rel_path = path.resolve().relative_to(Path.cwd().resolve())
|
||||
if rel_path.stem == '__init__':
|
||||
return '.'.join(rel_path.parts[:-1])
|
||||
else:
|
||||
return '.'.join(rel_path.parts[:-1] + (rel_path.stem,))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `async_wrapper() -> Callable[..., Coroutine]`
|
||||
|
||||
|
||||
|
||||
**说明**: 异步包装器
|
||||
|
||||
**参数**:
|
||||
> - func: Sync Callable
|
||||
|
||||
**返回**: Coroutine: Asynchronous Callable
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>源代码</b> </summary>
|
||||
|
||||
```python
|
||||
def async_wrapper(func: Callable[..., Any]) -> Callable[..., Coroutine]:
|
||||
"""
|
||||
异步包装器
|
||||
Args:
|
||||
func: Sync Callable
|
||||
Returns:
|
||||
Coroutine: Asynchronous Callable
|
||||
"""
|
||||
|
||||
async def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
wrapper.__signature__ = inspect.signature(func)
|
||||
return wrapper
|
||||
```
|
||||
</details>
|
||||
|
@ -1,74 +0,0 @@
|
||||
---
|
||||
title: 轻雪函数
|
||||
icon: code
|
||||
order: 2
|
||||
category: 开发
|
||||
---
|
||||
|
||||
## **轻雪函数**
|
||||
|
||||
轻雪函数 Liteyuki Function 是轻雪的一个功能,它允许你在轻雪中运行一些自定义的由数据驱动的命令,类似于Minecraft的mcfunction,属于资源包的一部分,但需单独起篇幅.
|
||||
|
||||
### **函数文件**
|
||||
|
||||
函数文件放在资源包的`functions`目录下,文件名以`.mcfunction` `.lyfunction` `.lyf`结尾,例如`test.mcfunction`,文件内容为一系列的命令,每行一个命令,支持单行注释`#`(编辑时的语法高亮可采取`shell`格式),例如:
|
||||
|
||||
```shell
|
||||
# 在发信器输出"hello world"
|
||||
cmd echo hello world
|
||||
|
||||
# 如果你想同时输出多行内容可以尝试换行符(Python格式)
|
||||
cmd echo hello world\nLiteyuki bot
|
||||
```
|
||||
|
||||
也支持句末注释,例如:
|
||||
```shell
|
||||
cmd echo hello world # 输出"hello world"
|
||||
```
|
||||
|
||||
### **命令文档**
|
||||
|
||||
```shell
|
||||
var <var1=value1> [var2=value2] ... # 定义变量
|
||||
cmd <command> # 在设备上执行命令
|
||||
api <api_name> [var=value...] # 调用Bot API
|
||||
function <func_name> # 调用函数,可递归
|
||||
sleep <time> # 异步等待,单位s
|
||||
nohup <command> # 使用新的task执行命令,即不等待
|
||||
end # 结束函数关键字,包括子task
|
||||
await # 等待所有异步任务结束,若函数中启动了其他task,需要在最后调用,否则task对象会被销毁
|
||||
```
|
||||
|
||||
|
||||
#### **示例**
|
||||
|
||||
```shell
|
||||
# 疯狂戳好友
|
||||
# 使用 /function poke user_id=123456 执行
|
||||
# 每隔0.2s戳两次,无限戳,会触发最大递归深度限制
|
||||
# 若要戳20s后停止,则需要删除await,添加sleep 20和end
|
||||
api friend_poke user_id=user_id
|
||||
api friend_poke user_id=user_id
|
||||
sleep 0.2
|
||||
nohup function poke
|
||||
await
|
||||
```
|
||||
|
||||
### **API**
|
||||
|
||||
理论上所有基于onebotv11的api都可调用,不同Adapter api也有差别.
|
||||
|
||||
[Onebot v11 API文档](https://283375.github.io/onebot_v11_vitepress/api/index.html)
|
||||
|
||||
### **结束关键字**
|
||||
|
||||
由于LiteyukiBot基于异步运行, 所以在编写lyfunction时也要注意异步的调用,避免出现"单线程走到底"的情况是效率提升的关键.
|
||||
|
||||
`await` 异步任务结束关键字,用于结束当前已完成function的执行
|
||||
|
||||
> [!warning]
|
||||
> 但若出现非单function的情况,有一个task任务没有完成而await被执行了,那么当前所有函数包的task都会被截停销毁
|
||||
|
||||
|
||||
> [!tip]
|
||||
> 编写轻雪函数推荐你使用VS Code插件[Liteyuki Function](https://github.com/LiteyukiStudio/lyfunctionTextmate)实现语法高亮
|
@ -1,82 +0,0 @@
|
||||
---
|
||||
title: 轻雪插件开发
|
||||
icon: laptop-code
|
||||
order: 3
|
||||
category: 开发
|
||||
---
|
||||
|
||||
## 简介
|
||||
|
||||
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
|
||||
|
||||
## 开始
|
||||
|
||||
### 创建插件
|
||||
|
||||
一个`.py`文件或一个包含`__init__.py`的文件夹即可被识别为插件
|
||||
|
||||
首先创建一个文件夹,例如`watchdog_plugin`,并在其中创建一个`__init__.py`文件,即可创建一个插件
|
||||
|
||||
`__init__.py`
|
||||
```python
|
||||
from liteyuki.plugin import PluginMetadata, PluginType
|
||||
from .watch_dog import * # 导入逻辑部分
|
||||
|
||||
# 定义插件元数据
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="NoneDog", # 插件名称
|
||||
version="1.0.0", # 插件版本
|
||||
description="A simple plugin for nonebot developer", # 插件描述
|
||||
type=PluginType.SERVICE # 插件类型
|
||||
)
|
||||
|
||||
# 你的插件代码
|
||||
...
|
||||
```
|
||||
|
||||
### 编写逻辑部分
|
||||
|
||||
轻雪主进程不涉及聊天部分,因此插件主要是一些后台任务或者与聊天机器人的通信
|
||||
以下我们会编写一个简单的插件,用于开发NoneBot时进行文件系统变更重载
|
||||
`watch_dog.py`
|
||||
```python
|
||||
import os
|
||||
from liteyuki.dev import observer # 导入文件系统观察器
|
||||
from liteyuki import get_bot, logger # 导入轻雪Bot和日志
|
||||
from watchdog.events import FileSystemEvent # 导入文件系统事件
|
||||
|
||||
liteyuki = get_bot() # 获取唯一的轻雪Bot实例
|
||||
|
||||
exclude_extensions = (".pyc", ".pyo") # 排除的文件扩展名
|
||||
|
||||
|
||||
# 用observer的on_file_system_event装饰器监听文件系统事件
|
||||
@observer.on_file_system_event(
|
||||
directories=("src/nonebot_plugins",),
|
||||
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path) and os.path.isfile(event.src_path)
|
||||
)
|
||||
def restart_nonebot_process(event: FileSystemEvent):
|
||||
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
|
||||
liteyuki.restart_process("nonebot") # 调用重启进程方法
|
||||
```
|
||||
|
||||
### 加载插件
|
||||
|
||||
#### 方法1
|
||||
|
||||
- 在配置文件中的`liteyuki.plugins`中添加你的插件路径,例如`watchdog_plugin`,重启轻雪即可加载插件。
|
||||
|
||||
#### 方法2
|
||||
|
||||
- 使用开发工具快速运行插件,无需手动创建实例
|
||||
- 创建入口文件,例如`main.py`,并在其中写入以下代码
|
||||
|
||||
```python
|
||||
from liteyuki.dev.plugin import run_plugins
|
||||
|
||||
run_plugins("watchdog_plugin")
|
||||
```
|
||||
|
||||
然后运行`python main.py`即可启动插件
|
||||
|
||||
启用插件后,我们在src/nonebot_plugins下创建一个文件,例如`test.py`,并在其中写入一些代码,保存后轻雪会自动重载NoneBot进程
|
@ -1,53 +0,0 @@
|
||||
---
|
||||
title: 资源包开发
|
||||
icon: box
|
||||
order: 1
|
||||
category: 开发
|
||||
---
|
||||
|
||||
## 简介
|
||||
|
||||
资源包,亦可根据用途称为主题包、字体包、语言包等,它允许你一定程度上自定义轻雪的外观,并且不用修改源代码
|
||||
|
||||
- [资源/主题商店](/store/)提供了一些资源包供你选择,你也可以自己制作资源包
|
||||
- 资源包的制作很简单,如果你接触过`Minecraft`的资源包,那么你能够很快就上手,仅需按照原有路径进行文件替换即可,讲起打包成一个新的资源包。
|
||||
- 部分内容制作需要一点点前端基础,例如`html`,`css`
|
||||
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
|
||||
- 欢迎各位投稿资源包到轻雪资源商店
|
||||
|
||||
请注意,主题包中的html渲染使用Js来规定数据的渲染位置,请确保您所编写的html代码能被Bot解析,否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码,以避免出现无法预料的问题。
|
||||
|
||||
---
|
||||
|
||||
## 加载资源包
|
||||
|
||||
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
|
||||
|
||||
```shell
|
||||
main.py
|
||||
resources
|
||||
└─resource_pack_1
|
||||
├─metadata.yml
|
||||
├─templates
|
||||
└───...
|
||||
└─resource_pack_2
|
||||
├─metadata.yml
|
||||
└─...
|
||||
```
|
||||
|
||||
- 你自己制作的资源包也应该遵循这个规则,并且应该在`metadata.yml`中填写一些信息
|
||||
- 若没有`metadata.yml`文件,则该文件夹不会被识别为资源包
|
||||
|
||||
```yaml
|
||||
name: "资源包名称"
|
||||
version: "1.0.0"
|
||||
description: "资源包描述"
|
||||
# 你可以自定义一些信息,但请保证以上三个字段
|
||||
...
|
||||
```
|
||||
|
||||
- 资源包加载遵循一个优先级,即后加载的资源包会覆盖前面的资源包,例如,你在A包中定义了一个`index.html`文件,B包也定义了一个`index.html`文件,那么加载B包后,A包中的`index.html`文件会被覆盖
|
||||
- 对于不同资源包的不同文件,是可以相对引用的,例如你在A中定义了`templates/index.html`,在B中定义了`templates/style.css`,可以在A的`index.html`中用`./style.css`相对路径引用B中的css
|
||||
|
||||
> [!tip]
|
||||
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包
|
@ -1,82 +0,0 @@
|
||||
---
|
||||
home: true
|
||||
icon: home
|
||||
title: Home
|
||||
heroImage: https://cdn.liteyuki.icu/static/svg/lylogo-full.svg
|
||||
heroImageDark: https://cdn.liteyuki.icu/static/svg/lylogo-full-dark.svg
|
||||
bgImage:
|
||||
bgImageDark:
|
||||
bgImageStyle:
|
||||
background-attachment: fixed
|
||||
heroText: LiteyukiBot
|
||||
tagline: LiteyukiBot A high-performance, easy-to-use chatbot framework and application
|
||||
|
||||
actions:
|
||||
- text: Get Started
|
||||
icon: rocket
|
||||
link: ./deploy/install.html
|
||||
type: primary
|
||||
|
||||
- text: Usage
|
||||
icon: book
|
||||
link: ./usage/basic_command.html
|
||||
|
||||
highlights:
|
||||
- header: Simple and Efficient
|
||||
image: /assets/image/layout.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/2-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/2-dark.svg
|
||||
bgImageStyle:
|
||||
background-repeat: repeat
|
||||
background-size: initial
|
||||
features:
|
||||
- title: Multi-Framework Support
|
||||
icon: robot
|
||||
details: Compatible with nonebot, melobot, etc., with good ecological support
|
||||
link: https://nonebot.dev/
|
||||
|
||||
- title: Convenient Management
|
||||
icon: plug
|
||||
details: Use package manager to manage plugins and resource packs
|
||||
|
||||
- title: Custom Themes Support
|
||||
icon: paint-brush
|
||||
details: Fully customize the appearance with resource packs
|
||||
link: https://bot.liteyuki.icu/usage/resource_pack.html
|
||||
|
||||
- title: i18n
|
||||
icon: globe
|
||||
details: Support multiple languages through resource packs
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: Easy to Use
|
||||
icon: cog
|
||||
details: No need for cumbersome pre-processes, ready to use
|
||||
link: https://bot.liteyuki.icu/deployment/config.html
|
||||
|
||||
- title: High Performance
|
||||
icon: tachometer-alt
|
||||
details: 500 plugins, start within 2s
|
||||
|
||||
- title: Rolling Update
|
||||
icon: cloud-download
|
||||
details: Keep your bot up to date
|
||||
|
||||
- title: OpenSource
|
||||
icon: code
|
||||
details: MIT LICENCE open source project, welcome your contribution
|
||||
|
||||
- header: Quick Start
|
||||
image: /assets/image/box.svg
|
||||
bgImage: https://theme-hope-assets.vuejs.press/bg/3-light.svg
|
||||
bgImageDark: https://theme-hope-assets.vuejs.press/bg/3-dark.svg
|
||||
highlights:
|
||||
- title: Install Git and Python3.10+ environment
|
||||
- title: Use <code>git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1</code> to clone the project locally
|
||||
- title: Use <code>cd LiteyukiBot</code> to change the directory to the project root
|
||||
- title: Use <code>pip install -r requirements.txt</code> install the project dependencies
|
||||
details: If you have multiple Python environments, please use <code>pythonx -m pip install -r requirements.txt</code>.
|
||||
- title: Start bot with <code>python main.py</code>.
|
||||
copyright: © 2021-2024 SnowyKami All Rights Reserved
|
||||
|
||||
---
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: Deploy
|
||||
index: false
|
||||
icon: laptop-code
|
||||
category: deploy
|
||||
---
|
||||
|
||||
<Catalog />
|
@ -1,9 +0,0 @@
|
||||
# note
|
||||
开发者选项
|
||||
allow_update: true # 是否允许更新
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方插件
|
||||
dev_mode: false # 开发者模式,开启后将会启动看门狗
|
@ -1,77 +1,77 @@
|
||||
---
|
||||
title: Configuration
|
||||
icon: cog
|
||||
title: Configurations
|
||||
order: 2
|
||||
category: deployment
|
||||
tag:
|
||||
- Configuration
|
||||
---
|
||||
|
||||
轻雪支持`yaml`、`json`和`toml`作为配置文件,取决于你个人的喜好
|
||||
# Configurations
|
||||
|
||||
首次运行后生成`config.yml`和`config`目录,你可修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`及`nickname`字段即可
|
||||
LiteyukiBot supports `yaml`, `json`, and `toml` as configuration files, depending on your personal preference.
|
||||
|
||||
启动时会加载项目目录下`config.yml/yaml/json/toml`和`config`目录下的所有配置文件,你可在`config`目录下创建多个配置文件,轻雪会自动合并这些配置文件
|
||||
When you first run LiteyukiBot, it will generate `config.yml` and the `config` directory. You can modify the configuration items and restart LiteyukiBot. In most cases, you only need to modify
|
||||
the `superusers` and `nickname` fields.
|
||||
|
||||
## **基础配置项**
|
||||
When starting, LiteyukiBot will load all configuration files in the project directory `config.yml/yaml/json/toml` and the `config` directory. You can create multiple configuration files in
|
||||
the `config` directory, and LiteyukiBot will automatically merge these configuration files.
|
||||
|
||||
## **Basic Configuration**
|
||||
|
||||
```yaml
|
||||
# NoneBot configuration
|
||||
nonebot:
|
||||
# Nonebot机器人的配置,以前的最外层配置项仍可为Nonebot服务,但是部分内容会被覆盖,请尽快迁移
|
||||
command_start: [ "/", "" ] # 指令前缀,若没有""空命令头,请开启alconna_use_command_start保证alconna解析正常
|
||||
host: 127.0.0.1 # 监听地址,默认为本机,若要接收外部请求请填写0.0.0.0
|
||||
port: 20216 # 绑定端口
|
||||
nickname: [ "liteyuki" ] # 机器人昵称列表
|
||||
superusers: [ "1919810" ] # 超级用户列表
|
||||
command_start: [ "/", "" ] # Command prefix, if there is no "" empty command header, please enable alconna_use_command_start to ensure alconna parsing is normal
|
||||
host: 127.0.0.1 # Listening address, default is local, if you want to receive external requests, please fill in
|
||||
port: 20216 # Binding port
|
||||
nickname: [ "liteyuki" ] # Bot nickname list
|
||||
superusers: [ "1919810" ] # Superuser list
|
||||
liteyuki:
|
||||
# 写在外层的配置项将会被覆盖,建议迁移到liteyuki下
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
plugins: [ ] # 轻雪插件列表
|
||||
plugin_dirs: [ ] # 轻雪插件目录列表
|
||||
log_level: "INFO" # Log level
|
||||
log_icon: true # Whether to display the log level icon (some console fonts are not available)
|
||||
auto_report: true # Whether to automatically report problems to Liteyuki server
|
||||
auto_update: true # Whether to automatically update Liteyuki, check for updates at 4 am every day
|
||||
plugins: [ ] # Liteyuki plugin list
|
||||
plugin_dirs: [ ] # Liteyuki plugin directory list
|
||||
```
|
||||
|
||||
## **其他配置**
|
||||
## **Other configurations**
|
||||
|
||||
以下为默认值,如需自定义请手动添加
|
||||
The following is the default value. If you need to customize it, please add it manually
|
||||
|
||||
```yaml
|
||||
# 高级NoneBot配置
|
||||
# Advanced configuration
|
||||
nonebot:
|
||||
onebot_access_token: "" # 访问令牌,对公开放时建议设置
|
||||
default_language: "zh-CN" # 默认语言
|
||||
alconna_auto_completion: false # alconna是否自动补全指令,默认false,建议开启
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方NoneBot插件
|
||||
# 其他Nonebot插件的配置项
|
||||
onebot_access_token: "" # OneBot access token
|
||||
default_language: "zh-CN" # Default language
|
||||
alconna_auto_completion: false # alconna auto completion
|
||||
safe_mode: false # Safe mode, if true, the bot will not load any plugins
|
||||
# other nonebot configurations
|
||||
custom_config_1: "custom_value1"
|
||||
custom_config_2: "custom_value2"
|
||||
|
||||
# 开发者选项
|
||||
# development configuration
|
||||
liteyuki:
|
||||
allow_update: true # 是否允许更新
|
||||
debug: false # 轻雪调试,开启会自动重载Bot或者资源,其他插件自带的调试功能也将开启
|
||||
dev_mode: false # 开发者模式,开启后将会启动监视者,监视文件变化并自动重载
|
||||
|
||||
allow_update: true # Whether to allow Liteyuki to update
|
||||
debug: false # Debug mode, if true, Liteyuki will output more detailed logs
|
||||
dev_mode: false # development mode, if true, Liteyuki will load all plugins in the development directory
|
||||
...
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> 如果要使用NoneBot和dotenv配置文件,请自行创建`.env.{ENVIRONMENT}`,并在`config.yml`中添加`nonebot.environment:{ENVIRONMENT}`字段
|
||||
```yaml
|
||||
|
||||
## **与NoneBot对接的OneBot实现端配置**
|
||||
```
|
||||
|
||||
生产环境中推荐反向WebSocket
|
||||
不同的实现端给出的字段可能不同,但是基本上都是一样的,这里给出一个参考值
|
||||
## **Example: Configuration of OneBot implementation side connected to NoneBot**
|
||||
|
||||
| 字段 | 参考值 | 说明 |
|
||||
|-------------|------------------------------------|----------------------------------|
|
||||
| 协议 | 反向WebSocket | 推荐使用反向ws协议进行通信,即轻雪作为服务端 |
|
||||
| 地址 | ws://127.0.0.1:20216/onebot/v11/ws | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
|
||||
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
|
||||
In production environments, it is recommended to use reverse WebSocket
|
||||
The fields provided by different implementation sides may be different, but basically the same. Here is a reference value
|
||||
|
||||
## **其他**
|
||||
| Fields | Value | Description |
|
||||
|-------------|------------------------------------|---------------------------------------------------------------------------------------|
|
||||
| protocol | Reverse WebSocket | Liteyuki-NoneBot as server |
|
||||
| address | ws://127.0.0.1:20216/onebot/v11/ws | The address depends on the configuration file, the default is ` |
|
||||
| AccessToken | `""` | If you have configured `AccessToken` for Liteyuki, please fill in the same value here |
|
||||
|
||||
- 要使用其他通信方式请访问[OneBot Adapter](https://onebot.adapters.nonebot.dev/)获取详细信息
|
||||
- 轻雪不局限于OneBot适配器,你可以使用NoneBot2支持的任何适配器
|
||||
- To use other communication methods, please visit [OneBot Adapter](https://onebot.adapters.nonebot.dev/) for detailed information
|
||||
|
||||
## **Other**
|
||||
|
||||
- Liteyuki is not limited to the OneBot adapter and NoneBot2. You can use any adapter supported by NoneBot2 or use the Liteyuki message delivery plugin
|
||||
|
@ -1,56 +1,60 @@
|
||||
---
|
||||
title: FAQ
|
||||
icon: question
|
||||
order: 3
|
||||
category: deployment
|
||||
tag:
|
||||
- FAQ
|
||||
---
|
||||
# FAQ
|
||||
|
||||
## **常见问题**
|
||||
## **Frequently Asked Questions**
|
||||
|
||||
- 设备上Python环境太乱了,pip和python不对应怎么办?
|
||||
- 请使用`/path/to/python -m pip install -r requirements.txt`来安装依赖,
|
||||
然后用`/path/to/python main.py`来启动Bot,
|
||||
其中`/path/to/python`是你要用来运行Bot的可执行文件
|
||||
- There are too many python interpreters on my computer, how do I know which one to use?
|
||||
- You can specify which python interpreter to use by using the full path to the python executable, for example, `/path/to/python main.py`
|
||||
- Use virtual environments to avoid conflicts between different python interpreters
|
||||
|
||||
- 为什么我启动后机器人没有反应?
|
||||
- 请检查配置文件的`command_start`或`superusers`,确认你有权限使用命令并按照正确的命令发送
|
||||
- 确认命令头没有和`nickname{}`冲突,例如一个命令是`help`,但是`Bot`昵称有一个`help`,那么将会被解析为nickname而不是命令
|
||||
- Why does the bot not respond after I start it?
|
||||
- Please check the configuration file `command_start` or `superusers`, make sure you have permission to use the command and send it correctly
|
||||
- Make sure the command header does not conflict with `nickname{}`, for example, a command is `help`, but the `Bot` nickname has a `help`, then it will be parsed as a nickname instead of a command
|
||||
|
||||
- 更新轻雪失败,报错`InvalidGitRepositoryError`
|
||||
- 请正确安装`Git`,并使用克隆而非直接下载的方式部署轻雪
|
||||
- Update Liteyuki failed, error `InvalidGitRepositoryError`
|
||||
- Please install `Git` correctly and deploy Liteyuki using cloning instead of direct download
|
||||
|
||||
- 怎么登录聊天平台,例如QQ?
|
||||
- 你有这个问题说明你不是很了解这个项目,本项目不负责实现登录功能,只负责处理和回应消息,登录功能由实现端(协议端)提供,
|
||||
实现端本身不负责处理响应逻辑,将消息按照OneBot标准处理好上报给轻雪
|
||||
你需要使用Onebot标准的实现端来连接到轻雪并将消息上报给轻雪,下面已经列出一些推荐的实现端
|
||||
- `Playwright`安装失败
|
||||
- 输入`playwright install`安装浏览器
|
||||
- 有的插件安装后报错无法启动
|
||||
- 请先查阅插件文档,确认插件必要配置项完好后,仍然出现问题,请联系插件作者或在安全模式`safe_mode: true`下启动轻雪,在安全模式下你可以使用`npm uninstall`卸载问题插件
|
||||
- 其他问题
|
||||
-
|
||||
加入QQ群[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
|
||||
- How to log in to chat platforms such as Telegram?
|
||||
- If you have this question, it means you don't know much about this project.
|
||||
This project does not implement the login function, only the message processing and response.
|
||||
The login function is provided by the implementation side (protocol side). The implementation side itself does not handle response logic.
|
||||
It processes and reports messages to Liteyuki according to the OneBot standard.
|
||||
You need to use an implementation side that complies with the OneBot standard to connect to Liteyuki and report messages to Liteyuki.
|
||||
Some recommended implementation sides have been listed below
|
||||
|
||||
## **推荐方案(QQ)**
|
||||
- `Playwright` installation failed
|
||||
- Enter `playwright install` to install the browser
|
||||
|
||||
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core),基于NTQQ的OneBot实现,目前Markdown消息支持Lagrange
|
||||
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot),NTQQ的OneBot插件,需要安装NTQQ
|
||||
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock),基于Lsposed的OneBot11实现
|
||||
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai),基于`node.js`,可使用`ws-plugin`进行通信
|
||||
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),`go`语言实现的OneBot11实现端,目前可用性较低
|
||||
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo),基于 OneBot QQ官方机器人Api Golang 原生实现,需要官方机器人权限
|
||||
7. 人工实现的`Onebot`协议,自己整一个WebSocket客户端,看着QQ的消息,然后给轻雪传输数据
|
||||
- Some plugins report errors after installation and cannot be started
|
||||
- Please refer to the plugin documentation first, confirm that the necessary configuration items of the plugin are intact,
|
||||
and if the problem persists, please contact the plugin author or start Liteyuki in safe mode `safe_mode: true`.
|
||||
In safe mode, you can use `npm uninstall` to uninstall problematic plugins
|
||||
|
||||
## **推荐方案(Minecraft)**
|
||||
## Other questions
|
||||
|
||||
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot),我们专门为Minecraft开发的服务器Bot,支持OneBotV11标准
|
||||
- Join chat group[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
|
||||
- If you don't have a QQ account, you can [submit an issue on GitHub](https://github.com/LiteyukiStudio/LiteyukiBot/issues/new?assignees=&labels=&projects=&template=%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88.md&title=)
|
||||
|
||||
使用其他项目连接请先自行查阅文档,若有困难请联系对应开发者而不是Liteyuki的开发者
|
||||
## **Recommended Solution(QQ)**
|
||||
|
||||
## **鸣谢**
|
||||
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core), based on `Lagrange.Core`, a Linux QQ implementation, supports OneBotv11 protocol
|
||||
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot), a plugin for `Liteloader NTQQ`, supports OneBotv11 protocol
|
||||
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock), based on Lsposed, supports kritor protocol
|
||||
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai), based on `Node.js`, supports OneBotv11 protocol
|
||||
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),A QQ Client based on `go`, supports OneBotv11 protocol
|
||||
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo), use QQ protocol
|
||||
|
||||
- [Nonebot2](https://nonebot.dev)提供的框架支持
|
||||
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
|
||||
- [MiSans](https://hyperos.mi.com/font/zh/),[MapleMono](https://gitee.com/mirrors/Maple-Mono)提供的字体,且遵守了相关字体开源协议
|
||||
## **Recommended Solution(Minecraft)**
|
||||
|
||||
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot), We develop a Minecraft server chat bot
|
||||
|
||||
Other project encountered issues, please prioritize the documentation and issues of the project itself, don't ask LiteyukiBot developers
|
||||
|
||||
## **Acknowledgements**
|
||||
|
||||
- [Nonebot2](https://nonebot.dev) provides the underlying framework
|
||||
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna) provides the command parser
|
||||
- [MiSans](https://hyperos.mi.com/font/zh/),[MapleMono](https://gitee.com/mirrors/Maple-Mono) provides the font
|
||||
|
@ -1,61 +1,57 @@
|
||||
---
|
||||
title: Installation
|
||||
icon: download
|
||||
title: Install
|
||||
order: 1
|
||||
category: deployment
|
||||
tag:
|
||||
- 安装
|
||||
---
|
||||
# Install
|
||||
|
||||
## **Installation**
|
||||
## **Normal Installation**
|
||||
|
||||
### **Conventional deployment**
|
||||
|
||||
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
|
||||
# Clone the project locally, --depth=1 to reduce the size of the cloned repository, this project updates depend on Git
|
||||
# Clone the project
|
||||
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
|
||||
# change the directory to the project root
|
||||
# change directory
|
||||
cd LiteyukiBot
|
||||
# install the project dependencies
|
||||
# install dependencies
|
||||
pip install -r requirements.txt
|
||||
# start bot
|
||||
# start the bot!
|
||||
python main.py
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> Recommended to use `venv` to run Liteyuki to avoid dependency conflicts, you can use `python -m venv .venv` to create a virtual environment, and then use `.venv\Scripts\activate` to activate the virtual environment (use `source .venv/bin/activate` to activate on Linux)
|
||||
> It is recommended to use a virtual environment to run Liteyuki to avoid dependency conflicts.
|
||||
> You can use `python -m venv .venv` to create a virtual environment, and then use `.venv\Scripts\activate` to activate the virtual environment
|
||||
> (use `source .venv/bin/activate` to activate on Linux).
|
||||
|
||||
### **Use docker**
|
||||
|
||||
## **Run with Docker**
|
||||
|
||||
1. Install [`Docker`](https://docs.docker.com/get-docker/)
|
||||
2. Clone project repo `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
3. change directory `cd LiteyukiBot`
|
||||
4. build image with `docker build -t liteyukibot .`
|
||||
5. start container `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
|
||||
2. Clone Repo `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
3. Change directory `cd LiteyukiBot`
|
||||
4. Build docker image `docker build -t liteyukibot .`
|
||||
5. Run container `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
|
||||
|
||||
> [!tip]
|
||||
> For Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of $(pwd)
|
||||
>
|
||||
> If you have changed the port number, replace `20216` in `20216:20216` with your port number
|
||||
> If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$(pwd)` <br>
|
||||
> If you have modified the port number, please replace `20216:20216` with your port number
|
||||
|
||||
### **Use TRSS Scripts**
|
||||
[TRSS_Liteyuki LiteyukiBot manage script](https://timerainstarsky.github.io/TRSS_Liteyuki/), This feature is supported by TRSS and is not an official feature of LiteyukiBot. It is recommended to use Arch Linux.
|
||||
## **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**
|
||||
|
||||
- Minimum Windows system version: `Windows 10+` / `Windows Server 2019+`
|
||||
- Linux systems need to support Python 3.10+, with `Ubuntu 20.04+` recommended
|
||||
- CPU: `1 vCPU` and more(Bot is multi processes, the more cores, the better the performance)
|
||||
- Memory: Without other plugins, the Bot will occupy `300~500MB`, including processes like `chromium` and `node`. The memory occupied by other plugins depends on the specific plugins, and it is recommended to have more than `1GB`.
|
||||
- Storage: At least `1GB` of space is required.
|
||||
## **Device Requirements**
|
||||
- Windows system version minimum `Windows10+`/`Windows Server 2019+`
|
||||
- Linux system requires Python3.10+, recommended `Ubuntu 20.04+`
|
||||
- CPU: at least `1vCPU`
|
||||
- Memory: Bot without other plugins will occupy `300~500MB`, including `chromium` and `node` processes, other plugins depend on specific plugins, recommended `1GB` or more
|
||||
- Disk: at least `1GB` of space
|
||||
|
||||
> [!warning]
|
||||
> If there are multiple environments on the device, please use `path/to/python -m pip install -r requirements.txt` to install dependencies, where `path/to/python` is the path to your Python executable.
|
||||
> If there are multiple environments on the device, please use `path/to/python -m pip install -r requirements.txt` to install dependencies, `path/to/python` is the path to your Python executable
|
||||
|
||||
> [!warning]
|
||||
> The update feature of Liteyuki depends on Git. If you have not installed Git and directly download the source code to run, you will not be able to use the update feature.
|
||||
> Liteyuki's update function depends on Git. If you do not have Git installed and run the source code directly, you will not be able to use the update function
|
||||
|
||||
#### For other issues, please go to [Q&A](/deployment/fandq)
|
||||
#### For other questions, please refer to [FAQ](/deployment/fandq)
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: 开发及贡献
|
||||
index: false
|
||||
icon: laptop-code
|
||||
category: 开发
|
||||
---
|
||||
|
||||
<Catalog />
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki
|
||||
---
|
@ -1,562 +0,0 @@
|
||||
---
|
||||
title: liteyuki.bot
|
||||
---
|
||||
### *func* `get_bot() -> LiteyukiBot`
|
||||
|
||||
|
||||
|
||||
**Description**: 获取轻雪实例
|
||||
|
||||
|
||||
**Return**: LiteyukiBot: 当前的轻雪实例
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def get_bot() -> LiteyukiBot:
|
||||
"""
|
||||
获取轻雪实例
|
||||
|
||||
Returns:
|
||||
LiteyukiBot: 当前的轻雪实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
if _BOT_INSTANCE is None:
|
||||
raise RuntimeError('Liteyuki instance not initialized.')
|
||||
return _BOT_INSTANCE
|
||||
else:
|
||||
raise RuntimeError("Can't get bot instance in sub process.")
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_config(key: str = None) -> Any`
|
||||
|
||||
|
||||
|
||||
**Description**: 获取配置
|
||||
|
||||
**Arguments**:
|
||||
> - key: 配置键
|
||||
> - default: 默认值
|
||||
|
||||
**Return**: Any: 配置值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def get_config(key: str, default: Any=None) -> Any:
|
||||
"""
|
||||
获取配置
|
||||
Args:
|
||||
key: 配置键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
return get_bot().config.get(key, default)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `get_config_with_compat(key: str = None) -> Any`
|
||||
|
||||
|
||||
|
||||
**Description**: 获取配置,兼容旧版本
|
||||
|
||||
**Arguments**:
|
||||
> - key: 配置键
|
||||
> - compat_keys: 兼容键
|
||||
> - default: 默认值
|
||||
|
||||
**Return**: Any: 配置值
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any=None) -> Any:
|
||||
"""
|
||||
获取配置,兼容旧版本
|
||||
Args:
|
||||
key: 配置键
|
||||
compat_keys: 兼容键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
if key in get_bot().config:
|
||||
return get_bot().config[key]
|
||||
for compat_key in compat_keys:
|
||||
if compat_key in get_bot().config:
|
||||
logger.warning(f'Config key "{compat_key}" will be deprecated, use "{key}" instead.')
|
||||
return get_bot().config[compat_key]
|
||||
return default
|
||||
```
|
||||
</details>
|
||||
|
||||
### *func* `print_logo()`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def print_logo():
|
||||
print('\x1b[34m' + '\n __ ______ ________ ________ __ __ __ __ __ __ ______ \n / | / |/ |/ |/ \\ / |/ | / |/ | / |/ |\n $$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \\ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/ \n $$ | $$ | $$ | $$ |__ $$ \\/$$/ $$ | $$ |$$ |/$$/ $$ | \n $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ | \n $$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \\ $$ | \n $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \\__$$ |$$ |$$ \\ _$$ |_ \n $$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |\n $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ \n ' + '\x1b[0m')
|
||||
```
|
||||
</details>
|
||||
|
||||
### **class** `LiteyukiBot`
|
||||
### *method* `__init__(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 初始化轻雪实例
|
||||
|
||||
**Arguments**:
|
||||
> - *args:
|
||||
> - **kwargs: 配置
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
初始化轻雪实例
|
||||
Args:
|
||||
*args:
|
||||
**kwargs: 配置
|
||||
|
||||
"""
|
||||
'常规操作'
|
||||
print_logo()
|
||||
global _BOT_INSTANCE
|
||||
_BOT_INSTANCE = self
|
||||
'配置'
|
||||
self.config: dict[str, Any] = kwargs
|
||||
'初始化'
|
||||
self.init(**self.config)
|
||||
logger.info('Liteyuki is initializing...')
|
||||
'生命周期管理'
|
||||
self.lifespan = Lifespan()
|
||||
self.process_manager: ProcessManager = ProcessManager(lifespan=self.lifespan)
|
||||
'事件循环'
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.stop_event = threading.Event()
|
||||
self.call_restart_count = 0
|
||||
'加载插件加载器'
|
||||
load_plugin('liteyuki.plugins.plugin_loader')
|
||||
'信号处理'
|
||||
signal.signal(signal.SIGINT, self._handle_exit)
|
||||
signal.signal(signal.SIGTERM, self._handle_exit)
|
||||
atexit.register(self.process_manager.terminate_all)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `run(self)`
|
||||
|
||||
|
||||
|
||||
**Description**: 启动逻辑
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def run(self):
|
||||
"""
|
||||
启动逻辑
|
||||
"""
|
||||
self.lifespan.before_start()
|
||||
self.process_manager.start_all()
|
||||
self.lifespan.after_start()
|
||||
self.keep_alive()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `keep_alive(self)`
|
||||
|
||||
|
||||
|
||||
**Description**: 保持轻雪运行
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def keep_alive(self):
|
||||
"""
|
||||
保持轻雪运行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
try:
|
||||
while not self.stop_event.is_set():
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
logger.info('Liteyuki is stopping...')
|
||||
self.stop()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `_handle_exit(self, signum, frame)`
|
||||
|
||||
|
||||
|
||||
**Description**: 信号处理
|
||||
|
||||
**Arguments**:
|
||||
> - signum:
|
||||
> - frame:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def _handle_exit(self, signum, frame):
|
||||
"""
|
||||
信号处理
|
||||
Args:
|
||||
signum:
|
||||
frame:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.info('Received signal, stopping all processes.')
|
||||
self.stop()
|
||||
sys.exit(0)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `restart(self, delay: int = 0)`
|
||||
|
||||
|
||||
|
||||
**Description**: 重启轻雪本体
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def restart(self, delay: int=0):
|
||||
"""
|
||||
重启轻雪本体
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if self.call_restart_count < 1:
|
||||
executable = sys.executable
|
||||
args = sys.argv
|
||||
logger.info('Restarting LiteyukiBot...')
|
||||
time.sleep(delay)
|
||||
if platform.system() == 'Windows':
|
||||
cmd = 'start'
|
||||
elif platform.system() == 'Linux':
|
||||
cmd = 'nohup'
|
||||
elif platform.system() == 'Darwin':
|
||||
cmd = 'open'
|
||||
else:
|
||||
cmd = 'nohup'
|
||||
self.process_manager.terminate_all()
|
||||
threading.Thread(target=os.system, args=(f"{cmd} {executable} {' '.join(args)}",)).start()
|
||||
sys.exit(0)
|
||||
self.call_restart_count += 1
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `restart_process(self, name: Optional[str] = None)`
|
||||
|
||||
|
||||
|
||||
**Description**: 停止轻雪
|
||||
|
||||
**Arguments**:
|
||||
> - name: 进程名称, 默认为None, 所有进程
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def restart_process(self, name: Optional[str]=None):
|
||||
"""
|
||||
停止轻雪
|
||||
Args:
|
||||
name: 进程名称, 默认为None, 所有进程
|
||||
Returns:
|
||||
"""
|
||||
self.lifespan.before_process_shutdown()
|
||||
self.lifespan.before_process_shutdown()
|
||||
if name is not None:
|
||||
chan_active = get_channel(f'{name}-active')
|
||||
chan_active.send(1)
|
||||
else:
|
||||
for process_name in self.process_manager.processes:
|
||||
chan_active = get_channel(f'{process_name}-active')
|
||||
chan_active.send(1)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `init(self)`
|
||||
|
||||
|
||||
|
||||
**Description**: 初始化轻雪, 自动调用
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def init(self, *args, **kwargs):
|
||||
"""
|
||||
初始化轻雪, 自动调用
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.init_logger()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `init_logger(self)`
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def init_logger(self):
|
||||
init_log(config=self.config)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `stop(self)`
|
||||
|
||||
|
||||
|
||||
**Description**: 停止轻雪
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def stop(self):
|
||||
"""
|
||||
停止轻雪
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self.stop_event.set()
|
||||
self.loop.stop()
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_start(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册启动前的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动前的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_start(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_start(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册启动后的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_start(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_shutdown(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册停止后的函数:未实现
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册停止后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_shutdown(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_shutdown(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册进程停止前的函数,为子进程停止时调用
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程停止前的函数,为子进程停止时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_process_shutdown(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_restart(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册进程重启前的函数,为子进程重启时调用
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程重启前的函数,为子进程重启时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_process_restart(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_restart(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册重启后的函数:未实现
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启后的函数:未实现
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_restart(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_nonebot_init(self, func: LIFESPAN_FUNC)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册nonebot初始化后的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_nonebot_init(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册nonebot初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_after_nonebot_init(func)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `_BOT_INSTANCE = NO_DEFAULT`
|
||||
|
||||
- **Type**: `LiteyukiBot`
|
||||
|
@ -1,423 +0,0 @@
|
||||
---
|
||||
title: liteyuki.bot.lifespan
|
||||
---
|
||||
### **class** `Lifespan`
|
||||
### *method* `__init__(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 轻雪生命周期管理,启动、停止、重启
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
轻雪生命周期管理,启动、停止、重启
|
||||
"""
|
||||
self.life_flag: int = 0
|
||||
self._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
||||
```
|
||||
</details>
|
||||
|
||||
### `@staticmethod`
|
||||
### *method* `run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC]) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 运行函数
|
||||
|
||||
**Arguments**:
|
||||
> - funcs:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
def run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None:
|
||||
"""
|
||||
运行函数
|
||||
Args:
|
||||
funcs:
|
||||
Returns:
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
tasks = []
|
||||
for func in funcs:
|
||||
if is_coroutine_callable(func):
|
||||
tasks.append(func(*args, **kwargs))
|
||||
else:
|
||||
tasks.append(async_wrapper(func)(*args, **kwargs))
|
||||
loop.run_until_complete(asyncio.gather(*tasks))
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册启动时的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_start_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册启动时的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_start_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册停止前的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止前的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_process_shutdown_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册停止后的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
|
||||
"""
|
||||
self._after_shutdown_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册重启时的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启时的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_process_restart_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册重启后的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
**Return**: LIFESPAN_FUNC:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启后的函数
|
||||
Args:
|
||||
func:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._after_restart_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `on_after_nonebot_init(self, func)`
|
||||
|
||||
|
||||
|
||||
**Description**: 注册 NoneBot 初始化后的函数
|
||||
|
||||
**Arguments**:
|
||||
> - func:
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def on_after_nonebot_init(self, func):
|
||||
"""
|
||||
注册 NoneBot 初始化后的函数
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._after_nonebot_init_funcs.append(func)
|
||||
return func
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_start(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 启动前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def before_start(self) -> None:
|
||||
"""
|
||||
启动前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_start functions')
|
||||
self.run_funcs(self._before_start_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_start(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 启动后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def after_start(self) -> None:
|
||||
"""
|
||||
启动后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running after_start functions')
|
||||
self.run_funcs(self._after_start_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_process_shutdown(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 停止前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def before_process_shutdown(self) -> None:
|
||||
"""
|
||||
停止前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_shutdown functions')
|
||||
self.run_funcs(self._before_process_shutdown_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_shutdown(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 停止后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def after_shutdown(self) -> None:
|
||||
"""
|
||||
停止后
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running after_shutdown functions')
|
||||
self.run_funcs(self._after_shutdown_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `before_process_restart(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 重启前
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def before_process_restart(self) -> None:
|
||||
"""
|
||||
重启前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug('Running before_restart functions')
|
||||
self.run_funcs(self._before_process_restart_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### *method* `after_restart(self) -> None`
|
||||
|
||||
|
||||
|
||||
**Description**: 重启后
|
||||
|
||||
|
||||
<details>
|
||||
<summary> <b>Source code</b> </summary>
|
||||
|
||||
```python
|
||||
def after_restart(self) -> None:
|
||||
"""
|
||||
重启后
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.debug('Running after_restart functions')
|
||||
self.run_funcs(self._after_restart_funcs)
|
||||
```
|
||||
</details>
|
||||
|
||||
### ***var*** `SYNC_LIFESPAN_FUNC = Callable[[], Any]`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_LIFESPAN_FUNC = Callable[[], Awaitable[Any]]`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
||||
### ***var*** `LIFESPAN_FUNC = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
||||
### ***var*** `SYNC_PROCESS_LIFESPAN_FUNC = Callable[[str], Any]`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
||||
### ***var*** `ASYNC_PROCESS_LIFESPAN_FUNC = Callable[[str], Awaitable[Any]]`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
||||
### ***var*** `PROCESS_LIFESPAN_FUNC = SYNC_PROCESS_LIFESPAN_FUNC | ASYNC_PROCESS_LIFESPAN_FUNC`
|
||||
|
||||
- **Type**: `TypeAlias`
|
||||
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
title: liteyuki.comm
|
||||
---
|