diff --git a/.gitignore b/.gitignore index 90bbdef7..d9da460e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ _config.yml config.yml config.example.yml compile.bat -.cache/ liteyuki/resources/templates/latest-debug.html # vuepress .github @@ -32,4 +31,7 @@ main.cmd docs/.vuepress/.cache/ docs/.vuepress/.temp/ docs/.vuepress/dist/ -prompt.txt \ No newline at end of file +prompt.txt + +# js +**/echarts.js \ No newline at end of file diff --git a/liteyuki/plugins/liteyuki_uniblacklist/api.py b/liteyuki/plugins/liteyuki_uniblacklist/api.py index be1383e5..f5fb49e3 100644 --- a/liteyuki/plugins/liteyuki_uniblacklist/api.py +++ b/liteyuki/plugins/liteyuki_uniblacklist/api.py @@ -1,5 +1,6 @@ import datetime +import aiohttp import httpx import nonebot from nonebot import require @@ -33,9 +34,9 @@ async def request_for_blacklist(): for plat in platforms: for url in urls: url += f"{plat}.txt" - async with httpx.AsyncClient() as client: + async with aiohttp.ClientSession() as client: resp = await client.get(url) - blacklist_data[plat] = set(resp.text.splitlines()) + blacklist_data[plat] = set((await resp.text()).splitlines()) blacklist = get_uni_set() nonebot.logger.info("blacklists updated") diff --git a/liteyuki/plugins/liteyuki_weather/qw_api.py b/liteyuki/plugins/liteyuki_weather/qw_api.py index ed95813b..9f9afee5 100644 --- a/liteyuki/plugins/liteyuki_weather/qw_api.py +++ b/liteyuki/plugins/liteyuki_weather/qw_api.py @@ -1,3 +1,5 @@ +import aiohttp + from .qw_models import * import httpx @@ -27,7 +29,7 @@ async def check_key_dev(key: str) -> bool: } async with httpx.AsyncClient() as client: 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: diff --git a/liteyuki/plugins/sign_status.py b/liteyuki/plugins/sign_status.py index 751ab748..80824473 100644 --- a/liteyuki/plugins/sign_status.py +++ b/liteyuki/plugins/sign_status.py @@ -1,19 +1,19 @@ import datetime -import json import time -from urllib.parse import urlparse -import httpx +import aiohttp from nonebot import require from nonebot.plugin import PluginMetadata from liteyuki.utils.base.config import get_config 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_apscheduler") 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" __plugin_meta__ = PluginMetadata( @@ -46,7 +46,8 @@ sign_db.auto_migrate(SignCount()) sign_status = on_alconna(Alconna( "sign", Subcommand( - "chart" + "chart", + Args["limit", int, 60] ), Subcommand( "count" @@ -83,8 +84,10 @@ async def _(): @sign_status.assign("chart") -async def _(): - pass +async def _(arp: CommandResult = AlconnaResult()): + 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()) @@ -104,11 +107,11 @@ async def get_now_sign() -> dict[str, tuple[float, int]]: """ data = {} now = time.time() - async with httpx.AsyncClient() as client: + async with aiohttp.ClientSession() as client: for name, url in SIGN_COUNT_URLS.items(): - resp = await client.get(url) - count = resp.json()["count"] - data[name] = (now, count) + async with client.get(url) as resp: + count = (await resp.json())["count"] + data[name] = (now, count) 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)) -async def generate_chart(duration: int = 60): - pass +async def generate_chart(limit): + 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 diff --git a/liteyuki/resources/lagrange_sign/metadata.yml b/liteyuki/resources/lagrange_sign/metadata.yml new file mode 100644 index 00000000..a7ea1ff4 --- /dev/null +++ b/liteyuki/resources/lagrange_sign/metadata.yml @@ -0,0 +1,3 @@ +name: Sign Status +description: for Lagrange +version: 2024.4.26 \ No newline at end of file diff --git a/liteyuki/resources/lagrange_sign/templates/css/sign_status.css b/liteyuki/resources/lagrange_sign/templates/css/sign_status.css new file mode 100644 index 00000000..7d43a4bb --- /dev/null +++ b/liteyuki/resources/lagrange_sign/templates/css/sign_status.css @@ -0,0 +1,4 @@ +.sign-chart { + height: 400px; + background-color: rgba(255, 255, 255, 0.7); +} \ No newline at end of file diff --git a/liteyuki/resources/lagrange_sign/templates/js/sign_status.js b/liteyuki/resources/lagrange_sign/templates/js/sign_status.js new file mode 100644 index 00000000..ae1bbd17 --- /dev/null +++ b/liteyuki/resources/lagrange_sign/templates/js/sign_status.js @@ -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' + } + ] + } + ) +}) \ No newline at end of file diff --git a/liteyuki/resources/lagrange_sign/templates/sign_status.html b/liteyuki/resources/lagrange_sign/templates/sign_status.html new file mode 100644 index 00000000..eebac37d --- /dev/null +++ b/liteyuki/resources/lagrange_sign/templates/sign_status.html @@ -0,0 +1,22 @@ + + + + + Liteyuki Status + + + + + + + + +
{{ data | tojson }}
+ + + + + \ No newline at end of file diff --git a/liteyuki/resources/liteyuki_weather/templates/js/weather_now.js b/liteyuki/resources/liteyuki_weather/templates/js/weather_now.js index 708563fd..97d120bf 100644 --- a/liteyuki/resources/liteyuki_weather/templates/js/weather_now.js +++ b/liteyuki/resources/liteyuki_weather/templates/js/weather_now.js @@ -62,6 +62,8 @@ let maxHourlyItem = 8 let percentWidth = 1 / (maxHourlyItem * 1.5) * 100 let hourlyStep = 2 // n小时一个数据 let hourlyCount = 0 + +let hourlyItemDivTemplate = document.importNode(document.getElementById("hourly-item-template").content, true) weatherHourly['hourly'].forEach( (item, index) => { if (index % hourlyStep !== 0) { @@ -71,7 +73,7 @@ weatherHourly['hourly'].forEach( return } - let hourlyItemDiv = document.importNode(document.getElementById("hourly-item-template").content, true) + let hourlyItemDiv = document.importNode(hourlyItemDivTemplate, true) hourlyItemDiv.className = "hourly-item" hourlyItemDiv.querySelector('.hourly-icon').setAttribute("src", `./img/qw_icon/${item["icon"]}.png`) hourlyItemDiv.querySelector('.hourly-time').innerText = get_time_hour(item["fxTime"]) @@ -90,8 +92,10 @@ let daysStandard = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'fri let todayDay = new Date().getDay() let days = [localData['today'], localData['tomorrow']] 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( (item, index) => { if (index >= maxDailyItem) { @@ -101,7 +105,7 @@ weatherDaily['daily'].forEach( if (index >= 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-night').setAttribute("src", `./img/qw_icon/${item["iconNight"]}.png`) diff --git a/liteyuki/resources/liteyuki_weather/templates/weather_now.html b/liteyuki/resources/liteyuki_weather/templates/weather_now.html index 5176d20f..3b178fe7 100644 --- a/liteyuki/resources/liteyuki_weather/templates/weather_now.html +++ b/liteyuki/resources/liteyuki_weather/templates/weather_now.html @@ -72,5 +72,5 @@
- + \ No newline at end of file diff --git a/liteyuki/resources/vanilla_resource/templates/css/card.css b/liteyuki/resources/vanilla_resource/templates/css/card.css index 79dd1c6d..7a63d3fc 100644 --- a/liteyuki/resources/vanilla_resource/templates/css/card.css +++ b/liteyuki/resources/vanilla_resource/templates/css/card.css @@ -1,3 +1,11 @@ +:root { + --main-text-color: #fff; + --sub-text-color: #ccc; + --tip-text-color: #999; + --device-info-width: 240px; +} + + .data-storage { display: none; } diff --git a/liteyuki/resources/vanilla_resource/templates/status.html b/liteyuki/resources/vanilla_resource/templates/status.html index 12f36670..bc1903ce 100644 --- a/liteyuki/resources/vanilla_resource/templates/status.html +++ b/liteyuki/resources/vanilla_resource/templates/status.html @@ -42,7 +42,7 @@
- +