forked from bot/app
✨ 独立status插件...
This commit is contained in:
parent
ece71ca1e7
commit
53bc6df30f
@ -48,7 +48,7 @@ for lang in get_all_lang():
|
||||
}
|
||||
|
||||
|
||||
@scheduler.scheduled_job("cron", second="*/20")
|
||||
@scheduler.scheduled_job("cron", minute="*/20")
|
||||
async def _():
|
||||
nonebot.logger.info("数据已刷新")
|
||||
for lang_code in get_all_lang():
|
||||
|
@ -0,0 +1,195 @@
|
||||
import platform
|
||||
import time
|
||||
|
||||
import nonebot
|
||||
import psutil
|
||||
from cpuinfo import cpuinfo
|
||||
from liteyuki.utils import __NAME__, __VERSION__
|
||||
from liteyuki.utils.base.data_manager import TempConfig, common_db
|
||||
from liteyuki.utils.base.language import Language
|
||||
from liteyuki.utils.base.resource import get_path
|
||||
from liteyuki.utils.message.html_tool import template2image
|
||||
|
||||
protocol_names = {
|
||||
0: "iPad",
|
||||
1: "Android Phone",
|
||||
2: "Android Watch",
|
||||
3: "Mac",
|
||||
5: "iPad",
|
||||
6: "Android Pad",
|
||||
}
|
||||
|
||||
"""
|
||||
Universal Interface
|
||||
data
|
||||
- bot
|
||||
- name: str
|
||||
icon: str
|
||||
id: int
|
||||
protocol_name: str
|
||||
groups: int
|
||||
friends: int
|
||||
message_sent: int
|
||||
message_received: int
|
||||
app_name: str
|
||||
- hardware
|
||||
- cpu
|
||||
- percent: float
|
||||
- name: str
|
||||
- mem
|
||||
- percent: float
|
||||
- total: int
|
||||
- used: int
|
||||
- free: int
|
||||
- swap
|
||||
- percent: float
|
||||
- total: int
|
||||
- used: int
|
||||
- free: int
|
||||
- disk: list
|
||||
- name: str
|
||||
- percent: float
|
||||
- total: int
|
||||
|
||||
"""
|
||||
|
||||
|
||||
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0") -> bytes:
|
||||
return await template2image(
|
||||
get_path("templates/status.html", abs_path=True),
|
||||
{
|
||||
"data": {
|
||||
"bot" : bot,
|
||||
"hardware" : hardware,
|
||||
"liteyuki" : liteyuki,
|
||||
"localization": get_local_data(lang)
|
||||
}
|
||||
},
|
||||
debug=True
|
||||
)
|
||||
|
||||
|
||||
def get_local_data(lang_code) -> dict:
|
||||
lang = Language(lang_code)
|
||||
return {
|
||||
"friends" : lang.get("status.friends"),
|
||||
"groups" : lang.get("status.groups"),
|
||||
"plugins" : lang.get("status.plugins"),
|
||||
"message_sent" : lang.get("status.message_sent"),
|
||||
"message_received": lang.get("status.message_received"),
|
||||
"cpu" : lang.get("status.cpu"),
|
||||
"memory" : lang.get("status.memory"),
|
||||
"swap" : lang.get("status.swap"),
|
||||
"disk" : lang.get("status.disk"),
|
||||
|
||||
"usage" : lang.get("status.usage"),
|
||||
"total" : lang.get("status.total"),
|
||||
"used" : lang.get("status.used"),
|
||||
"free" : lang.get("status.free"),
|
||||
}
|
||||
|
||||
|
||||
async def get_bots_data(self_id: str = "0") -> dict:
|
||||
"""获取当前所有机器人数据
|
||||
Returns:
|
||||
"""
|
||||
result = {
|
||||
"self_id": self_id,
|
||||
"bots" : [],
|
||||
}
|
||||
for bot_id, bot in nonebot.get_bots().items():
|
||||
groups = 0
|
||||
friends = 0
|
||||
status = {}
|
||||
bot_name = bot_id
|
||||
version_info = {}
|
||||
try:
|
||||
# API fetch
|
||||
bot_name = (await bot.get_login_info())["nickname"]
|
||||
groups = len(await bot.get_group_list())
|
||||
friends = len(await bot.get_friend_list())
|
||||
status = await bot.get_status()
|
||||
version_info = await bot.get_version_info()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
statistics = status.get("stat", {})
|
||||
app_name = version_info.get("app_name", "UnknownImplementation")
|
||||
if app_name in ["Lagrange.OneBot", "LLOneBot", "Shamrock"]:
|
||||
icon = f"https://q.qlogo.cn/g?b=qq&nk={bot_id}&s=640"
|
||||
else:
|
||||
icon = None
|
||||
bot_data = {
|
||||
"name" : bot_name,
|
||||
"icon" : icon,
|
||||
"id" : bot_id,
|
||||
"protocol_name" : protocol_names.get(version_info.get("protocol_name"), "Online"),
|
||||
"groups" : groups,
|
||||
"friends" : friends,
|
||||
"message_sent" : statistics.get("message_sent"),
|
||||
"message_received": statistics.get("message_received"),
|
||||
"app_name" : app_name
|
||||
}
|
||||
result["bots"].append(bot_data)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def get_hardware_data() -> dict:
|
||||
mem = psutil.virtual_memory()
|
||||
swap = psutil.swap_memory()
|
||||
cpu_brand_raw = cpuinfo.get_cpu_info().get("brand_raw", "Unknown")
|
||||
if "AMD" in cpu_brand_raw:
|
||||
brand = "AMD"
|
||||
elif "Intel" in cpu_brand_raw:
|
||||
brand = "Intel"
|
||||
else:
|
||||
brand = "Unknown"
|
||||
result = {
|
||||
"cpu" : {
|
||||
"percent": psutil.cpu_percent(),
|
||||
"name" : f"{brand} {cpuinfo.get_cpu_info().get('arch', 'Unknown')}"
|
||||
},
|
||||
"mem" : {
|
||||
"percent": mem.percent,
|
||||
"total" : mem.total,
|
||||
"used" : mem.used,
|
||||
"free" : mem.free
|
||||
},
|
||||
"swap": {
|
||||
"percent": swap.percent,
|
||||
"total" : swap.total,
|
||||
"used" : swap.used,
|
||||
"free" : swap.free
|
||||
},
|
||||
"disk": [],
|
||||
}
|
||||
|
||||
for disk in psutil.disk_partitions(all=True):
|
||||
try:
|
||||
disk_usage = psutil.disk_usage(disk.mountpoint)
|
||||
result["disk"].append({
|
||||
"name" : disk.mountpoint,
|
||||
"percent": disk_usage.percent,
|
||||
"total" : disk_usage.total,
|
||||
"used" : disk_usage.used,
|
||||
"free" : disk_usage.free
|
||||
})
|
||||
except:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def get_liteyuki_data() -> dict:
|
||||
temp_data: TempConfig = common_db.first(TempConfig(), default=TempConfig())
|
||||
result = {
|
||||
"name" : __NAME__,
|
||||
"version": __VERSION__,
|
||||
"plugins": len(nonebot.get_loaded_plugins()),
|
||||
"nonebot": f"{nonebot.__version__}",
|
||||
"python" : f"{platform.python_implementation()} {platform.python_version()}",
|
||||
"system" : f"{platform.system()} {platform.release()}",
|
||||
"runtime": time.time() - temp_data.data.get("start_time", time.time()), # 运行时间秒数
|
||||
}
|
||||
return result
|
@ -1,12 +1,18 @@
|
||||
from nonebot import require
|
||||
|
||||
from liteyuki.utils.base.resource import get_path
|
||||
from liteyuki.utils.message.html_tool import template2image
|
||||
from liteyuki.utils.base.language import get_user_lang
|
||||
from .api import *
|
||||
from ...utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma
|
||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, UniMessage
|
||||
|
||||
status_alc = on_alconna(
|
||||
aliases={"status"},
|
||||
aliases={"#状态"},
|
||||
command=Alconna(
|
||||
"status",
|
||||
"#status",
|
||||
Subcommand(
|
||||
"memory",
|
||||
alias={"mem", "m", "内存"},
|
||||
@ -19,6 +25,19 @@ status_alc = on_alconna(
|
||||
)
|
||||
|
||||
|
||||
@status_alc.handle()
|
||||
async def _(event: T_MessageEvent, bot: T_Bot):
|
||||
ulang = get_user_lang(event.user_id)
|
||||
image = await generate_status_card(
|
||||
bot=await get_bots_data(),
|
||||
hardware=await get_hardware_data(),
|
||||
liteyuki=await get_liteyuki_data(),
|
||||
lang=ulang.lang_code,
|
||||
bot_id=bot.self_id
|
||||
)
|
||||
await status_alc.finish(UniMessage.image(raw=image))
|
||||
|
||||
|
||||
@status_alc.assign("memory")
|
||||
async def _():
|
||||
print("memory")
|
||||
|
@ -133,3 +133,17 @@ rpm.move_top=置顶
|
||||
weather.city_not_found=未找到城市 {CITY}
|
||||
weather.weather_not_found=未找到城市 {CITY} 的天气信息
|
||||
weather.no_key=未设置天气api key,请在配置文件添加weather_key
|
||||
|
||||
status.friends=好友
|
||||
status.groups=群
|
||||
status.plugins=插件
|
||||
status.message_sent=发送消息
|
||||
status.message_received=接收消息
|
||||
status.cpu=处理器
|
||||
status.memory=内存
|
||||
status.swap=交换空间
|
||||
status.disk=磁盘
|
||||
status.usage=使用率
|
||||
status.total=总计
|
||||
status.used=已用
|
||||
status.free=空闲
|
@ -1,9 +1,84 @@
|
||||
const data = JSON.parse(document.getElementById('data').innerText);
|
||||
const bot_data = data['bot']; // 机器人数据
|
||||
const hardware_data = data['hardware']; // 硬件数据
|
||||
const liteyuki_data = data['liteyuki']; // LiteYuki数据
|
||||
const local_data = data['localization']; // 本地化语言数据
|
||||
|
||||
function createPieChartOption(title, data){
|
||||
console.log(data)
|
||||
|
||||
/**
|
||||
* 创建饼图
|
||||
* @param title
|
||||
* @param {Array<{name: string, value: number}>} data 数据
|
||||
*/
|
||||
function createPieChartOption(title, data) {
|
||||
// data为各项占比列表
|
||||
return {
|
||||
animation: false,
|
||||
title: {
|
||||
text: title,
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 30
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item',
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
color: data.length === 3 ? ['#00a6ff', '#a2d8f4', "#ffffff44"] : ['#a2d8f4', '#ffffff44'],
|
||||
series: [
|
||||
{
|
||||
name: 'info',
|
||||
type: 'pie',
|
||||
radius: ['80%', '100%'],
|
||||
center: ['50%', '50%'],
|
||||
itemStyle: {
|
||||
normal: {
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: '50',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function createBarChartOption(title, percent){
|
||||
/**
|
||||
* 创建柱状图
|
||||
* @param title
|
||||
* @param percent 数据
|
||||
*/
|
||||
function createBarChartOption(title, percent) {
|
||||
// percent为百分比,最大值为100
|
||||
}
|
||||
|
||||
// 主函数
|
||||
function main() {
|
||||
bot_data['bots'].forEach(
|
||||
(bot, index) => {
|
||||
let botInfoDiv = document.importNode(document.getElementById('bot-template').content, true)
|
||||
document.body.insertBefore(botInfoDiv, document.getElementById('hardware-info'))
|
||||
botInfoDiv.className = 'info-box bot-info'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
@ -7,9 +7,24 @@
|
||||
<link rel="stylesheet" href="./css/status.css">
|
||||
</head>
|
||||
<body>
|
||||
<template id="bot-template">
|
||||
<div class="info-box bot-info">
|
||||
<div id="bot-icon">
|
||||
<img id="bot-icon-img" src="" alt="">
|
||||
</div>
|
||||
<div id="bot-detail">
|
||||
<div id="bot-name">
|
||||
Liteyuki
|
||||
</div>
|
||||
<div id="bot-tags">
|
||||
<!-- tag span-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||
<div class="info-box" id="system-info"></div>
|
||||
<div class="info-box" id="hardware-info"></div>
|
||||
<div class="info-box" id="disk-info"></div>
|
||||
<div class="info-box" id="motto-info"></div>
|
||||
|
||||
|
@ -2,6 +2,7 @@ import json
|
||||
import os.path
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
|
||||
import nonebot
|
||||
|
||||
@ -12,7 +13,7 @@ import requests
|
||||
|
||||
from liteyuki.utils.base.config import load_from_yaml, config
|
||||
from liteyuki.utils.base.log import init_log
|
||||
from liteyuki.utils.base.data_manager import auto_migrate
|
||||
from liteyuki.utils.base.data_manager import TempConfig, auto_migrate, common_db
|
||||
|
||||
major, minor, patch = map(int, __VERSION__.split("."))
|
||||
__VERSION_I__ = major * 10000 + minor * 100 + patch
|
||||
@ -54,6 +55,10 @@ def init():
|
||||
if sys.version_info < (3, 10):
|
||||
nonebot.logger.error("This project requires Python3.10+ to run, please upgrade your Python Environment.")
|
||||
exit(1)
|
||||
temp_data: TempConfig = common_db.first(TempConfig(), default=TempConfig())
|
||||
temp_data.data["start_time"] = time.time()
|
||||
common_db.upsert(temp_data)
|
||||
|
||||
auto_migrate()
|
||||
# 在加载完成语言后再初始化日志
|
||||
nonebot.logger.info("Liteyuki is initializing...")
|
||||
|
@ -6,10 +6,10 @@ from .data import Database, LiteModel, Database
|
||||
|
||||
DATA_PATH = "data/liteyuki"
|
||||
|
||||
user_db = Database(os.path.join(DATA_PATH, "users.ldb"))
|
||||
group_db = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
||||
plugin_db = Database(os.path.join(DATA_PATH, "plugins.ldb"))
|
||||
common_db = Database(os.path.join(DATA_PATH, "common.ldb"))
|
||||
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
|
||||
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
||||
plugin_db: Database = Database(os.path.join(DATA_PATH, "plugins.ldb"))
|
||||
common_db: Database = Database(os.path.join(DATA_PATH, "common.ldb"))
|
||||
|
||||
|
||||
class User(LiteModel):
|
||||
|
@ -47,7 +47,7 @@ def load_resource_from_dir(path: str):
|
||||
_loaded_resource_packs.insert(0, ResourceMetadata(**metadata))
|
||||
|
||||
|
||||
def get_path(path: str, abs_path: bool = False, default: Any = None, debug: bool=False) -> str | Any:
|
||||
def get_path(path: str, abs_path: bool = True, default: Any = None, debug: bool=False) -> str | Any:
|
||||
"""
|
||||
获取资源包中的文件
|
||||
Args:
|
||||
|
@ -62,7 +62,6 @@ async def template2image(
|
||||
|
||||
if debug:
|
||||
# 重载资源
|
||||
load_resources()
|
||||
|
||||
raw_html = await template_to_html(
|
||||
template_name=template_name,
|
||||
|
Loading…
Reference in New Issue
Block a user