Add sign status for Lagrange.Core

This commit is contained in:
远野千束 2024-05-11 00:16:38 +08:00
parent 75a4d1fdcb
commit 205b69e5cb
12 changed files with 133 additions and 23 deletions

4
.gitignore vendored
View File

@ -15,7 +15,6 @@ _config.yml
config.yml config.yml
config.example.yml config.example.yml
compile.bat compile.bat
.cache/
liteyuki/resources/templates/latest-debug.html liteyuki/resources/templates/latest-debug.html
# vuepress # vuepress
.github .github
@ -33,3 +32,6 @@ docs/.vuepress/.cache/
docs/.vuepress/.temp/ docs/.vuepress/.temp/
docs/.vuepress/dist/ docs/.vuepress/dist/
prompt.txt prompt.txt
# js
**/echarts.js

View File

@ -1,5 +1,6 @@
import datetime import datetime
import aiohttp
import httpx import httpx
import nonebot import nonebot
from nonebot import require from nonebot import require
@ -33,9 +34,9 @@ async def request_for_blacklist():
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 httpx.AsyncClient() as client: async with aiohttp.ClientSession() as client:
resp = await client.get(url) resp = await client.get(url)
blacklist_data[plat] = set(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")

View File

@ -1,3 +1,5 @@
import aiohttp
from .qw_models import * from .qw_models import *
import httpx import httpx
@ -27,7 +29,7 @@ async def check_key_dev(key: str) -> bool:
} }
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().get("code") != "200" # 查询不到付费数据为开发版 return (resp.json()).get("code") != "200" # 查询不到付费数据为开发版
def get_local_data(ulang_code: str) -> dict: def get_local_data(ulang_code: str) -> dict:

View File

@ -1,19 +1,19 @@
import datetime import datetime
import json
import time import time
from urllib.parse import urlparse
import httpx 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 liteyuki.utils.base.config import get_config
from liteyuki.utils.base.data import Database, LiteModel from liteyuki.utils.base.data import Database, LiteModel
from liteyuki.utils.base.resource import get_path
from liteyuki.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, Subcommand, on_alconna from nonebot_plugin_alconna import Alconna, AlconnaResult, CommandResult, Subcommand, UniMessage, on_alconna, Args
__author__ = "snowykami" __author__ = "snowykami"
__plugin_meta__ = PluginMetadata( __plugin_meta__ = PluginMetadata(
@ -46,7 +46,8 @@ sign_db.auto_migrate(SignCount())
sign_status = on_alconna(Alconna( sign_status = on_alconna(Alconna(
"sign", "sign",
Subcommand( Subcommand(
"chart" "chart",
Args["limit", int, 60]
), ),
Subcommand( Subcommand(
"count" "count"
@ -83,8 +84,10 @@ async def _():
@sign_status.assign("chart") @sign_status.assign("chart")
async def _(): async def _(arp: CommandResult = AlconnaResult()):
pass limit = arp.result.main_args.get("limit", 60)
img = await generate_chart(limit)
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())
@ -104,10 +107,10 @@ async def get_now_sign() -> dict[str, tuple[float, int]]:
""" """
data = {} data = {}
now = time.time() now = time.time()
async with httpx.AsyncClient() as client: async with aiohttp.ClientSession() as client:
for name, url in SIGN_COUNT_URLS.items(): for name, url in SIGN_COUNT_URLS.items():
resp = await client.get(url) async with client.get(url) as resp:
count = resp.json()["count"] count = (await resp.json())["count"]
data[name] = (now, count) data[name] = (now, count)
return data return data
@ -123,5 +126,25 @@ async def save_sign_count(timestamp: float, count: int, sid: str):
sign_db.save(SignCount(time=timestamp, count=count, sid=sid)) sign_db.save(SignCount(time=timestamp, count=count, sid=sid))
async def generate_chart(duration: int = 60): async def generate_chart(limit):
pass data = []
for name, url in SIGN_COUNT_URLS.items():
count_rows = sign_db.all(SignCount(), "sid = ? LIMIT ?", url, limit)
data.append(
{
"name" : name,
# "data": [[row.time, row.count] for row in count_rows]
"times" : [row.time for row in count_rows],
"counts": [row.count for row in count_rows]
}
)
img = await template2image(
template=get_path("templates/sign_status.html", debug=True),
templates={
"data": data
},
debug=True
)
return img

View File

@ -0,0 +1,3 @@
name: Sign Status
description: for Lagrange
version: 2024.4.26

View File

@ -0,0 +1,4 @@
.sign-chart {
height: 400px;
background-color: rgba(255, 255, 255, 0.7);
}

View File

@ -0,0 +1,41 @@
// 数据类型声明
// import * as echarts from 'echarts';
let data = JSON.parse(document.getElementById("data").innerText) // object
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
data.forEach((item) => {
let signChartDiv = signChartDivTemplate.cloneNode(true)
let chartID = item["name"]
// 初始化ECharts实例
// 设置id
signChartDiv.querySelector(".sign-chart").id = chartID
document.body.appendChild(signChartDiv)
let signChart = echarts.init(document.getElementById(chartID))
signChart.setOption(
{
animation: false,
title: {
text: item["name"],
textStyle: {
color: '#000000' // 设置标题文本颜色为红色
}
},
xAxis: {
type: 'category',
data: item["times"],
},
yAxis: {
type: 'value',
min: Math.min(...item["counts"]),
},
series: [
{
data: item["counts"],
type: 'line'
}
]
}
)
})

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Liteyuki Status</title>
<link rel="stylesheet" href="./css/card.css">
<link rel="stylesheet" href="./css/fonts.css">
<link rel="stylesheet" href="./css/sign_status.css">
</head>
<body>
<template id="sign-chart-template">
<div class="info-box sign-chart">
</div>
</template>
<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="./js/sign_status.js"></script>
<script src="./js/card.js"></script>
</body>

View File

@ -62,6 +62,8 @@ let maxHourlyItem = 8
let percentWidth = 1 / (maxHourlyItem * 1.5) * 100 let percentWidth = 1 / (maxHourlyItem * 1.5) * 100
let hourlyStep = 2 // n小时一个数据 let hourlyStep = 2 // n小时一个数据
let hourlyCount = 0 let hourlyCount = 0
let hourlyItemDivTemplate = document.importNode(document.getElementById("hourly-item-template").content, true)
weatherHourly['hourly'].forEach( weatherHourly['hourly'].forEach(
(item, index) => { (item, index) => {
if (index % hourlyStep !== 0) { if (index % hourlyStep !== 0) {
@ -71,7 +73,7 @@ weatherHourly['hourly'].forEach(
return return
} }
let hourlyItemDiv = document.importNode(document.getElementById("hourly-item-template").content, true) let hourlyItemDiv = document.importNode(hourlyItemDivTemplate, true)
hourlyItemDiv.className = "hourly-item" hourlyItemDiv.className = "hourly-item"
hourlyItemDiv.querySelector('.hourly-icon').setAttribute("src", `./img/qw_icon/${item["icon"]}.png`) hourlyItemDiv.querySelector('.hourly-icon').setAttribute("src", `./img/qw_icon/${item["icon"]}.png`)
hourlyItemDiv.querySelector('.hourly-time').innerText = get_time_hour(item["fxTime"]) hourlyItemDiv.querySelector('.hourly-time').innerText = get_time_hour(item["fxTime"])
@ -92,6 +94,8 @@ let days = [localData['today'], localData['tomorrow']]
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
days.push(localData[daysStandard[(todayDay + 2 + i) % 7]]) days.push(localData[daysStandard[(todayDay + 2 + i) % 7]])
} }
let dailyItemDivTemplate = document.importNode(document.getElementById("daily-item-template").content, true)
weatherDaily['daily'].forEach( weatherDaily['daily'].forEach(
(item, index) => { (item, index) => {
if (index >= maxDailyItem) { if (index >= maxDailyItem) {
@ -101,7 +105,7 @@ weatherDaily['daily'].forEach(
if (index >= 2) { if (index >= 2) {
today += `(${item["fxDate"].split("-")[1]}.${item["fxDate"].split("-")[2]})` today += `(${item["fxDate"].split("-")[1]}.${item["fxDate"].split("-")[2]})`
} }
let dailyItemDiv = document.importNode(document.getElementById("daily-item-template").content, true) let dailyItemDiv = document.importNode(dailyItemDivTemplate, true)
dailyItemDiv.querySelector('.icon-day').setAttribute("src", `./img/qw_icon/${item["iconDay"]}.png`) dailyItemDiv.querySelector('.icon-day').setAttribute("src", `./img/qw_icon/${item["iconDay"]}.png`)
dailyItemDiv.querySelector('.icon-night').setAttribute("src", `./img/qw_icon/${item["iconNight"]}.png`) dailyItemDiv.querySelector('.icon-night').setAttribute("src", `./img/qw_icon/${item["iconNight"]}.png`)

View File

@ -72,5 +72,5 @@
<div class="info-box" id="days-info"></div> <div class="info-box" id="days-info"></div>
<script src="./js/card.js"></script> <script src="./js/card.js"></script>
<script src="js/weather_now.js"></script> <script src="./js/weather_now.js"></script>
</body> </body>

View File

@ -1,3 +1,11 @@
:root {
--main-text-color: #fff;
--sub-text-color: #ccc;
--tip-text-color: #999;
--device-info-width: 240px;
}
.data-storage { .data-storage {
display: none; display: none;
} }

View File

@ -42,7 +42,7 @@
<div id="motto-text"></div> <div id="motto-text"></div>
<div id="motto-from"></div> <div id="motto-from"></div>
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.3.0/echarts.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
<script src="./js/motto.js"></script> <script src="./js/motto.js"></script>
<script src="./js/card.js"></script> <script src="./js/card.js"></script>
<script src="./js/status.js"></script> <script src="./js/status.js"></script>