Skip to content

Commit 8b787f7

Browse files
committed
feat: experimental htmlkit
1 parent 385e211 commit 8b787f7

File tree

7 files changed

+841
-484
lines changed

7 files changed

+841
-484
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:alpine AS base
1+
FROM python:3.13-alpine AS base
22
WORKDIR /app
33

44

pdm.lock

Lines changed: 480 additions & 463 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ name = ""
33
version = "0.1.0"
44
description = "Twelve"
55
readme = "README.md"
6-
requires-python = "~=3.12"
6+
requires-python = "~=3.13"
77
dependencies = [
88
"backoff~=2.2",
99
"nonebot2[fastapi,httpx,websockets]~=2.4",
10-
"nonebot-adapter-milky~=0.5.0rc3",
11-
"nonebot-plugin-alconna~=0.59",
10+
"nonebot-adapter-milky~=1.0",
11+
"nonebot-plugin-alconna~=0.60",
1212
"nonebot-plugin-apscheduler~=0.5",
13+
"nonebot-plugin-htmlkit~=0.1.0rc4",
1314
"nonebot-plugin-htmlrender~=0.6",
1415
"nonebot-plugin-orm[sqlite]~=0.8",
1516
"nonebot-plugin-uninfo @ https://github.com/RF-Tar-Railt/nonebot-plugin-uninfo/archive/5b87d916af09d6a878039d3445193efda2ed1af3.zip",
@@ -30,6 +31,7 @@ adapters = [
3031
plugins = [
3132
"nonebot_plugin_alconna",
3233
"nonebot_plugin_apscheduler",
34+
"nonebot_plugin_htmlkit",
3335
"nonebot_plugin_htmlrender",
3436
"nonebot_plugin_orm",
3537
"nonebot_plugin_uninfo",

src/plugins/bilibili/plugins/dynamic/__init__.py

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from asyncio import gather
22
from contextlib import asynccontextmanager
3+
from importlib.resources import as_file, files
34
from queue import PriorityQueue
45
from typing import Annotated, Any, AsyncGenerator
56

@@ -11,6 +12,7 @@
1112
from nonebot.plugin import PluginMetadata
1213
from nonebot_plugin_alconna import Alconna, Image, Subcommand, UniMessage, on_alconna
1314
from nonebot_plugin_apscheduler import scheduler
15+
from nonebot_plugin_htmlkit import template_to_pic
1416
from nonebot_plugin_htmlrender.browser import get_browser
1517
from nonebot_plugin_orm import async_scoped_session, get_session
1618
from nonebot_plugin_uninfo import MEMBER
@@ -21,6 +23,7 @@
2123
from .....utils import run_task, send_message
2224
from ... import plugin_config as bilibili_config
2325
from ...utils import UID_ARG, get_share_click, handle_error, raise_for_status
26+
from . import templates
2427
from .config import Config
2528
from .models import Dynamic, Dynamics, Subscription
2629

@@ -45,6 +48,10 @@
4548
)
4649

4750

51+
async def img_fetch_fn(url: str) -> bytes:
52+
return (await client.get(url + "@.avif")).content
53+
54+
4855
class Cache:
4956
data: set[str]
5057
queue: PriorityQueue[str]
@@ -82,7 +89,19 @@ async def get_dynamics(page: int = 1) -> Dynamics:
8289
"type": "all",
8390
"page": page,
8491
"features": ",".join(
85-
("itemOpusStyle", "listOnlyfans", "opusBigCover", "onlyfansVote")
92+
(
93+
"itemOpusStyle",
94+
"listOnlyfans",
95+
"opusBigCover",
96+
"onlyfansVote",
97+
"decorationCard",
98+
"onlyfansAssetsV2",
99+
"forwardListHidden",
100+
"ugcDelete",
101+
"onlyfansQaCard",
102+
"commentsNewVersion",
103+
"avatarAutoTheme",
104+
)
86105
),
87106
},
88107
)
@@ -163,7 +182,7 @@ async def render_screenshot(id_str: str) -> bytes:
163182
)
164183
await page.add_style_tag(
165184
content="""
166-
* {
185+
body {
167186
font-family: "LXGW ZhenKai GB", "LXGW WenKai GB", sans-serif !important;
168187
}
169188
@@ -198,15 +217,37 @@ async def render_screenshot(id_str: str) -> bytes:
198217
async def broadcast(dynamics: list[Dynamic]):
199218
async with get_session() as session:
200219
for dynamic in dynamics:
201-
screenshot, url, subs = await gather(
202-
render_screenshot(dynamic["id_str"]),
203-
get_share_click(dynamic["id_str"], "dynamic", "dt.dt-detail.0.0.pv"),
204-
session.scalars(
205-
select(Subscription).where(
206-
Subscription.uid == dynamic["modules"]["module_author"]["mid"]
207-
)
208-
),
209-
)
220+
with as_file(files(templates)) as templates_path:
221+
screenshot, url, subs = await gather(
222+
(
223+
template_to_pic(
224+
str(templates_path),
225+
"draw.html.j2",
226+
dynamic,
227+
max_width=360 * 3,
228+
device_height=640 * 3,
229+
img_fetch_fn=img_fetch_fn,
230+
allow_refit=False,
231+
image_format="jpeg",
232+
jpeg_quality=80,
233+
)
234+
if dynamic["type"] == "DYNAMIC_TYPE_WORD"
235+
or (
236+
dynamic["type"] == "DYNAMIC_TYPE_DRAW"
237+
and not dynamic["modules"]["module_dynamic"]["additional"]
238+
)
239+
else render_screenshot(dynamic["id_str"])
240+
),
241+
get_share_click(
242+
dynamic["id_str"], "dynamic", "dt.dt-detail.0.0.pv"
243+
),
244+
session.scalars(
245+
select(Subscription).where(
246+
Subscription.uid
247+
== dynamic["modules"]["module_author"]["mid"]
248+
)
249+
),
250+
)
210251

211252
msg = plugin_config.template.format(
212253
name=dynamic["modules"]["module_author"]["name"],
@@ -310,16 +351,52 @@ async def _(id_str: str):
310351
dynamic = raise_for_status(
311352
await client.get(
312353
"/polymer/web-dynamic/v1/detail",
313-
params={"id": id_str, "features": "itemOpusStyle"},
354+
params={
355+
"id": id_str,
356+
"features": ",".join(
357+
(
358+
"itemOpusStyle",
359+
"listOnlyfans",
360+
"opusBigCover",
361+
"onlyfansVote",
362+
"decorationCard",
363+
"onlyfansAssetsV2",
364+
"forwardListHidden",
365+
"ugcDelete",
366+
"onlyfansQaCard",
367+
"commentsNewVersion",
368+
"avatarAutoTheme",
369+
)
370+
),
371+
},
314372
)
315373
)["item"]
316374
except Exception:
317375
await handle_error("获取动态信息失败")
318376

319-
screenshot, url = await gather(
320-
render_screenshot(id_str),
321-
get_share_click(id_str, "dynamic", "dt.dt-detail.0.0.pv"),
322-
)
377+
with as_file(files(templates)) as templates_path:
378+
screenshot, url = await gather(
379+
(
380+
template_to_pic(
381+
str(templates_path),
382+
"draw.html.j2",
383+
dynamic,
384+
max_width=360 * 3,
385+
device_height=640 * 3,
386+
img_fetch_fn=img_fetch_fn,
387+
allow_refit=False,
388+
image_format="jpeg",
389+
jpeg_quality=80,
390+
)
391+
if dynamic["type"] == "DYNAMIC_TYPE_WORD"
392+
or (
393+
dynamic["type"] == "DYNAMIC_TYPE_DRAW"
394+
and not dynamic["modules"]["module_dynamic"]["additional"]
395+
)
396+
else render_screenshot(dynamic["id_str"])
397+
),
398+
get_share_click(id_str, "dynamic", "dt.dt-detail.0.0.pv"),
399+
)
323400
await plugin_config.template.format(
324401
name=dynamic["modules"]["module_author"]["name"],
325402
action=dynamic["modules"]["module_author"]["pub_action"]

src/plugins/bilibili/plugins/dynamic/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TypedDict
1+
from typing import Any, TypedDict
22

33
from nonebot_plugin_orm import Model
44
from nonebot_plugin_uninfo.orm import SceneModel
@@ -14,6 +14,7 @@ class ModuleAuthor(TypedDict):
1414

1515
class Modules(TypedDict):
1616
module_author: ModuleAuthor
17+
module_dynamic: dict[str, Any]
1718

1819

1920
class Dynamic(TypedDict):

0 commit comments

Comments
 (0)