"""阴阳师等长公告的安全 HTML 截图渲染。""" import base64 import html import re from typing import Optional from pyppeteer import launch try: import bleach except ImportError: bleach = None ARTICLE_WIDTH = 900 ARTICLE_PADDING = 48 _ALLOWED_TAGS = [ "p", "br", "strong", "b", "em", "i", "u", "span", "div", "h1", "h2", "h3", "ul", "ol", "li", "blockquote", "hr", ] def _sanitize_article_html(content_html: str) -> str: """净化外部公告 HTML;缺少 bleach 时退化为安全的纯文本段落。""" if bleach is not None: return bleach.clean(content_html, tags=_ALLOWED_TAGS, attributes={}, strip=True) # 生产环境缺少可选依赖时,宁可丢失样式也不把不可信标签交给浏览器执行。 text = re.sub(r"(?is)<(script|style).*?>.*?", "", content_html) text = re.sub(r"(?i)", "\n", text) text = re.sub(r"(?i)", "\n", text) text = re.sub(r"<[^>]+>", "", text) return "

" + html.escape(text).replace("\n", "
") + "

" def build_article_document(content_html: str, title: str, published_at: Optional[str]) -> str: """净化上游公告 HTML,并包装为固定版式的截图页面。""" safe_content = _sanitize_article_html(content_html) safe_title = html.escape(title) safe_time = html.escape(published_at or "") return f"""
阴阳师维护公告

{safe_title}

发布时间:{safe_time}
{safe_content}
""" async def render_article_to_base64(content_html: str, title: str, published_at: Optional[str]) -> str: """使用无头 Chromium 渲染完整公告为一张长图。""" browser = None page = None try: browser = await launch( headless=True, args=["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu"], ) page = await browser.newPage() await page.setViewport({"width": ARTICLE_WIDTH, "height": 1200, "deviceScaleFactor": 1}) await page.setContent(build_article_document(content_html, title, published_at)) height = await page.evaluate("document.documentElement.scrollHeight") if height <= 0: raise ValueError("公告截图高度无效") image = await page.screenshot({"fullPage": True, "type": "png"}) return f"base64://{base64.b64encode(image).decode('ascii')}" finally: if page is not None: await page.close() if browser is not None: await browser.close()