feat: 轻雪天气实时天气功能已更新
@ -8,14 +8,20 @@ category: 使用手册
|
|||||||
|
|
||||||
## 功能插件命令
|
## 功能插件命令
|
||||||
|
|
||||||
### 轻雪天气`liteyuki_weather`
|
### **轻雪天气`liteyuki_weather`**
|
||||||
配置项
|
配置项
|
||||||
```yaml
|
```yaml
|
||||||
weather-key # 和风天气的天气key
|
weather_key: "" # 和风天气的天气key
|
||||||
|
weather_dev: false # 是否为开发版
|
||||||
```
|
```
|
||||||
|
|
||||||
命令
|
命令
|
||||||
```shell
|
```shell
|
||||||
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
weather <keywords...> # 查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||||
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
||||||
别名:weather 天气
|
```
|
||||||
|
|
||||||
|
命令别名
|
||||||
|
```shell
|
||||||
|
weather 天气, bind-city 绑定城市
|
||||||
```
|
```
|
@ -65,12 +65,18 @@ async def _(bot: T_Bot, event: T_MessageEvent):
|
|||||||
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
||||||
await matcher.send("Liteyuki reloading")
|
await matcher.send("Liteyuki reloading")
|
||||||
temp_data = common_db.first(TempConfig(), default=TempConfig())
|
temp_data = common_db.first(TempConfig(), default=TempConfig())
|
||||||
temp_data.data["reload"] = True
|
|
||||||
temp_data.data["reload_time"] = time.time()
|
temp_data.data.update(
|
||||||
temp_data.data["reload_bot_id"] = bot.self_id
|
{
|
||||||
temp_data.data["reload_session_type"] = event.message_type
|
"reload" : True,
|
||||||
temp_data.data["reload_session_id"] = event.group_id if event.message_type == "group" else event.user_id
|
"reload_time" : time.time(),
|
||||||
temp_data.data["delta_time"] = 0
|
"reload_bot_id" : bot.self_id,
|
||||||
|
"reload_session_type": event.message_type,
|
||||||
|
"reload_session_id" : event.group_id if event.message_type == "group" else event.user_id,
|
||||||
|
"delta_time" : 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
common_db.upsert(temp_data)
|
common_db.upsert(temp_data)
|
||||||
Reloader.reload(0)
|
Reloader.reload(0)
|
||||||
|
|
||||||
@ -88,7 +94,12 @@ async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
|
|||||||
Subcommand(
|
Subcommand(
|
||||||
"get",
|
"get",
|
||||||
Args["key", str, None],
|
Args["key", str, None],
|
||||||
alias=["查询"]
|
alias=["查询", "获取"]
|
||||||
|
),
|
||||||
|
Subcommand(
|
||||||
|
"remove",
|
||||||
|
Args["key", str],
|
||||||
|
alias=["删除"]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
@ -120,9 +131,17 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher
|
|||||||
if len(stored_config.config) > 0:
|
if len(stored_config.config) > 0:
|
||||||
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
||||||
for k, v in stored_config.config.items():
|
for k, v in stored_config.config.items():
|
||||||
reply += f"\n{k}={v}"
|
reply += f"\n{k}={v} {type(v)}"
|
||||||
reply += "\n```"
|
reply += "\n```"
|
||||||
await md.send_md(reply, bot, event=event)
|
await md.send_md(reply, bot, event=event)
|
||||||
|
elif result.subcommands.get("remove"):
|
||||||
|
key = result.subcommands.get("remove").args.get("key")
|
||||||
|
if key in stored_config.config:
|
||||||
|
stored_config.config.pop(key)
|
||||||
|
common_db.upsert(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)}")
|
||||||
|
|
||||||
|
|
||||||
@on_alconna(
|
@on_alconna(
|
||||||
@ -227,6 +246,6 @@ async def every_day_update():
|
|||||||
if result:
|
if result:
|
||||||
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
||||||
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
||||||
Reloader.reload(1)
|
Reloader.reload(5)
|
||||||
else:
|
else:
|
||||||
nonebot.logger.info(logs)
|
nonebot.logger.info(logs)
|
||||||
|
@ -35,7 +35,6 @@ async def _(bot: T_Bot, event: T_MessageEvent):
|
|||||||
{
|
{
|
||||||
"data": await get_stats_data(bot.self_id, ulang.lang_code)
|
"data": await get_stats_data(bot.self_id, ulang.lang_code)
|
||||||
},
|
},
|
||||||
debug=True,
|
|
||||||
wait=1
|
wait=1
|
||||||
)
|
)
|
||||||
await stats.finish(MessageSegment.image(image))
|
await stats.finish(MessageSegment.image(image))
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
from .qw_models import *
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
language_map = {
|
||||||
|
"zh-CN" : "zh",
|
||||||
|
"zh-HK" : "zh-hant",
|
||||||
|
"en-US" : "en",
|
||||||
|
"ja-JP" : "ja",
|
||||||
|
"ko-KR" : "ko",
|
||||||
|
"fr-FR" : "fr",
|
||||||
|
"es-ES" : "es",
|
||||||
|
"de-DE" : "de",
|
||||||
|
"it-IT" : "it",
|
||||||
|
"ru-RU" : "ru",
|
||||||
|
"ar-SA" : "ar",
|
||||||
|
"pt-BR" : "pt",
|
||||||
|
"nl-NL" : "nl",
|
||||||
|
"pl-PL" : "pl",
|
||||||
|
"tr-TR" : "tr",
|
||||||
|
"th-TH" : "th",
|
||||||
|
"vi-VN" : "vi",
|
||||||
|
"id-ID" : "id",
|
||||||
|
"ms-MY" : "ms",
|
||||||
|
"fil-PH": "fil",
|
||||||
|
} # 其他使用默认对应
|
||||||
|
|
||||||
|
dev_url = "https://devapi.qweather.com/" # 开发HBa
|
||||||
|
com_url = "https://api.qweather.com/" # 正式环境
|
||||||
|
|
||||||
|
|
||||||
|
async def city_lookup(
|
||||||
|
location: str,
|
||||||
|
key: str,
|
||||||
|
adm: str = "",
|
||||||
|
number: int = 20,
|
||||||
|
lang: str = "zh",
|
||||||
|
) -> CityLookup:
|
||||||
|
"""
|
||||||
|
通过关键字搜索城市信息
|
||||||
|
Args:
|
||||||
|
location:
|
||||||
|
key:
|
||||||
|
adm:
|
||||||
|
number:
|
||||||
|
lang: 可传入标准i18n语言代码,如zh-CN、en-US等
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = "https://geoapi.qweather.com/v2/city/lookup?"
|
||||||
|
params = {
|
||||||
|
"location": location,
|
||||||
|
"adm" : adm,
|
||||||
|
"number" : number,
|
||||||
|
"key" : key,
|
||||||
|
"lang" : language_map.get(lang, lang),
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.get(url, params=params)
|
||||||
|
return CityLookup.parse_obj(resp.json())
|
||||||
|
|
||||||
|
|
||||||
|
async def get_weather_now(
|
||||||
|
key: str,
|
||||||
|
location: str,
|
||||||
|
lang: str = "zh",
|
||||||
|
unit: str = "m",
|
||||||
|
dev: bool = True,
|
||||||
|
) -> dict:
|
||||||
|
url_path = "v7/weather/now?"
|
||||||
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
|
params = {
|
||||||
|
"location": location,
|
||||||
|
"key" : key,
|
||||||
|
"lang" : language_map.get(lang, lang),
|
||||||
|
"unit" : unit,
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.get(url, params=params)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_weather_daily(
|
||||||
|
key: str,
|
||||||
|
location: str,
|
||||||
|
lang: str = "zh",
|
||||||
|
unit: str = "m",
|
||||||
|
dev: bool = True,
|
||||||
|
) -> dict:
|
||||||
|
url_path = "v7/weather/%dd?" % (7 if dev else 30)
|
||||||
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
|
params = {
|
||||||
|
"location": location,
|
||||||
|
"key" : key,
|
||||||
|
"lang" : language_map.get(lang, lang),
|
||||||
|
"unit" : unit,
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.get(url, params=params)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_weather_hourly(
|
||||||
|
key: str,
|
||||||
|
location: str,
|
||||||
|
lang: str = "zh",
|
||||||
|
unit: str = "m",
|
||||||
|
dev: bool = True,
|
||||||
|
) -> dict:
|
||||||
|
url_path = "v7/weather/%dh?" % (24 if dev else 168)
|
||||||
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
|
params = {
|
||||||
|
"location": location,
|
||||||
|
"key" : key,
|
||||||
|
"lang" : language_map.get(lang, lang),
|
||||||
|
"unit" : unit,
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.get(url, params=params)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_airquality(
|
||||||
|
key: str,
|
||||||
|
location: str,
|
||||||
|
lang: str,
|
||||||
|
pollutant: bool = False,
|
||||||
|
station: bool = False,
|
||||||
|
dev: bool = True
|
||||||
|
) -> dict:
|
||||||
|
url_path = f"airquality/v1/now/{location}?"
|
||||||
|
url = dev_url + url_path if dev else com_url + url_path
|
||||||
|
params = {
|
||||||
|
"key" : key,
|
||||||
|
"lang" : language_map.get(lang, lang),
|
||||||
|
"pollutant": pollutant,
|
||||||
|
"station" : station,
|
||||||
|
}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.get(url, params=params)
|
||||||
|
return resp.json()
|
||||||
|
|
@ -19,12 +19,12 @@ class Location(LiteModel):
|
|||||||
license: str = ""
|
license: str = ""
|
||||||
|
|
||||||
|
|
||||||
class CityLookupResponse(LiteModel):
|
class CityLookup(LiteModel):
|
||||||
code: str = ""
|
code: str = ""
|
||||||
location: Location = Location()
|
location: list[Location] = [Location()]
|
||||||
|
|
||||||
|
|
||||||
class WeatherNow(LiteModel):
|
class Now(LiteModel):
|
||||||
obsTime: str = ""
|
obsTime: str = ""
|
||||||
temp: str = ""
|
temp: str = ""
|
||||||
feelsLike: str = ""
|
feelsLike: str = ""
|
||||||
@ -44,8 +44,19 @@ class WeatherNow(LiteModel):
|
|||||||
license: str = ""
|
license: str = ""
|
||||||
|
|
||||||
|
|
||||||
class WeatherNowResponse(LiteModel):
|
class WeatherNow(LiteModel):
|
||||||
code: str = ""
|
code: str = ""
|
||||||
updateTime: str = ""
|
updateTime: str = ""
|
||||||
fxLink: str = ""
|
fxLink: str = ""
|
||||||
now: WeatherNow = WeatherNow()
|
now: Now = Now()
|
||||||
|
|
||||||
|
|
||||||
|
class Daily(LiteModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WeatherDaily(LiteModel):
|
||||||
|
code: str = ""
|
||||||
|
updateTime: str = ""
|
||||||
|
fxLink: str = ""
|
||||||
|
daily: list[str] = []
|
@ -1,7 +1,16 @@
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||||
|
from nonebot.internal.matcher import Matcher
|
||||||
|
|
||||||
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
|
from .qw_api import *
|
||||||
|
from ...utils.base.data_manager import User, user_db
|
||||||
|
from ...utils.base.language import get_user_lang
|
||||||
|
from ...utils.base.resource import get_path
|
||||||
|
from ...utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
||||||
|
|
||||||
@ -10,11 +19,67 @@ from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
|||||||
aliases={"天气"},
|
aliases={"天气"},
|
||||||
command=Alconna(
|
command=Alconna(
|
||||||
"weather",
|
"weather",
|
||||||
Args["keywords", MultiVar(str)],
|
Args["keywords", MultiVar(str), []],
|
||||||
),
|
),
|
||||||
).handle()
|
).handle()
|
||||||
async def _(result: Arparma, event: T_MessageEvent):
|
async def _(result: Arparma, event: T_MessageEvent, matcher: Matcher):
|
||||||
"""await alconna.send("weather", city)"""
|
"""await alconna.send("weather", city)"""
|
||||||
print(result["keywords"])
|
ulang = get_user_lang(str(event.user_id))
|
||||||
if len(result["keywords"]) == 0:
|
qw_lang = language_map.get(ulang.lang_code, ulang.lang_code)
|
||||||
pass
|
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())
|
||||||
|
|
||||||
|
# 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"))
|
||||||
|
|
||||||
|
kws = result.main_args.get("keywords")
|
||||||
|
|
||||||
|
if kws:
|
||||||
|
if len(kws) >= 2:
|
||||||
|
adm = kws[0]
|
||||||
|
city = kws[-1]
|
||||||
|
else:
|
||||||
|
adm = ""
|
||||||
|
city = kws[0]
|
||||||
|
city_info = await city_lookup(city, key, adm=adm, lang=qw_lang)
|
||||||
|
city_name = " ".join(kws)
|
||||||
|
else:
|
||||||
|
if not stored_location:
|
||||||
|
await matcher.finish(ulang.get("liteyuki.invalid_command", TEXT="location"))
|
||||||
|
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))
|
||||||
|
|
||||||
|
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)
|
||||||
|
aqi = await get_airquality(key, location_data.id, lang=qw_lang, dev=is_dev)
|
||||||
|
|
||||||
|
image = await template2image(
|
||||||
|
template=get_path("templates/weather_now.html", abs_path=True),
|
||||||
|
templates={
|
||||||
|
"data": {
|
||||||
|
"params" : {
|
||||||
|
"unit": unit,
|
||||||
|
"lang": ulang.lang_code,
|
||||||
|
},
|
||||||
|
"weatherNow" : weather_now,
|
||||||
|
"weatherDaily" : weather_daily,
|
||||||
|
"weatherHourly": weather_hourly,
|
||||||
|
"aqi" : aqi,
|
||||||
|
"location" : location_data.dump(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
debug=True,
|
||||||
|
wait=1
|
||||||
|
)
|
||||||
|
await matcher.finish(MessageSegment.image(image))
|
||||||
|
@ -24,7 +24,7 @@ liteyuki.stats.friends=好友
|
|||||||
liteyuki.stats.plugins=插件
|
liteyuki.stats.plugins=插件
|
||||||
liteyuki.image_mode_on=开启Markdown图片模式
|
liteyuki.image_mode_on=开启Markdown图片模式
|
||||||
liteyuki.image_mode_off=关闭Markdown图片模式
|
liteyuki.image_mode_off=关闭Markdown图片模式
|
||||||
liteyuki.invalid_command=无效的命令或参数
|
liteyuki.invalid_command=无效的命令或参数 {TEXT}
|
||||||
liteyuki.reload_resources=重载资源
|
liteyuki.reload_resources=重载资源
|
||||||
liteyuki.list_resources=资源包列表
|
liteyuki.list_resources=资源包列表
|
||||||
liteyuki.reload_resources_success=资源重载成功,共计 {NUM} 个资源包
|
liteyuki.reload_resources_success=资源重载成功,共计 {NUM} 个资源包
|
||||||
@ -44,6 +44,7 @@ liteyuki.change_priority_failed=资源包 {NAME} 优先级修改失败
|
|||||||
liteyuki.group_already=群 {GROUP} 已经是 {STATUS} 状态,无需重复操作
|
liteyuki.group_already=群 {GROUP} 已经是 {STATUS} 状态,无需重复操作
|
||||||
liteyuki.group_success=群 {GROUP} {STATUS} 成功
|
liteyuki.group_success=群 {GROUP} {STATUS} 成功
|
||||||
liteyuki.permission_denied=权限不足
|
liteyuki.permission_denied=权限不足
|
||||||
|
liteyuki.config_remove_success=配置项 {KEY} 移除成功
|
||||||
|
|
||||||
main.current_language=当前配置语言为: {LANG}
|
main.current_language=当前配置语言为: {LANG}
|
||||||
main.enable_webdash=已启用网页监控面板: {URL}
|
main.enable_webdash=已启用网页监控面板: {URL}
|
||||||
@ -121,3 +122,7 @@ user.profile.set_failed=设置 {ATTR} 失败,请检查输入是否合法
|
|||||||
rpm.move_up=上移
|
rpm.move_up=上移
|
||||||
rpm.move_down=下移
|
rpm.move_down=下移
|
||||||
rpm.move_top=置顶
|
rpm.move_top=置顶
|
||||||
|
|
||||||
|
weather.city_not_found=未找到城市 {CITY}
|
||||||
|
weather.weather_not_found=未找到城市 {CITY} 的天气信息
|
||||||
|
weather.no_key=未设置天气api key,请在配置文件添加weather-key
|
@ -1,4 +1,4 @@
|
|||||||
#data-storage {
|
.data-storage {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6,6 +6,7 @@ body {
|
|||||||
background-repeat: repeat-y;
|
background-repeat: repeat-y;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
text-shadow: 1px 1px 2px black;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,6 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
* {
|
||||||
font-family: 'MapleMono', 'MiSans', sans-serif;
|
font-family: 'MapleMono', 'MiSans', sans-serif;
|
||||||
}
|
}
|
83
liteyuki/resources/templates/css/weather_now.css
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#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;
|
||||||
|
}
|
BIN
liteyuki/resources/templates/img/qw_icon/100.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/101.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/102.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/103.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/104.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/150.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/151.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/152.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/153.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/154.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/300.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/301.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/302.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/303.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/304.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/305.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/306.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/307.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/308.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/309.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/310.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/311.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/312.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/313.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/314.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/315.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/316.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/317.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/318.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/350.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/351.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/399.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/400.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/401.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/402.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/403.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/404.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/405.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/406.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/407.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/408.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/409.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/410.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/456.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/457.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/499.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/500.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/501.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/502.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/503.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/504.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/507.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/508.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/509.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/510.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/511.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/512.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/513.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/514.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/515.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/800.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/801.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/802.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/803.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/804.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/805.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/806.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/807.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/900.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/901.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
liteyuki/resources/templates/img/qw_icon/999.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
59
liteyuki/resources/templates/js/weather_now.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* @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,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Liteyuki Status</title>
|
<title>Liteyuki Status</title>
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
<!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>
|
@ -12,6 +12,7 @@ from pydantic import BaseModel
|
|||||||
class LiteModel(BaseModel):
|
class LiteModel(BaseModel):
|
||||||
TABLE_NAME: str = None
|
TABLE_NAME: str = None
|
||||||
id: int = None
|
id: int = None
|
||||||
|
|
||||||
def dump(self, *args, **kwargs):
|
def dump(self, *args, **kwargs):
|
||||||
if pydantic.__version__ < "1.8.2":
|
if pydantic.__version__ < "1.8.2":
|
||||||
return self.dict(*args, **kwargs)
|
return self.dict(*args, **kwargs)
|
||||||
|
@ -6,10 +6,13 @@ import aiofiles
|
|||||||
import nonebot
|
import nonebot
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
|
from liteyuki.utils.base.resource import load_resources
|
||||||
|
|
||||||
require("nonebot_plugin_htmlrender")
|
require("nonebot_plugin_htmlrender")
|
||||||
|
|
||||||
from nonebot_plugin_htmlrender import *
|
from nonebot_plugin_htmlrender import *
|
||||||
|
|
||||||
|
|
||||||
async def template2html(
|
async def template2html(
|
||||||
template: str,
|
template: str,
|
||||||
templates: dict,
|
templates: dict,
|
||||||
@ -56,9 +59,11 @@ async def template2image(
|
|||||||
}
|
}
|
||||||
template_path = os.path.dirname(template)
|
template_path = os.path.dirname(template)
|
||||||
template_name = os.path.basename(template)
|
template_name = os.path.basename(template)
|
||||||
print(template_path, template_name)
|
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
|
# 重载资源
|
||||||
|
load_resources()
|
||||||
|
|
||||||
raw_html = await template_to_html(
|
raw_html = await template_to_html(
|
||||||
template_name=template_name,
|
template_name=template_name,
|
||||||
template_path=template_path,
|
template_path=template_path,
|
||||||
|
@ -3,6 +3,7 @@ aiofiles==23.2.1
|
|||||||
colored==2.2.4
|
colored==2.2.4
|
||||||
dash==2.16.1
|
dash==2.16.1
|
||||||
GitPython==3.1.42
|
GitPython==3.1.42
|
||||||
|
httpx==0.27.0
|
||||||
jieba==0.42.1
|
jieba==0.42.1
|
||||||
nb-cli==1.4.1
|
nb-cli==1.4.1
|
||||||
nonebot2[fastapi,httpx,websockets]==2.2.1
|
nonebot2[fastapi,httpx,websockets]==2.2.1
|
||||||
|