轻雪天气更新

This commit is contained in:
snowy 2024-04-27 02:20:44 +08:00
parent 9cfdd375ca
commit 86c7b70e63
107 changed files with 616 additions and 273 deletions

View File

@ -5,23 +5,25 @@ order: 2
category: 使用手册
---
## 功能插件命令
### **轻雪天气`liteyuki_weather`**
配置项
```yaml
weather_key: "" # 和风天气的天气key
weather_dev: false # 是否为开发版
weather_key: "" # 和风天气的天气key会自动判断key版本
```
命令
```shell
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
bind-city <keywords...> # 绑定查询城市,个人全局生效
```
命令别名
```shell
weather 天气, bind-city 绑定城市
```

View File

@ -83,7 +83,7 @@ async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
}
)
common_db.upsert(temp_data)
common_db.save(temp_data)
Reloader.reload(0)
@ -120,7 +120,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher
except:
pass
stored_config.config[key] = value
common_db.upsert(stored_config)
common_db.save(stored_config)
await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
elif result.subcommands.get("get"):
key = result.subcommands.get("get").args.get("key")
@ -144,7 +144,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher
key = result.subcommands.get("remove").args.get("key")
if key in stored_config.config:
stored_config.config.pop(key)
common_db.upsert(stored_config)
common_db.save(stored_config)
await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}")
else:
await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}")
@ -164,7 +164,7 @@ async def _(event: T_MessageEvent, matcher: Matcher):
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig())
stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False)
markdown_image = stored_config.config["markdown_image"]
common_db.upsert(stored_config)
common_db.save(stored_config)
await matcher.finish(ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off"))
@ -258,7 +258,7 @@ async def on_startup():
if temp_data.data.get("reload", False):
delta_time = time.time() - temp_data.data.get("reload_time", 0)
temp_data.data["delta_time"] = delta_time
common_db.upsert(temp_data) # 更新数据
common_db.save(temp_data) # 更新数据
@driver.on_shutdown
@ -278,7 +278,7 @@ async def _(bot: T_Bot):
reload_session_type = temp_data.data.get("reload_session_type", "private")
reload_session_id = temp_data.data.get("reload_session_id", 0)
delta_time = temp_data.data.get("delta_time", 0)
common_db.upsert(temp_data) # 更新数据
common_db.save(temp_data) # 更新数据
await bot.call_api(
"send_msg",
message_type=reload_session_type,

View File

@ -66,7 +66,7 @@ async def _(result: Arparma):
target=Node(bot_id=target[0], session_type=target[1], session_id=target[2]),
inde=len(pushes_db.all(Push(), default=[]))
)
pushes_db.upsert(push1)
pushes_db.save(push1)
if result.subcommands["add"].args.get("bidirectional"):
push2 = Push(
@ -74,7 +74,7 @@ async def _(result: Arparma):
target=Node(bot_id=source[0], session_type=source[1], session_id=source[2]),
inde=len(pushes_db.all(Push(), default=[]))
)
pushes_db.upsert(push2)
pushes_db.save(push2)
await add_push.finish("添加成功")
else:
await add_push.finish("参数缺失")

View File

@ -150,10 +150,10 @@ def set_plugin_session_enable(event: T_MessageEvent, plugin_name: str, enable: b
if event.message_type == "group":
__group_data[str(event.group_id)] = session
print(session)
group_db.upsert(session)
group_db.save(session)
else:
__user_data[str(event.user_id)] = session
user_db.upsert(session)
user_db.save(session)
def get_plugin_global_enable(plugin_name: str) -> bool:
@ -193,7 +193,7 @@ def set_plugin_global_enable(plugin_name: str, enable: bool):
default=GlobalPlugin(module_name=plugin_name, enabled=True))
plugin.enabled = enable
plugin_db.upsert(plugin)
plugin_db.save(plugin)
__global_enable[plugin_name] = enable
@ -242,4 +242,4 @@ def set_group_enable(group_id: str, enable: bool):
group.enable = enable
__group_data[group_id] = group
group_db.upsert(group)
group_db.save(group)

View File

@ -225,7 +225,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, npm: Matcher):
found_in_db_plugin = plugin_db.first(InstalledPlugin(), "module_name = ?", plugin_name) # 查询数据库中是否已经安装
if r_load:
if found_in_db_plugin is None:
plugin_db.upsert(installed_plugin)
plugin_db.save(installed_plugin)
info = md.escape(ulang.get("npm.install_success", NAME=store_plugin.name)) # markdown转义
await md.send_md(
f"{info}\n\n"

View File

@ -55,18 +55,22 @@ data
"""
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)
}
}
)
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0", use_cache=False) -> bytes:
if not use_cache:
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
)
else:
pass
def get_local_data(lang_code) -> dict:
@ -97,7 +101,7 @@ def get_local_data(lang_code) -> dict:
"cores" : lang.get("status.cores"),
"process" : lang.get("status.process"),
"resources" : lang.get("status.resources"),
"description" : lang.get("status.description"),
}

View File

@ -49,7 +49,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
r = set_profile(result.args["key"], result.args["value"], str(event.user_id))
if r:
user.profile[result.args["key"]] = result.args["value"]
user_db.upsert(user) # 数据库保存
user_db.save(user) # 数据库保存
await profile_alc.finish(
ulang.get(
"user.profile.set_success",

View File

@ -1,7 +1,7 @@
from nonebot.plugin import PluginMetadata
from nonebot import get_driver
from .qweather import *
__plugin_meta__ = PluginMetadata(
name="轻雪天气",
description="基于和风天气api的天气插件",
@ -9,9 +9,19 @@ __plugin_meta__ = PluginMetadata(
type="application",
homepage="https://github.com/snowykami/LiteyukiBot",
extra={
"liteyuki": True,
"toggleable" : True,
"default_enable" : True,
"liteyuki" : True,
"toggleable" : True,
"default_enable": True,
}
)
from ...utils.base.data_manager import set_memory_data
driver = get_driver()
@driver.on_startup
async def _():
# 检查是否为开发者模式
is_dev = await check_key_dev(get_config("weather_key", ""))
set_memory_data("weather.is_dev", is_dev)

View File

@ -1,6 +1,8 @@
from .qw_models import *
import httpx
from ...utils.base.data_manager import get_memory_data
from ...utils.base.language import Language
dev_url = "https://devapi.qweather.com/" # 开发HBa
com_url = "https://api.qweather.com/" # 正式环境
@ -17,6 +19,42 @@ def get_qw_lang(lang: str) -> str:
return lang
async def check_key_dev(key: str) -> bool:
url = "https://api.qweather.com/v7/weather/now?"
params = {
"location": "101010100",
"key" : key,
}
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params)
return resp.json().get("code") != "200" # 查询不到付费数据为开发版
def get_local_data(ulang_code: str) -> dict:
"""
获取本地化数据
Args:
ulang_code:
Returns:
"""
ulang = Language(ulang_code)
return {
"monday" : ulang.get("weather.monday"),
"tuesday" : ulang.get("weather.tuesday"),
"wednesday": ulang.get("weather.wednesday"),
"thursday" : ulang.get("weather.thursday"),
"friday" : ulang.get("weather.friday"),
"saturday" : ulang.get("weather.saturday"),
"sunday" : ulang.get("weather.sunday"),
"today" : ulang.get("weather.today"),
"tomorrow" : ulang.get("weather.tomorrow"),
"day" : ulang.get("weather.day"),
"night" : ulang.get("weather.night"),
}
async def city_lookup(
location: str,
key: str,
@ -54,7 +92,7 @@ async def get_weather_now(
location: str,
lang: str = "zh",
unit: str = "m",
dev: bool = True,
dev: bool = get_memory_data("is_dev", True),
) -> dict:
url_path = "v7/weather/now?"
url = dev_url + url_path if dev else com_url + url_path
@ -74,7 +112,7 @@ async def get_weather_daily(
location: str,
lang: str = "zh",
unit: str = "m",
dev: bool = True,
dev: bool = get_memory_data("is_dev", True),
) -> dict:
url_path = "v7/weather/%dd?" % (7 if dev else 30)
url = dev_url + url_path if dev else com_url + url_path
@ -94,7 +132,7 @@ async def get_weather_hourly(
location: str,
lang: str = "zh",
unit: str = "m",
dev: bool = True,
dev: bool = get_memory_data("is_dev", True),
) -> dict:
url_path = "v7/weather/%dh?" % (24 if dev else 168)
url = dev_url + url_path if dev else com_url + url_path
@ -115,7 +153,7 @@ async def get_airquality(
lang: str,
pollutant: bool = False,
station: bool = False,
dev: bool = True
dev: bool = get_memory_data("is_dev", True),
) -> dict:
url_path = f"airquality/v1/now/{location}?"
url = dev_url + url_path if dev else com_url + url_path

View File

@ -1,4 +1,4 @@
from nonebot import require
from nonebot import require, on_endswith
from nonebot.adapters.onebot.v11 import MessageSegment
from nonebot.internal.matcher import Matcher
@ -7,7 +7,7 @@ from liteyuki.utils.base.ly_typing import T_MessageEvent
from .qw_api import *
from liteyuki.utils.base.data_manager import User, user_db
from liteyuki.utils.base.language import get_user_lang
from liteyuki.utils.base.language import Language, get_user_lang
from liteyuki.utils.base.resource import get_path
from liteyuki.utils.message.html_tool import template2image
@ -24,40 +24,50 @@ from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
).handle()
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
"""await alconna.send("weather", city)"""
ulang = get_user_lang(str(event.user_id))
kws = result.main_args.get("keywords")
image = await get_weather_now_card(matcher, event, kws)
await matcher.finish(MessageSegment.image(image))
@on_endswith(("天气", "weather")).handle()
async def _(event: T_MessageEvent, matcher: Matcher):
"""await alconna.send("weather", city)"""
kws = event.message.extract_plain_text()
image = await get_weather_now_card(matcher, event, [kws.replace("天气", "").replace("weather", "")], False)
await matcher.finish(MessageSegment.image(image))
async def get_weather_now_card(matcher: Matcher, event: T_MessageEvent, keyword: list[str], tip: bool = True):
ulang = get_user_lang(event.user_id)
qw_lang = get_qw_lang(ulang.lang_code)
key = get_config("weather_key")
is_dev = get_config("weather_dev")
user: User = user_db.first(User(), "user_id = ?", str(event.user_id), default=User())
is_dev = get_memory_data("weather.is_dev", True)
user: User = user_db.first(User(), "user_id = ?", event.user_id, default=User())
# params
unit = user.profile.get("unit", "m")
stored_location = user.profile.get("location", None)
if not key:
await matcher.finish(ulang.get("weather.no_key"))
await matcher.finish(ulang.get("weather.no_key") if tip else None)
kws = result.main_args.get("keywords")
if kws:
if len(kws) >= 2:
adm = kws[0]
city = kws[-1]
if keyword:
if len(keyword) >= 2:
adm = keyword[0]
city = keyword[-1]
else:
adm = ""
city = kws[0]
city = keyword[0]
city_info = await city_lookup(city, key, adm=adm, lang=qw_lang)
city_name = " ".join(kws)
city_name = " ".join(keyword)
else:
if not stored_location:
await matcher.finish(ulang.get("liteyuki.invalid_command", TEXT="location"))
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_name = stored_location
if city_info.code == "200":
location_data = city_info.location[0]
else:
await matcher.finish(ulang.get("weather.city_not_found", CITY=city_name))
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_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)
@ -76,9 +86,10 @@ async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
"weatherHourly": weather_hourly,
"aqi" : aqi,
"location" : location_data.dump(),
"localization" : get_local_data(ulang.lang_code)
}
},
debug=True,
wait=1
)
await matcher.finish(MessageSegment.image(image))
return image

View File

@ -0,0 +1,11 @@
weather.monday=Mon
weather.tuesday=Tue
weather.wednesday=Wed
weather.thursday=Thu
weather.friday=Fri
weather.saturday=Sat
weather.sunday=Sun
weather.day=Day
weather.night=Night
weather.today=Today
weather.tomorrow=Tomorrow

View File

@ -0,0 +1,11 @@
weather.monday=月
weather.tuesday=火
weather.wednesday=水
weather.thursday=木
weather.friday=金
weather.saturday=土
weather.sunday=日
weather.day=昼
weather.night=夜
weather.today=今日
weather.tomorrow=明日

View File

@ -0,0 +1,11 @@
weather.monday=周一
weather.tuesday=周二
weather.wednesday=周三
weather.thursday=周四
weather.friday=周五
weather.saturday=周六
weather.sunday=周日
weather.day=白天
weather.night=夜晚
weather.today=今天
weather.tomorrow=明天

View File

@ -0,0 +1,3 @@
name: 轻雪天气资源包
description: For Liteyuki Weather
version: 2024.4.26

View File

@ -0,0 +1,184 @@
:root {
--main-text-color: #fff;
--sub-text-color: #ccc;
--tip-text-color: #999;
--device-info-width: 240px;
--sub-border-radius: 60px;
}
#weather-info {
color: white;
/*justify-content: center;*/
/*align-items: center;*/
/*align-content: center;*/
}
.icon {
/* icon 类img阴影*/
filter: drop-shadow(1px 1px 10px #00000044);
}
#main-info {
display: flex;
justify-content: center;
align-items: center;
}
#main-left {
display: flex;
justify-content: flex-end;
width: 50%;
}
#main-right {
width: 50%;
}
#time {
font-size: 25px;
font-weight: bold;
font-style: italic;
text-align: right;
color: var(--sub-text-color);
}
#adm {
font-size: 32px;
font-weight: bold;
text-align: center;
color: var(--sub-text-color);
}
#city {
margin-top: 20px;
font-size: 70px;
font-weight: bold;
text-align: center;
}
#temperature {
display: flex;
align-items: baseline;
}
#temperature-now {
font-size: 70px;
font-weight: bold;
}
#temperature-range {
font-size: 40px;
font-weight: bold;
color: var(--sub-text-color);
}
#description {
font-size: 50px;
font-weight: bold;
}
#aqi {
height: 50px;
display: flex;
border-radius: 60px;
padding: 5px;
font-size: 40px;
text-align: center;
align-content: center;
align-items: center;
justify-content: center;
}
#aqi-dot {
height: 80%;
aspect-ratio: 1 / 1;
border-radius: 50%;
background-color: var(--sub-text-color);
margin-right: 20px;
}
.main-icon {
width: 240px;
height: 240px;
}
#hours-info {
display: flex;
justify-content: space-between;
}
.hourly-item {
text-align: center;
background-color: #ffffff44;
border-radius: var(--sub-border-radius);
align-items: center;
padding: 20px 10px;
}
.hourly-icon{
width: 80%;
margin-bottom: 20px;
}
.hourly-temperature {
text-align: center;
color: var(--main-text-color);
font-size: 30px;
margin-bottom: 20px;
}
.hourly-time {
text-align: center;
color: var(--main-text-color);
font-size: 25px;
margin-bottom: 10px;
}
/**/
.daily-item {
display: flex;
position: relative;
justify-content: space-between;
align-items: center;
background-color: #ffffff44;
height: 90px;
border-radius: var(--sub-border-radius);
margin-bottom: 20px;
padding: 0 30px;
}
/*最后一个没有margin_button*/
.daily-item:last-child {
margin-bottom: 0;
}
.icon-day {
position: absolute;
left: 60%;
height: 80px;
}
.icon-night {
position: absolute;
left: 70%;
height: 80px;
}
.daily-weather{
position: absolute;
left: 30%;
}
.daily-temperature{
position: absolute;
left: 83%;
}
.daily-day, .daily-weather, .daily-temperature {
text-align: center;
color: var(--main-text-color);
font-size: 30px;
}

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,114 @@
/**
* @typedef {Object} Location
* @property {string} city - The city name.
* @property {string} country - The country name.
*
* @typedef {Object} Weather
* @property {number} temperature - The current temperature.
* @property {string} description - The weather description.
*
* @typedef {Object} Data
* @property {Location} location - The location data.
* @property {Weather} weather - The weather data.
*/
/** @type {Data} */
let data = JSON.parse(document.getElementById("data").innerText)
let localData = data["localization"] // 本地化数据
let weatherNow = data["weatherNow"]
let weatherDaily = data["weatherDaily"]
let weatherHourly = data["weatherHourly"]
let aqi = data["aqi"]
let locationData = data["location"]
// 处理aqi
let aqiValue = 0
aqi["aqi"].forEach(
(item) => {
if (item["defaultLocalAqi"]) {
document.getElementById("aqi-data").innerText = "AQI " + item["valueDisplay"] + " " + item["category"]
// 将(255,255,255)这种格式的颜色设置给css
document.getElementById("aqi-dot").style.backgroundColor = "rgb(" + item["color"] + ")"
}
}
)
templates = {
"time": weatherNow["now"]["obsTime"],
"city": locationData["name"],
"adm": locationData["country"] + " " + locationData["adm1"] + " " + locationData["adm2"],
"temperature-now": weatherNow["now"]["temp"] + "°",
"temperature-range": weatherDaily["daily"][0]["tempMin"] + "°/" + weatherDaily["daily"][0]["tempMax"] + "°",
"description": weatherNow["now"]["text"]
}
// 遍历每一个id给其赋值
for (let id in templates) {
document.getElementById(id).innerText = templates[id]
}
let maxHourlyItem = 8
let percentWidth = 1 / (maxHourlyItem * 1.5) * 100
let hourlyStep = 2 // n小时一个数据
let hourlyCount = 0
weatherHourly['hourly'].forEach(
(item, index) => {
if (index % hourlyStep !== 0) {
return
}
if (hourlyCount >= maxHourlyItem) {
return
}
let hourlyItemDiv = document.importNode(document.getElementById("hourly-item-template").content, 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"])
hourlyItemDiv.querySelector('.hourly-temperature').innerText = " " + item["temp"] + "°"
// 设置最大宽度
hourlyItemDiv.querySelector('.hourly-item').style.maxWidth = percentWidth + "%"
hourlyItemDiv.querySelector('.hourly-icon').style.maxWidth = "100%"
document.getElementById("hours-info").appendChild(hourlyItemDiv)
hourlyCount++
}
)
let maxDailyItem = 7
// 第一和第二天用today和tomorrow后面用星期X英文小写
let daysStandard = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
let todayDay = new Date().getDay()
let days = [localData['today'], localData['tomorrow']]
for (let i = 0; i < 5; i++) {
days.push(localData[daysStandard[(todayDay + i) % 7]])
}
weatherDaily['daily'].forEach(
(item, index) => {
if (index >= maxDailyItem) {
return
}
let today = days[index]
if (index >= 2){
today += `(${item["fxDate"].split("-")[1]}.${item["fxDate"].split("-")[2]})`
}
let dailyItemDiv = document.importNode(document.getElementById("daily-item-template").content, 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`)
dailyItemDiv.querySelector('.daily-day').innerText = today
dailyItemDiv.querySelector('.daily-weather').innerText = item["textDay"]
dailyItemDiv.querySelector('.daily-temperature').innerText = item["tempMin"] + "°~" + item["tempMax"] + "°"
document.getElementById('days-info').appendChild(dailyItemDiv)
}
)
function get_time_hour(fxTime) {
// fxTime 2024-05-03T02:00+08:00'
return fxTime.split("T")[1].split("+")[0]
}

View File

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="zh">
<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/weather_now.css">
</head>
<!-- qw_icon: https://a.hecdn.net/img/common/icon/202106d/%d.png-->
<body>
<template id="hourly-item-template">
<div class="hourly-item">
<img class="hourly-icon icon" src="./img/qw_icon/101.png" alt="WeatherIcon">
<div class="hourly-temperature">90°</div>
<!-- <div class="hourly-windDir">None</div>-->
<div class="hourly-time">02:00</div>
</div>
</template>
<template id="daily-item-template">
<div class="daily-item">
<div class="daily-day">
周八
</div>
<div class="daily-weather">
小水
</div>
<img class="daily-icon icon-day icon" src="./img/qw_icon/101.png" alt="WeatherIcon">
<img class="daily-icon icon-night icon" src="./img/qw_icon/101.png" alt="WeatherIcon">
<div class="daily-temperature">
12°~23°
</div>
</div>
</template>
<div class="data-storage" id="data">{{ data | tojson }}</div>
<div class="info-box" id="weather-info">
<div id="detail-info">
<div id="time">2045-01-12 22:22:22</div>
<div id="adm">枫丹 白露 白露区</div>
<div id="city">白露区</div>
</div>
<div id="main-info">
<div id="main-left">
<img class="main-icon icon" src="./img/qw_icon/101.png" alt="WeatherIcon">
</div>
<div id="main-right">
<div id="temperature">
<div id="temperature-now">
90°
</div>
<div id="temperature-range">
10°~90°
</div>
</div>
<div id="description">
示例天气
</div>
</div>
</div>
<div id="aqi">
<div id="aqi-dot"></div>
<div id="aqi-data"> AQI 114 优</div>
</div>
</div>
<div class="info-box" id="sub-info"></div>
<div class="info-box" id="hours-info"></div>
<div class="info-box" id="days-info"></div>
<script src="./js/card.js"></script>
<script src="js/weather_now.js"></script>
</body>

View File

@ -156,4 +156,5 @@ status.minutes=Minuten
status.seconds=Sekunden
status.cores=Kerne
status.threads=Threads
status.process=Prozesse
status.process=Prozesse
status.description=Liteyuki Bot-Status

View File

@ -156,4 +156,5 @@ status.minutes=Minutes
status.seconds=Seconds
status.cores=Cores
status.threads=Threads
status.process=Processes
status.process=Processes
status.description=Liteyuki Dashboard

View File

@ -156,4 +156,5 @@ status.minutes=Minutos
status.seconds=Segundos
status.cores=Núcleos
status.threads=Hilos
status.process=Procesos
status.process=Procesos
status.description=Liteyuki Dashboard Status

View File

@ -156,4 +156,5 @@ status.minutes=分
status.seconds=秒
status.cores=コア
status.threads=スレッド
status.process=プロセス
status.process=プロセス
status.description=軽雪監視パネル

View File

@ -156,4 +156,5 @@ status.minutes=分
status.seconds=秒
status.cores=核心
status.threads=线程
status.process=进程
status.process=进程
status.description=轻雪机器人状态面板

View File

@ -11,9 +11,15 @@ body {
}
.info-box {
border-radius: 30px;
border-radius: 50px;
padding: 30px;
backdrop-filter: blur(10px);
background-color: rgba(0, 0, 0, 0.5);
margin-bottom: 20px;
}
#author-description {
text-align: center;
color: var(--sub-text-color);
font-size: 30px;
}

View File

@ -1,50 +1,46 @@
:root {
--main-text-color: #fff;
--sub-text-color: #bbb;
--tip-text-color: #888;
--sub-text-color: #ccc;
--tip-text-color: #999;
--device-info-width: 240px;
}
.bot-info {
display: flex;
height: 200px;
}
.bot-icon {
display: flex;
height: 100%;
aspect-ratio: 1;
width: 220px;
align-items: center;
justify-content: center;
margin-right: 20px;
}
.bot-icon-img {
height: 220px;
border-radius: 50%;
height: 100%;
width: 100%;
background-color: white;
}
.bot-name {
color: var(--main-text-color);
display: flex;
font-size: 40px;
font-size: 45px;
flex-direction: column;
justify-content: center;
}
.bot-tag {
white-space: nowrap;
white-space: break-spaces;
color: var(--sub-text-color);
font-size: 27px;
font-size: 30px;
font-weight: 700;
line-height: 1.6;
}
.bot-tag[suffix="1"]::after {
content: " | ";
content: "|";
display: inline-block;
margin: 0 5px;
height: 30%;
@ -127,4 +123,5 @@
font-style: italic;
color: var(--sub-text-color);
text-align: right;
}
}

View File

@ -1,83 +0,0 @@
#weather-info {
color: white;
/*justify-content: center;*/
/*align-items: center;*/
/*align-content: center;*/
}
#main-info {
display: flex;
justify-content: center;
align-items: center;
}
#time {
font-size: 25px;
font-weight: bold;
font-style: italic;
text-align: right;
color: #aaa;
}
#adm {
font-size: 30px;
font-weight: bold;
text-align: center;
color: #aaa;
}
#city {
margin-top: 20px;
font-size: 70px;
font-weight: bold;
text-align: center;
}
#temperature {
display: flex;
align-items: baseline;
}
#temperature-now {
font-size: 70px;
font-weight: bold;
}
#temperature-range {
font-size: 40px;
font-weight: bold;
color: #aaa;
}
#description {
font-size: 50px;
font-weight: bold;
}
#aqi {
height: 50px;
display: flex;
border-radius: 60px;
padding: 5px;
font-size: 40px;
text-align: center;
align-content: center;
align-items: center;
justify-content: center;
}
#aqi-dot {
height: 80%;
aspect-ratio: 1 / 1;
border-radius: 50%;
background-color: #aaa;
margin-right: 20px;
}
.main-icon {
width: 240px;
height: 240px;
}

View File

@ -9,3 +9,9 @@ const bgs = [
]
// 随机选择背景图片
document.body.style.backgroundImage = `url(./img/${bgs[Math.floor(Math.random() * bgs.length)]})`;
// body后插入info-box id=description
let descriptionDiv = document.createElement("div");
descriptionDiv.className = 'info-box'
descriptionDiv.id = 'author-description'
descriptionDiv.innerText = 'Designed by SnowyKami'
document.body.appendChild(descriptionDiv);

View File

@ -290,6 +290,8 @@ function main() {
document.getElementById('motto-text').innerText = mottoText
document.getElementById('motto-from').innerText = mottoFrom
document.getElementById('author-description').innerText = localData['description'] + ' Powered by Liteyuki'
}

View File

@ -1,59 +0,0 @@
/**
* @typedef {Object} Location
* @property {string} city - The city name.
* @property {string} country - The country name.
*
* @typedef {Object} Weather
* @property {number} temperature - The current temperature.
* @property {string} description - The weather description.
*
* @typedef {Object} Data
* @property {Location} location - The location data.
* @property {Weather} weather - The weather data.
*/
/** @type {Data} */
let data = JSON.parse(document.getElementById("data").innerText)
let weatherNow = data["weatherNow"]
let weatherDaily = data["weatherDaily"]
let weatherHourly = data["weatherHourly"]
let aqi = data["aqi"]
let locationData = data["location"]
// set info
// document.getElementById("time").innerText = weatherNow["now"]["obsTime"]
// document.getElementById("city").innerText = locationData["name"]
// document.getElementById("adm").innerText = locationData["country"] + " " + locationData["adm1"] + " " + locationData["adm2"]
// document.getElementById("temperature-now").innerText = weatherNow["now"]["temp"] + "°"
// document.getElementById("temperature-range").innerText = weatherNow["now"]["feelsLike"] + "°"
// document.getElementById("description").innerText = weatherNow["now"]["text"]
// 处理aqi
let aqiValue = 0
aqi["aqi"].forEach(
(item) => {
if (item["defaultLocalAqi"]) {
document.getElementById("aqi-data").innerText = "AQI " + item["valueDisplay"] + " " + item["category"]
// 将(255,255,255)这种格式的颜色设置给css
document.getElementById("aqi-dot").style.backgroundColor = "rgb(" + item["color"] + ")"
}
}
)
templates = {
"time": weatherNow["now"]["obsTime"],
"city": locationData["name"],
"adm": locationData["country"] + " " + locationData["adm1"] + " " + locationData["adm2"],
"temperature-now": weatherNow["now"]["temp"] + "°",
"temperature-range": weatherDaily["daily"][0]["tempMin"] + "°/" + weatherDaily["daily"][0]["tempMax"] + "°",
"description": weatherNow["now"]["text"]
}
// 遍历每一个id给其赋值
for (let id in templates) {
document.getElementById(id).innerText = templates[id]
}

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="zh">
<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/weather_now.css">
</head>
<!-- qw_icon: https://a.hecdn.net/img/common/icon/202106d/%d.png-->
<body>
<div class="data-storage" id="data">{{ data | tojson }}</div>
<div class="info-box" id="weather-info">
<div id="detail-info">
<div id="time">1145-01-12 22:22:22</div>
<div id="adm">国家 一级 二级</div>
<div id="city">城市</div>
</div>
<div id="main-info">
<div>
<img class="main-icon" src="./img/qw_icon/101.png" alt="AAA">
</div>
<div>
<div id="temperature">
<div id="temperature-now">
90°
</div>
<div id="temperature-range">
10°~90°
</div>
</div>
<div id="description">
好天气
</div>
</div>
</div>
<div id="aqi">
<div id="aqi-dot"></div>
<div id="aqi-data"> AQI 114 优</div>
</div>
</div>
<div class="info-box" id="sub-info"></div>
<div class="info-box" id="hours-info"></div>
<div class="info-box" id="days-info"></div>
<script src="./js/card.js"></script>
<script src="./js/weather_now.js"></script>
</body>

Some files were not shown because too many files have changed in this diff Show More