forked from bot/app
🔥 小型重构
This commit is contained in:
parent
35823be13e
commit
8b01943d14
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,7 +15,7 @@ _config.yml
|
|||||||
config.yml
|
config.yml
|
||||||
config.example.yml
|
config.example.yml
|
||||||
compile.bat
|
compile.bat
|
||||||
liteyuki/resources/templates/latest-debug.html
|
src/resources/templates/latest-debug.html
|
||||||
# vuepress
|
# vuepress
|
||||||
.github
|
.github
|
||||||
pyproject.toml
|
pyproject.toml
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {defineClientConfig} from "vuepress/client";
|
import {defineClientConfig} from "vuepress/client";
|
||||||
import resourceStoreComp from "./components/res_store.vue";
|
import resourceStoreComp from "./components/ResStore.vue";
|
||||||
import pluginStoreComp from "./components/plugin_store.vue";
|
import pluginStoreComp from "./components/PluginStore.vue";
|
||||||
//导入element-plus
|
//导入element-plus
|
||||||
import ElementPlus from 'element-plus';
|
import ElementPlus from 'element-plus';
|
||||||
|
|
||||||
@ -9,6 +9,5 @@ export default defineClientConfig({
|
|||||||
app.component("resourceStoreComp", resourceStoreComp);
|
app.component("resourceStoreComp", resourceStoreComp);
|
||||||
app.component("pluginStoreComp", pluginStoreComp);
|
app.component("pluginStoreComp", pluginStoreComp);
|
||||||
app.use(ElementPlus);
|
app.use(ElementPlus);
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref} from 'vue'
|
import {ref} from 'vue'
|
||||||
import ItemCard from './plugin_item_card.vue'
|
import ItemCard from './PluginItemCard.vue'
|
||||||
|
|
||||||
// 插件商店Nonebot
|
// 插件商店Nonebot
|
||||||
let items = ref([])
|
let items = ref([])
|
@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref} from 'vue'
|
import {ref} from 'vue'
|
||||||
import ItemCard from './res_item_card.vue'
|
import ItemCard from './ResItemCard.vue'
|
||||||
|
|
||||||
// 从public/assets/resources.json加载插件
|
// 从public/assets/resources.json加载插件
|
||||||
let items = ref([])
|
let items = ref([])
|
||||||
fetch('https://bot.liteyuki.icu/assets/resources.json')
|
fetch('/assets/resources.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
items.value = data
|
items.value = data
|
@ -10,6 +10,7 @@ export default defineUserConfig({
|
|||||||
description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人",
|
description: "LiteyukiBot | 轻雪机器人 | An OneBot Standard ChatBot | 一个OneBot标准的聊天机器人",
|
||||||
head: [
|
head: [
|
||||||
// 设置 favor.ico,.vuepress/public 下
|
// 设置 favor.ico,.vuepress/public 下
|
||||||
|
["script", {src: "/js/style.js", "type": "module"}],
|
||||||
['link', {rel: 'icon', href: 'https://cdn.liteyuki.icu/favicon.ico'},],
|
['link', {rel: 'icon', href: 'https://cdn.liteyuki.icu/favicon.ico'},],
|
||||||
|
|
||||||
['link', {rel: 'stylesheet', href: 'https://cdn.bootcdn.net/ajax/libs/firacode/6.2.0/fira_code.min.css'}],
|
['link', {rel: 'stylesheet', href: 'https://cdn.bootcdn.net/ajax/libs/firacode/6.2.0/fira_code.min.css'}],
|
||||||
|
22
docs/.vuepress/public/js/style.js
Normal file
22
docs/.vuepress/public/js/style.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
function applyStyle() {
|
||||||
|
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' 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()
|
@ -1,5 +1,10 @@
|
|||||||
// place your custom styles here
|
// place your custom styles here
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--code-window-border-radius: 10px;
|
||||||
|
--button-distance: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#main-title {
|
#main-title {
|
||||||
font-family: ColorTube, "Fira Code", serif;
|
font-family: ColorTube, "Fira Code", serif;
|
||||||
color: #ff0000 !important; /* 你想要的颜色 */
|
color: #ff0000 !important; /* 你想要的颜色 */
|
||||||
@ -15,11 +20,62 @@ code {
|
|||||||
font-family: "Fira Code", monospace !important;
|
font-family: "Fira Code", monospace !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vp-hero-image{
|
.vp-hero-image {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: -50px;
|
padding: -50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-title{
|
#main-title {
|
||||||
display: none;
|
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;
|
||||||
}
|
}
|
@ -21,6 +21,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"element-plus": "^2.7.0",
|
"element-plus": "^2.7.0",
|
||||||
"element-ui": "^2.15.14"
|
"element-ui": "^2.15.14",
|
||||||
|
"vue-router": "^4.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ dependencies:
|
|||||||
element-ui:
|
element-ui:
|
||||||
specifier: ^2.15.14
|
specifier: ^2.15.14
|
||||||
version: 2.15.14(vue@3.4.21)
|
version: 2.15.14(vue@3.4.21)
|
||||||
|
vue-router:
|
||||||
|
specifier: ^4.4.0
|
||||||
|
version: 4.4.0(vue@3.4.21)
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@vuepress/bundler-vite':
|
'@vuepress/bundler-vite':
|
||||||
@ -938,7 +941,6 @@ packages:
|
|||||||
|
|
||||||
/@vue/devtools-api@6.6.1:
|
/@vue/devtools-api@6.6.1:
|
||||||
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
|
resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/reactivity@3.4.21:
|
/@vue/reactivity@3.4.21:
|
||||||
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
||||||
@ -985,7 +987,7 @@ packages:
|
|||||||
rollup: 4.13.1
|
rollup: 4.13.1
|
||||||
vite: 5.2.6
|
vite: 5.2.6
|
||||||
vue: 3.4.21
|
vue: 3.4.21
|
||||||
vue-router: 4.3.0(vue@3.4.21)
|
vue-router: 4.4.0(vue@3.4.21)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- jiti
|
- jiti
|
||||||
@ -1021,7 +1023,7 @@ packages:
|
|||||||
'@vue/devtools-api': 6.6.1
|
'@vue/devtools-api': 6.6.1
|
||||||
'@vuepress/shared': 2.0.0-rc.9
|
'@vuepress/shared': 2.0.0-rc.9
|
||||||
vue: 3.4.21
|
vue: 3.4.21
|
||||||
vue-router: 4.3.0(vue@3.4.21)
|
vue-router: 4.4.0(vue@3.4.21)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
@ -2739,14 +2741,13 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.4.21
|
vue: 3.4.21
|
||||||
|
|
||||||
/vue-router@4.3.0(vue@3.4.21):
|
/vue-router@4.4.0(vue@3.4.21):
|
||||||
resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
|
resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/devtools-api': 6.6.1
|
'@vue/devtools-api': 6.6.1
|
||||||
vue: 3.4.21
|
vue: 3.4.21
|
||||||
dev: true
|
|
||||||
|
|
||||||
/vue@3.4.21:
|
/vue@3.4.21:
|
||||||
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
|
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
|
||||||
|
12
main.py
12
main.py
@ -1,8 +1,8 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from liteyuki.utils import adapter_manager, driver_manager, init
|
from src.utils import adapter_manager, driver_manager, init
|
||||||
from liteyuki.utils.base.config import load_from_yaml
|
from src.utils.base.config import load_from_yaml
|
||||||
from liteyuki.utils.base.data_manager import StoredConfig, common_db
|
from src.utils.base.data_manager import StoredConfig, common_db
|
||||||
from liteyuki.utils.base.ly_api import liteyuki_api
|
from src.utils.base.ly_api import liteyuki_api
|
||||||
|
|
||||||
if __name__ == "__mp_main__":
|
if __name__ == "__mp_main__":
|
||||||
# Start as multiprocessing
|
# Start as multiprocessing
|
||||||
@ -15,7 +15,7 @@ if __name__ == "__mp_main__":
|
|||||||
nonebot.init(**store_config)
|
nonebot.init(**store_config)
|
||||||
adapter_manager.register()
|
adapter_manager.register()
|
||||||
try:
|
try:
|
||||||
nonebot.load_plugin("liteyuki.liteyuki_main")
|
nonebot.load_plugin("src.liteyuki_main")
|
||||||
nonebot.load_from_toml("pyproject.toml")
|
nonebot.load_from_toml("pyproject.toml")
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
if not isinstance(e, KeyboardInterrupt):
|
if not isinstance(e, KeyboardInterrupt):
|
||||||
@ -24,6 +24,6 @@ if __name__ == "__mp_main__":
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Start as __main__
|
# Start as __main__
|
||||||
from liteyuki.utils.base.reloader import Reloader
|
from src.utils.base.reloader import Reloader
|
||||||
|
|
||||||
nonebot.run()
|
nonebot.run()
|
||||||
|
@ -10,6 +10,7 @@ nonebot-adapter-onebot~=2.4.3
|
|||||||
nonebot-plugin-alconna~=0.46.3
|
nonebot-plugin-alconna~=0.46.3
|
||||||
nonebot_plugin_apscheduler~=0.4.0
|
nonebot_plugin_apscheduler~=0.4.0
|
||||||
nonebot-adapter-satori~=0.11.5
|
nonebot-adapter-satori~=0.11.5
|
||||||
|
numpy~=2.0.0
|
||||||
packaging~=23.1
|
packaging~=23.1
|
||||||
psutil~=5.9.8
|
psutil~=5.9.8
|
||||||
py-cpuinfo~=9.0.0
|
py-cpuinfo~=9.0.0
|
||||||
|
6
src/liteyuki/bot/__init__.py
Normal file
6
src/liteyuki/bot/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import abc
|
||||||
|
|
||||||
|
|
||||||
|
class Bot(abc.ABC):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
10
src/liteyuki/exception.py
Normal file
10
src/liteyuki/exception.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"""exception模块包含了liteyuki运行中的所有错误
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class LiteyukiException(BaseException):
|
||||||
|
"""Liteyuki的异常基类。"""
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.__repr__()
|
@ -1,34 +1,34 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from .core import *
|
from .core import *
|
||||||
from .loader import *
|
from .loader import *
|
||||||
from .dev import *
|
from .dev import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪核心插件",
|
name="轻雪核心插件",
|
||||||
description="轻雪主程序插件,包含了许多初始化的功能",
|
description="轻雪主程序插件,包含了许多初始化的功能",
|
||||||
usage="",
|
usage="",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable": False,
|
"toggleable": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..utils.base.language import Language, get_default_lang_code
|
from ..utils.base.language import Language, get_default_lang_code
|
||||||
|
|
||||||
print("\033[34m" + r"""
|
print("\033[34m" + r"""
|
||||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||||
""" + "\033[0m")
|
""" + "\033[0m")
|
||||||
|
|
||||||
sys_lang = Language(get_default_lang_code())
|
sys_lang = Language(get_default_lang_code())
|
||||||
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
@ -1,47 +1,47 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from git import Repo
|
from git import Repo
|
||||||
|
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
|
|
||||||
remote_urls = [
|
remote_urls = [
|
||||||
"https://github.com/snowykami/LiteyukiBot.git",
|
"https://github.com/snowykami/LiteyukiBot.git",
|
||||||
"https://gitee.com/snowykami/LiteyukiBot.git"
|
"https://gitee.com/snowykami/LiteyukiBot.git"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def detect_update() -> bool:
|
def detect_update() -> bool:
|
||||||
# 对每个远程仓库进行检查,只要有一个仓库有更新,就返回True
|
# 对每个远程仓库进行检查,只要有一个仓库有更新,就返回True
|
||||||
for remote_url in remote_urls:
|
for remote_url in remote_urls:
|
||||||
repo = Repo(".")
|
repo = Repo(".")
|
||||||
repo.remotes.origin.set_url(remote_url)
|
repo.remotes.origin.set_url(remote_url)
|
||||||
repo.remotes.origin.fetch()
|
repo.remotes.origin.fetch()
|
||||||
if repo.head.commit != repo.commit('origin/main'):
|
if repo.head.commit != repo.commit('origin/main'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def update_liteyuki() -> tuple[bool, str]:
|
def update_liteyuki() -> tuple[bool, str]:
|
||||||
"""更新轻雪
|
"""更新轻雪
|
||||||
:return: 是否更新成功,更新变动"""
|
:return: 是否更新成功,更新变动"""
|
||||||
|
|
||||||
if get_config("allow_update", True):
|
if get_config("allow_update", True):
|
||||||
new_commit_detected = detect_update()
|
new_commit_detected = detect_update()
|
||||||
if new_commit_detected:
|
if new_commit_detected:
|
||||||
repo = Repo(".")
|
repo = Repo(".")
|
||||||
logs = ""
|
logs = ""
|
||||||
# 对每个远程仓库进行更新
|
# 对每个远程仓库进行更新
|
||||||
for remote_url in remote_urls:
|
for remote_url in remote_urls:
|
||||||
try:
|
try:
|
||||||
logs += f"\nremote: {remote_url}"
|
logs += f"\nremote: {remote_url}"
|
||||||
repo.remotes.origin.set_url(remote_url)
|
repo.remotes.origin.set_url(remote_url)
|
||||||
repo.remotes.origin.pull()
|
repo.remotes.origin.pull()
|
||||||
diffs = repo.head.commit.diff("origin/main")
|
diffs = repo.head.commit.diff("origin/main")
|
||||||
for diff in diffs.iter_change_type('M'):
|
for diff in diffs.iter_change_type('M'):
|
||||||
logs += f"\n{diff.a_path}"
|
logs += f"\n{diff.a_path}"
|
||||||
return True, logs
|
return True, logs
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
return False, "Nothing Changed"
|
return False, "Nothing Changed"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise PermissionError("Update is not allowed.")
|
raise PermissionError("Update is not allowed.")
|
@ -1,399 +1,399 @@
|
|||||||
import base64
|
import base64
|
||||||
import time
|
import time
|
||||||
from typing import Any, AnyStr
|
from typing import Any, AnyStr
|
||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
import pip
|
import pip
|
||||||
from nonebot import Bot, get_driver, require
|
from nonebot import Bot, get_driver, require
|
||||||
from nonebot.adapters import satori
|
from nonebot.adapters import satori
|
||||||
from nonebot.adapters.onebot.v11 import Message, escape, unescape
|
from nonebot.adapters.onebot.v11 import Message, escape, unescape
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
|
|
||||||
from liteyuki.utils.base.config import get_config, load_from_yaml
|
from src.utils.base.config import get_config, load_from_yaml
|
||||||
from liteyuki.utils.base.data_manager import StoredConfig, TempConfig, common_db
|
from src.utils.base.data_manager import StoredConfig, TempConfig, common_db
|
||||||
from liteyuki.utils.base.language import get_user_lang
|
from src.utils.base.language import get_user_lang
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||||
from liteyuki.utils.base.reloader import Reloader
|
from src.utils.base.reloader import Reloader
|
||||||
from liteyuki.utils import event as event_utils, satori_utils
|
from src.utils import event as event_utils, satori_utils
|
||||||
from .api import update_liteyuki
|
from .api import update_liteyuki
|
||||||
from ..utils.base.ly_function import get_function
|
from ..utils.base.ly_function import get_function
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
|
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
markdown_image = common_db.where_one(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
|
markdown_image = common_db.where_one(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False)
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"liteecho",
|
"liteecho",
|
||||||
Args["text", str, ""],
|
Args["text", str, ""],
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
|
async def _(bot: T_Bot, matcher: Matcher, result: Arparma):
|
||||||
if result.main_args.get("text"):
|
if result.main_args.get("text"):
|
||||||
await matcher.finish(Message(unescape(result.main_args.get("text"))))
|
await matcher.finish(Message(unescape(result.main_args.get("text"))))
|
||||||
else:
|
else:
|
||||||
await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}")
|
await matcher.finish(f"Hello, Liteyuki!\nBot {bot.self_id}")
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
aliases={"更新轻雪"},
|
aliases={"更新轻雪"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"update-liteyuki"
|
"update-liteyuki"
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent):
|
async def _(bot: T_Bot, event: T_MessageEvent):
|
||||||
# 使用git pull更新
|
# 使用git pull更新
|
||||||
|
|
||||||
ulang = get_user_lang(str(event.user.id if isinstance(event, satori.event.Event) else event.user_id))
|
ulang = get_user_lang(str(event.user.id if isinstance(event, satori.event.Event) else event.user_id))
|
||||||
success, logs = update_liteyuki()
|
success, logs = update_liteyuki()
|
||||||
reply = "Liteyuki updated!\n"
|
reply = "Liteyuki updated!\n"
|
||||||
reply += f"```\n{logs}\n```\n"
|
reply += f"```\n{logs}\n```\n"
|
||||||
btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki")
|
btn_restart = md.btn_cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki")
|
||||||
pip.main(["install", "-r", "requirements.txt"])
|
pip.main(["install", "-r", "requirements.txt"])
|
||||||
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}"
|
||||||
await md.send_md(reply, bot, event=event, at_sender=False)
|
await md.send_md(reply, bot, event=event, at_sender=False)
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
aliases={"重启轻雪"},
|
aliases={"重启轻雪"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"reload-liteyuki"
|
"reload-liteyuki"
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
||||||
await matcher.send("Liteyuki reloading")
|
await matcher.send("Liteyuki reloading")
|
||||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||||
|
|
||||||
temp_data.data.update(
|
temp_data.data.update(
|
||||||
{
|
{
|
||||||
"reload" : True,
|
"reload" : True,
|
||||||
"reload_time" : time.time(),
|
"reload_time" : time.time(),
|
||||||
"reload_bot_id" : bot.self_id,
|
"reload_bot_id" : bot.self_id,
|
||||||
"reload_session_type": event_utils.get_message_type(event),
|
"reload_session_type": event_utils.get_message_type(event),
|
||||||
"reload_session_id" : (event.group_id if event.message_type == "group" else event.user_id) if not isinstance(event,
|
"reload_session_id" : (event.group_id if event.message_type == "group" else event.user_id) if not isinstance(event,
|
||||||
satori.event.Event) else event.channel.id,
|
satori.event.Event) else event.channel.id,
|
||||||
"delta_time" : 0
|
"delta_time" : 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
common_db.save(temp_data)
|
common_db.save(temp_data)
|
||||||
Reloader.reload(0)
|
Reloader.reload(0)
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
aliases={"配置"},
|
aliases={"配置"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"config",
|
"config",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"set",
|
"set",
|
||||||
Args["key", str]["value", Any],
|
Args["key", str]["value", Any],
|
||||||
alias=["设置"],
|
alias=["设置"],
|
||||||
|
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"get",
|
"get",
|
||||||
Args["key", str, None],
|
Args["key", str, None],
|
||||||
alias=["查询", "获取"]
|
alias=["查询", "获取"]
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"remove",
|
"remove",
|
||||||
Args["key", str],
|
Args["key", str],
|
||||||
alias=["删除"]
|
alias=["删除"]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher):
|
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher):
|
||||||
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
||||||
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
||||||
if result.subcommands.get("set"):
|
if result.subcommands.get("set"):
|
||||||
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
|
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
|
||||||
try:
|
try:
|
||||||
value = eval(value)
|
value = eval(value)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
stored_config.config[key] = value
|
stored_config.config[key] = value
|
||||||
common_db.save(stored_config)
|
common_db.save(stored_config)
|
||||||
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
|
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
|
||||||
elif result.subcommands.get("get"):
|
elif result.subcommands.get("get"):
|
||||||
key = result.subcommands.get("get").args.get("key")
|
key = result.subcommands.get("get").args.get("key")
|
||||||
file_config = load_from_yaml("config.yml")
|
file_config = load_from_yaml("config.yml")
|
||||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||||
if key:
|
if key:
|
||||||
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
|
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
|
||||||
else:
|
else:
|
||||||
reply = f"{ulang.get('liteyuki.current_config')}"
|
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||||
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
|
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
|
||||||
for k, v in file_config.items():
|
for k, v in file_config.items():
|
||||||
reply += f"\n{k}={v}"
|
reply += f"\n{k}={v}"
|
||||||
reply += "\n```"
|
reply += "\n```"
|
||||||
if len(stored_config.config) > 0:
|
if len(stored_config.config) > 0:
|
||||||
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
||||||
for k, v in stored_config.config.items():
|
for k, v in stored_config.config.items():
|
||||||
reply += f"\n{k}={v} {type(v)}"
|
reply += f"\n{k}={v} {type(v)}"
|
||||||
reply += "\n```"
|
reply += "\n```"
|
||||||
await md.send_md(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
elif result.subcommands.get("remove"):
|
elif result.subcommands.get("remove"):
|
||||||
key = result.subcommands.get("remove").args.get("key")
|
key = result.subcommands.get("remove").args.get("key")
|
||||||
if key in stored_config.config:
|
if key in stored_config.config:
|
||||||
stored_config.config.pop(key)
|
stored_config.config.pop(key)
|
||||||
common_db.save(stored_config)
|
common_db.save(stored_config)
|
||||||
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
|
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
|
||||||
else:
|
else:
|
||||||
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
|
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
aliases={"切换图片模式"},
|
aliases={"切换图片模式"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"switch-image-mode"
|
"switch-image-mode"
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(event: T_MessageEvent, matcher: Matcher):
|
async def _(event: T_MessageEvent, matcher: Matcher):
|
||||||
global markdown_image
|
global markdown_image
|
||||||
# 切换图片模式,False以图片形式发送,True以markdown形式发送
|
# 切换图片模式,False以图片形式发送,True以markdown形式发送
|
||||||
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
||||||
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig())
|
||||||
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
|
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
|
||||||
markdown_image = stored_config.config["markdown_image"]
|
markdown_image = stored_config.config["markdown_image"]
|
||||||
common_db.save(stored_config)
|
common_db.save(stored_config)
|
||||||
await matcher.finish(
|
await matcher.finish(
|
||||||
ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
|
ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"liteyuki-docs",
|
"liteyuki-docs",
|
||||||
),
|
),
|
||||||
aliases={"轻雪文档"},
|
aliases={"轻雪文档"},
|
||||||
).handle()
|
).handle()
|
||||||
# Satori OK
|
# Satori OK
|
||||||
async def _(matcher: Matcher):
|
async def _(matcher: Matcher):
|
||||||
await matcher.finish("https://bot.liteyuki.icu/usage")
|
await matcher.finish("https://bot.liteyuki.icu/usage")
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"/function",
|
"/function",
|
||||||
Args["function", str]["args", MultiVar(str), ()],
|
Args["function", str]["args", MultiVar(str), ()],
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
||||||
"""
|
"""
|
||||||
调用轻雪函数
|
调用轻雪函数
|
||||||
Args:
|
Args:
|
||||||
result:
|
result:
|
||||||
bot:
|
bot:
|
||||||
event:
|
event:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
function_name = result.main_args.get("function")
|
function_name = result.main_args.get("function")
|
||||||
args: tuple[str] = result.main_args.get("args", ())
|
args: tuple[str] = result.main_args.get("args", ())
|
||||||
_args = []
|
_args = []
|
||||||
_kwargs = {
|
_kwargs = {
|
||||||
"USER_ID" : str(event.user_id),
|
"USER_ID" : str(event.user_id),
|
||||||
"GROUP_ID": str(event.group_id) if event.message_type == "group" else "0",
|
"GROUP_ID": str(event.group_id) if event.message_type == "group" else "0",
|
||||||
"BOT_ID" : str(bot.self_id)
|
"BOT_ID" : str(bot.self_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in args:
|
for arg in args:
|
||||||
arg = arg.replace("\\=", "EQUAL_SIGN")
|
arg = arg.replace("\\=", "EQUAL_SIGN")
|
||||||
if "=" in arg:
|
if "=" in arg:
|
||||||
key, value = arg.split("=", 1)
|
key, value = arg.split("=", 1)
|
||||||
value = unescape(value.replace("EQUAL_SIGN", "="))
|
value = unescape(value.replace("EQUAL_SIGN", "="))
|
||||||
try:
|
try:
|
||||||
value = eval(value)
|
value = eval(value)
|
||||||
except:
|
except:
|
||||||
value = value
|
value = value
|
||||||
_kwargs[key] = value
|
_kwargs[key] = value
|
||||||
else:
|
else:
|
||||||
_args.append(arg.replace("EQUAL_SIGN", "="))
|
_args.append(arg.replace("EQUAL_SIGN", "="))
|
||||||
|
|
||||||
ly_func = get_function(function_name)
|
ly_func = get_function(function_name)
|
||||||
ly_func.bot = bot if "BOT_ID" not in _kwargs else nonebot.get_bot(_kwargs["BOT_ID"])
|
ly_func.bot = bot if "BOT_ID" not in _kwargs else nonebot.get_bot(_kwargs["BOT_ID"])
|
||||||
ly_func.matcher = matcher
|
ly_func.matcher = matcher
|
||||||
|
|
||||||
await ly_func(*tuple(_args), **_kwargs)
|
await ly_func(*tuple(_args), **_kwargs)
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"/api",
|
"/api",
|
||||||
Args["api", str]["args", MultiVar(AnyStr), ()],
|
Args["api", str]["args", MultiVar(AnyStr), ()],
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher):
|
||||||
"""
|
"""
|
||||||
调用API
|
调用API
|
||||||
Args:
|
Args:
|
||||||
result:
|
result:
|
||||||
bot:
|
bot:
|
||||||
event:
|
event:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
api_name = result.main_args.get("api")
|
api_name = result.main_args.get("api")
|
||||||
args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数,但每个参数间用空格分隔,空格是%20
|
args: tuple[str] = result.main_args.get("args", ()) # 类似于url参数,但每个参数间用空格分隔,空格是%20
|
||||||
args_dict = {}
|
args_dict = {}
|
||||||
|
|
||||||
for arg in args:
|
for arg in args:
|
||||||
key, value = arg.split("=", 1)
|
key, value = arg.split("=", 1)
|
||||||
|
|
||||||
args_dict[key] = unescape(value.replace("%20", " "))
|
args_dict[key] = unescape(value.replace("%20", " "))
|
||||||
|
|
||||||
if api_name in need_user_id and "user_id" not in args_dict:
|
if api_name in need_user_id and "user_id" not in args_dict:
|
||||||
args_dict["user_id"] = str(event.user_id)
|
args_dict["user_id"] = str(event.user_id)
|
||||||
if api_name in need_group_id and "group_id" not in args_dict and event.message_type == "group":
|
if api_name in need_group_id and "group_id" not in args_dict and event.message_type == "group":
|
||||||
args_dict["group_id"] = str(event.group_id)
|
args_dict["group_id"] = str(event.group_id)
|
||||||
|
|
||||||
if "message" in args_dict:
|
if "message" in args_dict:
|
||||||
args_dict["message"] = Message(eval(args_dict["message"]))
|
args_dict["message"] = Message(eval(args_dict["message"]))
|
||||||
|
|
||||||
if "messages" in args_dict:
|
if "messages" in args_dict:
|
||||||
args_dict["messages"] = Message(eval(args_dict["messages"]))
|
args_dict["messages"] = Message(eval(args_dict["messages"]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await bot.call_api(api_name, **args_dict)
|
result = await bot.call_api(api_name, **args_dict)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result = str(e)
|
result = str(e)
|
||||||
|
|
||||||
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items())
|
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items())
|
||||||
print(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
print(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
||||||
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
|
||||||
|
|
||||||
|
|
||||||
# system hook
|
# system hook
|
||||||
@Bot.on_calling_api # 图片模式检测
|
@Bot.on_calling_api # 图片模式检测
|
||||||
async def test_for_md_image(bot: T_Bot, api: str, data: dict):
|
async def test_for_md_image(bot: T_Bot, api: str, data: dict):
|
||||||
# 截获大图发送,转换为markdown发送
|
# 截获大图发送,转换为markdown发送
|
||||||
if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get(
|
if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get(
|
||||||
"user_id") != bot.self_id:
|
"user_id") != bot.self_id:
|
||||||
if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg":
|
if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg":
|
||||||
session_type = "private"
|
session_type = "private"
|
||||||
session_id = data.get("user_id")
|
session_id = data.get("user_id")
|
||||||
elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg":
|
elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg":
|
||||||
session_type = "group"
|
session_type = "group"
|
||||||
session_id = data.get("group_id")
|
session_id = data.get("group_id")
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
|
if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image":
|
||||||
file: str = data["message"][0].data.get("file")
|
file: str = data["message"][0].data.get("file")
|
||||||
# file:// http:// base64://
|
# file:// http:// base64://
|
||||||
if file.startswith("http"):
|
if file.startswith("http"):
|
||||||
result = await md.send_md(await md.image_async(file), bot, message_type=session_type,
|
result = await md.send_md(await md.image_async(file), bot, message_type=session_type,
|
||||||
session_id=session_id)
|
session_id=session_id)
|
||||||
elif file.startswith("file"):
|
elif file.startswith("file"):
|
||||||
file = file.replace("file://", "")
|
file = file.replace("file://", "")
|
||||||
result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type,
|
result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type,
|
||||||
session_id=session_id)
|
session_id=session_id)
|
||||||
elif file.startswith("base64"):
|
elif file.startswith("base64"):
|
||||||
file_bytes = base64.b64decode(file.replace("base64://", ""))
|
file_bytes = base64.b64decode(file.replace("base64://", ""))
|
||||||
result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id)
|
result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
raise MockApiException(result=result)
|
raise MockApiException(result=result)
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def on_startup():
|
async def on_startup():
|
||||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||||
# 储存重启信息
|
# 储存重启信息
|
||||||
if temp_data.data.get("reload", False):
|
if temp_data.data.get("reload", False):
|
||||||
delta_time = time.time() - temp_data.data.get("reload_time", 0)
|
delta_time = time.time() - temp_data.data.get("reload_time", 0)
|
||||||
temp_data.data["delta_time"] = delta_time
|
temp_data.data["delta_time"] = delta_time
|
||||||
common_db.save(temp_data) # 更新数据
|
common_db.save(temp_data) # 更新数据
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
async def on_shutdown():
|
async def on_shutdown():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def _(bot: T_Bot):
|
async def _(bot: T_Bot):
|
||||||
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
temp_data = common_db.where_one(TempConfig(), default=TempConfig())
|
||||||
if isinstance(bot, satori.Bot):
|
if isinstance(bot, satori.Bot):
|
||||||
await satori_utils.user_infos.load_friends(bot)
|
await satori_utils.user_infos.load_friends(bot)
|
||||||
# 用于重启计时
|
# 用于重启计时
|
||||||
if temp_data.data.get("reload", False):
|
if temp_data.data.get("reload", False):
|
||||||
temp_data.data["reload"] = False
|
temp_data.data["reload"] = False
|
||||||
reload_bot_id = temp_data.data.get("reload_bot_id", 0)
|
reload_bot_id = temp_data.data.get("reload_bot_id", 0)
|
||||||
if reload_bot_id != bot.self_id:
|
if reload_bot_id != bot.self_id:
|
||||||
return
|
return
|
||||||
reload_session_type = temp_data.data.get("reload_session_type", "private")
|
reload_session_type = temp_data.data.get("reload_session_type", "private")
|
||||||
reload_session_id = temp_data.data.get("reload_session_id", 0)
|
reload_session_id = temp_data.data.get("reload_session_id", 0)
|
||||||
delta_time = temp_data.data.get("delta_time", 0)
|
delta_time = temp_data.data.get("delta_time", 0)
|
||||||
common_db.save(temp_data) # 更新数据
|
common_db.save(temp_data) # 更新数据
|
||||||
if isinstance(bot, satori.Bot):
|
if isinstance(bot, satori.Bot):
|
||||||
await bot.send_message(
|
await bot.send_message(
|
||||||
channel_id=reload_session_id,
|
channel_id=reload_session_id,
|
||||||
message="Liteyuki reloaded in %.2f s" % delta_time
|
message="Liteyuki reloaded in %.2f s" % delta_time
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await bot.call_api(
|
await bot.call_api(
|
||||||
"send_msg",
|
"send_msg",
|
||||||
message_type=reload_session_type,
|
message_type=reload_session_type,
|
||||||
user_id=reload_session_id,
|
user_id=reload_session_id,
|
||||||
group_id=reload_session_id,
|
group_id=reload_session_id,
|
||||||
message="Liteyuki reloaded in %.2f s" % delta_time
|
message="Liteyuki reloaded in %.2f s" % delta_time
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# 每天4点更新
|
# 每天4点更新
|
||||||
@scheduler.scheduled_job("cron", hour=4)
|
@scheduler.scheduled_job("cron", hour=4)
|
||||||
async def every_day_update():
|
async def every_day_update():
|
||||||
if get_config("auto_update", default=True):
|
if get_config("auto_update", default=True):
|
||||||
result, logs = update_liteyuki()
|
result, logs = update_liteyuki()
|
||||||
pip.main(["install", "-r", "requirements.txt"])
|
pip.main(["install", "-r", "requirements.txt"])
|
||||||
if result:
|
if result:
|
||||||
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
||||||
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
||||||
Reloader.reload(5)
|
Reloader.reload(5)
|
||||||
else:
|
else:
|
||||||
nonebot.logger.info(logs)
|
nonebot.logger.info(logs)
|
||||||
|
|
||||||
|
|
||||||
# 需要用户id的api
|
# 需要用户id的api
|
||||||
need_user_id = (
|
need_user_id = (
|
||||||
"send_private_msg",
|
"send_private_msg",
|
||||||
"send_msg",
|
"send_msg",
|
||||||
"set_group_card",
|
"set_group_card",
|
||||||
"set_group_special_title",
|
"set_group_special_title",
|
||||||
"get_stranger_info",
|
"get_stranger_info",
|
||||||
"get_group_member_info"
|
"get_group_member_info"
|
||||||
)
|
)
|
||||||
|
|
||||||
need_group_id = (
|
need_group_id = (
|
||||||
"send_group_msg",
|
"send_group_msg",
|
||||||
"send_msg",
|
"send_msg",
|
||||||
"set_group_card",
|
"set_group_card",
|
||||||
"set_group_name",
|
"set_group_name",
|
||||||
|
|
||||||
"set_group_special_title",
|
"set_group_special_title",
|
||||||
"get_group_member_info",
|
"get_group_member_info",
|
||||||
"get_group_member_list",
|
"get_group_member_list",
|
||||||
"get_group_honor_info"
|
"get_group_honor_info"
|
||||||
)
|
)
|
@ -1,55 +1,59 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.reloader import Reloader
|
from src.utils.base.reloader import Reloader
|
||||||
from liteyuki.utils.base.resource import load_resources
|
from src.utils.base.resource import load_resources
|
||||||
|
|
||||||
if get_config("debug", False):
|
if get_config("debug", False):
|
||||||
|
|
||||||
src_directories = (
|
src_directories = (
|
||||||
"liteyuki/liteyuki_main",
|
"src/liteyuki_main",
|
||||||
"liteyuki/plugins",
|
"src/plugins",
|
||||||
"liteyuki/utils",
|
"src/utils",
|
||||||
)
|
)
|
||||||
src_excludes_extensions = (
|
src_excludes_extensions = (
|
||||||
"pyc",
|
"pyc",
|
||||||
)
|
)
|
||||||
|
|
||||||
res_directories = (
|
res_directories = (
|
||||||
"liteyuki/resources",
|
"src/resources",
|
||||||
"resources",
|
"resources",
|
||||||
)
|
)
|
||||||
|
|
||||||
nonebot.logger.info("Liteyuki Reload is enable, watching for file changes...")
|
nonebot.logger.info("Liteyuki Reload is enable, watching for file changes...")
|
||||||
|
|
||||||
class CodeModifiedHandler(FileSystemEventHandler):
|
|
||||||
"""
|
class CodeModifiedHandler(FileSystemEventHandler):
|
||||||
Handler for code file changes
|
"""
|
||||||
"""
|
Handler for code file changes
|
||||||
def on_modified(self, event):
|
"""
|
||||||
if event.src_path.endswith(src_excludes_extensions) or event.is_directory or "__pycache__" in event.src_path:
|
|
||||||
return
|
def on_modified(self, event):
|
||||||
nonebot.logger.info(f"{event.src_path} modified, reloading bot...")
|
if event.src_path.endswith(
|
||||||
Reloader.reload()
|
src_excludes_extensions) or event.is_directory or "__pycache__" in event.src_path:
|
||||||
|
return
|
||||||
|
nonebot.logger.info(f"{event.src_path} modified, reloading bot...")
|
||||||
class ResourceModifiedHandler(FileSystemEventHandler):
|
Reloader.reload()
|
||||||
"""
|
|
||||||
Handler for resource file changes
|
|
||||||
"""
|
class ResourceModifiedHandler(FileSystemEventHandler):
|
||||||
def on_modified(self, event):
|
"""
|
||||||
nonebot.logger.info(f"{event.src_path} modified, reloading resource...")
|
Handler for resource file changes
|
||||||
load_resources()
|
"""
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
code_modified_handler = CodeModifiedHandler()
|
nonebot.logger.info(f"{event.src_path} modified, reloading resource...")
|
||||||
resource_modified_handle = ResourceModifiedHandler()
|
load_resources()
|
||||||
|
|
||||||
observer = Observer()
|
|
||||||
for directory in src_directories:
|
code_modified_handler = CodeModifiedHandler()
|
||||||
observer.schedule(code_modified_handler, directory, recursive=True)
|
resource_modified_handle = ResourceModifiedHandler()
|
||||||
for directory in res_directories:
|
|
||||||
observer.schedule(resource_modified_handle, directory, recursive=True)
|
observer = Observer()
|
||||||
observer.start()
|
for directory in src_directories:
|
||||||
|
observer.schedule(code_modified_handler, directory, recursive=True)
|
||||||
|
for directory in res_directories:
|
||||||
|
observer.schedule(resource_modified_handle, directory, recursive=True)
|
||||||
|
observer.start()
|
@ -1,31 +1,31 @@
|
|||||||
import nonebot.plugin
|
import nonebot.plugin
|
||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from liteyuki.utils import init_log
|
from src.utils import init_log
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.data_manager import InstalledPlugin, plugin_db
|
from src.utils.base.data_manager import InstalledPlugin, plugin_db
|
||||||
from liteyuki.utils.base.resource import load_resources
|
from src.utils.base.resource import load_resources
|
||||||
from liteyuki.utils.message.tools import check_for_package
|
from src.utils.message.tools import check_for_package
|
||||||
|
|
||||||
load_resources()
|
load_resources()
|
||||||
init_log()
|
init_log()
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def load_plugins():
|
async def load_plugins():
|
||||||
nonebot.plugin.load_plugins("liteyuki/plugins")
|
nonebot.plugin.load_plugins("liteyuki/plugins")
|
||||||
# 从数据库读取已安装的插件
|
# 从数据库读取已安装的插件
|
||||||
if not get_config("safe_mode", False):
|
if not get_config("safe_mode", False):
|
||||||
# 安全模式下,不加载插件
|
# 安全模式下,不加载插件
|
||||||
installed_plugins: list[InstalledPlugin] = plugin_db.where_all(InstalledPlugin())
|
installed_plugins: list[InstalledPlugin] = plugin_db.where_all(InstalledPlugin())
|
||||||
if installed_plugins:
|
if installed_plugins:
|
||||||
for installed_plugin in installed_plugins:
|
for installed_plugin in installed_plugins:
|
||||||
if not check_for_package(installed_plugin.module_name):
|
if not check_for_package(installed_plugin.module_name):
|
||||||
nonebot.logger.error(
|
nonebot.logger.error(
|
||||||
f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
|
f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
|
||||||
else:
|
else:
|
||||||
nonebot.load_plugin(installed_plugin.module_name)
|
nonebot.load_plugin(installed_plugin.module_name)
|
||||||
nonebot.plugin.load_plugins("plugins")
|
nonebot.plugin.load_plugins("plugins")
|
||||||
else:
|
else:
|
||||||
nonebot.logger.info("Safe mode is on, no plugin loaded.")
|
nonebot.logger.info("Safe mode is on, no plugin loaded.")
|
@ -1,16 +1,16 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .rt_guide import *
|
from .rt_guide import *
|
||||||
from .crt_matchers import *
|
from .crt_matchers import *
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="CRT生成工具",
|
name="CRT生成工具",
|
||||||
description="一些CRT牌子生成器",
|
description="一些CRT牌子生成器",
|
||||||
usage="我觉得你应该会用",
|
usage="我觉得你应该会用",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
File diff suppressed because it is too large
Load Diff
@ -1,78 +1,78 @@
|
|||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.utils.event import get_user_id
|
from src.utils.event import get_user_id
|
||||||
from liteyuki.utils.base.language import Language
|
from src.utils.base.language import Language
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
from liteyuki.utils.base.resource import get_path
|
from src.utils.base.resource import get_path
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from src.utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
|
||||||
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, Option
|
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, Option
|
||||||
|
|
||||||
crt_cmd = on_alconna(
|
crt_cmd = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"crt",
|
"crt",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"route",
|
"route",
|
||||||
Args["start", str, "沙坪坝"]["end", str, "上新街"],
|
Args["start", str, "沙坪坝"]["end", str, "上新街"],
|
||||||
alias=("r",),
|
alias=("r",),
|
||||||
help_text="查询两地之间的地铁路线"
|
help_text="查询两地之间的地铁路线"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@crt_cmd.assign("route")
|
@crt_cmd.assign("route")
|
||||||
async def _(result: Arparma, event: T_MessageEvent):
|
async def _(result: Arparma, event: T_MessageEvent):
|
||||||
# 获取语言
|
# 获取语言
|
||||||
ulang = Language(get_user_id(event))
|
ulang = Language(get_user_id(event))
|
||||||
|
|
||||||
# 获取参数
|
# 获取参数
|
||||||
# 你也别问我为什么要quote两次,问就是CRT官网的锅,只有这样才可以运行
|
# 你也别问我为什么要quote两次,问就是CRT官网的锅,只有这样才可以运行
|
||||||
start = quote(quote(result.other_args.get("start")))
|
start = quote(quote(result.other_args.get("start")))
|
||||||
end = quote(quote(result.other_args.get("end")))
|
end = quote(quote(result.other_args.get("end")))
|
||||||
|
|
||||||
# 判断参数语言
|
# 判断参数语言
|
||||||
query_lang_code = ""
|
query_lang_code = ""
|
||||||
if start.isalpha() and end.isalpha():
|
if start.isalpha() and end.isalpha():
|
||||||
query_lang_code = "Eng"
|
query_lang_code = "Eng"
|
||||||
|
|
||||||
# 构造请求 URL
|
# 构造请求 URL
|
||||||
url = f"https://www.cqmetro.cn/Front/html/TakeLine!queryYs{query_lang_code}TakeLine.action?entity.startStaName={start}&entity.endStaName={end}"
|
url = f"https://www.cqmetro.cn/Front/html/TakeLine!queryYs{query_lang_code}TakeLine.action?entity.startStaName={start}&entity.endStaName={end}"
|
||||||
|
|
||||||
# 请求数据
|
# 请求数据
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(url) as resp:
|
async with session.get(url) as resp:
|
||||||
result = await resp.json()
|
result = await resp.json()
|
||||||
|
|
||||||
# 检查结果/无则终止
|
# 检查结果/无则终止
|
||||||
if not result.get("result"):
|
if not result.get("result"):
|
||||||
await crt_cmd.send(ulang.get("crt.no_result"))
|
await crt_cmd.send(ulang.get("crt.no_result"))
|
||||||
return
|
return
|
||||||
|
|
||||||
# 模板传参定义
|
# 模板传参定义
|
||||||
templates = {
|
templates = {
|
||||||
"data" : {
|
"data" : {
|
||||||
"result": result["result"],
|
"result": result["result"],
|
||||||
},
|
},
|
||||||
"localization": ulang.get_many(
|
"localization": ulang.get_many(
|
||||||
"crt.station",
|
"crt.station",
|
||||||
"crt.hour",
|
"crt.hour",
|
||||||
"crt.minute",
|
"crt.minute",
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 生成图片
|
# 生成图片
|
||||||
image = await template2image(
|
image = await template2image(
|
||||||
template=get_path("templates/crt_route.html"),
|
template=get_path("templates/crt_route.html"),
|
||||||
templates=templates,
|
templates=templates,
|
||||||
debug=True
|
debug=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# 发送图片
|
# 发送图片
|
||||||
await crt_cmd.send(UniMessage.image(raw=image))
|
await crt_cmd.send(UniMessage.image(raw=image))
|
@ -1,419 +1,419 @@
|
|||||||
import json
|
import json
|
||||||
from typing import List, Any
|
from typing import List, Any
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from arclet.alconna import Alconna
|
from arclet.alconna import Alconna
|
||||||
from nb_cli import run_sync
|
from nb_cli import run_sync
|
||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiVar, Arparma, UniMessage
|
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiVar, Arparma, UniMessage
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from .canvas import *
|
from .canvas import *
|
||||||
from ...utils.base.resource import get_path
|
from ...utils.base.resource import get_path
|
||||||
|
|
||||||
resolution = 256
|
resolution = 256
|
||||||
|
|
||||||
|
|
||||||
class Entrance(BaseModel):
|
class Entrance(BaseModel):
|
||||||
identifier: str
|
identifier: str
|
||||||
size: tuple[int, int]
|
size: tuple[int, int]
|
||||||
dest: List[str]
|
dest: List[str]
|
||||||
|
|
||||||
|
|
||||||
class Station(BaseModel):
|
class Station(BaseModel):
|
||||||
identifier: str
|
identifier: str
|
||||||
chineseName: str
|
chineseName: str
|
||||||
englishName: str
|
englishName: str
|
||||||
position: tuple[int, int]
|
position: tuple[int, int]
|
||||||
|
|
||||||
|
|
||||||
class Line(BaseModel):
|
class Line(BaseModel):
|
||||||
identifier: str
|
identifier: str
|
||||||
chineseName: str
|
chineseName: str
|
||||||
englishName: str
|
englishName: str
|
||||||
color: Any
|
color: Any
|
||||||
stations: List["Station"]
|
stations: List["Station"]
|
||||||
|
|
||||||
|
|
||||||
font_light = get_path("templates/fonts/MiSans/MiSans-Light.woff2")
|
font_light = get_path("templates/fonts/MiSans/MiSans-Light.woff2")
|
||||||
font_bold = get_path("templates/fonts/MiSans/MiSans-Bold.woff2")
|
font_bold = get_path("templates/fonts/MiSans/MiSans-Bold.woff2")
|
||||||
|
|
||||||
@run_sync
|
@run_sync
|
||||||
def generate_entrance_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
def generate_entrance_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
||||||
reso: int = resolution):
|
reso: int = resolution):
|
||||||
"""
|
"""
|
||||||
Generates an entrance sign for the ride.
|
Generates an entrance sign for the ride.
|
||||||
"""
|
"""
|
||||||
width, height = ratio[0] * reso, ratio[1] * reso
|
width, height = ratio[0] * reso, ratio[1] * reso
|
||||||
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.WHITE))
|
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.WHITE))
|
||||||
# 加黑色图框
|
# 加黑色图框
|
||||||
baseCanvas.outline = Img(
|
baseCanvas.outline = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0, 0),
|
parent_point=(0, 0),
|
||||||
point=(0, 0),
|
point=(0, 0),
|
||||||
img=Shape.rectangle(
|
img=Shape.rectangle(
|
||||||
size=(width, height),
|
size=(width, height),
|
||||||
fillet=0,
|
fillet=0,
|
||||||
fill=(0, 0, 0, 0),
|
fill=(0, 0, 0, 0),
|
||||||
width=15,
|
width=15,
|
||||||
outline=Color.BLACK
|
outline=Color.BLACK
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
baseCanvas.contentPanel = Panel(
|
baseCanvas.contentPanel = Panel(
|
||||||
uv_size=(width, height),
|
uv_size=(width, height),
|
||||||
box_size=(width - 28, height - 28),
|
box_size=(width - 28, height - 28),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
)
|
)
|
||||||
|
|
||||||
linePanelHeight = 0.7 * ratio[1]
|
linePanelHeight = 0.7 * ratio[1]
|
||||||
linePanelWidth = linePanelHeight * 1.3
|
linePanelWidth = linePanelHeight * 1.3
|
||||||
|
|
||||||
# 画线路面板部分
|
# 画线路面板部分
|
||||||
|
|
||||||
for i, line in enumerate(lineInfo):
|
for i, line in enumerate(lineInfo):
|
||||||
linePanel = baseCanvas.contentPanel.__dict__[f"Line_{i}_Panel"] = Panel(
|
linePanel = baseCanvas.contentPanel.__dict__[f"Line_{i}_Panel"] = Panel(
|
||||||
uv_size=ratio,
|
uv_size=ratio,
|
||||||
box_size=(linePanelWidth, linePanelHeight),
|
box_size=(linePanelWidth, linePanelHeight),
|
||||||
parent_point=(i * linePanelWidth / ratio[0], 1),
|
parent_point=(i * linePanelWidth / ratio[0], 1),
|
||||||
point=(0, 1),
|
point=(0, 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
linePanel.colorCube = Img(
|
linePanel.colorCube = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.15, 1),
|
box_size=(0.15, 1),
|
||||||
parent_point=(0.125, 1),
|
parent_point=(0.125, 1),
|
||||||
point=(0, 1),
|
point=(0, 1),
|
||||||
img=Shape.rectangle(
|
img=Shape.rectangle(
|
||||||
size=(100, 100),
|
size=(100, 100),
|
||||||
fillet=0,
|
fillet=0,
|
||||||
fill=line.color,
|
fill=line.color,
|
||||||
),
|
),
|
||||||
keep_ratio=False
|
keep_ratio=False
|
||||||
)
|
)
|
||||||
|
|
||||||
textPanel = linePanel.TextPanel = Panel(
|
textPanel = linePanel.TextPanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.625, 1),
|
box_size=(0.625, 1),
|
||||||
parent_point=(1, 1),
|
parent_point=(1, 1),
|
||||||
point=(1, 1)
|
point=(1, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 中文线路名
|
# 中文线路名
|
||||||
textPanel.namePanel = Panel(
|
textPanel.namePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 2 / 3),
|
box_size=(1, 2 / 3),
|
||||||
parent_point=(0, 0),
|
parent_point=(0, 0),
|
||||||
point=(0, 0),
|
point=(0, 0),
|
||||||
)
|
)
|
||||||
nameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.namePanel".format(i))
|
nameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.namePanel".format(i))
|
||||||
textPanel.namePanel.text = Text(
|
textPanel.namePanel.text = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
text=line.chineseName,
|
text=line.chineseName,
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(nameSize[1] * 0.5),
|
font_size=int(nameSize[1] * 0.5),
|
||||||
force_size=True,
|
force_size=True,
|
||||||
font=font_bold
|
font=font_bold
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 英文线路名
|
# 英文线路名
|
||||||
textPanel.englishNamePanel = Panel(
|
textPanel.englishNamePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1 / 3),
|
box_size=(1, 1 / 3),
|
||||||
parent_point=(0, 1),
|
parent_point=(0, 1),
|
||||||
point=(0, 1),
|
point=(0, 1),
|
||||||
)
|
)
|
||||||
englishNameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.englishNamePanel".format(i))
|
englishNameSize = baseCanvas.get_actual_pixel_size("contentPanel.Line_{}_Panel.TextPanel.englishNamePanel".format(i))
|
||||||
textPanel.englishNamePanel.text = Text(
|
textPanel.englishNamePanel.text = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
text=line.englishName,
|
text=line.englishName,
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(englishNameSize[1] * 0.6),
|
font_size=int(englishNameSize[1] * 0.6),
|
||||||
force_size=True,
|
force_size=True,
|
||||||
font=font_light
|
font=font_light
|
||||||
)
|
)
|
||||||
|
|
||||||
# 画名称部分
|
# 画名称部分
|
||||||
namePanel = baseCanvas.contentPanel.namePanel = Panel(
|
namePanel = baseCanvas.contentPanel.namePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 0.4),
|
box_size=(1, 0.4),
|
||||||
parent_point=(0.5, 0),
|
parent_point=(0.5, 0),
|
||||||
point=(0.5, 0),
|
point=(0.5, 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
namePanel.text = Text(
|
namePanel.text = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
text=name,
|
text=name,
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(height * 0.3),
|
font_size=int(height * 0.3),
|
||||||
force_size=True,
|
force_size=True,
|
||||||
font=font_bold
|
font=font_bold
|
||||||
)
|
)
|
||||||
|
|
||||||
aliasesPanel = baseCanvas.contentPanel.aliasesPanel = Panel(
|
aliasesPanel = baseCanvas.contentPanel.aliasesPanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 0.5),
|
box_size=(1, 0.5),
|
||||||
parent_point=(0.5, 1),
|
parent_point=(0.5, 1),
|
||||||
point=(0.5, 1),
|
point=(0.5, 1),
|
||||||
|
|
||||||
)
|
)
|
||||||
for j, alias in enumerate(aliases):
|
for j, alias in enumerate(aliases):
|
||||||
aliasesPanel.__dict__[alias] = Text(
|
aliasesPanel.__dict__[alias] = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.35, 0.5),
|
box_size=(0.35, 0.5),
|
||||||
parent_point=(0.5, 0.5 * j),
|
parent_point=(0.5, 0.5 * j),
|
||||||
point=(0.5, 0),
|
point=(0.5, 0),
|
||||||
text=alias,
|
text=alias,
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(height * 0.15),
|
font_size=int(height * 0.15),
|
||||||
font=font_light
|
font=font_light
|
||||||
)
|
)
|
||||||
|
|
||||||
# 画入口标识
|
# 画入口标识
|
||||||
entrancePanel = baseCanvas.contentPanel.entrancePanel = Panel(
|
entrancePanel = baseCanvas.contentPanel.entrancePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.2, 1),
|
box_size=(0.2, 1),
|
||||||
parent_point=(1, 0.5),
|
parent_point=(1, 0.5),
|
||||||
point=(1, 0.5),
|
point=(1, 0.5),
|
||||||
)
|
)
|
||||||
# 中文文本
|
# 中文文本
|
||||||
entrancePanel.namePanel = Panel(
|
entrancePanel.namePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 0.5),
|
box_size=(1, 0.5),
|
||||||
parent_point=(1, 0),
|
parent_point=(1, 0),
|
||||||
point=(1, 0),
|
point=(1, 0),
|
||||||
)
|
)
|
||||||
entrancePanel.namePanel.text = Text(
|
entrancePanel.namePanel.text = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0, 0.5),
|
parent_point=(0, 0.5),
|
||||||
point=(0, 0.5),
|
point=(0, 0.5),
|
||||||
text=f"{entranceIdentifier}出入口",
|
text=f"{entranceIdentifier}出入口",
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(height * 0.2),
|
font_size=int(height * 0.2),
|
||||||
force_size=True,
|
force_size=True,
|
||||||
font=font_bold
|
font=font_bold
|
||||||
)
|
)
|
||||||
# 英文文本
|
# 英文文本
|
||||||
entrancePanel.englishNamePanel = Panel(
|
entrancePanel.englishNamePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 0.5),
|
box_size=(1, 0.5),
|
||||||
parent_point=(1, 1),
|
parent_point=(1, 1),
|
||||||
point=(1, 1),
|
point=(1, 1),
|
||||||
)
|
)
|
||||||
entrancePanel.englishNamePanel.text = Text(
|
entrancePanel.englishNamePanel.text = Text(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0, 0.5),
|
parent_point=(0, 0.5),
|
||||||
point=(0, 0.5),
|
point=(0, 0.5),
|
||||||
text=f"Entrance {entranceIdentifier}",
|
text=f"Entrance {entranceIdentifier}",
|
||||||
color=Color.BLACK,
|
color=Color.BLACK,
|
||||||
font_size=int(height * 0.15),
|
font_size=int(height * 0.15),
|
||||||
force_size=True,
|
force_size=True,
|
||||||
font=font_light
|
font=font_light
|
||||||
)
|
)
|
||||||
|
|
||||||
return baseCanvas.base_img.tobytes()
|
return baseCanvas.base_img.tobytes()
|
||||||
|
|
||||||
|
|
||||||
crt_alc = on_alconna(
|
crt_alc = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"crt",
|
"crt",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"entrance",
|
"entrance",
|
||||||
Args["name", str]["lines", str, ""]["entrance", int, 1], # /crt entrance 璧山&Bishan 1号线&Line1&#ff0000,27号线&Line1&#ff0000 1A
|
Args["name", str]["lines", str, ""]["entrance", int, 1], # /crt entrance 璧山&Bishan 1号线&Line1&#ff0000,27号线&Line1&#ff0000 1A
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@crt_alc.assign("entrance")
|
@crt_alc.assign("entrance")
|
||||||
async def _(result: Arparma):
|
async def _(result: Arparma):
|
||||||
args = result.subcommands.get("entrance").args
|
args = result.subcommands.get("entrance").args
|
||||||
name = args["name"]
|
name = args["name"]
|
||||||
lines = args["lines"]
|
lines = args["lines"]
|
||||||
entrance = args["entrance"]
|
entrance = args["entrance"]
|
||||||
line_info = []
|
line_info = []
|
||||||
for line in lines.split(","):
|
for line in lines.split(","):
|
||||||
line_args = line.split("&")
|
line_args = line.split("&")
|
||||||
line_info.append(Line(
|
line_info.append(Line(
|
||||||
identifier=1,
|
identifier=1,
|
||||||
chineseName=line_args[0],
|
chineseName=line_args[0],
|
||||||
englishName=line_args[1],
|
englishName=line_args[1],
|
||||||
color=line_args[2],
|
color=line_args[2],
|
||||||
stations=[]
|
stations=[]
|
||||||
))
|
))
|
||||||
img_bytes = await generate_entrance_sign(
|
img_bytes = await generate_entrance_sign(
|
||||||
name=name,
|
name=name,
|
||||||
aliases=name.split("&"),
|
aliases=name.split("&"),
|
||||||
lineInfo=line_info,
|
lineInfo=line_info,
|
||||||
entranceIdentifier=entrance,
|
entranceIdentifier=entrance,
|
||||||
ratio=(8, 1),
|
ratio=(8, 1),
|
||||||
reso=256,
|
reso=256,
|
||||||
)
|
)
|
||||||
await crt_alc.finish(
|
await crt_alc.finish(
|
||||||
UniMessage.image(raw=img_bytes)
|
UniMessage.image(raw=img_bytes)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_platform_line_pic(line: Line, station: Station, ratio=None, reso: int = resolution):
|
def generate_platform_line_pic(line: Line, station: Station, ratio=None, reso: int = resolution):
|
||||||
"""
|
"""
|
||||||
生成站台线路图
|
生成站台线路图
|
||||||
:param line: 线路对象
|
:param line: 线路对象
|
||||||
:param station: 本站点对象
|
:param station: 本站点对象
|
||||||
:param ratio: 比例
|
:param ratio: 比例
|
||||||
:param reso: 分辨率,1:reso
|
:param reso: 分辨率,1:reso
|
||||||
:return: 两个方向的站牌
|
:return: 两个方向的站牌
|
||||||
"""
|
"""
|
||||||
if ratio is None:
|
if ratio is None:
|
||||||
ratio = [4, 1]
|
ratio = [4, 1]
|
||||||
width, height = ratio[0] * reso, ratio[1] * reso
|
width, height = ratio[0] * reso, ratio[1] * reso
|
||||||
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.YELLOW))
|
baseCanvas = Canvas(Image.new("RGBA", (width, height), Color.YELLOW))
|
||||||
# 加黑色图框
|
# 加黑色图框
|
||||||
baseCanvas.linePanel = Panel(
|
baseCanvas.linePanel = Panel(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.8, 0.15),
|
box_size=(0.8, 0.15),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
)
|
)
|
||||||
|
|
||||||
# 直线块
|
# 直线块
|
||||||
baseCanvas.linePanel.recLine = Img(
|
baseCanvas.linePanel.recLine = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=Shape.rectangle(
|
img=Shape.rectangle(
|
||||||
size=(10, 10),
|
size=(10, 10),
|
||||||
fill=line.color,
|
fill=line.color,
|
||||||
),
|
),
|
||||||
keep_ratio=False
|
keep_ratio=False
|
||||||
)
|
)
|
||||||
# 灰色直线块
|
# 灰色直线块
|
||||||
baseCanvas.linePanel.recLineGrey = Img(
|
baseCanvas.linePanel.recLineGrey = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=Shape.rectangle(
|
img=Shape.rectangle(
|
||||||
size=(10, 10),
|
size=(10, 10),
|
||||||
fill=Color.GREY,
|
fill=Color.GREY,
|
||||||
),
|
),
|
||||||
keep_ratio=False
|
keep_ratio=False
|
||||||
)
|
)
|
||||||
# 生成各站圆点
|
# 生成各站圆点
|
||||||
outline_width = 40
|
outline_width = 40
|
||||||
circleForward = Shape.circular(
|
circleForward = Shape.circular(
|
||||||
radius=200,
|
radius=200,
|
||||||
fill=Color.WHITE,
|
fill=Color.WHITE,
|
||||||
width=outline_width,
|
width=outline_width,
|
||||||
outline=line.color,
|
outline=line.color,
|
||||||
)
|
)
|
||||||
|
|
||||||
circleThisPanel = Canvas(Image.new("RGBA", (200, 200), (0, 0, 0, 0)))
|
circleThisPanel = Canvas(Image.new("RGBA", (200, 200), (0, 0, 0, 0)))
|
||||||
circleThisPanel.circleOuter = Img(
|
circleThisPanel.circleOuter = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1, 1),
|
box_size=(1, 1),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=Shape.circular(
|
img=Shape.circular(
|
||||||
radius=200,
|
radius=200,
|
||||||
fill=Color.WHITE,
|
fill=Color.WHITE,
|
||||||
width=outline_width,
|
width=outline_width,
|
||||||
outline=line.color,
|
outline=line.color,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
circleThisPanel.circleOuter.circleInner = Img(
|
circleThisPanel.circleOuter.circleInner = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(0.7, 0.7),
|
box_size=(0.7, 0.7),
|
||||||
parent_point=(0.5, 0.5),
|
parent_point=(0.5, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=Shape.circular(
|
img=Shape.circular(
|
||||||
radius=200,
|
radius=200,
|
||||||
fill=line.color,
|
fill=line.color,
|
||||||
width=0,
|
width=0,
|
||||||
outline=line.color,
|
outline=line.color,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
circleThisPanel.export("a.png", alpha=True)
|
circleThisPanel.export("a.png", alpha=True)
|
||||||
circleThis = circleThisPanel.base_img
|
circleThis = circleThisPanel.base_img
|
||||||
|
|
||||||
circlePassed = Shape.circular(
|
circlePassed = Shape.circular(
|
||||||
radius=200,
|
radius=200,
|
||||||
fill=Color.WHITE,
|
fill=Color.WHITE,
|
||||||
width=outline_width,
|
width=outline_width,
|
||||||
outline=Color.GREY,
|
outline=Color.GREY,
|
||||||
)
|
)
|
||||||
|
|
||||||
arrival = False
|
arrival = False
|
||||||
distance = 1 / (len(line.stations) - 1)
|
distance = 1 / (len(line.stations) - 1)
|
||||||
for i, sta in enumerate(line.stations):
|
for i, sta in enumerate(line.stations):
|
||||||
box_size = (1.618, 1.618)
|
box_size = (1.618, 1.618)
|
||||||
if sta.identifier == station.identifier:
|
if sta.identifier == station.identifier:
|
||||||
arrival = True
|
arrival = True
|
||||||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=(1.8, 1.8),
|
box_size=(1.8, 1.8),
|
||||||
parent_point=(distance * i, 0.5),
|
parent_point=(distance * i, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=circleThis,
|
img=circleThis,
|
||||||
keep_ratio=True
|
keep_ratio=True
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if arrival:
|
if arrival:
|
||||||
# 后方站绘制
|
# 后方站绘制
|
||||||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=box_size,
|
box_size=box_size,
|
||||||
parent_point=(distance * i, 0.5),
|
parent_point=(distance * i, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=circleForward,
|
img=circleForward,
|
||||||
keep_ratio=True
|
keep_ratio=True
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 前方站绘制
|
# 前方站绘制
|
||||||
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
baseCanvas.linePanel.recLine.__dict__["station_{}".format(sta.identifier)] = Img(
|
||||||
uv_size=(1, 1),
|
uv_size=(1, 1),
|
||||||
box_size=box_size,
|
box_size=box_size,
|
||||||
parent_point=(distance * i, 0.5),
|
parent_point=(distance * i, 0.5),
|
||||||
point=(0.5, 0.5),
|
point=(0.5, 0.5),
|
||||||
img=circlePassed,
|
img=circlePassed,
|
||||||
keep_ratio=True
|
keep_ratio=True
|
||||||
)
|
)
|
||||||
return baseCanvas
|
return baseCanvas
|
||||||
|
|
||||||
|
|
||||||
def generate_platform_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
def generate_platform_sign(name: str, aliases: List[str], lineInfo: List[Line], entranceIdentifier: str, ratio: tuple[int | float, int | float],
|
||||||
reso: int = resolution
|
reso: int = resolution
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# def main():
|
# def main():
|
||||||
# generate_entrance_sign(
|
# generate_entrance_sign(
|
||||||
# "璧山",
|
# "璧山",
|
||||||
# aliases=["Bishan"],
|
# aliases=["Bishan"],
|
||||||
# lineInfo=[
|
# lineInfo=[
|
||||||
#
|
#
|
||||||
# Line(identifier="2", chineseName="1号线", englishName="Line 1", color=Color.RED, stations=[]),
|
# Line(identifier="2", chineseName="1号线", englishName="Line 1", color=Color.RED, stations=[]),
|
||||||
# Line(identifier="3", chineseName="27号线", englishName="Line 27", color="#685bc7", stations=[]),
|
# Line(identifier="3", chineseName="27号线", englishName="Line 27", color="#685bc7", stations=[]),
|
||||||
# Line(identifier="1", chineseName="璧铜线", englishName="BT Line", color="#685BC7", stations=[]),
|
# Line(identifier="1", chineseName="璧铜线", englishName="BT Line", color="#685BC7", stations=[]),
|
||||||
# ],
|
# ],
|
||||||
# entranceIdentifier="1",
|
# entranceIdentifier="1",
|
||||||
# ratio=(8, 1)
|
# ratio=(8, 1)
|
||||||
# )
|
# )
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# main()
|
# main()
|
@ -1,125 +1,125 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from nonebot import on_message, require
|
from nonebot import on_message, require
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.utils.base.data import Database, LiteModel
|
from src.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md
|
from src.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna
|
from nonebot_plugin_alconna import on_alconna
|
||||||
from arclet.alconna import Arparma, Alconna, Args, Option, Subcommand
|
from arclet.alconna import Arparma, Alconna, Args, Option, Subcommand
|
||||||
|
|
||||||
|
|
||||||
class Node(LiteModel):
|
class Node(LiteModel):
|
||||||
TABLE_NAME: str = "node"
|
TABLE_NAME: str = "node"
|
||||||
bot_id: str = ""
|
bot_id: str = ""
|
||||||
session_type: str = ""
|
session_type: str = ""
|
||||||
session_id: str = ""
|
session_id: str = ""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.bot_id}.{self.session_type}.{self.session_id}"
|
return f"{self.bot_id}.{self.session_type}.{self.session_id}"
|
||||||
|
|
||||||
|
|
||||||
class Push(LiteModel):
|
class Push(LiteModel):
|
||||||
TABLE_NAME: str = "push"
|
TABLE_NAME: str = "push"
|
||||||
source: Node = Node()
|
source: Node = Node()
|
||||||
target: Node = Node()
|
target: Node = Node()
|
||||||
inde: int = 0
|
inde: int = 0
|
||||||
|
|
||||||
|
|
||||||
pushes_db = Database("data/pushes.ldb")
|
pushes_db = Database("data/pushes.ldb")
|
||||||
pushes_db.auto_migrate(Push(), Node())
|
pushes_db.auto_migrate(Push(), Node())
|
||||||
|
|
||||||
alc = Alconna(
|
alc = Alconna(
|
||||||
"lep",
|
"lep",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"add",
|
"add",
|
||||||
Args["source", str],
|
Args["source", str],
|
||||||
Args["target", str],
|
Args["target", str],
|
||||||
Option("bidirectional", Args["bidirectional", bool])
|
Option("bidirectional", Args["bidirectional", bool])
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"rm",
|
"rm",
|
||||||
Args["index", int],
|
Args["index", int],
|
||||||
|
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"list",
|
"list",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add_push = on_alconna(alc)
|
add_push = on_alconna(alc)
|
||||||
|
|
||||||
|
|
||||||
@add_push.handle()
|
@add_push.handle()
|
||||||
async def _(result: Arparma):
|
async def _(result: Arparma):
|
||||||
"""bot_id.session_type.session_id"""
|
"""bot_id.session_type.session_id"""
|
||||||
if result.subcommands.get("add"):
|
if result.subcommands.get("add"):
|
||||||
source = result.subcommands["add"].args.get("source")
|
source = result.subcommands["add"].args.get("source")
|
||||||
target = result.subcommands["add"].args.get("target")
|
target = result.subcommands["add"].args.get("target")
|
||||||
if source and target:
|
if source and target:
|
||||||
source = source.split(".")
|
source = source.split(".")
|
||||||
target = target.split(".")
|
target = target.split(".")
|
||||||
push1 = Push(
|
push1 = Push(
|
||||||
source=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
source=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
||||||
target=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
target=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
||||||
inde=len(pushes_db.where_all(Push(), default=[]))
|
inde=len(pushes_db.where_all(Push(), default=[]))
|
||||||
)
|
)
|
||||||
pushes_db.save(push1)
|
pushes_db.save(push1)
|
||||||
|
|
||||||
if result.subcommands["add"].args.get("bidirectional"):
|
if result.subcommands["add"].args.get("bidirectional"):
|
||||||
push2 = Push(
|
push2 = Push(
|
||||||
source=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
source=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
|
||||||
target=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
target=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
|
||||||
inde=len(pushes_db.where_all(Push(), default=[]))
|
inde=len(pushes_db.where_all(Push(), default=[]))
|
||||||
)
|
)
|
||||||
pushes_db.save(push2)
|
pushes_db.save(push2)
|
||||||
await add_push.finish("添加成功")
|
await add_push.finish("添加成功")
|
||||||
else:
|
else:
|
||||||
await add_push.finish("参数缺失")
|
await add_push.finish("参数缺失")
|
||||||
elif result.subcommands.get("rm"):
|
elif result.subcommands.get("rm"):
|
||||||
index = result.subcommands["rm"].args.get("index")
|
index = result.subcommands["rm"].args.get("index")
|
||||||
if index is not None:
|
if index is not None:
|
||||||
try:
|
try:
|
||||||
pushes_db.delete(Push(), "inde = ?", index)
|
pushes_db.delete(Push(), "inde = ?", index)
|
||||||
await add_push.finish("删除成功")
|
await add_push.finish("删除成功")
|
||||||
except IndexError:
|
except IndexError:
|
||||||
await add_push.finish("索引错误")
|
await add_push.finish("索引错误")
|
||||||
else:
|
else:
|
||||||
await add_push.finish("参数缺失")
|
await add_push.finish("参数缺失")
|
||||||
elif result.subcommands.get("list"):
|
elif result.subcommands.get("list"):
|
||||||
await add_push.finish(
|
await add_push.finish(
|
||||||
"\n".join([f"{push.inde} {push.source.bot_id}.{push.source.session_type}.{push.source.session_id} -> "
|
"\n".join([f"{push.inde} {push.source.bot_id}.{push.source.session_type}.{push.source.session_id} -> "
|
||||||
f"{push.target.bot_id}.{push.target.session_type}.{push.target.session_id}" for i, push in
|
f"{push.target.bot_id}.{push.target.session_type}.{push.target.session_id}" for i, push in
|
||||||
enumerate(pushes_db.where_all(Push(), default=[]))]))
|
enumerate(pushes_db.where_all(Push(), default=[]))]))
|
||||||
else:
|
else:
|
||||||
await add_push.finish("参数错误")
|
await add_push.finish("参数错误")
|
||||||
|
|
||||||
|
|
||||||
@on_message(block=False).handle()
|
@on_message(block=False).handle()
|
||||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
async def _(event: T_MessageEvent, bot: T_Bot):
|
||||||
for push in pushes_db.where_all(Push(), default=[]):
|
for push in pushes_db.where_all(Push(), default=[]):
|
||||||
if str(push.source) == f"{bot.self_id}.{event.message_type}.{event.user_id if event.message_type == 'private' else event.group_id}":
|
if str(push.source) == f"{bot.self_id}.{event.message_type}.{event.user_id if event.message_type == 'private' else event.group_id}":
|
||||||
bot2 = nonebot.get_bot(push.target.bot_id)
|
bot2 = nonebot.get_bot(push.target.bot_id)
|
||||||
msg_formatted = ""
|
msg_formatted = ""
|
||||||
for line in str(event.message).split("\n"):
|
for line in str(event.message).split("\n"):
|
||||||
msg_formatted += f"**{line.strip()}**\n"
|
msg_formatted += f"**{line.strip()}**\n"
|
||||||
push_message = (
|
push_message = (
|
||||||
f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n"
|
f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n"
|
||||||
f"{msg_formatted}")
|
f"{msg_formatted}")
|
||||||
await md.send_md(push_message, bot2, message_type=push.target.session_type,
|
await md.send_md(push_message, bot2, message_type=push.target.session_type,
|
||||||
session_id=push.target.session_id)
|
session_id=push.target.session_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪事件推送",
|
name="轻雪事件推送",
|
||||||
description="事件推送插件,支持单向和双向推送,支持跨Bot推送",
|
description="事件推送插件,支持单向和双向推送,支持跨Bot推送",
|
||||||
usage="",
|
usage="",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,52 +1,52 @@
|
|||||||
from nonebot import on_command, require
|
from nonebot import on_command, require
|
||||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||||
from nonebot.params import CommandArg
|
from nonebot.params import CommandArg
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent, v11
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent, v11
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||||
from liteyuki.utils.message.html_tool import *
|
from src.utils.message.html_tool import *
|
||||||
|
|
||||||
md_test = on_command("mdts", permission=SUPERUSER)
|
md_test = on_command("mdts", permission=SUPERUSER)
|
||||||
btn_test = on_command("btnts", permission=SUPERUSER)
|
btn_test = on_command("btnts", permission=SUPERUSER)
|
||||||
latex_test = on_command("latex", permission=SUPERUSER)
|
latex_test = on_command("latex", permission=SUPERUSER)
|
||||||
|
|
||||||
|
|
||||||
@md_test.handle()
|
@md_test.handle()
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
||||||
await md.send_md(
|
await md.send_md(
|
||||||
v11.utils.unescape(str(arg)),
|
v11.utils.unescape(str(arg)),
|
||||||
bot,
|
bot,
|
||||||
message_type=event.message_type,
|
message_type=event.message_type,
|
||||||
session_id=event.user_id if event.message_type == "private" else event.group_id
|
session_id=event.user_id if event.message_type == "private" else event.group_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@btn_test.handle()
|
@btn_test.handle()
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
||||||
await md.send_btn(
|
await md.send_btn(
|
||||||
str(arg),
|
str(arg),
|
||||||
bot,
|
bot,
|
||||||
message_type=event.message_type,
|
message_type=event.message_type,
|
||||||
session_id=event.user_id if event.message_type == "private" else event.group_id
|
session_id=event.user_id if event.message_type == "private" else event.group_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@latex_test.handle()
|
@latex_test.handle()
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()):
|
||||||
latex_text = f"$${v11.utils.unescape(str(arg))}$$"
|
latex_text = f"$${v11.utils.unescape(str(arg))}$$"
|
||||||
img = await md_to_pic(latex_text)
|
img = await md_to_pic(latex_text)
|
||||||
await bot.send(event=event, message=MessageSegment.image(img))
|
await bot.send(event=event, message=MessageSegment.image(img))
|
||||||
|
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪Markdown测试",
|
name="轻雪Markdown测试",
|
||||||
description="用于测试Markdown的插件",
|
description="用于测试Markdown的插件",
|
||||||
usage="",
|
usage="",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,15 +1,15 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="Minecraft工具箱",
|
name="Minecraft工具箱",
|
||||||
description="一些Minecraft相关工具箱",
|
description="一些Minecraft相关工具箱",
|
||||||
usage="我觉得你应该会用",
|
usage="我觉得你应该会用",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,15 +1,15 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .minesweeper import *
|
from .minesweeper import *
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪小游戏",
|
name="轻雪小游戏",
|
||||||
description="内置了一些小游戏",
|
description="内置了一些小游戏",
|
||||||
usage="",
|
usage="",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,169 +1,169 @@
|
|||||||
import random
|
import random
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md
|
from src.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
class Dot(BaseModel):
|
class Dot(BaseModel):
|
||||||
row: int
|
row: int
|
||||||
col: int
|
col: int
|
||||||
mask: bool = True
|
mask: bool = True
|
||||||
value: int = 0
|
value: int = 0
|
||||||
flagged: bool = False
|
flagged: bool = False
|
||||||
|
|
||||||
|
|
||||||
class Minesweeper:
|
class Minesweeper:
|
||||||
# 0-8: number of mines around, 9: mine, -1: undefined
|
# 0-8: number of mines around, 9: mine, -1: undefined
|
||||||
NUMS = "⓪①②③④⑤⑥⑦⑧🅑⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳"
|
NUMS = "⓪①②③④⑤⑥⑦⑧🅑⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳"
|
||||||
MASK = "🅜"
|
MASK = "🅜"
|
||||||
FLAG = "🅕"
|
FLAG = "🅕"
|
||||||
MINE = "🅑"
|
MINE = "🅑"
|
||||||
|
|
||||||
def __init__(self, rows, cols, num_mines, session_type, session_id):
|
def __init__(self, rows, cols, num_mines, session_type, session_id):
|
||||||
assert rows > 0 and cols > 0 and 0 < num_mines < rows * cols
|
assert rows > 0 and cols > 0 and 0 < num_mines < rows * cols
|
||||||
self.session_type = session_type
|
self.session_type = session_type
|
||||||
self.session_id = session_id
|
self.session_id = session_id
|
||||||
self.rows = rows
|
self.rows = rows
|
||||||
self.cols = cols
|
self.cols = cols
|
||||||
self.num_mines = num_mines
|
self.num_mines = num_mines
|
||||||
self.board: list[list[Dot]] = [[Dot(row=i, col=j) for j in range(cols)] for i in range(rows)]
|
self.board: list[list[Dot]] = [[Dot(row=i, col=j) for j in range(cols)] for i in range(rows)]
|
||||||
self.is_first = True
|
self.is_first = True
|
||||||
|
|
||||||
def reveal(self, row, col) -> bool:
|
def reveal(self, row, col) -> bool:
|
||||||
"""
|
"""
|
||||||
展开
|
展开
|
||||||
Args:
|
Args:
|
||||||
row:
|
row:
|
||||||
col:
|
col:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
游戏是否继续
|
游戏是否继续
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.is_first:
|
if self.is_first:
|
||||||
# 第一次展开,生成地雷
|
# 第一次展开,生成地雷
|
||||||
self.generate_board(self.board[row][col])
|
self.generate_board(self.board[row][col])
|
||||||
self.is_first = False
|
self.is_first = False
|
||||||
|
|
||||||
if self.board[row][col].value == 9:
|
if self.board[row][col].value == 9:
|
||||||
self.board[row][col].mask = False
|
self.board[row][col].mask = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self.board[row][col].mask:
|
if not self.board[row][col].mask:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.board[row][col].mask = False
|
self.board[row][col].mask = False
|
||||||
|
|
||||||
if self.board[row][col].value == 0:
|
if self.board[row][col].value == 0:
|
||||||
self.reveal_neighbors(row, col)
|
self.reveal_neighbors(row, col)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_win(self) -> bool:
|
def is_win(self) -> bool:
|
||||||
"""
|
"""
|
||||||
是否胜利
|
是否胜利
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.cols):
|
for col in range(self.cols):
|
||||||
if self.board[row][col].mask and self.board[row][col].value != 9:
|
if self.board[row][col].mask and self.board[row][col].value != 9:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def generate_board(self, first_dot: Dot):
|
def generate_board(self, first_dot: Dot):
|
||||||
"""
|
"""
|
||||||
避开第一个点,生成地雷
|
避开第一个点,生成地雷
|
||||||
Args:
|
Args:
|
||||||
first_dot: 第一个点
|
first_dot: 第一个点
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
generate_count = 0
|
generate_count = 0
|
||||||
while generate_count < self.num_mines:
|
while generate_count < self.num_mines:
|
||||||
row = random.randint(0, self.rows - 1)
|
row = random.randint(0, self.rows - 1)
|
||||||
col = random.randint(0, self.cols - 1)
|
col = random.randint(0, self.cols - 1)
|
||||||
if self.board[row][col].value == 9 or (row, col) == (first_dot.row, first_dot.col):
|
if self.board[row][col].value == 9 or (row, col) == (first_dot.row, first_dot.col):
|
||||||
continue
|
continue
|
||||||
self.board[row][col] = Dot(row=row, col=col, mask=True, value=9)
|
self.board[row][col] = Dot(row=row, col=col, mask=True, value=9)
|
||||||
generate_count += 1
|
generate_count += 1
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.cols):
|
for col in range(self.cols):
|
||||||
if self.board[row][col].value != 9:
|
if self.board[row][col].value != 9:
|
||||||
self.board[row][col].value = self.count_adjacent_mines(row, col)
|
self.board[row][col].value = self.count_adjacent_mines(row, col)
|
||||||
|
|
||||||
def count_adjacent_mines(self, row, col):
|
def count_adjacent_mines(self, row, col):
|
||||||
"""
|
"""
|
||||||
计算周围地雷数量
|
计算周围地雷数量
|
||||||
Args:
|
Args:
|
||||||
row:
|
row:
|
||||||
col:
|
col:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
for r in range(max(0, row - 1), min(self.rows, row + 2)):
|
for r in range(max(0, row - 1), min(self.rows, row + 2)):
|
||||||
for c in range(max(0, col - 1), min(self.cols, col + 2)):
|
for c in range(max(0, col - 1), min(self.cols, col + 2)):
|
||||||
if self.board[r][c].value == 9:
|
if self.board[r][c].value == 9:
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
def reveal_neighbors(self, row, col):
|
def reveal_neighbors(self, row, col):
|
||||||
"""
|
"""
|
||||||
递归展开,使用深度优先搜索
|
递归展开,使用深度优先搜索
|
||||||
Args:
|
Args:
|
||||||
row:
|
row:
|
||||||
col:
|
col:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for r in range(max(0, row - 1), min(self.rows, row + 2)):
|
for r in range(max(0, row - 1), min(self.rows, row + 2)):
|
||||||
for c in range(max(0, col - 1), min(self.cols, col + 2)):
|
for c in range(max(0, col - 1), min(self.cols, col + 2)):
|
||||||
if self.board[r][c].mask:
|
if self.board[r][c].mask:
|
||||||
self.board[r][c].mask = False
|
self.board[r][c].mask = False
|
||||||
if self.board[r][c].value == 0:
|
if self.board[r][c].value == 0:
|
||||||
self.reveal_neighbors(r, c)
|
self.reveal_neighbors(r, c)
|
||||||
|
|
||||||
def mark(self, row, col) -> bool:
|
def mark(self, row, col) -> bool:
|
||||||
"""
|
"""
|
||||||
标记
|
标记
|
||||||
Args:
|
Args:
|
||||||
row:
|
row:
|
||||||
col:
|
col:
|
||||||
Returns:
|
Returns:
|
||||||
是否标记成功,如果已经展开则无法标记
|
是否标记成功,如果已经展开则无法标记
|
||||||
"""
|
"""
|
||||||
if self.board[row][col].mask:
|
if self.board[row][col].mask:
|
||||||
self.board[row][col].flagged = not self.board[row][col].flagged
|
self.board[row][col].flagged = not self.board[row][col].flagged
|
||||||
return self.board[row][col].flagged
|
return self.board[row][col].flagged
|
||||||
|
|
||||||
def board_markdown(self) -> str:
|
def board_markdown(self) -> str:
|
||||||
"""
|
"""
|
||||||
打印地雷板
|
打印地雷板
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
dis = " "
|
dis = " "
|
||||||
start = "> " if self.cols >= 10 else ""
|
start = "> " if self.cols >= 10 else ""
|
||||||
text = start + self.NUMS[0] + dis*2
|
text = start + self.NUMS[0] + dis*2
|
||||||
# 横向两个雷之间的间隔字符
|
# 横向两个雷之间的间隔字符
|
||||||
# 生成横向索引
|
# 生成横向索引
|
||||||
for i in range(self.cols):
|
for i in range(self.cols):
|
||||||
text += f"{self.NUMS[i]}" + dis
|
text += f"{self.NUMS[i]}" + dis
|
||||||
text += "\n\n"
|
text += "\n\n"
|
||||||
for i, row in enumerate(self.board):
|
for i, row in enumerate(self.board):
|
||||||
text += start + f"{self.NUMS[i]}" + dis*2
|
text += start + f"{self.NUMS[i]}" + dis*2
|
||||||
print([d.value for d in row])
|
print([d.value for d in row])
|
||||||
for dot in row:
|
for dot in row:
|
||||||
if dot.mask and not dot.flagged:
|
if dot.mask and not dot.flagged:
|
||||||
text += md.btn_cmd(self.MASK, f"minesweeper reveal {dot.row} {dot.col}")
|
text += md.btn_cmd(self.MASK, f"minesweeper reveal {dot.row} {dot.col}")
|
||||||
elif dot.flagged:
|
elif dot.flagged:
|
||||||
text += md.btn_cmd(self.FLAG, f"minesweeper mark {dot.row} {dot.col}")
|
text += md.btn_cmd(self.FLAG, f"minesweeper mark {dot.row} {dot.col}")
|
||||||
else:
|
else:
|
||||||
text += self.NUMS[dot.value]
|
text += self.NUMS[dot.value]
|
||||||
text += dis
|
text += dis
|
||||||
text += "\n"
|
text += "\n"
|
||||||
btn_mark = md.btn_cmd("标记", f"minesweeper mark ", enter=False)
|
btn_mark = md.btn_cmd("标记", f"minesweeper mark ", enter=False)
|
||||||
btn_end = md.btn_cmd("结束", "minesweeper end", enter=True)
|
btn_end = md.btn_cmd("结束", "minesweeper end", enter=True)
|
||||||
text += f" {btn_mark} {btn_end}"
|
text += f" {btn_mark} {btn_end}"
|
||||||
return text
|
return text
|
@ -1,103 +1,103 @@
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md
|
from src.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from .game import Minesweeper
|
from .game import Minesweeper
|
||||||
|
|
||||||
from nonebot_plugin_alconna import Alconna, on_alconna, Subcommand, Args, Arparma
|
from nonebot_plugin_alconna import Alconna, on_alconna, Subcommand, Args, Arparma
|
||||||
|
|
||||||
minesweeper = on_alconna(
|
minesweeper = on_alconna(
|
||||||
aliases={"扫雷"},
|
aliases={"扫雷"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"minesweeper",
|
"minesweeper",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"start",
|
"start",
|
||||||
Args["row", int, 8]["col", int, 8]["mines", int, 10],
|
Args["row", int, 8]["col", int, 8]["mines", int, 10],
|
||||||
alias=["开始"],
|
alias=["开始"],
|
||||||
|
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"end",
|
"end",
|
||||||
alias=["结束"]
|
alias=["结束"]
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"reveal",
|
"reveal",
|
||||||
Args["row", int]["col", int],
|
Args["row", int]["col", int],
|
||||||
alias=["展开"]
|
alias=["展开"]
|
||||||
|
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"mark",
|
"mark",
|
||||||
Args["row", int]["col", int],
|
Args["row", int]["col", int],
|
||||||
alias=["标记"]
|
alias=["标记"]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
minesweeper_cache: list[Minesweeper] = []
|
minesweeper_cache: list[Minesweeper] = []
|
||||||
|
|
||||||
|
|
||||||
def get_minesweeper_cache(event: T_MessageEvent) -> Minesweeper | None:
|
def get_minesweeper_cache(event: T_MessageEvent) -> Minesweeper | None:
|
||||||
for i in minesweeper_cache:
|
for i in minesweeper_cache:
|
||||||
if i.session_type == event.message_type:
|
if i.session_type == event.message_type:
|
||||||
if i.session_id == event.user_id or i.session_id == event.group_id:
|
if i.session_id == event.user_id or i.session_id == event.group_id:
|
||||||
return i
|
return i
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@minesweeper.handle()
|
@minesweeper.handle()
|
||||||
async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot):
|
async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot):
|
||||||
game = get_minesweeper_cache(event)
|
game = get_minesweeper_cache(event)
|
||||||
if result.subcommands.get("start"):
|
if result.subcommands.get("start"):
|
||||||
if game:
|
if game:
|
||||||
await minesweeper.finish("当前会话不能同时进行多个扫雷游戏")
|
await minesweeper.finish("当前会话不能同时进行多个扫雷游戏")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
new_game = Minesweeper(
|
new_game = Minesweeper(
|
||||||
rows=result.subcommands["start"].args["row"],
|
rows=result.subcommands["start"].args["row"],
|
||||||
cols=result.subcommands["start"].args["col"],
|
cols=result.subcommands["start"].args["col"],
|
||||||
num_mines=result.subcommands["start"].args["mines"],
|
num_mines=result.subcommands["start"].args["mines"],
|
||||||
session_type=event.message_type,
|
session_type=event.message_type,
|
||||||
session_id=event.user_id if event.message_type == "private" else event.group_id,
|
session_id=event.user_id if event.message_type == "private" else event.group_id,
|
||||||
)
|
)
|
||||||
minesweeper_cache.append(new_game)
|
minesweeper_cache.append(new_game)
|
||||||
await minesweeper.send("游戏开始")
|
await minesweeper.send("游戏开始")
|
||||||
await md.send_md(new_game.board_markdown(), bot, event=event)
|
await md.send_md(new_game.board_markdown(), bot, event=event)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
elif result.subcommands.get("end"):
|
elif result.subcommands.get("end"):
|
||||||
if game:
|
if game:
|
||||||
minesweeper_cache.remove(game)
|
minesweeper_cache.remove(game)
|
||||||
await minesweeper.finish("游戏结束")
|
await minesweeper.finish("游戏结束")
|
||||||
else:
|
else:
|
||||||
await minesweeper.finish("当前没有扫雷游戏")
|
await minesweeper.finish("当前没有扫雷游戏")
|
||||||
elif result.subcommands.get("reveal"):
|
elif result.subcommands.get("reveal"):
|
||||||
if not game:
|
if not game:
|
||||||
await minesweeper.finish("当前没有扫雷游戏")
|
await minesweeper.finish("当前没有扫雷游戏")
|
||||||
else:
|
else:
|
||||||
row = result.subcommands["reveal"].args["row"]
|
row = result.subcommands["reveal"].args["row"]
|
||||||
col = result.subcommands["reveal"].args["col"]
|
col = result.subcommands["reveal"].args["col"]
|
||||||
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
if not game.reveal(row, col):
|
if not game.reveal(row, col):
|
||||||
minesweeper_cache.remove(game)
|
minesweeper_cache.remove(game)
|
||||||
await md.send_md(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
await minesweeper.finish("游戏结束")
|
await minesweeper.finish("游戏结束")
|
||||||
await md.send_md(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
if game.is_win():
|
if game.is_win():
|
||||||
minesweeper_cache.remove(game)
|
minesweeper_cache.remove(game)
|
||||||
await minesweeper.finish("游戏胜利")
|
await minesweeper.finish("游戏胜利")
|
||||||
elif result.subcommands.get("mark"):
|
elif result.subcommands.get("mark"):
|
||||||
if not game:
|
if not game:
|
||||||
await minesweeper.finish("当前没有扫雷游戏")
|
await minesweeper.finish("当前没有扫雷游戏")
|
||||||
else:
|
else:
|
||||||
row = result.subcommands["mark"].args["row"]
|
row = result.subcommands["mark"].args["row"]
|
||||||
col = result.subcommands["mark"].args["col"]
|
col = result.subcommands["mark"].args["col"]
|
||||||
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
if not (0 <= row < game.rows and 0 <= col < game.cols):
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
||||||
game.board[row][col].flagged = not game.board[row][col].flagged
|
game.board[row][col].flagged = not game.board[row][col].flagged
|
||||||
await md.send_md(game.board_markdown(), bot, event=event)
|
await md.send_md(game.board_markdown(), bot, event=event)
|
||||||
else:
|
else:
|
||||||
await minesweeper.finish("参数错误")
|
await minesweeper.finish("参数错误")
|
@ -1,20 +1,20 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪包管理器v2",
|
name="轻雪包管理器v2",
|
||||||
description="详细看文档",
|
description="详细看文档",
|
||||||
usage=(
|
usage=(
|
||||||
"npm list\n"
|
"npm list\n"
|
||||||
"npm enable/disable <plugin_name>\n"
|
"npm enable/disable <plugin_name>\n"
|
||||||
"npm search <keywords...>\n"
|
"npm search <keywords...>\n"
|
||||||
"npm install/uninstall <plugin_name>\n"
|
"npm install/uninstall <plugin_name>\n"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable" : False,
|
"default_enable" : False,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,22 +1,22 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .npm import *
|
from .npm import *
|
||||||
from .rpm import *
|
from .rpm import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪包管理器",
|
name="轻雪包管理器",
|
||||||
description="本地插件管理和插件商店支持,资源包管理,支持启用/停用,安装/卸载插件",
|
description="本地插件管理和插件商店支持,资源包管理,支持启用/停用,安装/卸载插件",
|
||||||
usage=(
|
usage=(
|
||||||
"npm list\n"
|
"npm list\n"
|
||||||
"npm enable/disable <plugin_name>\n"
|
"npm enable/disable <plugin_name>\n"
|
||||||
"npm search <keywords...>\n"
|
"npm search <keywords...>\n"
|
||||||
"npm install/uninstall <plugin_name>\n"
|
"npm install/uninstall <plugin_name>\n"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,256 +1,256 @@
|
|||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
import nonebot.plugin
|
import nonebot.plugin
|
||||||
from nonebot.adapters import satori
|
from nonebot.adapters import satori
|
||||||
|
|
||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
from liteyuki.utils.base.data import LiteModel
|
from src.utils.base.data import LiteModel
|
||||||
from liteyuki.utils.base.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
|
from src.utils.base.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
__group_data = {} # 群数据缓存, {group_id: Group}
|
__group_data = {} # 群数据缓存, {group_id: Group}
|
||||||
__user_data = {} # 用户数据缓存, {user_id: User}
|
__user_data = {} # 用户数据缓存, {user_id: User}
|
||||||
__default_enable = {} # 插件默认启用状态缓存, {plugin_name: bool} static
|
__default_enable = {} # 插件默认启用状态缓存, {plugin_name: bool} static
|
||||||
__global_enable = {} # 插件全局启用状态缓存, {plugin_name: bool} dynamic
|
__global_enable = {} # 插件全局启用状态缓存, {plugin_name: bool} dynamic
|
||||||
|
|
||||||
|
|
||||||
class PluginTag(LiteModel):
|
class PluginTag(LiteModel):
|
||||||
label: str
|
label: str
|
||||||
color: str = '#000000'
|
color: str = '#000000'
|
||||||
|
|
||||||
|
|
||||||
class StorePlugin(LiteModel):
|
class StorePlugin(LiteModel):
|
||||||
name: str
|
name: str
|
||||||
desc: str
|
desc: str
|
||||||
module_name: str # 插件商店中的模块名不等于本地的模块名,前者是文件夹名,后者是点分割模块名
|
module_name: str # 插件商店中的模块名不等于本地的模块名,前者是文件夹名,后者是点分割模块名
|
||||||
project_link: str = ""
|
project_link: str = ""
|
||||||
homepage: str = ""
|
homepage: str = ""
|
||||||
author: str = ""
|
author: str = ""
|
||||||
type: str | None = None
|
type: str | None = None
|
||||||
version: str | None = ""
|
version: str | None = ""
|
||||||
time: str = ""
|
time: str = ""
|
||||||
tags: list[PluginTag] = []
|
tags: list[PluginTag] = []
|
||||||
is_official: bool = False
|
is_official: bool = False
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_exist(plugin_name: str) -> bool:
|
def get_plugin_exist(plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件是否存在于加载列表
|
获取插件是否存在于加载列表
|
||||||
Args:
|
Args:
|
||||||
plugin_name:
|
plugin_name:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for plugin in nonebot.plugin.get_loaded_plugins():
|
for plugin in nonebot.plugin.get_loaded_plugins():
|
||||||
if plugin.name == plugin_name:
|
if plugin.name == plugin_name:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def get_store_plugin(plugin_name: str) -> Optional[StorePlugin]:
|
async def get_store_plugin(plugin_name: str) -> Optional[StorePlugin]:
|
||||||
"""
|
"""
|
||||||
获取插件信息
|
获取插件信息
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plugin_name (str): 插件模块名
|
plugin_name (str): 插件模块名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[StorePlugin]: 插件信息
|
Optional[StorePlugin]: 插件信息
|
||||||
"""
|
"""
|
||||||
async with aiofiles.open("data/liteyuki/plugins.json", "r", encoding="utf-8") as f:
|
async with aiofiles.open("data/liteyuki/plugins.json", "r", encoding="utf-8") as f:
|
||||||
plugins: list[StorePlugin] = [StorePlugin(**pobj) for pobj in json.loads(await f.read())]
|
plugins: list[StorePlugin] = [StorePlugin(**pobj) for pobj in json.loads(await f.read())]
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
if plugin.module_name == plugin_name:
|
if plugin.module_name == plugin_name:
|
||||||
return plugin
|
return plugin
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_default_enable(plugin_name: str) -> bool:
|
def get_plugin_default_enable(plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件默认启用状态,由插件定义,不存在则默认为启用,优先从缓存中获取
|
获取插件默认启用状态,由插件定义,不存在则默认为启用,优先从缓存中获取
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plugin_name (str): 插件模块名
|
plugin_name (str): 插件模块名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 插件默认状态
|
bool: 插件默认状态
|
||||||
"""
|
"""
|
||||||
if plugin_name not in __default_enable:
|
if plugin_name not in __default_enable:
|
||||||
plug = nonebot.plugin.get_plugin(plugin_name)
|
plug = nonebot.plugin.get_plugin(plugin_name)
|
||||||
default_enable = (plug.metadata.extra.get("default_enable", True) if plug.metadata else True) if plug else True
|
default_enable = (plug.metadata.extra.get("default_enable", True) if plug.metadata else True) if plug else True
|
||||||
__default_enable[plugin_name] = default_enable
|
__default_enable[plugin_name] = default_enable
|
||||||
|
|
||||||
return __default_enable[plugin_name]
|
return __default_enable[plugin_name]
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_session_enable(event: T_MessageEvent, plugin_name: str) -> bool:
|
def get_plugin_session_enable(event: T_MessageEvent, plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件当前会话启用状态
|
获取插件当前会话启用状态
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event: 会话事件
|
event: 会话事件
|
||||||
plugin_name (str): 插件模块名
|
plugin_name (str): 插件模块名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 插件当前状态
|
bool: 插件当前状态
|
||||||
"""
|
"""
|
||||||
if isinstance(event, satori.event.Event):
|
if isinstance(event, satori.event.Event):
|
||||||
if event.guild is not None:
|
if event.guild is not None:
|
||||||
message_type = "group"
|
message_type = "group"
|
||||||
else:
|
else:
|
||||||
message_type = "private"
|
message_type = "private"
|
||||||
else:
|
else:
|
||||||
message_type = event.message_type
|
message_type = event.message_type
|
||||||
if message_type == "group":
|
if message_type == "group":
|
||||||
group_id = str(event.guild.id if isinstance(event, satori.event.Event) else event.group_id)
|
group_id = str(event.guild.id if isinstance(event, satori.event.Event) else event.group_id)
|
||||||
if group_id not in __group_data:
|
if group_id not in __group_data:
|
||||||
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
||||||
__group_data[str(group_id)] = group
|
__group_data[str(group_id)] = group
|
||||||
|
|
||||||
session = __group_data[group_id]
|
session = __group_data[group_id]
|
||||||
else:
|
else:
|
||||||
# session: User = user_db.first(User(), "user_id = ?", event.user_id, default=User(user_id=str(event.user_id)))
|
# session: User = user_db.first(User(), "user_id = ?", event.user_id, default=User(user_id=str(event.user_id)))
|
||||||
user_id = str(event.user.id if isinstance(event, satori.event.Event) else event.user_id)
|
user_id = str(event.user.id if isinstance(event, satori.event.Event) else event.user_id)
|
||||||
if user_id not in __user_data:
|
if user_id not in __user_data:
|
||||||
user: User = user_db.where_one(User(), "user_id = ?", user_id, default=User(user_id=user_id))
|
user: User = user_db.where_one(User(), "user_id = ?", user_id, default=User(user_id=user_id))
|
||||||
__user_data[user_id] = user
|
__user_data[user_id] = user
|
||||||
session = __user_data[user_id]
|
session = __user_data[user_id]
|
||||||
# 默认停用插件在启用列表内表示启用
|
# 默认停用插件在启用列表内表示启用
|
||||||
# 默认停用插件不在启用列表内表示停用
|
# 默认停用插件不在启用列表内表示停用
|
||||||
# 默认启用插件在停用列表内表示停用
|
# 默认启用插件在停用列表内表示停用
|
||||||
# 默认启用插件不在停用列表内表示启用
|
# 默认启用插件不在停用列表内表示启用
|
||||||
default_enable = get_plugin_default_enable(plugin_name)
|
default_enable = get_plugin_default_enable(plugin_name)
|
||||||
if default_enable:
|
if default_enable:
|
||||||
return plugin_name not in session.disabled_plugins
|
return plugin_name not in session.disabled_plugins
|
||||||
else:
|
else:
|
||||||
return plugin_name in session.enabled_plugins
|
return plugin_name in session.enabled_plugins
|
||||||
|
|
||||||
|
|
||||||
def set_plugin_session_enable(event: T_MessageEvent, plugin_name: str, enable: bool):
|
def set_plugin_session_enable(event: T_MessageEvent, plugin_name: str, enable: bool):
|
||||||
"""
|
"""
|
||||||
设置插件会话启用状态,同时更新数据库和缓存
|
设置插件会话启用状态,同时更新数据库和缓存
|
||||||
Args:
|
Args:
|
||||||
event:
|
event:
|
||||||
plugin_name:
|
plugin_name:
|
||||||
enable:
|
enable:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if event_utils.get_message_type(event) == "group":
|
if event_utils.get_message_type(event) == "group":
|
||||||
session: Group = group_db.where_one(Group(), "group_id = ?", str(event_utils.get_group_id(event)),
|
session: Group = group_db.where_one(Group(), "group_id = ?", str(event_utils.get_group_id(event)),
|
||||||
default=Group(group_id=str(event_utils.get_group_id(event))))
|
default=Group(group_id=str(event_utils.get_group_id(event))))
|
||||||
else:
|
else:
|
||||||
session: User = user_db.where_one(User(), "user_id = ?", str(event_utils.get_user_id(event)),
|
session: User = user_db.where_one(User(), "user_id = ?", str(event_utils.get_user_id(event)),
|
||||||
default=User(user_id=str(event_utils.get_user_id(event))))
|
default=User(user_id=str(event_utils.get_user_id(event))))
|
||||||
print(session)
|
print(session)
|
||||||
default_enable = get_plugin_default_enable(plugin_name)
|
default_enable = get_plugin_default_enable(plugin_name)
|
||||||
if default_enable:
|
if default_enable:
|
||||||
if enable:
|
if enable:
|
||||||
session.disabled_plugins.remove(plugin_name)
|
session.disabled_plugins.remove(plugin_name)
|
||||||
else:
|
else:
|
||||||
session.disabled_plugins.append(plugin_name)
|
session.disabled_plugins.append(plugin_name)
|
||||||
else:
|
else:
|
||||||
if enable:
|
if enable:
|
||||||
session.enabled_plugins.append(plugin_name)
|
session.enabled_plugins.append(plugin_name)
|
||||||
else:
|
else:
|
||||||
session.enabled_plugins.remove(plugin_name)
|
session.enabled_plugins.remove(plugin_name)
|
||||||
|
|
||||||
if event_utils.get_message_type(event) == "group":
|
if event_utils.get_message_type(event) == "group":
|
||||||
__group_data[str(event_utils.get_group_id(event))] = session
|
__group_data[str(event_utils.get_group_id(event))] = session
|
||||||
group_db.save(session)
|
group_db.save(session)
|
||||||
else:
|
else:
|
||||||
__user_data[str(event_utils.get_user_id(event))] = session
|
__user_data[str(event_utils.get_user_id(event))] = session
|
||||||
user_db.save(session)
|
user_db.save(session)
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_global_enable(plugin_name: str) -> bool:
|
def get_plugin_global_enable(plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件全局启用状态, 优先从缓存中获取
|
获取插件全局启用状态, 优先从缓存中获取
|
||||||
Args:
|
Args:
|
||||||
plugin_name:
|
plugin_name:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if plugin_name not in __global_enable:
|
if plugin_name not in __global_enable:
|
||||||
plugin = plugin_db.where_one(
|
plugin = plugin_db.where_one(
|
||||||
GlobalPlugin(),
|
GlobalPlugin(),
|
||||||
"module_name = ?",
|
"module_name = ?",
|
||||||
plugin_name,
|
plugin_name,
|
||||||
default=GlobalPlugin(module_name=plugin_name, enabled=True))
|
default=GlobalPlugin(module_name=plugin_name, enabled=True))
|
||||||
__global_enable[plugin_name] = plugin.enabled
|
__global_enable[plugin_name] = plugin.enabled
|
||||||
|
|
||||||
return __global_enable[plugin_name]
|
return __global_enable[plugin_name]
|
||||||
|
|
||||||
|
|
||||||
def set_plugin_global_enable(plugin_name: str, enable: bool):
|
def set_plugin_global_enable(plugin_name: str, enable: bool):
|
||||||
"""
|
"""
|
||||||
设置插件全局启用状态,同时更新数据库和缓存
|
设置插件全局启用状态,同时更新数据库和缓存
|
||||||
Args:
|
Args:
|
||||||
plugin_name:
|
plugin_name:
|
||||||
enable:
|
enable:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
plugin = plugin_db.where_one(
|
plugin = plugin_db.where_one(
|
||||||
GlobalPlugin(),
|
GlobalPlugin(),
|
||||||
"module_name = ?",
|
"module_name = ?",
|
||||||
plugin_name,
|
plugin_name,
|
||||||
default=GlobalPlugin(module_name=plugin_name, enabled=True))
|
default=GlobalPlugin(module_name=plugin_name, enabled=True))
|
||||||
plugin.enabled = enable
|
plugin.enabled = enable
|
||||||
|
|
||||||
plugin_db.save(plugin)
|
plugin_db.save(plugin)
|
||||||
__global_enable[plugin_name] = enable
|
__global_enable[plugin_name] = enable
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_can_be_toggle(plugin_name: str) -> bool:
|
def get_plugin_can_be_toggle(plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取插件是否可以被启用/停用
|
获取插件是否可以被启用/停用
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plugin_name (str): 插件模块名
|
plugin_name (str): 插件模块名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 插件是否可以被启用/停用
|
bool: 插件是否可以被启用/停用
|
||||||
"""
|
"""
|
||||||
plug = nonebot.plugin.get_plugin(plugin_name)
|
plug = nonebot.plugin.get_plugin(plugin_name)
|
||||||
return plug.metadata.extra.get("toggleable", True) if plug and plug.metadata else True
|
return plug.metadata.extra.get("toggleable", True) if plug and plug.metadata else True
|
||||||
|
|
||||||
|
|
||||||
def get_group_enable(group_id: str) -> bool:
|
def get_group_enable(group_id: str) -> bool:
|
||||||
"""
|
"""
|
||||||
获取群组是否启用插机器人
|
获取群组是否启用插机器人
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
group_id (str): 群组ID
|
group_id (str): 群组ID
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 群组是否启用插件
|
bool: 群组是否启用插件
|
||||||
"""
|
"""
|
||||||
group_id = str(group_id)
|
group_id = str(group_id)
|
||||||
if group_id not in __group_data:
|
if group_id not in __group_data:
|
||||||
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
||||||
__group_data[group_id] = group
|
__group_data[group_id] = group
|
||||||
|
|
||||||
return __group_data[group_id].enable
|
return __group_data[group_id].enable
|
||||||
|
|
||||||
|
|
||||||
def set_group_enable(group_id: str, enable: bool):
|
def set_group_enable(group_id: str, enable: bool):
|
||||||
"""
|
"""
|
||||||
设置群组是否启用插机器人
|
设置群组是否启用插机器人
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
group_id (str): 群组ID
|
group_id (str): 群组ID
|
||||||
enable (bool): 是否启用
|
enable (bool): 是否启用
|
||||||
"""
|
"""
|
||||||
group_id = str(group_id)
|
group_id = str(group_id)
|
||||||
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=group_id))
|
||||||
group.enable = enable
|
group.enable = enable
|
||||||
|
|
||||||
__group_data[group_id] = group
|
__group_data[group_id] = group
|
||||||
group_db.save(group)
|
group_db.save(group)
|
File diff suppressed because it is too large
Load Diff
@ -1,186 +1,186 @@
|
|||||||
# 轻雪资源包管理器
|
# 轻雪资源包管理器
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
import yaml
|
import yaml
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
|
|
||||||
from liteyuki.utils.base.language import get_user_lang
|
from src.utils.base.language import get_user_lang
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md
|
from src.utils.message.message import MarkdownMessage as md
|
||||||
from liteyuki.utils.base.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
|
from src.utils.base.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import Alconna, Args, on_alconna, Arparma, Subcommand
|
from nonebot_plugin_alconna import Alconna, Args, on_alconna, Arparma, Subcommand
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
aliases={"资源包"},
|
aliases={"资源包"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"rpm",
|
"rpm",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"list",
|
"list",
|
||||||
Args["page", int, 1]["num", int, 10],
|
Args["page", int, 1]["num", int, 10],
|
||||||
alias=["ls", "列表", "列出"],
|
alias=["ls", "列表", "列出"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"load",
|
"load",
|
||||||
Args["name", str],
|
Args["name", str],
|
||||||
alias=["安装"],
|
alias=["安装"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"unload",
|
"unload",
|
||||||
Args["name", str],
|
Args["name", str],
|
||||||
alias=["卸载"],
|
alias=["卸载"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"up",
|
"up",
|
||||||
Args["name", str],
|
Args["name", str],
|
||||||
alias=["上移"],
|
alias=["上移"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"down",
|
"down",
|
||||||
Args["name", str],
|
Args["name", str],
|
||||||
alias=["下移"],
|
alias=["下移"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"top",
|
"top",
|
||||||
Args["name", str],
|
Args["name", str],
|
||||||
alias=["置顶"],
|
alias=["置顶"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"reload",
|
"reload",
|
||||||
alias=["重载"],
|
alias=["重载"],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
).handle()
|
).handle()
|
||||||
async def _(bot: T_Bot, event: T_MessageEvent, result: Arparma, matcher: Matcher):
|
async def _(bot: T_Bot, event: T_MessageEvent, result: Arparma, matcher: Matcher):
|
||||||
ulang = get_user_lang(str(event.user_id))
|
ulang = get_user_lang(str(event.user_id))
|
||||||
reply = ""
|
reply = ""
|
||||||
send_as_md = False
|
send_as_md = False
|
||||||
if result.subcommands.get("list"):
|
if result.subcommands.get("list"):
|
||||||
send_as_md = True
|
send_as_md = True
|
||||||
loaded_rps = get_loaded_resource_packs()
|
loaded_rps = get_loaded_resource_packs()
|
||||||
reply += f"{ulang.get('liteyuki.loaded_resources', NUM=len(loaded_rps))}\n"
|
reply += f"{ulang.get('liteyuki.loaded_resources', NUM=len(loaded_rps))}\n"
|
||||||
for rp in loaded_rps:
|
for rp in loaded_rps:
|
||||||
btn_unload = md.btn_cmd(
|
btn_unload = md.btn_cmd(
|
||||||
ulang.get("npm.uninstall"),
|
ulang.get("npm.uninstall"),
|
||||||
f"rpm unload {rp.folder}"
|
f"rpm unload {rp.folder}"
|
||||||
)
|
)
|
||||||
btn_move_up = md.btn_cmd(
|
btn_move_up = md.btn_cmd(
|
||||||
ulang.get("rpm.move_up"),
|
ulang.get("rpm.move_up"),
|
||||||
f"rpm up {rp.folder}"
|
f"rpm up {rp.folder}"
|
||||||
)
|
)
|
||||||
btn_move_down = md.btn_cmd(
|
btn_move_down = md.btn_cmd(
|
||||||
ulang.get("rpm.move_down"),
|
ulang.get("rpm.move_down"),
|
||||||
f"rpm down {rp.folder}"
|
f"rpm down {rp.folder}"
|
||||||
)
|
)
|
||||||
btn_move_top = md.btn_cmd(
|
btn_move_top = md.btn_cmd(
|
||||||
ulang.get("rpm.move_top"),
|
ulang.get("rpm.move_top"),
|
||||||
f"rpm top {rp.folder}"
|
f"rpm top {rp.folder}"
|
||||||
)
|
)
|
||||||
# 添加新行
|
# 添加新行
|
||||||
reply += (f"\n**{md.escape(rp.name)}**({md.escape(rp.folder)})\n\n"
|
reply += (f"\n**{md.escape(rp.name)}**({md.escape(rp.folder)})\n\n"
|
||||||
f"> {btn_move_up} {btn_move_down} {btn_move_top} {btn_unload}\n\n***")
|
f"> {btn_move_up} {btn_move_down} {btn_move_top} {btn_unload}\n\n***")
|
||||||
reply += f"\n\n{ulang.get('liteyuki.unloaded_resources')}\n"
|
reply += f"\n\n{ulang.get('liteyuki.unloaded_resources')}\n"
|
||||||
loaded_folders = [rp.folder for rp in get_loaded_resource_packs()]
|
loaded_folders = [rp.folder for rp in get_loaded_resource_packs()]
|
||||||
# 遍历resources文件夹,获取未加载的资源包
|
# 遍历resources文件夹,获取未加载的资源包
|
||||||
for folder in os.listdir("resources"):
|
for folder in os.listdir("resources"):
|
||||||
if folder not in loaded_folders:
|
if folder not in loaded_folders:
|
||||||
if os.path.exists(os.path.join("resources", folder, "metadata.yml")):
|
if os.path.exists(os.path.join("resources", folder, "metadata.yml")):
|
||||||
metadata = ResourceMetadata(
|
metadata = ResourceMetadata(
|
||||||
**yaml.load(
|
**yaml.load(
|
||||||
open(
|
open(
|
||||||
os.path.join("resources", folder, "metadata.yml"),
|
os.path.join("resources", folder, "metadata.yml"),
|
||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
),
|
),
|
||||||
Loader=yaml.FullLoader
|
Loader=yaml.FullLoader
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
metadata.folder = folder
|
metadata.folder = folder
|
||||||
metadata.path = os.path.join("resources", folder)
|
metadata.path = os.path.join("resources", folder)
|
||||||
btn_load = md.btn_cmd(
|
btn_load = md.btn_cmd(
|
||||||
ulang.get("npm.install"),
|
ulang.get("npm.install"),
|
||||||
f"rpm load {metadata.folder}"
|
f"rpm load {metadata.folder}"
|
||||||
)
|
)
|
||||||
# 添加新行
|
# 添加新行
|
||||||
reply += (f"\n**{md.escape(metadata.name)}**({md.escape(metadata.folder)})\n\n"
|
reply += (f"\n**{md.escape(metadata.name)}**({md.escape(metadata.folder)})\n\n"
|
||||||
f"> {btn_load}\n\n***")
|
f"> {btn_load}\n\n***")
|
||||||
elif os.path.isfile(os.path.join("resources", folder)) and folder.endswith(".zip"):
|
elif os.path.isfile(os.path.join("resources", folder)) and folder.endswith(".zip"):
|
||||||
# zip文件
|
# zip文件
|
||||||
# 临时解压并读取metadata.yml
|
# 临时解压并读取metadata.yml
|
||||||
with zipfile.ZipFile(os.path.join("resources", folder), "r") as zip_ref:
|
with zipfile.ZipFile(os.path.join("resources", folder), "r") as zip_ref:
|
||||||
with zip_ref.open("metadata.yml") as f:
|
with zip_ref.open("metadata.yml") as f:
|
||||||
metadata = ResourceMetadata(
|
metadata = ResourceMetadata(
|
||||||
**yaml.load(f, Loader=yaml.FullLoader)
|
**yaml.load(f, Loader=yaml.FullLoader)
|
||||||
)
|
)
|
||||||
btn_load = md.btn_cmd(
|
btn_load = md.btn_cmd(
|
||||||
ulang.get("npm.install"),
|
ulang.get("npm.install"),
|
||||||
f"rpm load {folder}"
|
f"rpm load {folder}"
|
||||||
)
|
)
|
||||||
# 添加新行
|
# 添加新行
|
||||||
reply += (f"\n**{md.escape(metadata.name)}**({md.escape(folder)})\n\n"
|
reply += (f"\n**{md.escape(metadata.name)}**({md.escape(folder)})\n\n"
|
||||||
f"> {btn_load}\n\n***")
|
f"> {btn_load}\n\n***")
|
||||||
elif result.subcommands.get("load") or result.subcommands.get("unload"):
|
elif result.subcommands.get("load") or result.subcommands.get("unload"):
|
||||||
load = result.subcommands.get("load") is not None
|
load = result.subcommands.get("load") is not None
|
||||||
rp_name = result.args.get("name")
|
rp_name = result.args.get("name")
|
||||||
r = False # 操作结果
|
r = False # 操作结果
|
||||||
if check_exist(rp_name):
|
if check_exist(rp_name):
|
||||||
if load != check_status(rp_name):
|
if load != check_status(rp_name):
|
||||||
# 状态不同
|
# 状态不同
|
||||||
if load:
|
if load:
|
||||||
r = add_resource_pack(rp_name)
|
r = add_resource_pack(rp_name)
|
||||||
else:
|
else:
|
||||||
r = remove_resource_pack(rp_name)
|
r = remove_resource_pack(rp_name)
|
||||||
rp_meta = get_resource_metadata(rp_name)
|
rp_meta = get_resource_metadata(rp_name)
|
||||||
reply += ulang.get(
|
reply += ulang.get(
|
||||||
f"liteyuki.{'load' if load else 'unload'}_resource_{'success' if r else 'failed'}",
|
f"liteyuki.{'load' if load else 'unload'}_resource_{'success' if r else 'failed'}",
|
||||||
NAME=rp_meta.name
|
NAME=rp_meta.name
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 重复操作
|
# 重复操作
|
||||||
reply += ulang.get(f"liteyuki.resource_already_{'load' if load else 'unload'}ed", NAME=rp_name)
|
reply += ulang.get(f"liteyuki.resource_already_{'load' if load else 'unload'}ed", NAME=rp_name)
|
||||||
else:
|
else:
|
||||||
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
||||||
if r:
|
if r:
|
||||||
btn_reload = md.btn_cmd(
|
btn_reload = md.btn_cmd(
|
||||||
ulang.get("liteyuki.reload_resources"),
|
ulang.get("liteyuki.reload_resources"),
|
||||||
f"rpm reload"
|
f"rpm reload"
|
||||||
)
|
)
|
||||||
reply += "\n" + ulang.get("liteyuki.need_reload", BTN=btn_reload)
|
reply += "\n" + ulang.get("liteyuki.need_reload", BTN=btn_reload)
|
||||||
elif result.subcommands.get("up") or result.subcommands.get("down") or result.subcommands.get("top"):
|
elif result.subcommands.get("up") or result.subcommands.get("down") or result.subcommands.get("top"):
|
||||||
rp_name = result.args.get("name")
|
rp_name = result.args.get("name")
|
||||||
if result.subcommands.get("up"):
|
if result.subcommands.get("up"):
|
||||||
delta = -1
|
delta = -1
|
||||||
elif result.subcommands.get("down"):
|
elif result.subcommands.get("down"):
|
||||||
delta = 1
|
delta = 1
|
||||||
else:
|
else:
|
||||||
delta = 0
|
delta = 0
|
||||||
if check_exist(rp_name):
|
if check_exist(rp_name):
|
||||||
if check_status(rp_name):
|
if check_status(rp_name):
|
||||||
r = change_priority(rp_name, delta)
|
r = change_priority(rp_name, delta)
|
||||||
reply += ulang.get(f"liteyuki.change_priority_{'success' if r else 'failed'}", NAME=rp_name)
|
reply += ulang.get(f"liteyuki.change_priority_{'success' if r else 'failed'}", NAME=rp_name)
|
||||||
if r:
|
if r:
|
||||||
btn_reload = md.btn_cmd(
|
btn_reload = md.btn_cmd(
|
||||||
ulang.get("liteyuki.reload_resources"),
|
ulang.get("liteyuki.reload_resources"),
|
||||||
f"rpm reload"
|
f"rpm reload"
|
||||||
)
|
)
|
||||||
reply += "\n" + ulang.get("liteyuki.need_reload", BTN=btn_reload)
|
reply += "\n" + ulang.get("liteyuki.need_reload", BTN=btn_reload)
|
||||||
else:
|
else:
|
||||||
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
||||||
else:
|
else:
|
||||||
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
reply += ulang.get("liteyuki.resource_not_found", NAME=rp_name)
|
||||||
elif result.subcommands.get("reload"):
|
elif result.subcommands.get("reload"):
|
||||||
load_resources()
|
load_resources()
|
||||||
reply = ulang.get(
|
reply = ulang.get(
|
||||||
"liteyuki.reload_resources_success",
|
"liteyuki.reload_resources_success",
|
||||||
NUM=len(get_loaded_resource_packs())
|
NUM=len(get_loaded_resource_packs())
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
if send_as_md:
|
if send_as_md:
|
||||||
await md.send_md(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
else:
|
else:
|
||||||
await matcher.finish(reply)
|
await matcher.finish(reply)
|
@ -1,16 +1,16 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .auto_update import *
|
from .auto_update import *
|
||||||
|
|
||||||
__author__ = "expliyh"
|
__author__ = "expliyh"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="Satori 用户数据自动更新(临时措施)",
|
name="Satori 用户数据自动更新(临时措施)",
|
||||||
description="",
|
description="",
|
||||||
usage="",
|
usage="",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,21 +1,21 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
|
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
# from nonebot_plugin_alconna.typings import Event
|
# from nonebot_plugin_alconna.typings import Event
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
from liteyuki.utils import satori_utils
|
from src.utils import satori_utils
|
||||||
from nonebot.adapters import satori
|
from nonebot.adapters import satori
|
||||||
from nonebot_plugin_alconna.typings import Event
|
from nonebot_plugin_alconna.typings import Event
|
||||||
from liteyuki.plugins.liteyuki_status.counter_for_satori import satori_counter
|
from src.plugins.liteyuki_status.counter_for_satori import satori_counter
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def pre_handle(event: Event):
|
async def pre_handle(event: Event):
|
||||||
if isinstance(event, satori.MessageEvent):
|
if isinstance(event, satori.MessageEvent):
|
||||||
if event.user.id == event.self_id:
|
if event.user.id == event.self_id:
|
||||||
satori_counter.msg_sent += 1
|
satori_counter.msg_sent += 1
|
||||||
else:
|
else:
|
||||||
satori_counter.msg_received += 1
|
satori_counter.msg_received += 1
|
||||||
if event.user.name is not None:
|
if event.user.name is not None:
|
||||||
if await satori_utils.user_infos.put(event.user):
|
if await satori_utils.user_infos.put(event.user):
|
||||||
nonebot.logger.info(f"Satori user {event.user.name}<{event.user.id}> updated")
|
nonebot.logger.info(f"Satori user {event.user.name}<{event.user.id}> updated")
|
@ -1,163 +1,163 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.data import Database, LiteModel
|
from src.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.utils.base.resource import get_path
|
from src.utils.base.resource import get_path
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from src.utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
from nonebot_plugin_alconna import Alconna, AlconnaResult, CommandResult, Subcommand, UniMessage, on_alconna, Args
|
from nonebot_plugin_alconna import Alconna, AlconnaResult, CommandResult, Subcommand, UniMessage, on_alconna, Args
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="签名服务器状态",
|
name="签名服务器状态",
|
||||||
description="适用于ntqq的签名状态查看",
|
description="适用于ntqq的签名状态查看",
|
||||||
usage=(
|
usage=(
|
||||||
"sign count 查看当前签名数\n"
|
"sign count 查看当前签名数\n"
|
||||||
"sign data 查看签名数变化\n"
|
"sign data 查看签名数变化\n"
|
||||||
"sign chart [limit] 查看签名数变化图表\n"
|
"sign chart [limit] 查看签名数变化图表\n"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SIGN_COUNT_URLS: dict[str, str] = get_config("sign_count_urls", None)
|
SIGN_COUNT_URLS: dict[str, str] = get_config("sign_count_urls", None)
|
||||||
SIGN_COUNT_DURATION = get_config("sign_count_duration", 10)
|
SIGN_COUNT_DURATION = get_config("sign_count_duration", 10)
|
||||||
|
|
||||||
|
|
||||||
class SignCount(LiteModel):
|
class SignCount(LiteModel):
|
||||||
TABLE_NAME: str = "sign_count"
|
TABLE_NAME: str = "sign_count"
|
||||||
time: float = 0.0
|
time: float = 0.0
|
||||||
count: int = 0
|
count: int = 0
|
||||||
sid: str = ""
|
sid: str = ""
|
||||||
|
|
||||||
|
|
||||||
sign_db = Database("data/liteyuki/ntqq_sign.ldb")
|
sign_db = Database("data/liteyuki/ntqq_sign.ldb")
|
||||||
sign_db.auto_migrate(SignCount())
|
sign_db.auto_migrate(SignCount())
|
||||||
|
|
||||||
sign_status = on_alconna(Alconna(
|
sign_status = on_alconna(Alconna(
|
||||||
"sign",
|
"sign",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"chart",
|
"chart",
|
||||||
Args["limit", int, 10000]
|
Args["limit", int, 10000]
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"count"
|
"count"
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"data"
|
"data"
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
cache_img: bytes = None
|
cache_img: bytes = None
|
||||||
|
|
||||||
|
|
||||||
@sign_status.assign("count")
|
@sign_status.assign("count")
|
||||||
async def _():
|
async def _():
|
||||||
reply = "Current sign count:"
|
reply = "Current sign count:"
|
||||||
for name, count in (await get_now_sign()).items():
|
for name, count in (await get_now_sign()).items():
|
||||||
reply += f"\n{name}: {count[1]}"
|
reply += f"\n{name}: {count[1]}"
|
||||||
await sign_status.send(reply)
|
await sign_status.send(reply)
|
||||||
|
|
||||||
|
|
||||||
@sign_status.assign("data")
|
@sign_status.assign("data")
|
||||||
async def _():
|
async def _():
|
||||||
query_stamp = [1, 5, 10, 15]
|
query_stamp = [1, 5, 10, 15]
|
||||||
|
|
||||||
reply = "QPS from last " + ", ".join([str(i) for i in query_stamp]) + "mins"
|
reply = "QPS from last " + ", ".join([str(i) for i in query_stamp]) + "mins"
|
||||||
for name, url in SIGN_COUNT_URLS.items():
|
for name, url in SIGN_COUNT_URLS.items():
|
||||||
count_data = []
|
count_data = []
|
||||||
for stamp in query_stamp:
|
for stamp in query_stamp:
|
||||||
count_rows = sign_db.where_all(SignCount(), "sid = ? and time > ?", url, time.time() - 60 * stamp)
|
count_rows = sign_db.where_all(SignCount(), "sid = ? and time > ?", url, time.time() - 60 * stamp)
|
||||||
if len(count_rows) < 2:
|
if len(count_rows) < 2:
|
||||||
count_data.append(-1)
|
count_data.append(-1)
|
||||||
else:
|
else:
|
||||||
count_data.append((count_rows[-1].count - count_rows[0].count)/(stamp*60))
|
count_data.append((count_rows[-1].count - count_rows[0].count)/(stamp*60))
|
||||||
reply += f"\n{name}: " + ", ".join([f"{i:.1f}" for i in count_data])
|
reply += f"\n{name}: " + ", ".join([f"{i:.1f}" for i in count_data])
|
||||||
await sign_status.send(reply)
|
await sign_status.send(reply)
|
||||||
|
|
||||||
|
|
||||||
@sign_status.assign("chart")
|
@sign_status.assign("chart")
|
||||||
async def _(arp: CommandResult = AlconnaResult()):
|
async def _(arp: CommandResult = AlconnaResult()):
|
||||||
limit = arp.result.subcommands.get("chart").args.get("limit")
|
limit = arp.result.subcommands.get("chart").args.get("limit")
|
||||||
if limit == 10000:
|
if limit == 10000:
|
||||||
if cache_img:
|
if cache_img:
|
||||||
await sign_status.send(UniMessage.image(raw=cache_img))
|
await sign_status.send(UniMessage.image(raw=cache_img))
|
||||||
return
|
return
|
||||||
img = await generate_chart(limit)
|
img = await generate_chart(limit)
|
||||||
await sign_status.send(UniMessage.image(raw=img))
|
await sign_status.send(UniMessage.image(raw=img))
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job("interval", seconds=SIGN_COUNT_DURATION, next_run_time=datetime.datetime.now())
|
@scheduler.scheduled_job("interval", seconds=SIGN_COUNT_DURATION, next_run_time=datetime.datetime.now())
|
||||||
async def update_sign_count():
|
async def update_sign_count():
|
||||||
global cache_img
|
global cache_img
|
||||||
if not SIGN_COUNT_URLS:
|
if not SIGN_COUNT_URLS:
|
||||||
return
|
return
|
||||||
data = await get_now_sign()
|
data = await get_now_sign()
|
||||||
for name, count in data.items():
|
for name, count in data.items():
|
||||||
await save_sign_count(count[0], count[1], SIGN_COUNT_URLS[name])
|
await save_sign_count(count[0], count[1], SIGN_COUNT_URLS[name])
|
||||||
|
|
||||||
cache_img = await generate_chart(10000)
|
cache_img = await generate_chart(10000)
|
||||||
|
|
||||||
|
|
||||||
async def get_now_sign() -> dict[str, tuple[float, int]]:
|
async def get_now_sign() -> dict[str, tuple[float, int]]:
|
||||||
"""
|
"""
|
||||||
Get the sign count and the time of the latest sign
|
Get the sign count and the time of the latest sign
|
||||||
Returns:
|
Returns:
|
||||||
tuple[float, int] | None: (time, count)
|
tuple[float, int] | None: (time, count)
|
||||||
"""
|
"""
|
||||||
data = {}
|
data = {}
|
||||||
now = time.time()
|
now = time.time()
|
||||||
async with aiohttp.ClientSession() as client:
|
async with aiohttp.ClientSession() as client:
|
||||||
for name, url in SIGN_COUNT_URLS.items():
|
for name, url in SIGN_COUNT_URLS.items():
|
||||||
async with client.get(url) as resp:
|
async with client.get(url) as resp:
|
||||||
count = (await resp.json())["count"]
|
count = (await resp.json())["count"]
|
||||||
data[name] = (now, count)
|
data[name] = (now, count)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
async def save_sign_count(timestamp: float, count: int, sid: str):
|
async def save_sign_count(timestamp: float, count: int, sid: str):
|
||||||
"""
|
"""
|
||||||
Save the sign count to the database
|
Save the sign count to the database
|
||||||
Args:
|
Args:
|
||||||
sid: the sign id, use url as the id
|
sid: the sign id, use url as the id
|
||||||
count:
|
count:
|
||||||
timestamp (float): the time of the sign count (int): the count of the sign
|
timestamp (float): the time of the sign count (int): the count of the sign
|
||||||
"""
|
"""
|
||||||
sign_db.save(SignCount(time=timestamp, count=count, sid=sid))
|
sign_db.save(SignCount(time=timestamp, count=count, sid=sid))
|
||||||
|
|
||||||
|
|
||||||
async def generate_chart(limit):
|
async def generate_chart(limit):
|
||||||
data = []
|
data = []
|
||||||
for name, url in SIGN_COUNT_URLS.items():
|
for name, url in SIGN_COUNT_URLS.items():
|
||||||
count_rows = sign_db.where_all(SignCount(), "sid = ? ORDER BY id DESC LIMIT ?", url, limit)
|
count_rows = sign_db.where_all(SignCount(), "sid = ? ORDER BY id DESC LIMIT ?", url, limit)
|
||||||
count_rows.reverse()
|
count_rows.reverse()
|
||||||
data.append(
|
data.append(
|
||||||
{
|
{
|
||||||
"name" : name,
|
"name" : name,
|
||||||
# "data": [[row.time, row.count] for row in count_rows]
|
# "data": [[row.time, row.count] for row in count_rows]
|
||||||
"times" : [row.time for row in count_rows],
|
"times" : [row.time for row in count_rows],
|
||||||
"counts": [row.count for row in count_rows]
|
"counts": [row.count for row in count_rows]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
img = await template2image(
|
img = await template2image(
|
||||||
template=get_path("templates/sign_status.html"),
|
template=get_path("templates/sign_status.html"),
|
||||||
templates={
|
templates={
|
||||||
"data": data
|
"data": data
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return img
|
return img
|
@ -1,18 +1,18 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .monitors import *
|
from .monitors import *
|
||||||
from .matchers import *
|
from .matchers import *
|
||||||
|
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪智障回复",
|
name="轻雪智障回复",
|
||||||
description="",
|
description="",
|
||||||
usage="",
|
usage="",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,106 +1,106 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
from nonebot import Bot, on_message, get_driver, require
|
from nonebot import Bot, on_message, get_driver, require
|
||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
from .utils import get_keywords
|
from .utils import get_keywords
|
||||||
from liteyuki.utils.base.word_bank import get_reply
|
from src.utils.base.word_bank import get_reply
|
||||||
from liteyuki.utils.event import get_message_type
|
from src.utils.event import get_message_type
|
||||||
from liteyuki.utils.base.permission import GROUP_ADMIN, GROUP_OWNER
|
from src.utils.base.permission import GROUP_ADMIN, GROUP_OWNER
|
||||||
from liteyuki.utils.base.data_manager import group_db, Group
|
from src.utils.base.data_manager import group_db, Group
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma
|
||||||
|
|
||||||
nicknames = set()
|
nicknames = set()
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
group_reply_probability: dict[str, float] = {
|
group_reply_probability: dict[str, float] = {
|
||||||
}
|
}
|
||||||
default_reply_probability = 0.05
|
default_reply_probability = 0.05
|
||||||
cut_probability = 0.4 # 分几句话的概率
|
cut_probability = 0.4 # 分几句话的概率
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"set-reply-probability",
|
"set-reply-probability",
|
||||||
Args["probability", float, default_reply_probability],
|
Args["probability", float, default_reply_probability],
|
||||||
),
|
),
|
||||||
aliases={"设置回复概率"},
|
aliases={"设置回复概率"},
|
||||||
permission=SUPERUSER | GROUP_ADMIN | GROUP_OWNER,
|
permission=SUPERUSER | GROUP_ADMIN | GROUP_OWNER,
|
||||||
).handle()
|
).handle()
|
||||||
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
||||||
# 修改内存和数据库的概率值
|
# 修改内存和数据库的概率值
|
||||||
if get_message_type(event) == "group":
|
if get_message_type(event) == "group":
|
||||||
group_id = event.group_id
|
group_id = event.group_id
|
||||||
probability = result.main_args.get("probability")
|
probability = result.main_args.get("probability")
|
||||||
# 保存到数据库
|
# 保存到数据库
|
||||||
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=str(group_id)))
|
group: Group = group_db.where_one(Group(), "group_id = ?", group_id, default=Group(group_id=str(group_id)))
|
||||||
group.config["reply_probability"] = probability
|
group.config["reply_probability"] = probability
|
||||||
group_db.save(group)
|
group_db.save(group)
|
||||||
|
|
||||||
await matcher.send(f"已将群组{group_id}的回复概率设置为{probability}")
|
await matcher.send(f"已将群组{group_id}的回复概率设置为{probability}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@group_db.on_save
|
@group_db.on_save
|
||||||
def _(model: Group):
|
def _(model: Group):
|
||||||
"""
|
"""
|
||||||
在数据库更新时,更新内存中的回复概率
|
在数据库更新时,更新内存中的回复概率
|
||||||
Args:
|
Args:
|
||||||
model:
|
model:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
group_reply_probability[model.group_id] = model.config.get("reply_probability", default_reply_probability)
|
group_reply_probability[model.group_id] = model.config.get("reply_probability", default_reply_probability)
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def _(bot: Bot):
|
async def _(bot: Bot):
|
||||||
global nicknames
|
global nicknames
|
||||||
nicknames.update(bot.config.nickname)
|
nicknames.update(bot.config.nickname)
|
||||||
# 从数据库加载群组的回复概率
|
# 从数据库加载群组的回复概率
|
||||||
groups = group_db.where_all(Group(), default=[])
|
groups = group_db.where_all(Group(), default=[])
|
||||||
for group in groups:
|
for group in groups:
|
||||||
group_reply_probability[group.group_id] = group.config.get("reply_probability", default_reply_probability)
|
group_reply_probability[group.group_id] = group.config.get("reply_probability", default_reply_probability)
|
||||||
|
|
||||||
|
|
||||||
@on_message(priority=100).handle()
|
@on_message(priority=100).handle()
|
||||||
async def _(event: T_MessageEvent, bot: Bot, state: T_State, matcher: Matcher):
|
async def _(event: T_MessageEvent, bot: Bot, state: T_State, matcher: Matcher):
|
||||||
kws = await get_keywords(event.message.extract_plain_text())
|
kws = await get_keywords(event.message.extract_plain_text())
|
||||||
|
|
||||||
tome = False
|
tome = False
|
||||||
if await to_me()(event=event, bot=bot, state=state):
|
if await to_me()(event=event, bot=bot, state=state):
|
||||||
tome = True
|
tome = True
|
||||||
else:
|
else:
|
||||||
for kw in kws:
|
for kw in kws:
|
||||||
if kw in nicknames:
|
if kw in nicknames:
|
||||||
tome = True
|
tome = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# 回复概率
|
# 回复概率
|
||||||
message_type = get_message_type(event)
|
message_type = get_message_type(event)
|
||||||
if tome or message_type == "private":
|
if tome or message_type == "private":
|
||||||
p = 1.0
|
p = 1.0
|
||||||
else:
|
else:
|
||||||
p = group_reply_probability.get(event.group_id, default_reply_probability)
|
p = group_reply_probability.get(event.group_id, default_reply_probability)
|
||||||
|
|
||||||
if random.random() < p:
|
if random.random() < p:
|
||||||
if reply := get_reply(kws):
|
if reply := get_reply(kws):
|
||||||
if random.random() < cut_probability:
|
if random.random() < cut_probability:
|
||||||
reply = reply.replace("。", "||").replace(",", "||").replace("!", "||").replace("?", "||")
|
reply = reply.replace("。", "||").replace(",", "||").replace("!", "||").replace("?", "||")
|
||||||
replies = reply.split("||")
|
replies = reply.split("||")
|
||||||
for r in replies:
|
for r in replies:
|
||||||
if r: # 防止空字符串
|
if r: # 防止空字符串
|
||||||
await asyncio.sleep(random.random() * 2)
|
await asyncio.sleep(random.random() * 2)
|
||||||
await matcher.send(r)
|
await matcher.send(r)
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(random.random() * 3)
|
await asyncio.sleep(random.random() * 3)
|
||||||
await matcher.send(reply)
|
await matcher.send(reply)
|
||||||
return
|
return
|
@ -1,13 +1,13 @@
|
|||||||
from jieba import lcut
|
from jieba import lcut
|
||||||
from nonebot.utils import run_sync
|
from nonebot.utils import run_sync
|
||||||
|
|
||||||
|
|
||||||
@run_sync
|
@run_sync
|
||||||
def get_keywords(text: str) -> list[str, ...]:
|
def get_keywords(text: str) -> list[str, ...]:
|
||||||
"""
|
"""
|
||||||
获取关键词
|
获取关键词
|
||||||
Args:
|
Args:
|
||||||
text: 文本
|
text: 文本
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
return lcut(text)
|
return lcut(text)
|
@ -1,29 +1,29 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .stat_matchers import *
|
from .stat_matchers import *
|
||||||
from .stat_monitors import *
|
from .stat_monitors import *
|
||||||
from .stat_restful_api import *
|
from .stat_restful_api import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="统计信息",
|
name="统计信息",
|
||||||
description="统计机器人的信息,包括消息、群聊等,支持排名、图表等功能",
|
description="统计机器人的信息,包括消息、群聊等,支持排名、图表等功能",
|
||||||
usage=(
|
usage=(
|
||||||
"```\nstatistic message 查看统计消息\n"
|
"```\nstatistic message 查看统计消息\n"
|
||||||
"可选参数:\n"
|
"可选参数:\n"
|
||||||
" -g|--group [group_id] 指定群聊\n"
|
" -g|--group [group_id] 指定群聊\n"
|
||||||
" -u|--user [user_id] 指定用户\n"
|
" -u|--user [user_id] 指定用户\n"
|
||||||
" -d|--duration [duration] 指定时长\n"
|
" -d|--duration [duration] 指定时长\n"
|
||||||
" -p|--period [period] 指定次数统计周期\n"
|
" -p|--period [period] 指定次数统计周期\n"
|
||||||
" -b|--bot [bot_id] 指定机器人\n"
|
" -b|--bot [bot_id] 指定机器人\n"
|
||||||
"命令别名:\n"
|
"命令别名:\n"
|
||||||
" statistic|stat message|msg|m\n"
|
" statistic|stat message|msg|m\n"
|
||||||
"```"
|
"```"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,21 +1,21 @@
|
|||||||
from liteyuki.utils.base.data import Database, LiteModel
|
from src.utils.base.data import Database, LiteModel
|
||||||
|
|
||||||
|
|
||||||
class MessageEventModel(LiteModel):
|
class MessageEventModel(LiteModel):
|
||||||
TABLE_NAME: str = "message_event"
|
TABLE_NAME: str = "message_event"
|
||||||
time: int = 0
|
time: int = 0
|
||||||
|
|
||||||
bot_id: str = ""
|
bot_id: str = ""
|
||||||
adapter: str = ""
|
adapter: str = ""
|
||||||
|
|
||||||
user_id: str = ""
|
user_id: str = ""
|
||||||
group_id: str = ""
|
group_id: str = ""
|
||||||
|
|
||||||
message_id: str = ""
|
message_id: str = ""
|
||||||
message: list = []
|
message: list = []
|
||||||
message_text: str = ""
|
message_text: str = ""
|
||||||
message_type: str = ""
|
message_type: str = ""
|
||||||
|
|
||||||
|
|
||||||
msg_db = Database("data/liteyuki/msg.ldb")
|
msg_db = Database("data/liteyuki/msg.ldb")
|
||||||
msg_db.auto_migrate(MessageEventModel())
|
msg_db.auto_migrate(MessageEventModel())
|
@ -1,172 +1,172 @@
|
|||||||
import time
|
import time
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
from nonebot import Bot
|
from nonebot import Bot
|
||||||
|
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from src.utils.message.html_tool import template2image
|
||||||
from .common import MessageEventModel, msg_db
|
from .common import MessageEventModel, msg_db
|
||||||
from liteyuki.utils.base.language import Language
|
from src.utils.base.language import Language
|
||||||
from liteyuki.utils.base.resource import get_path
|
from src.utils.base.resource import get_path
|
||||||
from liteyuki.utils.message.string_tool import convert_seconds_to_time
|
from src.utils.message.string_tool import convert_seconds_to_time
|
||||||
from ...utils.external.logo import get_group_icon, get_user_icon
|
from ...utils.external.logo import get_group_icon, get_user_icon
|
||||||
|
|
||||||
|
|
||||||
async def count_msg_by_bot_id(bot_id: str) -> int:
|
async def count_msg_by_bot_id(bot_id: str) -> int:
|
||||||
condition = " AND bot_id = ?"
|
condition = " AND bot_id = ?"
|
||||||
condition_args = [bot_id]
|
condition_args = [bot_id]
|
||||||
|
|
||||||
msg_rows = msg_db.where_all(
|
msg_rows = msg_db.where_all(
|
||||||
MessageEventModel(),
|
MessageEventModel(),
|
||||||
condition,
|
condition,
|
||||||
*condition_args
|
*condition_args
|
||||||
)
|
)
|
||||||
|
|
||||||
return len(msg_rows)
|
return len(msg_rows)
|
||||||
|
|
||||||
|
|
||||||
async def get_stat_msg_image(
|
async def get_stat_msg_image(
|
||||||
duration: int,
|
duration: int,
|
||||||
period: int,
|
period: int,
|
||||||
group_id: str = None,
|
group_id: str = None,
|
||||||
bot_id: str = None,
|
bot_id: str = None,
|
||||||
user_id: str = None,
|
user_id: str = None,
|
||||||
ulang: Language = Language()
|
ulang: Language = Language()
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
"""
|
"""
|
||||||
获取统计消息
|
获取统计消息
|
||||||
Args:
|
Args:
|
||||||
user_id:
|
user_id:
|
||||||
ulang:
|
ulang:
|
||||||
bot_id:
|
bot_id:
|
||||||
group_id:
|
group_id:
|
||||||
duration: 统计时间,单位秒
|
duration: 统计时间,单位秒
|
||||||
period: 统计周期,单位秒
|
period: 统计周期,单位秒
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: [int,], [int,] 两个列表,分别为周期中心时间戳和消息数量
|
tuple: [int,], [int,] 两个列表,分别为周期中心时间戳和消息数量
|
||||||
"""
|
"""
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
start_time = (now - duration)
|
start_time = (now - duration)
|
||||||
|
|
||||||
condition = "time > ?"
|
condition = "time > ?"
|
||||||
condition_args = [start_time]
|
condition_args = [start_time]
|
||||||
|
|
||||||
if group_id:
|
if group_id:
|
||||||
condition += " AND group_id = ?"
|
condition += " AND group_id = ?"
|
||||||
condition_args.append(group_id)
|
condition_args.append(group_id)
|
||||||
if bot_id:
|
if bot_id:
|
||||||
condition += " AND bot_id = ?"
|
condition += " AND bot_id = ?"
|
||||||
condition_args.append(bot_id)
|
condition_args.append(bot_id)
|
||||||
|
|
||||||
if user_id:
|
if user_id:
|
||||||
condition += " AND user_id = ?"
|
condition += " AND user_id = ?"
|
||||||
condition_args.append(user_id)
|
condition_args.append(user_id)
|
||||||
|
|
||||||
msg_rows = msg_db.where_all(
|
msg_rows = msg_db.where_all(
|
||||||
MessageEventModel(),
|
MessageEventModel(),
|
||||||
condition,
|
condition,
|
||||||
*condition_args
|
*condition_args
|
||||||
)
|
)
|
||||||
timestamps = []
|
timestamps = []
|
||||||
msg_count = []
|
msg_count = []
|
||||||
msg_rows.sort(key=lambda x: x.time)
|
msg_rows.sort(key=lambda x: x.time)
|
||||||
|
|
||||||
start_time = max(msg_rows[0].time, start_time)
|
start_time = max(msg_rows[0].time, start_time)
|
||||||
|
|
||||||
for i in range(start_time, now, period):
|
for i in range(start_time, now, period):
|
||||||
timestamps.append(i + period // 2)
|
timestamps.append(i + period // 2)
|
||||||
msg_count.append(0)
|
msg_count.append(0)
|
||||||
|
|
||||||
for msg in msg_rows:
|
for msg in msg_rows:
|
||||||
period_start_time = start_time + (msg.time - start_time) // period * period
|
period_start_time = start_time + (msg.time - start_time) // period * period
|
||||||
period_center_time = period_start_time + period // 2
|
period_center_time = period_start_time + period // 2
|
||||||
index = timestamps.index(period_center_time)
|
index = timestamps.index(period_center_time)
|
||||||
msg_count[index] += 1
|
msg_count[index] += 1
|
||||||
|
|
||||||
templates = {
|
templates = {
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"name" : ulang.get("stat.message")
|
"name" : ulang.get("stat.message")
|
||||||
+ f" Period {convert_seconds_to_time(period)}" + f" Duration {convert_seconds_to_time(duration)}"
|
+ f" Period {convert_seconds_to_time(period)}" + f" Duration {convert_seconds_to_time(duration)}"
|
||||||
+ (f" Group {group_id}" if group_id else "") + (f" Bot {bot_id}" if bot_id else "") + (
|
+ (f" Group {group_id}" if group_id else "") + (f" Bot {bot_id}" if bot_id else "") + (
|
||||||
f" User {user_id}" if user_id else ""),
|
f" User {user_id}" if user_id else ""),
|
||||||
"times" : timestamps,
|
"times" : timestamps,
|
||||||
"counts": msg_count
|
"counts": msg_count
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return await template2image(get_path("templates/stat_msg.html"), templates)
|
return await template2image(get_path("templates/stat_msg.html"), templates)
|
||||||
|
|
||||||
|
|
||||||
async def get_stat_rank_image(
|
async def get_stat_rank_image(
|
||||||
rank_type: str,
|
rank_type: str,
|
||||||
limit: dict[str, Any],
|
limit: dict[str, Any],
|
||||||
ulang: Language = Language(),
|
ulang: Language = Language(),
|
||||||
bot: Bot = None,
|
bot: Bot = None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
if rank_type == "user":
|
if rank_type == "user":
|
||||||
condition = "user_id != ''"
|
condition = "user_id != ''"
|
||||||
condition_args = []
|
condition_args = []
|
||||||
else:
|
else:
|
||||||
condition = "group_id != ''"
|
condition = "group_id != ''"
|
||||||
condition_args = []
|
condition_args = []
|
||||||
|
|
||||||
for k, v in limit.items():
|
for k, v in limit.items():
|
||||||
match k:
|
match k:
|
||||||
case "user_id":
|
case "user_id":
|
||||||
condition += " AND user_id = ?"
|
condition += " AND user_id = ?"
|
||||||
condition_args.append(v)
|
condition_args.append(v)
|
||||||
case "group_id":
|
case "group_id":
|
||||||
condition += " AND group_id = ?"
|
condition += " AND group_id = ?"
|
||||||
condition_args.append(v)
|
condition_args.append(v)
|
||||||
case "bot_id":
|
case "bot_id":
|
||||||
condition += " AND bot_id = ?"
|
condition += " AND bot_id = ?"
|
||||||
condition_args.append(v)
|
condition_args.append(v)
|
||||||
case "duration":
|
case "duration":
|
||||||
condition += " AND time > ?"
|
condition += " AND time > ?"
|
||||||
condition_args.append(v)
|
condition_args.append(v)
|
||||||
|
|
||||||
msg_rows = msg_db.where_all(
|
msg_rows = msg_db.where_all(
|
||||||
MessageEventModel(),
|
MessageEventModel(),
|
||||||
condition,
|
condition,
|
||||||
*condition_args
|
*condition_args
|
||||||
)
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
name: string, # user name or group name
|
name: string, # user name or group name
|
||||||
count: int, # message count
|
count: int, # message count
|
||||||
icon: string # icon url
|
icon: string # icon url
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if rank_type == "user":
|
if rank_type == "user":
|
||||||
ranking_counter = Counter([msg.user_id for msg in msg_rows])
|
ranking_counter = Counter([msg.user_id for msg in msg_rows])
|
||||||
else:
|
else:
|
||||||
ranking_counter = Counter([msg.group_id for msg in msg_rows])
|
ranking_counter = Counter([msg.group_id for msg in msg_rows])
|
||||||
sorted_data = sorted(ranking_counter.items(), key=lambda x: x[1], reverse=True)
|
sorted_data = sorted(ranking_counter.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
ranking: list[dict[str, Any]] = [
|
ranking: list[dict[str, Any]] = [
|
||||||
{
|
{
|
||||||
"name" : _[0],
|
"name" : _[0],
|
||||||
"count": _[1],
|
"count": _[1],
|
||||||
"icon" : await (get_group_icon(platform="qq", group_id=_[0]) if rank_type == "group" else get_user_icon(
|
"icon" : await (get_group_icon(platform="qq", group_id=_[0]) if rank_type == "group" else get_user_icon(
|
||||||
platform="qq", user_id=_[0]
|
platform="qq", user_id=_[0]
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
for _ in sorted_data[0:min(len(sorted_data), limit["rank"])]
|
for _ in sorted_data[0:min(len(sorted_data), limit["rank"])]
|
||||||
]
|
]
|
||||||
|
|
||||||
templates = {
|
templates = {
|
||||||
"data":
|
"data":
|
||||||
{
|
{
|
||||||
"name" : ulang.get("stat.rank") + f" Type {rank_type}" + f" Limit {limit}",
|
"name" : ulang.get("stat.rank") + f" Type {rank_type}" + f" Limit {limit}",
|
||||||
"ranking": ranking
|
"ranking": ranking
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await template2image(get_path("templates/stat_rank.html"), templates, debug=True)
|
return await template2image(get_path("templates/stat_rank.html"), templates, debug=True)
|
@ -1,135 +1,135 @@
|
|||||||
from nonebot import Bot, require
|
from nonebot import Bot, require
|
||||||
from liteyuki.utils.message.string_tool import convert_duration, convert_time_to_seconds
|
from src.utils.message.string_tool import convert_duration, convert_time_to_seconds
|
||||||
from .stat_api import *
|
from .data_source import *
|
||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
from liteyuki.utils.base.language import Language
|
from src.utils.base.language import Language
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
|
||||||
from nonebot_plugin_alconna import (
|
from nonebot_plugin_alconna import (
|
||||||
UniMessage,
|
UniMessage,
|
||||||
on_alconna,
|
on_alconna,
|
||||||
Alconna,
|
Alconna,
|
||||||
Args,
|
Args,
|
||||||
Subcommand,
|
Subcommand,
|
||||||
Arparma,
|
Arparma,
|
||||||
Option,
|
Option,
|
||||||
MultiVar
|
MultiVar
|
||||||
)
|
)
|
||||||
|
|
||||||
stat_msg = on_alconna(
|
stat_msg = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"statistic",
|
"statistic",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"message",
|
"message",
|
||||||
# Args["duration", str, "2d"]["period", str, "60s"], # 默认为1天
|
# Args["duration", str, "2d"]["period", str, "60s"], # 默认为1天
|
||||||
Option(
|
Option(
|
||||||
"-d|--duration",
|
"-d|--duration",
|
||||||
Args["duration", str, "2d"],
|
Args["duration", str, "2d"],
|
||||||
help_text="统计时间",
|
help_text="统计时间",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-p|--period",
|
"-p|--period",
|
||||||
Args["period", str, "60s"],
|
Args["period", str, "60s"],
|
||||||
help_text="统计周期",
|
help_text="统计周期",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-b|--bot", # 生成图表
|
"-b|--bot", # 生成图表
|
||||||
Args["bot_id", str, "current"],
|
Args["bot_id", str, "current"],
|
||||||
help_text="是否指定机器人",
|
help_text="是否指定机器人",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-g|--group",
|
"-g|--group",
|
||||||
Args["group_id", str, "current"],
|
Args["group_id", str, "current"],
|
||||||
help_text="指定群组"
|
help_text="指定群组"
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-u|--user",
|
"-u|--user",
|
||||||
Args["user_id", str, "current"],
|
Args["user_id", str, "current"],
|
||||||
help_text="指定用户"
|
help_text="指定用户"
|
||||||
),
|
),
|
||||||
alias={"msg", "m"},
|
alias={"msg", "m"},
|
||||||
help_text="查看统计次数内的消息"
|
help_text="查看统计次数内的消息"
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"rank",
|
"rank",
|
||||||
Option(
|
Option(
|
||||||
"-u|--user",
|
"-u|--user",
|
||||||
help_text="以用户为指标",
|
help_text="以用户为指标",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-g|--group",
|
"-g|--group",
|
||||||
help_text="以群组为指标",
|
help_text="以群组为指标",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-l|--limit",
|
"-l|--limit",
|
||||||
Args["limit", MultiVar(str)],
|
Args["limit", MultiVar(str)],
|
||||||
help_text="限制参数,使用key=val格式",
|
help_text="限制参数,使用key=val格式",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-d|--duration",
|
"-d|--duration",
|
||||||
Args["duration", str, "1d"],
|
Args["duration", str, "1d"],
|
||||||
help_text="统计时间",
|
help_text="统计时间",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-r|--rank",
|
"-r|--rank",
|
||||||
Args["rank", int, 20],
|
Args["rank", int, 20],
|
||||||
help_text="指定排名",
|
help_text="指定排名",
|
||||||
),
|
),
|
||||||
alias={"r"},
|
alias={"r"},
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
aliases={"stat"}
|
aliases={"stat"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@stat_msg.assign("message")
|
@stat_msg.assign("message")
|
||||||
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||||
ulang = Language(event_utils.get_user_id(event))
|
ulang = Language(event_utils.get_user_id(event))
|
||||||
try:
|
try:
|
||||||
duration = convert_time_to_seconds(result.other_args.get("duration", "2d")) # 秒数
|
duration = convert_time_to_seconds(result.other_args.get("duration", "2d")) # 秒数
|
||||||
period = convert_time_to_seconds(result.other_args.get("period", "1m"))
|
period = convert_time_to_seconds(result.other_args.get("period", "1m"))
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
await stat_msg.send(ulang.get("liteyuki.invalid_command", TEXT=str(e.__str__())))
|
await stat_msg.send(ulang.get("liteyuki.invalid_command", TEXT=str(e.__str__())))
|
||||||
return
|
return
|
||||||
|
|
||||||
group_id = result.other_args.get("group_id")
|
group_id = result.other_args.get("group_id")
|
||||||
bot_id = result.other_args.get("bot_id")
|
bot_id = result.other_args.get("bot_id")
|
||||||
user_id = result.other_args.get("user_id")
|
user_id = result.other_args.get("user_id")
|
||||||
|
|
||||||
if group_id in ["current", "c"]:
|
if group_id in ["current", "c"]:
|
||||||
group_id = str(event_utils.get_group_id(event))
|
group_id = str(event_utils.get_group_id(event))
|
||||||
|
|
||||||
if group_id in ["all", "a"]:
|
if group_id in ["all", "a"]:
|
||||||
group_id = "all"
|
group_id = "all"
|
||||||
|
|
||||||
if bot_id in ["current", "c"]:
|
if bot_id in ["current", "c"]:
|
||||||
bot_id = str(bot.self_id)
|
bot_id = str(bot.self_id)
|
||||||
|
|
||||||
if user_id in ["current", "c"]:
|
if user_id in ["current", "c"]:
|
||||||
user_id = str(event_utils.get_user_id(event))
|
user_id = str(event_utils.get_user_id(event))
|
||||||
|
|
||||||
img = await get_stat_msg_image(duration=duration, period=period, group_id=group_id, bot_id=bot_id, user_id=user_id, ulang=ulang)
|
img = await get_stat_msg_image(duration=duration, period=period, group_id=group_id, bot_id=bot_id, user_id=user_id, ulang=ulang)
|
||||||
await stat_msg.send(UniMessage.image(raw=img))
|
await stat_msg.send(UniMessage.image(raw=img))
|
||||||
|
|
||||||
|
|
||||||
@stat_msg.assign("rank")
|
@stat_msg.assign("rank")
|
||||||
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||||
ulang = Language(event_utils.get_user_id(event))
|
ulang = Language(event_utils.get_user_id(event))
|
||||||
rank_type = "user"
|
rank_type = "user"
|
||||||
duration = convert_time_to_seconds(result.other_args.get("duration", "1d"))
|
duration = convert_time_to_seconds(result.other_args.get("duration", "1d"))
|
||||||
print(result)
|
print(result)
|
||||||
if result.subcommands.get("rank").options.get("user"):
|
if result.subcommands.get("rank").options.get("user"):
|
||||||
rank_type = "user"
|
rank_type = "user"
|
||||||
elif result.subcommands.get("rank").options.get("group"):
|
elif result.subcommands.get("rank").options.get("group"):
|
||||||
rank_type = "group"
|
rank_type = "group"
|
||||||
|
|
||||||
limit = result.other_args.get("limit", {})
|
limit = result.other_args.get("limit", {})
|
||||||
if limit:
|
if limit:
|
||||||
limit = dict([i.split("=") for i in limit])
|
limit = dict([i.split("=") for i in limit])
|
||||||
limit["duration"] = time.time() - duration # 起始时间戳
|
limit["duration"] = time.time() - duration # 起始时间戳
|
||||||
limit["rank"] = result.other_args.get("rank", 20)
|
limit["rank"] = result.other_args.get("rank", 20)
|
||||||
|
|
||||||
img = await get_stat_rank_image(rank_type=rank_type, limit=limit, ulang=ulang)
|
img = await get_stat_rank_image(rank_type=rank_type, limit=limit, ulang=ulang)
|
||||||
await stat_msg.send(UniMessage.image(raw=img))
|
await stat_msg.send(UniMessage.image(raw=img))
|
@ -1,92 +1,92 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
from liteyuki.utils.base.data import Database, LiteModel
|
from src.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.utils.base.ly_typing import v11, v12, satori
|
from src.utils.base.ly_typing import v11, v12, satori
|
||||||
|
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
|
|
||||||
from .common import MessageEventModel, msg_db
|
from .common import MessageEventModel, msg_db
|
||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
|
||||||
|
|
||||||
async def general_event_monitor(bot: T_Bot, event: T_MessageEvent):
|
async def general_event_monitor(bot: T_Bot, event: T_MessageEvent):
|
||||||
print("POST PROCESS")
|
print("POST PROCESS")
|
||||||
# if isinstance(bot, satori.Bot):
|
# if isinstance(bot, satori.Bot):
|
||||||
# print("POST PROCESS SATORI EVENT")
|
# print("POST PROCESS SATORI EVENT")
|
||||||
# return await satori_event_monitor(bot, event)
|
# return await satori_event_monitor(bot, event)
|
||||||
# elif isinstance(bot, v11.Bot):
|
# elif isinstance(bot, v11.Bot):
|
||||||
# print("POST PROCESS V11 EVENT")
|
# print("POST PROCESS V11 EVENT")
|
||||||
# return await onebot_v11_event_monitor(bot, event)
|
# return await onebot_v11_event_monitor(bot, event)
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def onebot_v11_event_monitor(bot: v11.Bot, event: v11.MessageEvent):
|
async def onebot_v11_event_monitor(bot: v11.Bot, event: v11.MessageEvent):
|
||||||
if event.message_type == "group":
|
if event.message_type == "group":
|
||||||
event: v11.GroupMessageEvent
|
event: v11.GroupMessageEvent
|
||||||
group_id = str(event.group_id)
|
group_id = str(event.group_id)
|
||||||
else:
|
else:
|
||||||
group_id = ""
|
group_id = ""
|
||||||
mem = MessageEventModel(
|
mem = MessageEventModel(
|
||||||
time=int(time.time()),
|
time=int(time.time()),
|
||||||
bot_id=bot.self_id,
|
bot_id=bot.self_id,
|
||||||
adapter="onebot.v11",
|
adapter="onebot.v11",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
user_id=str(event.user_id),
|
user_id=str(event.user_id),
|
||||||
|
|
||||||
message_id=str(event.message_id),
|
message_id=str(event.message_id),
|
||||||
|
|
||||||
message=[ms.__dict__ for ms in event.message],
|
message=[ms.__dict__ for ms in event.message],
|
||||||
message_text=event.raw_message,
|
message_text=event.raw_message,
|
||||||
message_type=event.message_type,
|
message_type=event.message_type,
|
||||||
)
|
)
|
||||||
msg_db.save(mem)
|
msg_db.save(mem)
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def onebot_v12_event_monitor(bot: v12.Bot, event: v12.MessageEvent):
|
async def onebot_v12_event_monitor(bot: v12.Bot, event: v12.MessageEvent):
|
||||||
if event.message_type == "group":
|
if event.message_type == "group":
|
||||||
event: v12.GroupMessageEvent
|
event: v12.GroupMessageEvent
|
||||||
group_id = str(event.group_id)
|
group_id = str(event.group_id)
|
||||||
else:
|
else:
|
||||||
group_id = ""
|
group_id = ""
|
||||||
mem = MessageEventModel(
|
mem = MessageEventModel(
|
||||||
time=int(time.time()),
|
time=int(time.time()),
|
||||||
bot_id=bot.self_id,
|
bot_id=bot.self_id,
|
||||||
adapter="onebot.v12",
|
adapter="onebot.v12",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
user_id=str(event.user_id),
|
user_id=str(event.user_id),
|
||||||
|
|
||||||
message_id=[ms.__dict__ for ms in event.message],
|
message_id=[ms.__dict__ for ms in event.message],
|
||||||
|
|
||||||
message=event.message,
|
message=event.message,
|
||||||
message_text=event.raw_message,
|
message_text=event.raw_message,
|
||||||
message_type=event.message_type,
|
message_type=event.message_type,
|
||||||
)
|
)
|
||||||
msg_db.save(mem)
|
msg_db.save(mem)
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def satori_event_monitor(bot: satori.Bot, event: satori.MessageEvent):
|
async def satori_event_monitor(bot: satori.Bot, event: satori.MessageEvent):
|
||||||
if event.guild is not None:
|
if event.guild is not None:
|
||||||
event: satori.MessageEvent
|
event: satori.MessageEvent
|
||||||
group_id = str(event.guild.id)
|
group_id = str(event.guild.id)
|
||||||
else:
|
else:
|
||||||
group_id = ""
|
group_id = ""
|
||||||
|
|
||||||
mem = MessageEventModel(
|
mem = MessageEventModel(
|
||||||
time=int(time.time()),
|
time=int(time.time()),
|
||||||
bot_id=bot.self_id,
|
bot_id=bot.self_id,
|
||||||
adapter="satori",
|
adapter="satori",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
user_id=str(event.user.id),
|
user_id=str(event.user.id),
|
||||||
message_id=[ms.__str__() for ms in event.message],
|
message_id=[ms.__str__() for ms in event.message],
|
||||||
message=event.message,
|
message=event.message,
|
||||||
message_text=event.message.content,
|
message_text=event.message.content,
|
||||||
message_type=event_utils.get_message_type(event),
|
message_type=event_utils.get_message_type(event),
|
||||||
)
|
)
|
||||||
msg_db.save(mem)
|
msg_db.save(mem)
|
21
src/plugins/liteyuki_statistics/word_cloud/LICENSE
Normal file
21
src/plugins/liteyuki_statistics/word_cloud/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 hemengyang
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
107
src/plugins/liteyuki_statistics/word_cloud/data_source.py
Normal file
107
src/plugins/liteyuki_statistics/word_cloud/data_source.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import asyncio
|
||||||
|
import concurrent.futures
|
||||||
|
import contextlib
|
||||||
|
import re
|
||||||
|
from functools import partial
|
||||||
|
from io import BytesIO
|
||||||
|
from random import choice
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import jieba
|
||||||
|
import jieba.analyse
|
||||||
|
import numpy as np
|
||||||
|
from emoji import replace_emoji
|
||||||
|
from PIL import Image
|
||||||
|
from wordcloud import WordCloud
|
||||||
|
|
||||||
|
from .config import global_config, plugin_config
|
||||||
|
|
||||||
|
|
||||||
|
def pre_precess(msg: str) -> str:
|
||||||
|
"""对消息进行预处理"""
|
||||||
|
# 去除网址
|
||||||
|
# https://stackoverflow.com/a/17773849/9212748
|
||||||
|
url_regex = re.compile(
|
||||||
|
r"(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]"
|
||||||
|
r"+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})"
|
||||||
|
)
|
||||||
|
msg = url_regex.sub("", msg)
|
||||||
|
|
||||||
|
# 去除 \u200b
|
||||||
|
msg = re.sub(r"\u200b", "", msg)
|
||||||
|
|
||||||
|
# 去除 emoji
|
||||||
|
# https://github.com/carpedm20/emoji
|
||||||
|
msg = replace_emoji(msg)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def analyse_message(msg: str) -> dict[str, float]:
|
||||||
|
"""分析消息
|
||||||
|
|
||||||
|
分词,并统计词频
|
||||||
|
"""
|
||||||
|
# 设置停用词表
|
||||||
|
if plugin_config.wordcloud_stopwords_path:
|
||||||
|
jieba.analyse.set_stop_words(plugin_config.wordcloud_stopwords_path)
|
||||||
|
# 加载用户词典
|
||||||
|
if plugin_config.wordcloud_userdict_path:
|
||||||
|
jieba.load_userdict(str(plugin_config.wordcloud_userdict_path))
|
||||||
|
# 基于 TF-IDF 算法的关键词抽取
|
||||||
|
# 返回所有关键词,因为设置了数量其实也只是 tags[:topK],不如交给词云库处理
|
||||||
|
words = jieba.analyse.extract_tags(msg, topK=0, withWeight=True)
|
||||||
|
return dict(words)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mask(key: str):
|
||||||
|
"""获取 mask"""
|
||||||
|
mask_path = plugin_config.get_mask_path(key)
|
||||||
|
if mask_path.exists():
|
||||||
|
return np.array(Image.open(mask_path))
|
||||||
|
# 如果指定 mask 文件不存在,则尝试默认 mask
|
||||||
|
default_mask_path = plugin_config.get_mask_path()
|
||||||
|
if default_mask_path.exists():
|
||||||
|
return np.array(Image.open(default_mask_path))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_wordcloud(messages: list[str], mask_key: str) -> Optional[bytes]:
|
||||||
|
# 过滤掉命令
|
||||||
|
command_start = tuple(i for i in global_config.command_start if i)
|
||||||
|
message = " ".join(m for m in messages if not m.startswith(command_start))
|
||||||
|
# 预处理
|
||||||
|
message = pre_precess(message)
|
||||||
|
# 分析消息。分词,并统计词频
|
||||||
|
frequency = analyse_message(message)
|
||||||
|
# 词云参数
|
||||||
|
wordcloud_options = {}
|
||||||
|
wordcloud_options.update(plugin_config.wordcloud_options)
|
||||||
|
wordcloud_options.setdefault("font_path", str(plugin_config.wordcloud_font_path))
|
||||||
|
wordcloud_options.setdefault("width", plugin_config.wordcloud_width)
|
||||||
|
wordcloud_options.setdefault("height", plugin_config.wordcloud_height)
|
||||||
|
wordcloud_options.setdefault(
|
||||||
|
"background_color", plugin_config.wordcloud_background_color
|
||||||
|
)
|
||||||
|
# 如果 colormap 是列表,则随机选择一个
|
||||||
|
colormap = (
|
||||||
|
plugin_config.wordcloud_colormap
|
||||||
|
if isinstance(plugin_config.wordcloud_colormap, str)
|
||||||
|
else choice(plugin_config.wordcloud_colormap)
|
||||||
|
)
|
||||||
|
wordcloud_options.setdefault("colormap", colormap)
|
||||||
|
wordcloud_options.setdefault("mask", get_mask(mask_key))
|
||||||
|
with contextlib.suppress(ValueError):
|
||||||
|
wordcloud = WordCloud(**wordcloud_options)
|
||||||
|
image = wordcloud.generate_from_frequencies(frequency).to_image()
|
||||||
|
image_bytes = BytesIO()
|
||||||
|
image.save(image_bytes, format="PNG")
|
||||||
|
return image_bytes.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_wordcloud(messages: list[str], mask_key: str) -> Optional[bytes]:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
pfunc = partial(_get_wordcloud, messages, mask_key)
|
||||||
|
# 虽然不知道具体是哪里泄漏了,但是通过每次关闭线程池可以避免这个问题
|
||||||
|
# https://github.com/he0119/nonebot-plugin-wordcloud/issues/99
|
||||||
|
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||||
|
return await loop.run_in_executor(pool, pfunc)
|
@ -1,24 +1,24 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .status import *
|
from .status import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="状态查看器",
|
name="状态查看器",
|
||||||
description="",
|
description="",
|
||||||
usage=(
|
usage=(
|
||||||
"MARKDOWN### 状态查看器\n"
|
"MARKDOWN### 状态查看器\n"
|
||||||
"查看机器人的状态\n"
|
"查看机器人的状态\n"
|
||||||
"### 用法\n"
|
"### 用法\n"
|
||||||
"- `/status` 查看基本情况\n"
|
"- `/status` 查看基本情况\n"
|
||||||
"- `/status memory` 查看内存使用情况\n"
|
"- `/status memory` 查看内存使用情况\n"
|
||||||
"- `/status process` 查看进程情况\n"
|
"- `/status process` 查看进程情况\n"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -1,278 +1,278 @@
|
|||||||
import platform
|
import platform
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import nonebot
|
import nonebot
|
||||||
import psutil
|
import psutil
|
||||||
from cpuinfo import cpuinfo
|
from cpuinfo import cpuinfo
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.adapters import satori
|
from nonebot.adapters import satori
|
||||||
|
|
||||||
from liteyuki.utils import __NAME__, __VERSION__
|
from src.utils import __NAME__, __VERSION__
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.data_manager import TempConfig, common_db
|
from src.utils.base.data_manager import TempConfig, common_db
|
||||||
from liteyuki.utils.base.language import Language
|
from src.utils.base.language import Language
|
||||||
from liteyuki.utils.base.resource import get_loaded_resource_packs, get_path
|
from src.utils.base.resource import get_loaded_resource_packs, get_path
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from src.utils.message.html_tool import template2image
|
||||||
from liteyuki.utils import satori_utils
|
from src.utils import satori_utils
|
||||||
from .counter_for_satori import satori_counter
|
from .counter_for_satori import satori_counter
|
||||||
from git import Repo
|
from git import Repo
|
||||||
|
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
commit_hash = Repo(".").head.commit.hexsha
|
commit_hash = Repo(".").head.commit.hexsha
|
||||||
|
|
||||||
protocol_names = {
|
protocol_names = {
|
||||||
0: "iPad",
|
0: "iPad",
|
||||||
1: "Android Phone",
|
1: "Android Phone",
|
||||||
2: "Android Watch",
|
2: "Android Watch",
|
||||||
3: "Mac",
|
3: "Mac",
|
||||||
5: "iPad",
|
5: "iPad",
|
||||||
6: "Android Pad",
|
6: "Android Pad",
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Universal Interface
|
Universal Interface
|
||||||
data
|
data
|
||||||
- bot
|
- bot
|
||||||
- name: str
|
- name: str
|
||||||
icon: str
|
icon: str
|
||||||
id: int
|
id: int
|
||||||
protocol_name: str
|
protocol_name: str
|
||||||
groups: int
|
groups: int
|
||||||
friends: int
|
friends: int
|
||||||
message_sent: int
|
message_sent: int
|
||||||
message_received: int
|
message_received: int
|
||||||
app_name: str
|
app_name: str
|
||||||
- hardware
|
- hardware
|
||||||
- cpu
|
- cpu
|
||||||
- percent: float
|
- percent: float
|
||||||
- name: str
|
- name: str
|
||||||
- mem
|
- mem
|
||||||
- percent: float
|
- percent: float
|
||||||
- total: int
|
- total: int
|
||||||
- used: int
|
- used: int
|
||||||
- free: int
|
- free: int
|
||||||
- swap
|
- swap
|
||||||
- percent: float
|
- percent: float
|
||||||
- total: int
|
- total: int
|
||||||
- used: int
|
- used: int
|
||||||
- free: int
|
- free: int
|
||||||
- disk: list
|
- disk: list
|
||||||
- name: str
|
- name: str
|
||||||
- percent: float
|
- percent: float
|
||||||
- total: int
|
- total: int
|
||||||
"""
|
"""
|
||||||
status_card_cache = {} # lang -> bytes
|
status_card_cache = {} # lang -> bytes
|
||||||
|
|
||||||
|
|
||||||
# 60s刷新一次
|
# 60s刷新一次
|
||||||
@scheduler.scheduled_job("cron", second="*/40")
|
@scheduler.scheduled_job("cron", second="*/40")
|
||||||
async def refresh_status_card():
|
async def refresh_status_card():
|
||||||
nonebot.logger.debug("Refreshing status card cache...")
|
nonebot.logger.debug("Refreshing status card cache...")
|
||||||
global status_card_cache
|
global status_card_cache
|
||||||
bot_data = await get_bots_data()
|
bot_data = await get_bots_data()
|
||||||
hardware_data = await get_hardware_data()
|
hardware_data = await get_hardware_data()
|
||||||
liteyuki_data = await get_liteyuki_data()
|
liteyuki_data = await get_liteyuki_data()
|
||||||
for lang in status_card_cache.keys():
|
for lang in status_card_cache.keys():
|
||||||
status_card_cache[lang] = await generate_status_card(
|
status_card_cache[lang] = await generate_status_card(
|
||||||
bot_data,
|
bot_data,
|
||||||
hardware_data,
|
hardware_data,
|
||||||
liteyuki_data,
|
liteyuki_data,
|
||||||
lang=lang,
|
lang=lang,
|
||||||
use_cache=False
|
use_cache=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0",
|
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0",
|
||||||
use_cache=False
|
use_cache=False
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
if not use_cache:
|
if not use_cache:
|
||||||
return await template2image(
|
return await template2image(
|
||||||
get_path("templates/status.html", abs_path=True),
|
get_path("templates/status.html", abs_path=True),
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"bot" : bot,
|
"bot" : bot,
|
||||||
"hardware" : hardware,
|
"hardware" : hardware,
|
||||||
"liteyuki" : liteyuki,
|
"liteyuki" : liteyuki,
|
||||||
"localization": get_local_data(lang)
|
"localization": get_local_data(lang)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if lang not in status_card_cache:
|
if lang not in status_card_cache:
|
||||||
status_card_cache[lang] = await generate_status_card(bot, hardware, liteyuki, lang=lang, bot_id=bot_id)
|
status_card_cache[lang] = await generate_status_card(bot, hardware, liteyuki, lang=lang, bot_id=bot_id)
|
||||||
return status_card_cache[lang]
|
return status_card_cache[lang]
|
||||||
|
|
||||||
|
|
||||||
def get_local_data(lang_code) -> dict:
|
def get_local_data(lang_code) -> dict:
|
||||||
lang = Language(lang_code)
|
lang = Language(lang_code)
|
||||||
return {
|
return {
|
||||||
"friends" : lang.get("status.friends"),
|
"friends" : lang.get("status.friends"),
|
||||||
"groups" : lang.get("status.groups"),
|
"groups" : lang.get("status.groups"),
|
||||||
"plugins" : lang.get("status.plugins"),
|
"plugins" : lang.get("status.plugins"),
|
||||||
"bots" : lang.get("status.bots"),
|
"bots" : lang.get("status.bots"),
|
||||||
"message_sent" : lang.get("status.message_sent"),
|
"message_sent" : lang.get("status.message_sent"),
|
||||||
"message_received": lang.get("status.message_received"),
|
"message_received": lang.get("status.message_received"),
|
||||||
"cpu" : lang.get("status.cpu"),
|
"cpu" : lang.get("status.cpu"),
|
||||||
"memory" : lang.get("status.memory"),
|
"memory" : lang.get("status.memory"),
|
||||||
"swap" : lang.get("status.swap"),
|
"swap" : lang.get("status.swap"),
|
||||||
"disk" : lang.get("status.disk"),
|
"disk" : lang.get("status.disk"),
|
||||||
|
|
||||||
"usage" : lang.get("status.usage"),
|
"usage" : lang.get("status.usage"),
|
||||||
"total" : lang.get("status.total"),
|
"total" : lang.get("status.total"),
|
||||||
"used" : lang.get("status.used"),
|
"used" : lang.get("status.used"),
|
||||||
"free" : lang.get("status.free"),
|
"free" : lang.get("status.free"),
|
||||||
|
|
||||||
"days" : lang.get("status.days"),
|
"days" : lang.get("status.days"),
|
||||||
"hours" : lang.get("status.hours"),
|
"hours" : lang.get("status.hours"),
|
||||||
"minutes" : lang.get("status.minutes"),
|
"minutes" : lang.get("status.minutes"),
|
||||||
"seconds" : lang.get("status.seconds"),
|
"seconds" : lang.get("status.seconds"),
|
||||||
"runtime" : lang.get("status.runtime"),
|
"runtime" : lang.get("status.runtime"),
|
||||||
"threads" : lang.get("status.threads"),
|
"threads" : lang.get("status.threads"),
|
||||||
"cores" : lang.get("status.cores"),
|
"cores" : lang.get("status.cores"),
|
||||||
"process" : lang.get("status.process"),
|
"process" : lang.get("status.process"),
|
||||||
"resources" : lang.get("status.resources"),
|
"resources" : lang.get("status.resources"),
|
||||||
"description" : lang.get("status.description"),
|
"description" : lang.get("status.description"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def get_bots_data(self_id: str = "0") -> dict:
|
async def get_bots_data(self_id: str = "0") -> dict:
|
||||||
"""获取当前所有机器人数据
|
"""获取当前所有机器人数据
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
result = {
|
result = {
|
||||||
"self_id": self_id,
|
"self_id": self_id,
|
||||||
"bots" : [],
|
"bots" : [],
|
||||||
}
|
}
|
||||||
for bot_id, bot in nonebot.get_bots().items():
|
for bot_id, bot in nonebot.get_bots().items():
|
||||||
groups = 0
|
groups = 0
|
||||||
friends = 0
|
friends = 0
|
||||||
status = {}
|
status = {}
|
||||||
bot_name = bot_id
|
bot_name = bot_id
|
||||||
version_info = {}
|
version_info = {}
|
||||||
if isinstance(bot, satori.Bot):
|
if isinstance(bot, satori.Bot):
|
||||||
try:
|
try:
|
||||||
bot_name = (await satori_utils.user_infos.get(bot.self_id)).name
|
bot_name = (await satori_utils.user_infos.get(bot.self_id)).name
|
||||||
groups = str(await satori_utils.count_groups(bot))
|
groups = str(await satori_utils.count_groups(bot))
|
||||||
friends = str(await satori_utils.count_friends(bot))
|
friends = str(await satori_utils.count_friends(bot))
|
||||||
status = {}
|
status = {}
|
||||||
version_info = await bot.get_version_info()
|
version_info = await bot.get_version_info()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
# API fetch
|
# API fetch
|
||||||
bot_name = (await bot.get_login_info())["nickname"]
|
bot_name = (await bot.get_login_info())["nickname"]
|
||||||
groups = len(await bot.get_group_list())
|
groups = len(await bot.get_group_list())
|
||||||
friends = len(await bot.get_friend_list())
|
friends = len(await bot.get_friend_list())
|
||||||
status = await bot.get_status()
|
status = await bot.get_status()
|
||||||
version_info = await bot.get_version_info()
|
version_info = await bot.get_version_info()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
statistics = status.get("stat", {})
|
statistics = status.get("stat", {})
|
||||||
app_name = version_info.get("app_name", "UnknownImplementation")
|
app_name = version_info.get("app_name", "UnknownImplementation")
|
||||||
if app_name in ["Lagrange.OneBot", "LLOneBot", "Shamrock", "NapCat.Onebot"]:
|
if app_name in ["Lagrange.OneBot", "LLOneBot", "Shamrock", "NapCat.Onebot"]:
|
||||||
icon = f"https://q.qlogo.cn/g?b=qq&nk={bot_id}&s=640"
|
icon = f"https://q.qlogo.cn/g?b=qq&nk={bot_id}&s=640"
|
||||||
elif isinstance(bot, satori.Bot):
|
elif isinstance(bot, satori.Bot):
|
||||||
app_name = "Satori"
|
app_name = "Satori"
|
||||||
icon = (await bot.login_get()).user.avatar
|
icon = (await bot.login_get()).user.avatar
|
||||||
else:
|
else:
|
||||||
icon = None
|
icon = None
|
||||||
bot_data = {
|
bot_data = {
|
||||||
"name" : bot_name,
|
"name" : bot_name,
|
||||||
"icon" : icon,
|
"icon" : icon,
|
||||||
"id" : bot_id,
|
"id" : bot_id,
|
||||||
"protocol_name" : protocol_names.get(version_info.get("protocol_name"), "Online"),
|
"protocol_name" : protocol_names.get(version_info.get("protocol_name"), "Online"),
|
||||||
"groups" : groups,
|
"groups" : groups,
|
||||||
"friends" : friends,
|
"friends" : friends,
|
||||||
"message_sent" : satori_counter.msg_sent if isinstance(bot, satori.Bot) else statistics.get("message_sent", 0),
|
"message_sent" : satori_counter.msg_sent if isinstance(bot, satori.Bot) else statistics.get("message_sent", 0),
|
||||||
"message_received": satori_counter.msg_received if isinstance(bot, satori.Bot) else statistics.get("message_received", 0),
|
"message_received": satori_counter.msg_received if isinstance(bot, satori.Bot) else statistics.get("message_received", 0),
|
||||||
"app_name" : app_name
|
"app_name" : app_name
|
||||||
}
|
}
|
||||||
result["bots"].append(bot_data)
|
result["bots"].append(bot_data)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def get_hardware_data() -> dict:
|
async def get_hardware_data() -> dict:
|
||||||
mem = psutil.virtual_memory()
|
mem = psutil.virtual_memory()
|
||||||
all_processes = psutil.Process().children(recursive=True)
|
all_processes = psutil.Process().children(recursive=True)
|
||||||
all_processes.append(psutil.Process())
|
all_processes.append(psutil.Process())
|
||||||
|
|
||||||
mem_used_process = 0
|
mem_used_process = 0
|
||||||
process_mem = {}
|
process_mem = {}
|
||||||
for process in all_processes:
|
for process in all_processes:
|
||||||
try:
|
try:
|
||||||
ps_name = process.name().replace(".exe", "")
|
ps_name = process.name().replace(".exe", "")
|
||||||
if ps_name not in process_mem:
|
if ps_name not in process_mem:
|
||||||
process_mem[ps_name] = 0
|
process_mem[ps_name] = 0
|
||||||
process_mem[ps_name] += process.memory_info().rss
|
process_mem[ps_name] += process.memory_info().rss
|
||||||
mem_used_process += process.memory_info().rss
|
mem_used_process += process.memory_info().rss
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
swap = psutil.swap_memory()
|
swap = psutil.swap_memory()
|
||||||
cpu_brand_raw = cpuinfo.get_cpu_info().get("brand_raw", "Unknown")
|
cpu_brand_raw = cpuinfo.get_cpu_info().get("brand_raw", "Unknown")
|
||||||
if "AMD" in cpu_brand_raw:
|
if "AMD" in cpu_brand_raw:
|
||||||
brand = "AMD"
|
brand = "AMD"
|
||||||
elif "Intel" in cpu_brand_raw:
|
elif "Intel" in cpu_brand_raw:
|
||||||
brand = "Intel"
|
brand = "Intel"
|
||||||
else:
|
else:
|
||||||
brand = "Unknown"
|
brand = "Unknown"
|
||||||
result = {
|
result = {
|
||||||
"cpu" : {
|
"cpu" : {
|
||||||
"percent": psutil.cpu_percent(),
|
"percent": psutil.cpu_percent(),
|
||||||
"name" : f"{brand} {cpuinfo.get_cpu_info().get('arch', 'Unknown')}",
|
"name" : f"{brand} {cpuinfo.get_cpu_info().get('arch', 'Unknown')}",
|
||||||
"cores" : psutil.cpu_count(logical=False),
|
"cores" : psutil.cpu_count(logical=False),
|
||||||
"threads": psutil.cpu_count(logical=True),
|
"threads": psutil.cpu_count(logical=True),
|
||||||
"freq" : psutil.cpu_freq().current # MHz
|
"freq" : psutil.cpu_freq().current # MHz
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"percent" : mem.percent,
|
"percent" : mem.percent,
|
||||||
"total" : mem.total,
|
"total" : mem.total,
|
||||||
"used" : mem.used,
|
"used" : mem.used,
|
||||||
"free" : mem.free,
|
"free" : mem.free,
|
||||||
"usedProcess": mem_used_process,
|
"usedProcess": mem_used_process,
|
||||||
},
|
},
|
||||||
"swap" : {
|
"swap" : {
|
||||||
"percent": swap.percent,
|
"percent": swap.percent,
|
||||||
"total" : swap.total,
|
"total" : swap.total,
|
||||||
"used" : swap.used,
|
"used" : swap.used,
|
||||||
"free" : swap.free
|
"free" : swap.free
|
||||||
},
|
},
|
||||||
"disk" : [],
|
"disk" : [],
|
||||||
}
|
}
|
||||||
|
|
||||||
for disk in psutil.disk_partitions(all=True):
|
for disk in psutil.disk_partitions(all=True):
|
||||||
try:
|
try:
|
||||||
disk_usage = psutil.disk_usage(disk.mountpoint)
|
disk_usage = psutil.disk_usage(disk.mountpoint)
|
||||||
if disk_usage.total == 0:
|
if disk_usage.total == 0:
|
||||||
continue # 虚拟磁盘
|
continue # 虚拟磁盘
|
||||||
result["disk"].append({
|
result["disk"].append({
|
||||||
"name" : disk.mountpoint,
|
"name" : disk.mountpoint,
|
||||||
"percent": disk_usage.percent,
|
"percent": disk_usage.percent,
|
||||||
"total" : disk_usage.total,
|
"total" : disk_usage.total,
|
||||||
"used" : disk_usage.used,
|
"used" : disk_usage.used,
|
||||||
"free" : disk_usage.free
|
"free" : disk_usage.free
|
||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def get_liteyuki_data() -> dict:
|
async def get_liteyuki_data() -> dict:
|
||||||
temp_data: TempConfig = common_db.where_one(TempConfig(), default=TempConfig())
|
temp_data: TempConfig = common_db.where_one(TempConfig(), default=TempConfig())
|
||||||
result = {
|
result = {
|
||||||
"name" : list(get_config("nickname", [__NAME__]))[0],
|
"name" : list(get_config("nickname", [__NAME__]))[0],
|
||||||
"version" : f"{__VERSION__}{'-' + commit_hash[:7] if (commit_hash and len(commit_hash) > 8) else ''}",
|
"version" : f"{__VERSION__}{'-' + commit_hash[:7] if (commit_hash and len(commit_hash) > 8) else ''}",
|
||||||
"plugins" : len(nonebot.get_loaded_plugins()),
|
"plugins" : len(nonebot.get_loaded_plugins()),
|
||||||
"resources": len(get_loaded_resource_packs()),
|
"resources": len(get_loaded_resource_packs()),
|
||||||
"nonebot" : f"{nonebot.__version__}",
|
"nonebot" : f"{nonebot.__version__}",
|
||||||
"python" : f"{platform.python_implementation()} {platform.python_version()}",
|
"python" : f"{platform.python_implementation()} {platform.python_version()}",
|
||||||
"system" : f"{platform.system()} {platform.release()}",
|
"system" : f"{platform.system()} {platform.release()}",
|
||||||
"runtime" : time.time() - temp_data.data.get("start_time", time.time()), # 运行时间秒数
|
"runtime" : time.time() - temp_data.data.get("start_time", time.time()), # 运行时间秒数
|
||||||
"bots" : len(nonebot.get_bots())
|
"bots" : len(nonebot.get_bots())
|
||||||
}
|
}
|
||||||
return result
|
return result
|
@ -1,10 +1,10 @@
|
|||||||
class SatoriCounter:
|
class SatoriCounter:
|
||||||
msg_sent: int
|
msg_sent: int
|
||||||
msg_received: int
|
msg_received: int
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.msg_sent = 0
|
self.msg_sent = 0
|
||||||
self.msg_received = 0
|
self.msg_received = 0
|
||||||
|
|
||||||
|
|
||||||
satori_counter = SatoriCounter()
|
satori_counter = SatoriCounter()
|
@ -1,49 +1,49 @@
|
|||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
from liteyuki.utils.base.language import get_user_lang
|
from src.utils.base.language import get_user_lang
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from .api import *
|
from .api import *
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, UniMessage
|
from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, UniMessage
|
||||||
|
|
||||||
status_alc = on_alconna(
|
status_alc = on_alconna(
|
||||||
aliases={"状态"},
|
aliases={"状态"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"status",
|
"status",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"memory",
|
"memory",
|
||||||
alias={"mem", "m", "内存"},
|
alias={"mem", "m", "内存"},
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"process",
|
"process",
|
||||||
alias={"proc", "p", "进程"},
|
alias={"proc", "p", "进程"},
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@status_alc.handle()
|
@status_alc.handle()
|
||||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
async def _(event: T_MessageEvent, bot: T_Bot):
|
||||||
ulang = get_user_lang(event_utils.get_user_id(event))
|
ulang = get_user_lang(event_utils.get_user_id(event))
|
||||||
if ulang.lang_code in status_card_cache:
|
if ulang.lang_code in status_card_cache:
|
||||||
image = status_card_cache[ulang.lang_code]
|
image = status_card_cache[ulang.lang_code]
|
||||||
else:
|
else:
|
||||||
image = await generate_status_card(
|
image = await generate_status_card(
|
||||||
bot=await get_bots_data(),
|
bot=await get_bots_data(),
|
||||||
hardware=await get_hardware_data(),
|
hardware=await get_hardware_data(),
|
||||||
liteyuki=await get_liteyuki_data(),
|
liteyuki=await get_liteyuki_data(),
|
||||||
lang=ulang.lang_code,
|
lang=ulang.lang_code,
|
||||||
bot_id=bot.self_id,
|
bot_id=bot.self_id,
|
||||||
use_cache=True
|
use_cache=True
|
||||||
)
|
)
|
||||||
await status_alc.finish(UniMessage.image(raw=image))
|
await status_alc.finish(UniMessage.image(raw=image))
|
||||||
|
|
||||||
|
|
||||||
@status_alc.assign("memory")
|
@status_alc.assign("memory")
|
||||||
async def _():
|
async def _():
|
||||||
print("memory")
|
print("memory")
|
||||||
|
|
||||||
|
|
||||||
@status_alc.assign("process")
|
@status_alc.assign("process")
|
||||||
async def _():
|
async def _():
|
||||||
print("process")
|
print("process")
|
@ -1,17 +1,17 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from .api import *
|
from .api import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="联合黑名单(测试中...)",
|
name="联合黑名单(测试中...)",
|
||||||
description="",
|
description="",
|
||||||
usage="",
|
usage="",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki": True,
|
"liteyuki": True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -1,59 +1,59 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import httpx
|
import httpx
|
||||||
import nonebot
|
import nonebot
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
from nonebot_plugin_alconna.typings import Event
|
from nonebot_plugin_alconna.typings import Event
|
||||||
|
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
|
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
blacklist_data: dict[str, set[str]] = {}
|
blacklist_data: dict[str, set[str]] = {}
|
||||||
blacklist: set[str] = set()
|
blacklist: set[str] = set()
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job("interval", minutes=10, next_run_time=datetime.datetime.now())
|
@scheduler.scheduled_job("interval", minutes=10, next_run_time=datetime.datetime.now())
|
||||||
async def update_blacklist():
|
async def update_blacklist():
|
||||||
await request_for_blacklist()
|
await request_for_blacklist()
|
||||||
|
|
||||||
|
|
||||||
async def request_for_blacklist():
|
async def request_for_blacklist():
|
||||||
global blacklist
|
global blacklist
|
||||||
urls = [
|
urls = [
|
||||||
"https://cdn.liteyuki.icu/static/ubl/"
|
"https://cdn.liteyuki.icu/static/ubl/"
|
||||||
]
|
]
|
||||||
|
|
||||||
platforms = [
|
platforms = [
|
||||||
"qq"
|
"qq"
|
||||||
]
|
]
|
||||||
|
|
||||||
for plat in platforms:
|
for plat in platforms:
|
||||||
for url in urls:
|
for url in urls:
|
||||||
url += f"{plat}.txt"
|
url += f"{plat}.txt"
|
||||||
async with aiohttp.ClientSession() as client:
|
async with aiohttp.ClientSession() as client:
|
||||||
resp = await client.get(url)
|
resp = await client.get(url)
|
||||||
blacklist_data[plat] = set((await resp.text()).splitlines())
|
blacklist_data[plat] = set((await resp.text()).splitlines())
|
||||||
blacklist = get_uni_set()
|
blacklist = get_uni_set()
|
||||||
nonebot.logger.info("blacklists updated")
|
nonebot.logger.info("blacklists updated")
|
||||||
|
|
||||||
|
|
||||||
def get_uni_set() -> set:
|
def get_uni_set() -> set:
|
||||||
s = set()
|
s = set()
|
||||||
for new_set in blacklist_data.values():
|
for new_set in blacklist_data.values():
|
||||||
s.update(new_set)
|
s.update(new_set)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def pre_handle(event: Event):
|
async def pre_handle(event: Event):
|
||||||
try:
|
try:
|
||||||
user_id = str(event.get_user_id())
|
user_id = str(event.get_user_id())
|
||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
|
|
||||||
if user_id in get_uni_set():
|
if user_id in get_uni_set():
|
||||||
raise IgnoredException("UserId in blacklist")
|
raise IgnoredException("UserId in blacklist")
|
@ -1,16 +1,16 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from .profile_manager import *
|
from .profile_manager import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪用户管理",
|
name="轻雪用户管理",
|
||||||
description="用户管理插件",
|
description="用户管理插件",
|
||||||
usage="",
|
usage="",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,23 +1,23 @@
|
|||||||
representative_timezones_list = [
|
representative_timezones_list = [
|
||||||
"Etc/GMT+12", # 国际日期变更线西
|
"Etc/GMT+12", # 国际日期变更线西
|
||||||
"Pacific/Honolulu", # 夏威夷标准时间
|
"Pacific/Honolulu", # 夏威夷标准时间
|
||||||
"America/Anchorage", # 阿拉斯加标准时间
|
"America/Anchorage", # 阿拉斯加标准时间
|
||||||
"America/Los_Angeles", # 美国太平洋标准时间
|
"America/Los_Angeles", # 美国太平洋标准时间
|
||||||
"America/Denver", # 美国山地标准时间
|
"America/Denver", # 美国山地标准时间
|
||||||
"America/Chicago", # 美国中部标准时间
|
"America/Chicago", # 美国中部标准时间
|
||||||
"America/New_York", # 美国东部标准时间
|
"America/New_York", # 美国东部标准时间
|
||||||
"Europe/London", # 英国标准时间
|
"Europe/London", # 英国标准时间
|
||||||
"Europe/Paris", # 中欧标准时间
|
"Europe/Paris", # 中欧标准时间
|
||||||
"Europe/Moscow", # 莫斯科标准时间
|
"Europe/Moscow", # 莫斯科标准时间
|
||||||
"Asia/Dubai", # 阿联酋标准时间
|
"Asia/Dubai", # 阿联酋标准时间
|
||||||
"Asia/Kolkata", # 印度标准时间
|
"Asia/Kolkata", # 印度标准时间
|
||||||
"Asia/Shanghai", # 中国标准时间
|
"Asia/Shanghai", # 中国标准时间
|
||||||
"Asia/Hong_Kong", # 中国香港标准时间
|
"Asia/Hong_Kong", # 中国香港标准时间
|
||||||
"Asia/Chongqing", # 中国重庆标准时间
|
"Asia/Chongqing", # 中国重庆标准时间
|
||||||
"Asia/Macau", # 中国澳门标准时间
|
"Asia/Macau", # 中国澳门标准时间
|
||||||
"Asia/Taipei", # 中国台湾标准时间
|
"Asia/Taipei", # 中国台湾标准时间
|
||||||
"Asia/Tokyo", # 日本标准时间
|
"Asia/Tokyo", # 日本标准时间
|
||||||
"Australia/Sydney", # 澳大利亚东部标准时间
|
"Australia/Sydney", # 澳大利亚东部标准时间
|
||||||
"Pacific/Auckland" # 新西兰标准时间
|
"Pacific/Auckland" # 新西兰标准时间
|
||||||
]
|
]
|
||||||
representative_timezones_list.sort()
|
representative_timezones_list.sort()
|
@ -1,150 +1,150 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.utils.base.data import LiteModel, Database
|
from src.utils.base.data import LiteModel, Database
|
||||||
from liteyuki.utils.base.data_manager import User, user_db, group_db
|
from src.utils.base.data_manager import User, user_db, group_db
|
||||||
from liteyuki.utils.base.language import Language, change_user_lang, get_all_lang, get_user_lang
|
from src.utils.base.language import Language, change_user_lang, get_all_lang, get_user_lang
|
||||||
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.utils.message.message import MarkdownMessage as md
|
from src.utils.message.message import MarkdownMessage as md
|
||||||
from .const import representative_timezones_list
|
from .const import representative_timezones_list
|
||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import Alconna, Args, Arparma, Subcommand, on_alconna
|
from nonebot_plugin_alconna import Alconna, Args, Arparma, Subcommand, on_alconna
|
||||||
|
|
||||||
profile_alc = on_alconna(
|
profile_alc = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"profile",
|
"profile",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"set",
|
"set",
|
||||||
Args["key", str]["value", str, None],
|
Args["key", str]["value", str, None],
|
||||||
alias=["s", "设置"],
|
alias=["s", "设置"],
|
||||||
),
|
),
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"get",
|
"get",
|
||||||
Args["key", str],
|
Args["key", str],
|
||||||
alias=["g", "查询"],
|
alias=["g", "查询"],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
aliases={"用户信息"}
|
aliases={"用户信息"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# json储存
|
# json储存
|
||||||
class Profile(LiteModel):
|
class Profile(LiteModel):
|
||||||
lang: str = "zh-CN"
|
lang: str = "zh-CN"
|
||||||
nickname: str = ""
|
nickname: str = ""
|
||||||
timezone: str = "Asia/Shanghai"
|
timezone: str = "Asia/Shanghai"
|
||||||
location: str = ""
|
location: str = ""
|
||||||
|
|
||||||
|
|
||||||
@profile_alc.handle()
|
@profile_alc.handle()
|
||||||
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
||||||
user: User = user_db.where_one(User(), "user_id = ?", event_utils.get_user_id(event),
|
user: User = user_db.where_one(User(), "user_id = ?", event_utils.get_user_id(event),
|
||||||
default=User(user_id=str(event_utils.get_user_id(event))))
|
default=User(user_id=str(event_utils.get_user_id(event))))
|
||||||
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
ulang = get_user_lang(str(event_utils.get_user_id(event)))
|
||||||
if result.subcommands.get("set"):
|
if result.subcommands.get("set"):
|
||||||
if result.subcommands["set"].args.get("value"):
|
if result.subcommands["set"].args.get("value"):
|
||||||
# 对合法性进行校验后设置
|
# 对合法性进行校验后设置
|
||||||
r = set_profile(result.args["key"], result.args["value"], str(event_utils.get_user_id(event)))
|
r = set_profile(result.args["key"], result.args["value"], str(event_utils.get_user_id(event)))
|
||||||
if r:
|
if r:
|
||||||
user.profile[result.args["key"]] = result.args["value"]
|
user.profile[result.args["key"]] = result.args["value"]
|
||||||
user_db.save(user) # 数据库保存
|
user_db.save(user) # 数据库保存
|
||||||
await profile_alc.finish(
|
await profile_alc.finish(
|
||||||
ulang.get(
|
ulang.get(
|
||||||
"user.profile.set_success",
|
"user.profile.set_success",
|
||||||
ATTR=ulang.get(f"user.profile.{result.args['key']}"),
|
ATTR=ulang.get(f"user.profile.{result.args['key']}"),
|
||||||
VALUE=result.args["value"]
|
VALUE=result.args["value"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await profile_alc.finish(ulang.get("user.profile.set_failed", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
await profile_alc.finish(ulang.get("user.profile.set_failed", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
||||||
else:
|
else:
|
||||||
# 未输入值,尝试呼出菜单
|
# 未输入值,尝试呼出菜单
|
||||||
menu = get_profile_menu(result.args["key"], ulang)
|
menu = get_profile_menu(result.args["key"], ulang)
|
||||||
if menu:
|
if menu:
|
||||||
await md.send_md(menu, bot, event=event)
|
await md.send_md(menu, bot, event=event)
|
||||||
else:
|
else:
|
||||||
await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}")))
|
||||||
|
|
||||||
user.profile[result.args["key"]] = result.args["value"]
|
user.profile[result.args["key"]] = result.args["value"]
|
||||||
|
|
||||||
elif result.subcommands.get("get"):
|
elif result.subcommands.get("get"):
|
||||||
if result.args["key"] in user.profile:
|
if result.args["key"] in user.profile:
|
||||||
await profile_alc.finish(user.profile[result.args["key"]])
|
await profile_alc.finish(user.profile[result.args["key"]])
|
||||||
else:
|
else:
|
||||||
await profile_alc.finish("无此键值")
|
await profile_alc.finish("无此键值")
|
||||||
else:
|
else:
|
||||||
profile = Profile(**user.profile)
|
profile = Profile(**user.profile)
|
||||||
|
|
||||||
for k, v in user.profile.items():
|
for k, v in user.profile.items():
|
||||||
profile.__setattr__(k, v)
|
profile.__setattr__(k, v)
|
||||||
|
|
||||||
reply = f"# {ulang.get('user.profile.info')}\n***\n"
|
reply = f"# {ulang.get('user.profile.info')}\n***\n"
|
||||||
|
|
||||||
hidden_attr = ["id", "TABLE_NAME"]
|
hidden_attr = ["id", "TABLE_NAME"]
|
||||||
enter_attr = ["lang", "timezone"]
|
enter_attr = ["lang", "timezone"]
|
||||||
|
|
||||||
for key in sorted(profile.dict().keys()):
|
for key in sorted(profile.dict().keys()):
|
||||||
if key in hidden_attr:
|
if key in hidden_attr:
|
||||||
continue
|
continue
|
||||||
val = profile.dict()[key]
|
val = profile.dict()[key]
|
||||||
key_text = ulang.get(f"user.profile.{key}")
|
key_text = ulang.get(f"user.profile.{key}")
|
||||||
btn_set = md.btn_cmd(ulang.get("user.profile.edit"), f"profile set {key}",
|
btn_set = md.btn_cmd(ulang.get("user.profile.edit"), f"profile set {key}",
|
||||||
enter=True if key in enter_attr else False)
|
enter=True if key in enter_attr else False)
|
||||||
reply += (f"\n**{key_text}** **{val}**\n"
|
reply += (f"\n**{key_text}** **{val}**\n"
|
||||||
f"\n> {ulang.get(f'user.profile.{key}.desc')}"
|
f"\n> {ulang.get(f'user.profile.{key}.desc')}"
|
||||||
f"\n> {btn_set} \n\n***\n")
|
f"\n> {btn_set} \n\n***\n")
|
||||||
await md.send_md(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
|
||||||
|
|
||||||
def get_profile_menu(key: str, ulang: Language) -> Optional[str]:
|
def get_profile_menu(key: str, ulang: Language) -> Optional[str]:
|
||||||
"""获取属性的markdown菜单
|
"""获取属性的markdown菜单
|
||||||
Args:
|
Args:
|
||||||
ulang: 用户语言
|
ulang: 用户语言
|
||||||
key: 属性键
|
key: 属性键
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
setting_name = ulang.get(f"user.profile.{key}")
|
setting_name = ulang.get(f"user.profile.{key}")
|
||||||
|
|
||||||
no_menu = ["id", "nickname", "location"]
|
no_menu = ["id", "nickname", "location"]
|
||||||
|
|
||||||
if key in no_menu:
|
if key in no_menu:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
reply = f"**{setting_name} {ulang.get('user.profile.settings')}**\n***\n"
|
reply = f"**{setting_name} {ulang.get('user.profile.settings')}**\n***\n"
|
||||||
if key == "lang":
|
if key == "lang":
|
||||||
for lang_code, lang_name in get_all_lang().items():
|
for lang_code, lang_name in get_all_lang().items():
|
||||||
btn_set_lang = md.btn_cmd(f"{lang_name}({lang_code})", f"profile set {key} {lang_code}")
|
btn_set_lang = md.btn_cmd(f"{lang_name}({lang_code})", f"profile set {key} {lang_code}")
|
||||||
reply += f"\n{btn_set_lang}\n***\n"
|
reply += f"\n{btn_set_lang}\n***\n"
|
||||||
elif key == "timezone":
|
elif key == "timezone":
|
||||||
for tz in representative_timezones_list:
|
for tz in representative_timezones_list:
|
||||||
btn_set_tz = md.btn_cmd(tz, f"profile set {key} {tz}")
|
btn_set_tz = md.btn_cmd(tz, f"profile set {key} {tz}")
|
||||||
reply += f"{btn_set_tz}\n***\n"
|
reply += f"{btn_set_tz}\n***\n"
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
def set_profile(key: str, value: str, user_id: str) -> bool:
|
def set_profile(key: str, value: str, user_id: str) -> bool:
|
||||||
"""设置属性,使用if分支对每一个合法性进行检查
|
"""设置属性,使用if分支对每一个合法性进行检查
|
||||||
Args:
|
Args:
|
||||||
user_id:
|
user_id:
|
||||||
key:
|
key:
|
||||||
value:
|
value:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
是否成功设置,输入合法性不通过返回False
|
是否成功设置,输入合法性不通过返回False
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if key == "lang":
|
if key == "lang":
|
||||||
if value in get_all_lang():
|
if value in get_all_lang():
|
||||||
change_user_lang(user_id, value)
|
change_user_lang(user_id, value)
|
||||||
return True
|
return True
|
||||||
elif key == "timezone":
|
elif key == "timezone":
|
||||||
if value in pytz.all_timezones:
|
if value in pytz.all_timezones:
|
||||||
return True
|
return True
|
||||||
elif key == "nickname":
|
elif key == "nickname":
|
||||||
return True
|
return True
|
@ -1,27 +1,27 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from .qweather import *
|
from .qweather import *
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪天气",
|
name="轻雪天气",
|
||||||
description="基于和风天气api的天气插件",
|
description="基于和风天气api的天气插件",
|
||||||
usage="",
|
usage="",
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : True,
|
"toggleable" : True,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
from ...utils.base.data_manager import set_memory_data
|
from ...utils.base.data_manager import set_memory_data
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def _():
|
async def _():
|
||||||
# 检查是否为开发者模式
|
# 检查是否为开发者模式
|
||||||
is_dev = await check_key_dev(get_config("weather_key", ""))
|
is_dev = await check_key_dev(get_config("weather_key", ""))
|
||||||
set_memory_data("weather.is_dev", is_dev)
|
set_memory_data("weather.is_dev", is_dev)
|
@ -1,171 +1,171 @@
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from .qw_models import *
|
from .qw_models import *
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from ...utils.base.data_manager import get_memory_data
|
from ...utils.base.data_manager import get_memory_data
|
||||||
from ...utils.base.language import Language
|
from ...utils.base.language import Language
|
||||||
|
|
||||||
dev_url = "https://devapi.qweather.com/" # 开发HBa
|
dev_url = "https://devapi.qweather.com/" # 开发HBa
|
||||||
com_url = "https://api.qweather.com/" # 正式环境
|
com_url = "https://api.qweather.com/" # 正式环境
|
||||||
|
|
||||||
|
|
||||||
def get_qw_lang(lang: str) -> str:
|
def get_qw_lang(lang: str) -> str:
|
||||||
if lang in ["zh-HK", "zh-TW"]:
|
if lang in ["zh-HK", "zh-TW"]:
|
||||||
return "zh-hant"
|
return "zh-hant"
|
||||||
elif lang.startswith("zh"):
|
elif lang.startswith("zh"):
|
||||||
return "zh"
|
return "zh"
|
||||||
elif lang.startswith("en"):
|
elif lang.startswith("en"):
|
||||||
return "en"
|
return "en"
|
||||||
else:
|
else:
|
||||||
return lang
|
return lang
|
||||||
|
|
||||||
|
|
||||||
async def check_key_dev(key: str) -> bool:
|
async def check_key_dev(key: str) -> bool:
|
||||||
url = "https://api.qweather.com/v7/weather/now?"
|
url = "https://api.qweather.com/v7/weather/now?"
|
||||||
params = {
|
params = {
|
||||||
"location": "101010100",
|
"location": "101010100",
|
||||||
"key" : key,
|
"key" : key,
|
||||||
}
|
}
|
||||||
async with aiohttp.ClientSession() as client:
|
async with aiohttp.ClientSession() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return (await resp.json()).get("code") != "200" # 查询不到付费数据为开发版
|
return (await resp.json()).get("code") != "200" # 查询不到付费数据为开发版
|
||||||
|
|
||||||
|
|
||||||
def get_local_data(ulang_code: str) -> dict:
|
def get_local_data(ulang_code: str) -> dict:
|
||||||
"""
|
"""
|
||||||
获取本地化数据
|
获取本地化数据
|
||||||
Args:
|
Args:
|
||||||
ulang_code:
|
ulang_code:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ulang = Language(ulang_code)
|
ulang = Language(ulang_code)
|
||||||
return {
|
return {
|
||||||
"monday" : ulang.get("weather.monday"),
|
"monday" : ulang.get("weather.monday"),
|
||||||
"tuesday" : ulang.get("weather.tuesday"),
|
"tuesday" : ulang.get("weather.tuesday"),
|
||||||
"wednesday": ulang.get("weather.wednesday"),
|
"wednesday": ulang.get("weather.wednesday"),
|
||||||
"thursday" : ulang.get("weather.thursday"),
|
"thursday" : ulang.get("weather.thursday"),
|
||||||
"friday" : ulang.get("weather.friday"),
|
"friday" : ulang.get("weather.friday"),
|
||||||
"saturday" : ulang.get("weather.saturday"),
|
"saturday" : ulang.get("weather.saturday"),
|
||||||
"sunday" : ulang.get("weather.sunday"),
|
"sunday" : ulang.get("weather.sunday"),
|
||||||
"today" : ulang.get("weather.today"),
|
"today" : ulang.get("weather.today"),
|
||||||
"tomorrow" : ulang.get("weather.tomorrow"),
|
"tomorrow" : ulang.get("weather.tomorrow"),
|
||||||
"day" : ulang.get("weather.day"),
|
"day" : ulang.get("weather.day"),
|
||||||
"night" : ulang.get("weather.night"),
|
"night" : ulang.get("weather.night"),
|
||||||
"no_aqi" : ulang.get("weather.no_aqi"),
|
"no_aqi" : ulang.get("weather.no_aqi"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def city_lookup(
|
async def city_lookup(
|
||||||
location: str,
|
location: str,
|
||||||
key: str,
|
key: str,
|
||||||
adm: str = "",
|
adm: str = "",
|
||||||
number: int = 20,
|
number: int = 20,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
) -> CityLookup:
|
) -> CityLookup:
|
||||||
"""
|
"""
|
||||||
通过关键字搜索城市信息
|
通过关键字搜索城市信息
|
||||||
Args:
|
Args:
|
||||||
location:
|
location:
|
||||||
key:
|
key:
|
||||||
adm:
|
adm:
|
||||||
number:
|
number:
|
||||||
lang: 可传入标准i18n语言代码,如zh-CN、en-US等
|
lang: 可传入标准i18n语言代码,如zh-CN、en-US等
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
url = "https://geoapi.qweather.com/v2/city/lookup?"
|
url = "https://geoapi.qweather.com/v2/city/lookup?"
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": location,
|
||||||
"adm" : adm,
|
"adm" : adm,
|
||||||
"number" : number,
|
"number" : number,
|
||||||
"key" : key,
|
"key" : key,
|
||||||
"lang" : lang,
|
"lang" : lang,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return CityLookup.parse_obj(resp.json())
|
return CityLookup.parse_obj(resp.json())
|
||||||
|
|
||||||
|
|
||||||
async def get_weather_now(
|
async def get_weather_now(
|
||||||
key: str,
|
key: str,
|
||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = get_memory_data("is_dev", True),
|
dev: bool = get_memory_data("is_dev", True),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
url_path = "v7/weather/now?"
|
url_path = "v7/weather/now?"
|
||||||
url = dev_url + url_path if dev else com_url + url_path
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": location,
|
||||||
"key" : key,
|
"key" : key,
|
||||||
"lang" : lang,
|
"lang" : lang,
|
||||||
"unit" : unit,
|
"unit" : unit,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
async def get_weather_daily(
|
async def get_weather_daily(
|
||||||
key: str,
|
key: str,
|
||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = get_memory_data("is_dev", True),
|
dev: bool = get_memory_data("is_dev", True),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
url_path = "v7/weather/%dd?" % (7 if dev else 30)
|
url_path = "v7/weather/%dd?" % (7 if dev else 30)
|
||||||
url = dev_url + url_path if dev else com_url + url_path
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": location,
|
||||||
"key" : key,
|
"key" : key,
|
||||||
"lang" : lang,
|
"lang" : lang,
|
||||||
"unit" : unit,
|
"unit" : unit,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
async def get_weather_hourly(
|
async def get_weather_hourly(
|
||||||
key: str,
|
key: str,
|
||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = get_memory_data("is_dev", True),
|
dev: bool = get_memory_data("is_dev", True),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
url_path = "v7/weather/%dh?" % (24 if dev else 168)
|
url_path = "v7/weather/%dh?" % (24 if dev else 168)
|
||||||
url = dev_url + url_path if dev else com_url + url_path
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
params = {
|
params = {
|
||||||
"location": location,
|
"location": location,
|
||||||
"key" : key,
|
"key" : key,
|
||||||
"lang" : lang,
|
"lang" : lang,
|
||||||
"unit" : unit,
|
"unit" : unit,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
async def get_airquality(
|
async def get_airquality(
|
||||||
key: str,
|
key: str,
|
||||||
location: str,
|
location: str,
|
||||||
lang: str,
|
lang: str,
|
||||||
pollutant: bool = False,
|
pollutant: bool = False,
|
||||||
station: bool = False,
|
station: bool = False,
|
||||||
dev: bool = get_memory_data("is_dev", True),
|
dev: bool = get_memory_data("is_dev", True),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
url_path = f"airquality/v1/now/{location}?"
|
url_path = f"airquality/v1/now/{location}?"
|
||||||
url = dev_url + url_path if dev else com_url + url_path
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
params = {
|
params = {
|
||||||
"key" : key,
|
"key" : key,
|
||||||
"lang" : lang,
|
"lang" : lang,
|
||||||
"pollutant": pollutant,
|
"pollutant": pollutant,
|
||||||
"station" : station,
|
"station" : station,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return resp.json()
|
return resp.json()
|
@ -1,62 +1,62 @@
|
|||||||
from liteyuki.utils.base.data import LiteModel
|
from src.utils.base.data import LiteModel
|
||||||
|
|
||||||
|
|
||||||
class Location(LiteModel):
|
class Location(LiteModel):
|
||||||
name: str = ""
|
name: str = ""
|
||||||
id: str = ""
|
id: str = ""
|
||||||
lat: str = ""
|
lat: str = ""
|
||||||
lon: str = ""
|
lon: str = ""
|
||||||
adm2: str = ""
|
adm2: str = ""
|
||||||
adm1: str = ""
|
adm1: str = ""
|
||||||
country: str = ""
|
country: str = ""
|
||||||
tz: str = ""
|
tz: str = ""
|
||||||
utcOffset: str = ""
|
utcOffset: str = ""
|
||||||
isDst: str = ""
|
isDst: str = ""
|
||||||
type: str = ""
|
type: str = ""
|
||||||
rank: str = ""
|
rank: str = ""
|
||||||
fxLink: str = ""
|
fxLink: str = ""
|
||||||
sources: str = ""
|
sources: str = ""
|
||||||
license: str = ""
|
license: str = ""
|
||||||
|
|
||||||
|
|
||||||
class CityLookup(LiteModel):
|
class CityLookup(LiteModel):
|
||||||
code: str = ""
|
code: str = ""
|
||||||
location: list[Location] = [Location()]
|
location: list[Location] = [Location()]
|
||||||
|
|
||||||
|
|
||||||
class Now(LiteModel):
|
class Now(LiteModel):
|
||||||
obsTime: str = ""
|
obsTime: str = ""
|
||||||
temp: str = ""
|
temp: str = ""
|
||||||
feelsLike: str = ""
|
feelsLike: str = ""
|
||||||
icon: str = ""
|
icon: str = ""
|
||||||
text: str = ""
|
text: str = ""
|
||||||
wind360: str = ""
|
wind360: str = ""
|
||||||
windDir: str = ""
|
windDir: str = ""
|
||||||
windScale: str = ""
|
windScale: str = ""
|
||||||
windSpeed: str = ""
|
windSpeed: str = ""
|
||||||
humidity: str = ""
|
humidity: str = ""
|
||||||
precip: str = ""
|
precip: str = ""
|
||||||
pressure: str = ""
|
pressure: str = ""
|
||||||
vis: str = ""
|
vis: str = ""
|
||||||
cloud: str = ""
|
cloud: str = ""
|
||||||
dew: str = ""
|
dew: str = ""
|
||||||
sources: str = ""
|
sources: str = ""
|
||||||
license: str = ""
|
license: str = ""
|
||||||
|
|
||||||
|
|
||||||
class WeatherNow(LiteModel):
|
class WeatherNow(LiteModel):
|
||||||
code: str = ""
|
code: str = ""
|
||||||
updateTime: str = ""
|
updateTime: str = ""
|
||||||
fxLink: str = ""
|
fxLink: str = ""
|
||||||
now: Now = Now()
|
now: Now = Now()
|
||||||
|
|
||||||
|
|
||||||
class Daily(LiteModel):
|
class Daily(LiteModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WeatherDaily(LiteModel):
|
class WeatherDaily(LiteModel):
|
||||||
code: str = ""
|
code: str = ""
|
||||||
updateTime: str = ""
|
updateTime: str = ""
|
||||||
fxLink: str = ""
|
fxLink: str = ""
|
||||||
daily: list[str] = []
|
daily: list[str] = []
|
@ -1,101 +1,101 @@
|
|||||||
from nonebot import require, on_endswith
|
from nonebot import require, on_endswith
|
||||||
from nonebot.adapters import satori
|
from nonebot.adapters import satori
|
||||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
|
|
||||||
from liteyuki.utils.base.config import get_config
|
from src.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from src.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
from .qw_api import *
|
from .qw_api import *
|
||||||
from liteyuki.utils.base.data_manager import User, user_db
|
from src.utils.base.data_manager import User, user_db
|
||||||
from liteyuki.utils.base.language import Language, get_user_lang
|
from src.utils.base.language import Language, get_user_lang
|
||||||
from liteyuki.utils.base.resource import get_path
|
from src.utils.base.resource import get_path
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from src.utils.message.html_tool import template2image
|
||||||
from liteyuki.utils import event as event_utils
|
from src.utils import event as event_utils
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma, UniMessage
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma, UniMessage
|
||||||
|
|
||||||
wx_alc = on_alconna(
|
wx_alc = on_alconna(
|
||||||
aliases={"天气"},
|
aliases={"天气"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"weather",
|
"weather",
|
||||||
Args["keywords", MultiVar(str), []],
|
Args["keywords", MultiVar(str), []],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@wx_alc.handle()
|
@wx_alc.handle()
|
||||||
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
||||||
"""await alconna.send("weather", city)"""
|
"""await alconna.send("weather", city)"""
|
||||||
kws = result.main_args.get("keywords")
|
kws = result.main_args.get("keywords")
|
||||||
image = await get_weather_now_card(matcher, event, kws)
|
image = await get_weather_now_card(matcher, event, kws)
|
||||||
await wx_alc.finish(UniMessage.image(raw=image))
|
await wx_alc.finish(UniMessage.image(raw=image))
|
||||||
|
|
||||||
|
|
||||||
@on_endswith(("天气", "weather")).handle()
|
@on_endswith(("天气", "weather")).handle()
|
||||||
async def _(event: T_MessageEvent, matcher: Matcher):
|
async def _(event: T_MessageEvent, matcher: Matcher):
|
||||||
"""await alconna.send("weather", city)"""
|
"""await alconna.send("weather", city)"""
|
||||||
# kws = event.message.extract_plain_text()
|
# kws = event.message.extract_plain_text()
|
||||||
kws = event.get_plaintext()
|
kws = event.get_plaintext()
|
||||||
image = await get_weather_now_card(matcher, event, [kws.replace("天气", "").replace("weather", "")], False)
|
image = await get_weather_now_card(matcher, event, [kws.replace("天气", "").replace("weather", "")], False)
|
||||||
if isinstance(event, satori.event.Event):
|
if isinstance(event, satori.event.Event):
|
||||||
await matcher.finish(satori.MessageSegment.image(raw=image, mime="image/png"))
|
await matcher.finish(satori.MessageSegment.image(raw=image, mime="image/png"))
|
||||||
else:
|
else:
|
||||||
await matcher.finish(MessageSegment.image(image))
|
await matcher.finish(MessageSegment.image(image))
|
||||||
|
|
||||||
|
|
||||||
async def get_weather_now_card(matcher: Matcher, event: T_MessageEvent, keyword: list[str], tip: bool = True):
|
async def get_weather_now_card(matcher: Matcher, event: T_MessageEvent, keyword: list[str], tip: bool = True):
|
||||||
ulang = get_user_lang(event_utils.get_user_id(event))
|
ulang = get_user_lang(event_utils.get_user_id(event))
|
||||||
qw_lang = get_qw_lang(ulang.lang_code)
|
qw_lang = get_qw_lang(ulang.lang_code)
|
||||||
key = get_config("weather_key")
|
key = get_config("weather_key")
|
||||||
is_dev = get_memory_data("weather.is_dev", True)
|
is_dev = get_memory_data("weather.is_dev", True)
|
||||||
user: User = user_db.where_one(User(), "user_id = ?", event_utils.get_user_id(event), default=User())
|
user: User = user_db.where_one(User(), "user_id = ?", event_utils.get_user_id(event), default=User())
|
||||||
# params
|
# params
|
||||||
unit = user.profile.get("unit", "m")
|
unit = user.profile.get("unit", "m")
|
||||||
stored_location = user.profile.get("location", None)
|
stored_location = user.profile.get("location", None)
|
||||||
|
|
||||||
if not key:
|
if not key:
|
||||||
await matcher.finish(ulang.get("weather.no_key") if tip else None)
|
await matcher.finish(ulang.get("weather.no_key") if tip else None)
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
if len(keyword) >= 2:
|
if len(keyword) >= 2:
|
||||||
adm = keyword[0]
|
adm = keyword[0]
|
||||||
city = keyword[-1]
|
city = keyword[-1]
|
||||||
else:
|
else:
|
||||||
adm = ""
|
adm = ""
|
||||||
city = keyword[0]
|
city = keyword[0]
|
||||||
city_info = await city_lookup(city, key, adm=adm, lang=qw_lang)
|
city_info = await city_lookup(city, key, adm=adm, lang=qw_lang)
|
||||||
city_name = " ".join(keyword)
|
city_name = " ".join(keyword)
|
||||||
else:
|
else:
|
||||||
if not stored_location:
|
if not stored_location:
|
||||||
await matcher.finish(ulang.get("liteyuki.invalid_command", TEXT="location") if tip else None)
|
await matcher.finish(ulang.get("liteyuki.invalid_command", TEXT="location") if tip else None)
|
||||||
city_info = await city_lookup(stored_location, key, lang=qw_lang)
|
city_info = await city_lookup(stored_location, key, lang=qw_lang)
|
||||||
city_name = stored_location
|
city_name = stored_location
|
||||||
if city_info.code == "200":
|
if city_info.code == "200":
|
||||||
location_data = city_info.location[0]
|
location_data = city_info.location[0]
|
||||||
else:
|
else:
|
||||||
await matcher.finish(ulang.get("weather.city_not_found", CITY=city_name) if tip else None)
|
await matcher.finish(ulang.get("weather.city_not_found", CITY=city_name) if tip else None)
|
||||||
weather_now = await get_weather_now(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
weather_now = await get_weather_now(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
||||||
weather_daily = await get_weather_daily(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
weather_daily = await get_weather_daily(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
||||||
weather_hourly = await get_weather_hourly(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
weather_hourly = await get_weather_hourly(key, location_data.id, lang=qw_lang, unit=unit, dev=is_dev)
|
||||||
aqi = await get_airquality(key, location_data.id, lang=qw_lang, dev=is_dev)
|
aqi = await get_airquality(key, location_data.id, lang=qw_lang, dev=is_dev)
|
||||||
|
|
||||||
image = await template2image(
|
image = await template2image(
|
||||||
template=get_path("templates/weather_now.html", abs_path=True),
|
template=get_path("templates/weather_now.html", abs_path=True),
|
||||||
templates={
|
templates={
|
||||||
"data": {
|
"data": {
|
||||||
"params" : {
|
"params" : {
|
||||||
"unit": unit,
|
"unit": unit,
|
||||||
"lang": ulang.lang_code,
|
"lang": ulang.lang_code,
|
||||||
},
|
},
|
||||||
"weatherNow" : weather_now,
|
"weatherNow" : weather_now,
|
||||||
"weatherDaily" : weather_daily,
|
"weatherDaily" : weather_daily,
|
||||||
"weatherHourly": weather_hourly,
|
"weatherHourly": weather_hourly,
|
||||||
"aqi" : aqi,
|
"aqi" : aqi,
|
||||||
"location" : location_data.dump(),
|
"location" : location_data.dump(),
|
||||||
"localization" : get_local_data(ulang.lang_code)
|
"localization" : get_local_data(ulang.lang_code)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return image
|
return image
|
@ -1,21 +1,21 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from .main import *
|
from .main import *
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="网页监控面板",
|
name="网页监控面板",
|
||||||
description="网页监控面板,用于查看机器人的状态和信息",
|
description="网页监控面板,用于查看机器人的状态和信息",
|
||||||
usage=(
|
usage=(
|
||||||
"访问 127.0.0.1:port 查看机器人的状态信息\n"
|
"访问 127.0.0.1:port 查看机器人的状态信息\n"
|
||||||
"stat msg -g|--group [group_id] 查看群的统计信息,不带参数为全群\n"
|
"stat msg -g|--group [group_id] 查看群的统计信息,不带参数为全群\n"
|
||||||
"配置项:custom_domain,自定义域名,通常对外用,内网无需"
|
"配置项:custom_domain,自定义域名,通常对外用,内网无需"
|
||||||
),
|
),
|
||||||
type="application",
|
type="application",
|
||||||
homepage="https://github.com/snowykami/LiteyukiBot",
|
homepage="https://github.com/snowykami/LiteyukiBot",
|
||||||
extra={
|
extra={
|
||||||
"liteyuki" : True,
|
"liteyuki" : True,
|
||||||
"toggleable" : False,
|
"toggleable" : False,
|
||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
@ -1,4 +1,4 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from nonebot import get_app
|
from nonebot import get_app
|
||||||
|
|
||||||
app: FastAPI = get_app()
|
app: FastAPI = get_app()
|
@ -1,10 +1,10 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from nonebot import get_app
|
from nonebot import get_app
|
||||||
from .restful_api import *
|
from .restful_api import *
|
||||||
|
|
||||||
|
|
||||||
@app.get("/ping")
|
@app.get("/ping")
|
||||||
async def root():
|
async def root():
|
||||||
return {
|
return {
|
||||||
"message": "pong"
|
"message": "pong"
|
||||||
}
|
}
|
@ -1,24 +1,24 @@
|
|||||||
from fastapi import FastAPI, APIRouter
|
from fastapi import FastAPI, APIRouter
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
device_info_router = APIRouter(prefix="/api/device-info")
|
device_info_router = APIRouter(prefix="/api/device-info")
|
||||||
bot_info_router = APIRouter(prefix="/api/bot-info")
|
bot_info_router = APIRouter(prefix="/api/bot-info")
|
||||||
|
|
||||||
|
|
||||||
@device_info_router.get("/")
|
@device_info_router.get("/")
|
||||||
async def device_info():
|
async def device_info():
|
||||||
print("Hello Device Info")
|
print("Hello Device Info")
|
||||||
return {
|
return {
|
||||||
"message": "Hello Device Info"
|
"message": "Hello Device Info"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@bot_info_router.get("/")
|
@bot_info_router.get("/")
|
||||||
async def bot_info():
|
async def bot_info():
|
||||||
return {
|
return {
|
||||||
"message": "Hello Bot Info"
|
"message": "Hello Bot Info"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.include_router(device_info_router)
|
app.include_router(device_info_router)
|
||||||
app.include_router(bot_info_router)
|
app.include_router(bot_info_router)
|
@ -1,3 +1,3 @@
|
|||||||
name: Sign Status
|
name: Sign Status
|
||||||
description: for Lagrange
|
description: for Lagrange
|
||||||
version: 2024.4.26
|
version: 2024.4.26
|
@ -1,4 +1,4 @@
|
|||||||
.sign-chart {
|
.sign-chart {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
@ -1,75 +1,75 @@
|
|||||||
// 数据类型声明
|
// 数据类型声明
|
||||||
// import * as echarts from 'echarts';
|
// import * as echarts from 'echarts';
|
||||||
|
|
||||||
let data = JSON.parse(document.getElementById("data").innerText) // object
|
let data = JSON.parse(document.getElementById("data").innerText) // object
|
||||||
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
|
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
let signChartDiv = signChartDivTemplate.cloneNode(true)
|
let signChartDiv = signChartDivTemplate.cloneNode(true)
|
||||||
let chartID = item["name"]
|
let chartID = item["name"]
|
||||||
// 初始化ECharts实例
|
// 初始化ECharts实例
|
||||||
// 设置id
|
// 设置id
|
||||||
signChartDiv.querySelector(".sign-chart").id = chartID
|
signChartDiv.querySelector(".sign-chart").id = chartID
|
||||||
document.body.appendChild(signChartDiv)
|
document.body.appendChild(signChartDiv)
|
||||||
|
|
||||||
let signChart = echarts.init(document.getElementById(chartID))
|
let signChart = echarts.init(document.getElementById(chartID))
|
||||||
let timeCount = []
|
let timeCount = []
|
||||||
|
|
||||||
item["counts"].forEach((count, index) => {
|
item["counts"].forEach((count, index) => {
|
||||||
// 计算平均值,index - 1的count + index的count + index + 1的count /3
|
// 计算平均值,index - 1的count + index的count + index + 1的count /3
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
timeCount.push((item["counts"][index] - item["counts"][index - 1]) / (60*(item["times"][index] - item["times"][index - 1])))
|
timeCount.push((item["counts"][index] - item["counts"][index - 1]) / (60*(item["times"][index] - item["times"][index - 1])))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(timeCount)
|
console.log(timeCount)
|
||||||
|
|
||||||
signChart.setOption(
|
signChart.setOption(
|
||||||
{
|
{
|
||||||
animation: false,
|
animation: false,
|
||||||
title: {
|
title: {
|
||||||
text: item["name"],
|
text: item["name"],
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#000000' // 设置标题文本颜色为红色
|
color: '#000000' // 设置标题文本颜色为红色
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: item["times"].map(timestampToTime),
|
data: item["times"].map(timestampToTime),
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
min: Math.min(...item["counts"]),
|
min: Math.min(...item["counts"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
min: Math.min(...timeCount),
|
min: Math.min(...timeCount),
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: item["counts"],
|
data: item["counts"],
|
||||||
type: 'line',
|
type: 'line',
|
||||||
yAxisIndex: 0
|
yAxisIndex: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: timeCount,
|
data: timeCount,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
yAxisIndex: 1
|
yAxisIndex: 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function timestampToTime(timestamp) {
|
function timestampToTime(timestamp) {
|
||||||
let date = new Date(timestamp * 1000)
|
let date = new Date(timestamp * 1000)
|
||||||
let Y = date.getFullYear() + '-'
|
let Y = date.getFullYear() + '-'
|
||||||
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||||
let D = date.getDate() + ' '
|
let D = date.getDate() + ' '
|
||||||
let h = date.getHours() + ':'
|
let h = date.getHours() + ':'
|
||||||
let m = date.getMinutes() + ':'
|
let m = date.getMinutes() + ':'
|
||||||
let s = date.getSeconds()
|
let s = date.getSeconds()
|
||||||
return M + D + h + m + s
|
return M + D + h + m + s
|
||||||
}
|
}
|
@ -1,22 +1,22 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Liteyuki Status</title>
|
<title>Liteyuki Status</title>
|
||||||
<link rel="stylesheet" href="./css/card.css">
|
<link rel="stylesheet" href="./css/card.css">
|
||||||
<link rel="stylesheet" href="./css/fonts.css">
|
<link rel="stylesheet" href="./css/fonts.css">
|
||||||
<link rel="stylesheet" href="./css/sign_status.css">
|
<link rel="stylesheet" href="./css/sign_status.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<template id="sign-chart-template">
|
<template id="sign-chart-template">
|
||||||
<div class="info-box sign-chart">
|
<div class="info-box sign-chart">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
||||||
<script src="./js/sign_status.js"></script>
|
<script src="./js/sign_status.js"></script>
|
||||||
<script src="./js/card.js"></script>
|
<script src="./js/card.js"></script>
|
||||||
</body>
|
</body>
|
@ -1,3 +1,3 @@
|
|||||||
crt.station=Station(s)
|
crt.station=Station(s)
|
||||||
crt.hour=Hour(s)
|
crt.hour=Hour(s)
|
||||||
crt.minute=Min(s)
|
crt.minute=Min(s)
|
@ -1,3 +1,3 @@
|
|||||||
crt.station=站
|
crt.station=站
|
||||||
crt.hour=小时
|
crt.hour=小时
|
||||||
crt.minute=分钟
|
crt.minute=分钟
|
@ -1,3 +1,3 @@
|
|||||||
name: CRT相关资源包
|
name: CRT相关资源包
|
||||||
description: For Liteyuki CRT utils
|
description: For Liteyuki CRT utils
|
||||||
version: 2024.4.26
|
version: 2024.4.26
|
@ -1,161 +1,161 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>CRT 线路图</title>
|
<title>CRT 线路图</title>
|
||||||
<link rel="stylesheet" href="./css/card.css">
|
<link rel="stylesheet" href="./css/card.css">
|
||||||
<link rel="stylesheet" href="./css/fonts.css">
|
<link rel="stylesheet" href="./css/fonts.css">
|
||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-primary: #f00;
|
--color-primary: #f00;
|
||||||
--color-secondary: #fff;
|
--color-secondary: #fff;
|
||||||
--sub-text-color: #aaa;
|
--sub-text-color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.segment {
|
.segment {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #0d1117;
|
background-color: #0d1117;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-icon {
|
.line-icon {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical-bar {
|
.vertical-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
background-color: #f00;
|
background-color: #f00;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.station-dot {
|
.station-dot {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
aspect-ratio: 1/1;
|
aspect-ratio: 1/1;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 第一个点在bar顶端,第二个在底部*/
|
/* 第一个点在bar顶端,第二个在底部*/
|
||||||
.station-dot:first-child {
|
.station-dot:first-child {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.station-dot:last-child {
|
.station-dot:last-child {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transfer-info {
|
.transfer-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.station-name {
|
.station-name {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.end-station {
|
.end-station {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-info {
|
.line-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-name {
|
.line-name {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
background-color: #f00;
|
background-color: #f00;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-direction {
|
.line-direction {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--sub-text-color);
|
color: var(--sub-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.station-info {
|
.station-info {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
color: var(--sub-text-color);
|
color: var(--sub-text-color);
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.start-station {
|
.start-station {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.segment-index {
|
.segment-index {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--sub-text-color);
|
color: var(--sub-text-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||||
|
|
||||||
<!---->
|
<!---->
|
||||||
|
|
||||||
<template id="segment-template">
|
<template id="segment-template">
|
||||||
<div class="segment">
|
<div class="segment">
|
||||||
<div class="line-icon">
|
<div class="line-icon">
|
||||||
<!-- 竖条-->
|
<!-- 竖条-->
|
||||||
<div class="vertical-bar">
|
<div class="vertical-bar">
|
||||||
<div class="station-dot"></div>
|
<div class="station-dot"></div>
|
||||||
<div class="station-dot"></div>
|
<div class="station-dot"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="transfer-info">
|
<div class="transfer-info">
|
||||||
<div class="start-station">
|
<div class="start-station">
|
||||||
<div class="station-name start-station-name">
|
<div class="station-name start-station-name">
|
||||||
下北泽站
|
下北泽站
|
||||||
</div>
|
</div>
|
||||||
<div class="segment-index">
|
<div class="segment-index">
|
||||||
第1段
|
第1段
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="line-info">
|
<div class="line-info">
|
||||||
<div class="line-name">
|
<div class="line-name">
|
||||||
轨道交通环线外环
|
轨道交通环线外环
|
||||||
</div>
|
</div>
|
||||||
<div class="line-direction">
|
<div class="line-direction">
|
||||||
沙坪坝方向
|
沙坪坝方向
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="station-info">
|
<div class="station-info">
|
||||||
5站(14分钟)
|
5站(14分钟)
|
||||||
</div>
|
</div>
|
||||||
<div class="end-station">
|
<div class="end-station">
|
||||||
<div class="station-name end-station-name">
|
<div class="station-name end-station-name">
|
||||||
新桥站
|
新桥站
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template id="route-template">
|
<template id="route-template">
|
||||||
<div class="info-box route-info" id="route-info">
|
<div class="info-box route-info" id="route-info">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script src="./js/card.js"></script>
|
<script src="./js/card.js"></script>
|
||||||
<script src="./js/crt_route.js"></script>
|
<script src="./js/crt_route.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,35 +1,35 @@
|
|||||||
// Copyright (c) 2024 SnowyKami Liteyuki Studio All Rights Reserved.
|
// Copyright (c) 2024 SnowyKami Liteyuki Studio All Rights Reserved.
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{
|
* @type {{
|
||||||
* results: Array<{
|
* results: Array<{
|
||||||
* abstracts: string,
|
* abstracts: string,
|
||||||
* createdDt: string,
|
* createdDt: string,
|
||||||
* endStaName: string,
|
* endStaName: string,
|
||||||
* startStaName: string,
|
* startStaName: string,
|
||||||
* isValid: boolean,
|
* isValid: boolean,
|
||||||
* needTimeScope: number,
|
* needTimeScope: number,
|
||||||
* needTransferTimes: number,
|
* needTransferTimes: number,
|
||||||
* price: number,
|
* price: number,
|
||||||
* skipGenerateSequence: boolean,
|
* skipGenerateSequence: boolean,
|
||||||
* transferLines: string,
|
* transferLines: string,
|
||||||
* transferLinesColor: string,
|
* transferLinesColor: string,
|
||||||
* transferStaDerict: string,
|
* transferStaDerict: string,
|
||||||
* transferStaNames: string,
|
* transferStaNames: string,
|
||||||
* }>
|
* }>
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const data = JSON.parse(document.getElementById("data").innerText);
|
const data = JSON.parse(document.getElementById("data").innerText);
|
||||||
const results = data["result"];
|
const results = data["result"];
|
||||||
const route_template = document.importNode(document.getElementById("route-template").content, true)
|
const route_template = document.importNode(document.getElementById("route-template").content, true)
|
||||||
|
|
||||||
results.forEach(
|
results.forEach(
|
||||||
(item, index) => {
|
(item, index) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
|||||||
stat.message=统计消息
|
stat.message=统计消息
|
||||||
stat.rank=发言排名
|
stat.rank=发言排名
|
@ -1,3 +1,3 @@
|
|||||||
name: 轻雪统计信息附件
|
name: 轻雪统计信息附件
|
||||||
description: For Liteyuki statistic
|
description: For Liteyuki statistic
|
||||||
version: 2024.4.26
|
version: 2024.4.26
|
@ -1,4 +1,4 @@
|
|||||||
.sign-chart {
|
.sign-chart {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
@ -1,54 +1,54 @@
|
|||||||
// 数据类型声明
|
// 数据类型声明
|
||||||
// import * as echarts from 'echarts';
|
// import * as echarts from 'echarts';
|
||||||
|
|
||||||
let data = JSON.parse(document.getElementById("data").innerText) // object
|
let data = JSON.parse(document.getElementById("data").innerText) // object
|
||||||
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
|
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
let signChartDiv = signChartDivTemplate.cloneNode(true)
|
let signChartDiv = signChartDivTemplate.cloneNode(true)
|
||||||
let chartID = item["name"]
|
let chartID = item["name"]
|
||||||
// 初始化ECharts实例
|
// 初始化ECharts实例
|
||||||
// 设置id
|
// 设置id
|
||||||
signChartDiv.querySelector(".sign-chart").id = chartID
|
signChartDiv.querySelector(".sign-chart").id = chartID
|
||||||
document.body.appendChild(signChartDiv)
|
document.body.appendChild(signChartDiv)
|
||||||
|
|
||||||
let signChart = echarts.init(document.getElementById(chartID))
|
let signChart = echarts.init(document.getElementById(chartID))
|
||||||
|
|
||||||
signChart.setOption(
|
signChart.setOption(
|
||||||
{
|
{
|
||||||
animation: false,
|
animation: false,
|
||||||
title: {
|
title: {
|
||||||
text: item["name"],
|
text: item["name"],
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#000000' // 设置标题文本颜色为红色
|
color: '#000000' // 设置标题文本颜色为红色
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: item["times"].map(timestampToTime),
|
data: item["times"].map(timestampToTime),
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
min: Math.min(...item["counts"]),
|
min: Math.min(...item["counts"]),
|
||||||
},
|
},
|
||||||
|
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: item["counts"],
|
data: item["counts"],
|
||||||
type: 'line',
|
type: 'line',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function timestampToTime(timestamp) {
|
function timestampToTime(timestamp) {
|
||||||
let date = new Date(timestamp * 1000)
|
let date = new Date(timestamp * 1000)
|
||||||
let Y = date.getFullYear() + '-'
|
let Y = date.getFullYear() + '-'
|
||||||
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||||
let D = date.getDate() + ' '
|
let D = date.getDate() + ' '
|
||||||
let h = date.getHours() + ':'
|
let h = date.getHours() + ':'
|
||||||
let m = date.getMinutes() + ':'
|
let m = date.getMinutes() + ':'
|
||||||
let s = date.getSeconds()
|
let s = date.getSeconds()
|
||||||
return M + D + h + m + s
|
return M + D + h + m + s
|
||||||
}
|
}
|
@ -1,25 +1,25 @@
|
|||||||
let data = JSON.parse(document.getElementById("data").innerText) // object
|
let data = JSON.parse(document.getElementById("data").innerText) // object
|
||||||
|
|
||||||
const rowDiv = document.importNode(document.getElementById("row-template").content, true)
|
const rowDiv = document.importNode(document.getElementById("row-template").content, true)
|
||||||
|
|
||||||
function randomHideChar(str) {
|
function randomHideChar(str) {
|
||||||
// 随机隐藏6位以上字符串的中间连续四位字符,用*代替
|
// 随机隐藏6位以上字符串的中间连续四位字符,用*代替
|
||||||
if (str.length <= 6) {
|
if (str.length <= 6) {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
let start = Math.floor(str.length / 2) - 2
|
let start = Math.floor(str.length / 2) - 2
|
||||||
return str.slice(0, start) + "****" + str.slice(start + 4)
|
return str.slice(0, start) + "****" + str.slice(start + 4)
|
||||||
}
|
}
|
||||||
data["ranking"].forEach((item) => {
|
data["ranking"].forEach((item) => {
|
||||||
let row = rowDiv.cloneNode(true)
|
let row = rowDiv.cloneNode(true)
|
||||||
let rowID = item["name"]
|
let rowID = item["name"]
|
||||||
let rowIconSrc = item["icon"]
|
let rowIconSrc = item["icon"]
|
||||||
let rowCount = item["count"]
|
let rowCount = item["count"]
|
||||||
|
|
||||||
row.querySelector(".row-name").innerText = randomHideChar(rowID)
|
row.querySelector(".row-name").innerText = randomHideChar(rowID)
|
||||||
row.querySelector(".row-icon").src = rowIconSrc
|
row.querySelector(".row-icon").src = rowIconSrc
|
||||||
row.querySelector(".row-count").innerText = rowCount
|
row.querySelector(".row-count").innerText = rowCount
|
||||||
|
|
||||||
document.body.appendChild(row)
|
document.body.appendChild(row)
|
||||||
})
|
})
|
||||||
|
|
@ -1,22 +1,22 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Liteyuki Stats Message</title>
|
<title>Liteyuki Stats Message</title>
|
||||||
<link rel="stylesheet" href="./css/card.css">
|
<link rel="stylesheet" href="./css/card.css">
|
||||||
<link rel="stylesheet" href="./css/fonts.css">
|
<link rel="stylesheet" href="./css/fonts.css">
|
||||||
<link rel="stylesheet" href="./css/stat_msg.css">
|
<link rel="stylesheet" href="./css/stat_msg.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<template id="sign-chart-template">
|
<template id="sign-chart-template">
|
||||||
<div class="info-box sign-chart">
|
<div class="info-box sign-chart">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
||||||
<script src="./js/stat_msg.js"></script>
|
<script src="./js/stat_msg.js"></script>
|
||||||
<script src="./js/card.js"></script>
|
<script src="./js/card.js"></script>
|
||||||
</body>
|
</body>
|
@ -1,54 +1,54 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Liteyuki Stats Message</title>
|
<title>Liteyuki Stats Message</title>
|
||||||
<link rel="stylesheet" href="./css/card.css">
|
<link rel="stylesheet" href="./css/card.css">
|
||||||
<link rel="stylesheet" href="./css/fonts.css">
|
<link rel="stylesheet" href="./css/fonts.css">
|
||||||
<link rel="stylesheet" href="./css/stat_rank.css">
|
<link rel="stylesheet" href="./css/stat_rank.css">
|
||||||
<style>
|
<style>
|
||||||
.row {
|
.row {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: rgba(255, 255, 255, 0.9);
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-name {
|
.row-name {
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-icon {
|
.row-icon {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-count {
|
.row-count {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
/* 靠右*/
|
/* 靠右*/
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<template id="row-template">
|
<template id="row-template">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<img src="./img/arrow-up.svg" alt="up" class="row-icon">
|
<img src="./img/arrow-up.svg" alt="up" class="row-icon">
|
||||||
<div class="row-name"></div>
|
<div class="row-name"></div>
|
||||||
<div class="row-count"></div>
|
<div class="row-count"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
||||||
<script src="./js/stat_rank.js"></script>
|
<script src="./js/stat_rank.js"></script>
|
||||||
<script src="./js/card.js"></script>
|
<script src="./js/card.js"></script>
|
||||||
</body>
|
</body>
|
@ -1,12 +1,12 @@
|
|||||||
weather.monday=Mon
|
weather.monday=Mon
|
||||||
weather.tuesday=Tue
|
weather.tuesday=Tue
|
||||||
weather.wednesday=Wed
|
weather.wednesday=Wed
|
||||||
weather.thursday=Thu
|
weather.thursday=Thu
|
||||||
weather.friday=Fri
|
weather.friday=Fri
|
||||||
weather.saturday=Sat
|
weather.saturday=Sat
|
||||||
weather.sunday=Sun
|
weather.sunday=Sun
|
||||||
weather.day=Day
|
weather.day=Day
|
||||||
weather.night=Night
|
weather.night=Night
|
||||||
weather.today=Today
|
weather.today=Today
|
||||||
weather.tomorrow=Tomorrow
|
weather.tomorrow=Tomorrow
|
||||||
weather.no_aqi=No AQI data
|
weather.no_aqi=No AQI data
|
@ -1,12 +1,12 @@
|
|||||||
weather.monday=月
|
weather.monday=月
|
||||||
weather.tuesday=火
|
weather.tuesday=火
|
||||||
weather.wednesday=水
|
weather.wednesday=水
|
||||||
weather.thursday=木
|
weather.thursday=木
|
||||||
weather.friday=金
|
weather.friday=金
|
||||||
weather.saturday=土
|
weather.saturday=土
|
||||||
weather.sunday=日
|
weather.sunday=日
|
||||||
weather.day=昼
|
weather.day=昼
|
||||||
weather.night=夜
|
weather.night=夜
|
||||||
weather.today=今日
|
weather.today=今日
|
||||||
weather.tomorrow=明日
|
weather.tomorrow=明日
|
||||||
weather.no_aqi=空気質データなし
|
weather.no_aqi=空気質データなし
|
@ -1,12 +1,12 @@
|
|||||||
weather.monday=周一
|
weather.monday=周一
|
||||||
weather.tuesday=周二
|
weather.tuesday=周二
|
||||||
weather.wednesday=周三
|
weather.wednesday=周三
|
||||||
weather.thursday=周四
|
weather.thursday=周四
|
||||||
weather.friday=周五
|
weather.friday=周五
|
||||||
weather.saturday=周六
|
weather.saturday=周六
|
||||||
weather.sunday=周日
|
weather.sunday=周日
|
||||||
weather.day=白天
|
weather.day=白天
|
||||||
weather.night=夜晚
|
weather.night=夜晚
|
||||||
weather.today=今天
|
weather.today=今天
|
||||||
weather.tomorrow=明天
|
weather.tomorrow=明天
|
||||||
weather.no_aqi=暂无AQI数据
|
weather.no_aqi=暂无AQI数据
|
@ -1,3 +1,3 @@
|
|||||||
name: 轻雪天气资源包
|
name: 轻雪天气资源包
|
||||||
description: For Liteyuki Weather
|
description: For Liteyuki Weather
|
||||||
version: 2024.4.26
|
version: 2024.4.26
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user