mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
🎨 **渲染服务重构** - 统一图片渲染入口,引入主题系统支持 - 优化Jinja2环境管理,支持主题覆盖和插件命名空间 - 新增UI缓存机制和主题重载功能 ✨ **通用UI组件系统** - 新增 zhenxun.ui 模块,提供数据模型和构建器 - 引入BaseBuilder基类,支持链式调用 - 新增多种UI构建器:InfoCard, Markdown, Table, Chart, Layout等 - 新增通用组件:Divider, Badge, ProgressBar, UserInfoBlock 🔄 **插件迁移** - 迁移9个内置插件至新渲染系统 - 移除各插件中分散的图片生成工具 - 优化数据处理和渲染逻辑 💥 **Breaking Changes** - 移除旧的图片渲染接口和模板路径 - TEMPLATE_PATH 更名为 THEMES_PATH - 插件需适配新的RendererService和zhenxun.ui模块
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
from datetime import datetime
|
|
import time
|
|
|
|
from nonebot_plugin_htmlrender import template_to_pic
|
|
from pydantic import BaseModel
|
|
from tortoise.expressions import Q
|
|
|
|
from zhenxun.configs.config import BotConfig
|
|
from zhenxun.configs.path_config import THEMES_PATH
|
|
from zhenxun.models.goods_info import GoodsInfo
|
|
from zhenxun.utils._build_image import BuildImage
|
|
|
|
from .config import ICON_PATH
|
|
|
|
|
|
class GoodsItem(BaseModel):
|
|
goods_list: list[dict]
|
|
"""商品列表"""
|
|
partition: str
|
|
"""分区名称"""
|
|
|
|
|
|
def get_limit_time(end_time: int):
|
|
now = int(time.time())
|
|
if now > end_time:
|
|
return None
|
|
current_datetime = datetime.fromtimestamp(now)
|
|
end_datetime = datetime.fromtimestamp(end_time)
|
|
time_difference = end_datetime - current_datetime
|
|
total_seconds = time_difference.total_seconds()
|
|
hours = int(total_seconds // 3600)
|
|
minutes = int((total_seconds % 3600) // 60)
|
|
return f"{hours}:{minutes}"
|
|
|
|
|
|
def get_discount(price: int, discount: float):
|
|
return None if discount == 1.0 else int(price * discount)
|
|
|
|
|
|
async def html_image() -> bytes:
|
|
"""构建图片"""
|
|
goods_list = (
|
|
await GoodsInfo.filter(
|
|
Q(goods_limit_time__gte=time.time()) | Q(goods_limit_time=0)
|
|
)
|
|
.annotate()
|
|
.order_by("id")
|
|
.all()
|
|
)
|
|
partition_dict: dict[str, list[dict]] = {}
|
|
for idx, goods in enumerate(goods_list):
|
|
if not goods.partition:
|
|
goods.partition = "默认分区"
|
|
if goods.partition not in partition_dict:
|
|
partition_dict[goods.partition] = []
|
|
icon = None
|
|
if goods.icon:
|
|
path = ICON_PATH / goods.icon
|
|
if path.exists():
|
|
icon = (
|
|
"data:image/png;base64,"
|
|
f"{BuildImage.open(ICON_PATH / goods.icon).pic2bs4()[9:]}"
|
|
)
|
|
partition_dict[goods.partition].append(
|
|
{
|
|
"id": idx + 1,
|
|
"price": goods.goods_price,
|
|
"discount_price": get_discount(goods.goods_price, goods.goods_discount),
|
|
"limit_time": get_limit_time(goods.goods_limit_time),
|
|
"daily_limit": goods.daily_limit or "∞",
|
|
"name": goods.goods_name,
|
|
"icon": icon,
|
|
"description": goods.goods_description,
|
|
}
|
|
)
|
|
data_list = [
|
|
GoodsItem(goods_list=value, partition=partition)
|
|
for partition, value in partition_dict.items()
|
|
]
|
|
template_dir = THEMES_PATH / "default" / "templates" / "shop"
|
|
return await template_to_pic(
|
|
template_path=str(template_dir.absolute()),
|
|
template_name="main.html",
|
|
templates={"name": BotConfig.self_nickname, "data_list": data_list},
|
|
pages={
|
|
"viewport": {"width": 850, "height": 1024},
|
|
"base_url": f"file://{THEMES_PATH.parent.absolute()}",
|
|
},
|
|
wait=2,
|
|
)
|