✨ 轻雪天气更新
@ -5,23 +5,25 @@ order: 2
|
|||||||
category: 使用手册
|
category: 使用手册
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## 功能插件命令
|
## 功能插件命令
|
||||||
|
|
||||||
### **轻雪天气`liteyuki_weather`**
|
### **轻雪天气`liteyuki_weather`**
|
||||||
|
|
||||||
配置项
|
配置项
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
weather_key: "" # 和风天气的天气key
|
weather_key: "" # 和风天气的天气key,会自动判断key版本
|
||||||
weather_dev: false # 是否为开发版
|
|
||||||
```
|
```
|
||||||
|
|
||||||
命令
|
命令
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||||
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
||||||
```
|
```
|
||||||
|
|
||||||
命令别名
|
命令别名
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
weather 天气, bind-city 绑定城市
|
weather 天气, bind-city 绑定城市
|
||||||
```
|
```
|
@ -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)
|
Reloader.reload(0)
|
||||||
|
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
stored_config.config[key] = value
|
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)}")
|
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")
|
||||||
@ -144,7 +144,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher
|
|||||||
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.upsert(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)}")
|
||||||
@ -164,7 +164,7 @@ async def _(event: T_MessageEvent, matcher: Matcher):
|
|||||||
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig())
|
stored_config: StoredConfig = common_db.first(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.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"))
|
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):
|
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.upsert(temp_data) # 更新数据
|
common_db.save(temp_data) # 更新数据
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@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_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.upsert(temp_data) # 更新数据
|
common_db.save(temp_data) # 更新数据
|
||||||
await bot.call_api(
|
await bot.call_api(
|
||||||
"send_msg",
|
"send_msg",
|
||||||
message_type=reload_session_type,
|
message_type=reload_session_type,
|
||||||
|
@ -66,7 +66,7 @@ async def _(result: Arparma):
|
|||||||
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.all(Push(), default=[]))
|
inde=len(pushes_db.all(Push(), default=[]))
|
||||||
)
|
)
|
||||||
pushes_db.upsert(push1)
|
pushes_db.save(push1)
|
||||||
|
|
||||||
if result.subcommands["add"].args.get("bidirectional"):
|
if result.subcommands["add"].args.get("bidirectional"):
|
||||||
push2 = Push(
|
push2 = Push(
|
||||||
@ -74,7 +74,7 @@ async def _(result: Arparma):
|
|||||||
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.all(Push(), default=[]))
|
inde=len(pushes_db.all(Push(), default=[]))
|
||||||
)
|
)
|
||||||
pushes_db.upsert(push2)
|
pushes_db.save(push2)
|
||||||
await add_push.finish("添加成功")
|
await add_push.finish("添加成功")
|
||||||
else:
|
else:
|
||||||
await add_push.finish("参数缺失")
|
await add_push.finish("参数缺失")
|
||||||
|
@ -150,10 +150,10 @@ def set_plugin_session_enable(event: T_MessageEvent, plugin_name: str, enable: b
|
|||||||
if event.message_type == "group":
|
if event.message_type == "group":
|
||||||
__group_data[str(event.group_id)] = session
|
__group_data[str(event.group_id)] = session
|
||||||
print(session)
|
print(session)
|
||||||
group_db.upsert(session)
|
group_db.save(session)
|
||||||
else:
|
else:
|
||||||
__user_data[str(event.user_id)] = session
|
__user_data[str(event.user_id)] = session
|
||||||
user_db.upsert(session)
|
user_db.save(session)
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_global_enable(plugin_name: str) -> bool:
|
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))
|
default=GlobalPlugin(module_name=plugin_name, enabled=True))
|
||||||
plugin.enabled = enable
|
plugin.enabled = enable
|
||||||
|
|
||||||
plugin_db.upsert(plugin)
|
plugin_db.save(plugin)
|
||||||
__global_enable[plugin_name] = enable
|
__global_enable[plugin_name] = enable
|
||||||
|
|
||||||
|
|
||||||
@ -242,4 +242,4 @@ def set_group_enable(group_id: str, enable: bool):
|
|||||||
group.enable = enable
|
group.enable = enable
|
||||||
|
|
||||||
__group_data[group_id] = group
|
__group_data[group_id] = group
|
||||||
group_db.upsert(group)
|
group_db.save(group)
|
||||||
|
@ -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) # 查询数据库中是否已经安装
|
found_in_db_plugin = plugin_db.first(InstalledPlugin(), "module_name = ?", plugin_name) # 查询数据库中是否已经安装
|
||||||
if r_load:
|
if r_load:
|
||||||
if found_in_db_plugin is None:
|
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转义
|
info = md.escape(ulang.get("npm.install_success", NAME=store_plugin.name)) # markdown转义
|
||||||
await md.send_md(
|
await md.send_md(
|
||||||
f"{info}\n\n"
|
f"{info}\n\n"
|
||||||
|
@ -55,18 +55,22 @@ data
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0") -> bytes:
|
async def generate_status_card(bot: dict, hardware: dict, liteyuki: dict, lang="zh-CN", bot_id="0", use_cache=False) -> bytes:
|
||||||
return await template2image(
|
if not use_cache:
|
||||||
get_path("templates/status.html", abs_path=True),
|
return await template2image(
|
||||||
{
|
get_path("templates/status.html", abs_path=True),
|
||||||
"data": {
|
{
|
||||||
"bot" : bot,
|
"data": {
|
||||||
"hardware" : hardware,
|
"bot" : bot,
|
||||||
"liteyuki" : liteyuki,
|
"hardware" : hardware,
|
||||||
"localization": get_local_data(lang)
|
"liteyuki" : liteyuki,
|
||||||
}
|
"localization": get_local_data(lang)
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
|
debug=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_local_data(lang_code) -> dict:
|
def get_local_data(lang_code) -> dict:
|
||||||
@ -97,7 +101,7 @@ def get_local_data(lang_code) -> dict:
|
|||||||
"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"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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))
|
r = set_profile(result.args["key"], result.args["value"], str(event.user_id))
|
||||||
if r:
|
if r:
|
||||||
user.profile[result.args["key"]] = result.args["value"]
|
user.profile[result.args["key"]] = result.args["value"]
|
||||||
user_db.upsert(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",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
from nonebot import get_driver
|
||||||
from .qweather import *
|
from .qweather import *
|
||||||
|
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪天气",
|
name="轻雪天气",
|
||||||
description="基于和风天气api的天气插件",
|
description="基于和风天气api的天气插件",
|
||||||
@ -9,9 +9,19 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from .qw_models import *
|
from .qw_models import *
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
from ...utils.base.data_manager import get_memory_data
|
||||||
|
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/" # 正式环境
|
||||||
@ -17,6 +19,42 @@ def get_qw_lang(lang: str) -> str:
|
|||||||
return lang
|
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(
|
async def city_lookup(
|
||||||
location: str,
|
location: str,
|
||||||
key: str,
|
key: str,
|
||||||
@ -54,7 +92,7 @@ async def get_weather_now(
|
|||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = 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
|
||||||
@ -74,7 +112,7 @@ async def get_weather_daily(
|
|||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = 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
|
||||||
@ -94,7 +132,7 @@ async def get_weather_hourly(
|
|||||||
location: str,
|
location: str,
|
||||||
lang: str = "zh",
|
lang: str = "zh",
|
||||||
unit: str = "m",
|
unit: str = "m",
|
||||||
dev: bool = 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
|
||||||
@ -115,7 +153,7 @@ async def get_airquality(
|
|||||||
lang: str,
|
lang: str,
|
||||||
pollutant: bool = False,
|
pollutant: bool = False,
|
||||||
station: bool = False,
|
station: bool = False,
|
||||||
dev: bool = 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
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from nonebot import require
|
from nonebot import require, on_endswith
|
||||||
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
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ from liteyuki.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 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.base.resource import get_path
|
||||||
from liteyuki.utils.message.html_tool import template2image
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
|
|
||||||
@ -24,40 +24,50 @@ from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
|||||||
).handle()
|
).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)"""
|
||||||
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)
|
qw_lang = get_qw_lang(ulang.lang_code)
|
||||||
key = get_config("weather_key")
|
key = get_config("weather_key")
|
||||||
is_dev = get_config("weather_dev")
|
is_dev = get_memory_data("weather.is_dev", True)
|
||||||
|
user: User = user_db.first(User(), "user_id = ?", event.user_id, default=User())
|
||||||
user: User = user_db.first(User(), "user_id = ?", str(event.user_id), 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"))
|
await matcher.finish(ulang.get("weather.no_key") if tip else None)
|
||||||
|
|
||||||
kws = result.main_args.get("keywords")
|
if keyword:
|
||||||
if kws:
|
if len(keyword) >= 2:
|
||||||
if len(kws) >= 2:
|
adm = keyword[0]
|
||||||
adm = kws[0]
|
city = keyword[-1]
|
||||||
city = kws[-1]
|
|
||||||
else:
|
else:
|
||||||
adm = ""
|
adm = ""
|
||||||
city = kws[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(kws)
|
city_name = " ".join(keyword)
|
||||||
else:
|
else:
|
||||||
if not stored_location:
|
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_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))
|
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)
|
||||||
@ -76,9 +86,10 @@ async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
|||||||
"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)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
debug=True,
|
debug=True,
|
||||||
wait=1
|
wait=1
|
||||||
)
|
)
|
||||||
await matcher.finish(MessageSegment.image(image))
|
return image
|
||||||
|
11
liteyuki/resources/liteyuki_weather/lang/en.lang
Normal 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
|
11
liteyuki/resources/liteyuki_weather/lang/ja.lang
Normal 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=明日
|
11
liteyuki/resources/liteyuki_weather/lang/zh-CN.lang
Normal 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=明天
|
3
liteyuki/resources/liteyuki_weather/metadata.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
name: 轻雪天气资源包
|
||||||
|
description: For Liteyuki Weather
|
||||||
|
version: 2024.4.26
|
@ -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;
|
||||||
|
}
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
114
liteyuki/resources/liteyuki_weather/templates/js/weather_now.js
Normal 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]
|
||||||
|
}
|
@ -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>
|
@ -156,4 +156,5 @@ status.minutes=Minuten
|
|||||||
status.seconds=Sekunden
|
status.seconds=Sekunden
|
||||||
status.cores=Kerne
|
status.cores=Kerne
|
||||||
status.threads=Threads
|
status.threads=Threads
|
||||||
status.process=Prozesse
|
status.process=Prozesse
|
||||||
|
status.description=Liteyuki Bot-Status
|
@ -156,4 +156,5 @@ status.minutes=Minutes
|
|||||||
status.seconds=Seconds
|
status.seconds=Seconds
|
||||||
status.cores=Cores
|
status.cores=Cores
|
||||||
status.threads=Threads
|
status.threads=Threads
|
||||||
status.process=Processes
|
status.process=Processes
|
||||||
|
status.description=Liteyuki Dashboard
|
@ -156,4 +156,5 @@ status.minutes=Minutos
|
|||||||
status.seconds=Segundos
|
status.seconds=Segundos
|
||||||
status.cores=Núcleos
|
status.cores=Núcleos
|
||||||
status.threads=Hilos
|
status.threads=Hilos
|
||||||
status.process=Procesos
|
status.process=Procesos
|
||||||
|
status.description=Liteyuki Dashboard Status
|
@ -156,4 +156,5 @@ status.minutes=分
|
|||||||
status.seconds=秒
|
status.seconds=秒
|
||||||
status.cores=コア
|
status.cores=コア
|
||||||
status.threads=スレッド
|
status.threads=スレッド
|
||||||
status.process=プロセス
|
status.process=プロセス
|
||||||
|
status.description=軽雪監視パネル
|
@ -156,4 +156,5 @@ status.minutes=分
|
|||||||
status.seconds=秒
|
status.seconds=秒
|
||||||
status.cores=核心
|
status.cores=核心
|
||||||
status.threads=线程
|
status.threads=线程
|
||||||
status.process=进程
|
status.process=进程
|
||||||
|
status.description=轻雪机器人状态面板
|
@ -11,9 +11,15 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info-box {
|
.info-box {
|
||||||
border-radius: 30px;
|
border-radius: 50px;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#author-description {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--sub-text-color);
|
||||||
|
font-size: 30px;
|
||||||
}
|
}
|
@ -1,50 +1,46 @@
|
|||||||
:root {
|
:root {
|
||||||
--main-text-color: #fff;
|
--main-text-color: #fff;
|
||||||
--sub-text-color: #bbb;
|
--sub-text-color: #ccc;
|
||||||
--tip-text-color: #888;
|
--tip-text-color: #999;
|
||||||
|
|
||||||
--device-info-width: 240px;
|
--device-info-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-info {
|
.bot-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 200px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-icon {
|
.bot-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
width: 220px;
|
||||||
aspect-ratio: 1;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-icon-img {
|
.bot-icon-img {
|
||||||
|
height: 220px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-name {
|
.bot-name {
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 40px;
|
font-size: 45px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-tag {
|
.bot-tag {
|
||||||
white-space: nowrap;
|
white-space: break-spaces;
|
||||||
color: var(--sub-text-color);
|
color: var(--sub-text-color);
|
||||||
font-size: 27px;
|
font-size: 30px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bot-tag[suffix="1"]::after {
|
.bot-tag[suffix="1"]::after {
|
||||||
content: " | ";
|
content: "|";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
height: 30%;
|
height: 30%;
|
||||||
@ -127,4 +123,5 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: var(--sub-text-color);
|
color: var(--sub-text-color);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -9,3 +9,9 @@ const bgs = [
|
|||||||
]
|
]
|
||||||
// 随机选择背景图片
|
// 随机选择背景图片
|
||||||
document.body.style.backgroundImage = `url(./img/${bgs[Math.floor(Math.random() * bgs.length)]})`;
|
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);
|
||||||
|
@ -290,6 +290,8 @@ function main() {
|
|||||||
document.getElementById('motto-text').innerText = mottoText
|
document.getElementById('motto-text').innerText = mottoText
|
||||||
document.getElementById('motto-from').innerText = mottoFrom
|
document.getElementById('motto-from').innerText = mottoFrom
|
||||||
|
|
||||||
|
document.getElementById('author-description').innerText = localData['description'] + ' Powered by Liteyuki'
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]
|
|
||||||
}
|
|
@ -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>
|
|