mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2025-06-04 02:26:11 +08:00
114 lines
3.4 KiB
Python
114 lines
3.4 KiB
Python
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING, Literal
|
|
|
|
import jinja2
|
|
from pydantic import BaseModel, root_validator
|
|
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
|
|
|
from nonebot_bison.theme.utils import convert_to_qr
|
|
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
|
|
|
|
if TYPE_CHECKING:
|
|
from nonebot_bison.post import Post
|
|
|
|
|
|
class CeobeInfo(BaseModel):
|
|
"""卡片的信息部分
|
|
|
|
datasource: 数据来源
|
|
|
|
time: 时间
|
|
"""
|
|
|
|
datasource: str
|
|
time: str
|
|
|
|
|
|
class CeoboContent(BaseModel):
|
|
"""卡片的内容部分
|
|
|
|
image: 图片链接
|
|
text: 文字内容
|
|
"""
|
|
|
|
image: str | None
|
|
text: str | None
|
|
|
|
@root_validator
|
|
def check(cls, values):
|
|
if values["image"] is None and values["text"] is None:
|
|
raise ValueError("image and text cannot be both None")
|
|
return values
|
|
|
|
|
|
class CeobeCard(BaseModel):
|
|
info: CeobeInfo
|
|
content: CeoboContent
|
|
qr: str | None
|
|
|
|
|
|
class CeobeCanteenTheme(Theme):
|
|
"""小刻食堂 分享卡片风格主题
|
|
|
|
需要安装`nonebot_plugin_htmlrender`插件
|
|
"""
|
|
|
|
name: Literal["ceobecanteen"] = "ceobecanteen"
|
|
need_browser: bool = True
|
|
|
|
template_path: Path = Path(__file__).parent / "templates"
|
|
template_name: str = "ceobe_canteen.html.jinja"
|
|
|
|
def parse(self, post: "Post") -> CeobeCard:
|
|
"""解析 Post 为 CeobeCard"""
|
|
if not post.nickname:
|
|
raise ThemeRenderUnsupportError("post.nickname is None")
|
|
if not post.timestamp:
|
|
raise ThemeRenderUnsupportError("post.timestamp is None")
|
|
info = CeobeInfo(
|
|
datasource=post.nickname, time=datetime.fromtimestamp(post.timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
|
)
|
|
|
|
head_pic = post.images[0] if post.images else None
|
|
if head_pic is not None and not isinstance(head_pic, str):
|
|
raise ThemeRenderUnsupportError("post.images[0] is not str")
|
|
|
|
content = CeoboContent(image=head_pic, text=post.content)
|
|
return CeobeCard(info=info, content=content, qr=convert_to_qr(post.url or "No URL"))
|
|
|
|
async def render(self, post: "Post") -> list[MessageSegmentFactory]:
|
|
ceobe_card = self.parse(post)
|
|
from nonebot_plugin_htmlrender import get_new_page
|
|
|
|
template_env = jinja2.Environment(
|
|
loader=jinja2.FileSystemLoader(self.template_path),
|
|
enable_async=True,
|
|
)
|
|
template = template_env.get_template(self.template_name)
|
|
html = await template.render_async(card=ceobe_card)
|
|
pages = {
|
|
"viewport": {"width": 1000, "height": 3000},
|
|
"base_url": self.template_path.as_uri(),
|
|
}
|
|
try:
|
|
async with get_new_page(**pages) as page:
|
|
await page.goto(self.template_path.as_uri())
|
|
await page.set_content(html)
|
|
await page.wait_for_timeout(1)
|
|
img_raw = await page.locator("#ceobecanteen-card").screenshot(
|
|
type="png",
|
|
)
|
|
except Exception as e:
|
|
raise ThemeRenderError(f"Render error: {e}") from e
|
|
msgs: list[MessageSegmentFactory] = [Image(img_raw)]
|
|
|
|
text = f"来源: {post.platform.name} {post.nickname or ''}\n"
|
|
if post.url:
|
|
text += f"详情: {post.url}"
|
|
msgs.append(Text(text))
|
|
|
|
if post.images:
|
|
msgs.extend(map(Image, post.images))
|
|
return msgs
|