Files
DanDingNoneBot/danding_bot/plugins/chatai/screenshot.py
Mr.Xia c01338f496 refactor(plugins): comprehensive code review - ~35 fixes across 14 plugins
Phase 1 - Plugin code review (14/14 plugins):
- Security: 3x token leak in print→logger.debug, Bearer prefix handling
- Bug: bare except→specific exceptions, HorseState type safety, sync→async
- Critical: response_model undefined, route dead code, sync blocking event loop
- Quality: 11x print()→logger, variable name shadowing, consistent logging

Phase 2 - Deep analysis:
- Fix: payout int truncation→max(1, round(amount*odds))
- Fix: room_store get_lock race condition→dict.setdefault()
- Verify: data_manager f-string SQL is safe (uses ? placeholders)

Infrastructure: review reports generated for all plugins.
2026-05-09 23:22:28 +08:00

174 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import html as html_module
import markdown
from nonebot import logger
async def markdown_to_image(markdown_text: str, output_path: str, browser=None):
"""将 Markdown 转换为 HTML 并使用 Puppeteer 截图。"""
page = None
should_close_browser = False
try:
# 转义用户输入中的HTML特殊字符防止XSS
safe_text = html_module.escape(markdown_text)
html_content = markdown.markdown(safe_text)
# 使用传入的浏览器实例或创建新的
if browser is None:
from pyppeteer import launch
browser = await launch(headless=True, args=['--no-sandbox', '--disable-setuid-sandbox'])
should_close_browser = True
page = await browser.newPage()
# 设置页面样式,使内容更美观
await page.setContent(f"""
<html>
<head>
<style>
body {{
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
line-height: 1.6;
padding: 30px;
max-width: 800px;
margin: 0 auto;
background-color: transparent;
color: #333;
}}
.container {{
background-color: #ffffff;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 25px;
overflow: hidden;
}}
p {{
margin-bottom: 16px;
}}
code {{
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
background-color: #f5f7f9;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
}}
pre {{
background-color: #f5f7f9;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 20px 0;
}}
pre code {{
background-color: transparent;
padding: 0;
}}
h1, h2, h3, h4, h5, h6 {{
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}}
h1 {{
font-size: 1.8em;
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
}}
h2 {{
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
}}
blockquote {{
padding: 0 1em;
color: #6a737d;
border-left: 0.25em solid #dfe2e5;
margin: 16px 0;
}}
ul, ol {{
padding-left: 2em;
margin-bottom: 16px;
}}
img {{
max-width: 100%;
border-radius: 8px;
}}
a {{
color: #0366d6;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
table {{
border-collapse: collapse;
width: 100%;
margin: 16px 0;
}}
table th, table td {{
padding: 8px 12px;
border: 1px solid #dfe2e5;
}}
table th {{
background-color: #f6f8fa;
}}
</style>
</head>
<body>
<div class="container">
{html_content}
</div>
</body>
</html>
""")
# 等待内容渲染完成
await asyncio.sleep(0.5)
# 获取内容尺寸并设置视口
dimensions = await page.evaluate('''() => {
const container = document.querySelector('.container');
return {
width: container.offsetWidth + 60, // 加上 body 的 padding
height: container.offsetHeight + 60
}
}''')
# 设置视口大小
await page.setViewport({
'width': dimensions['width'],
'height': dimensions['height'],
'deviceScaleFactor': 2 # 提高图片清晰度
})
# 截图,使用透明背景
await page.screenshot({
'path': output_path,
'omitBackground': True, # 透明背景
'clip': {
'x': 0,
'y': 0,
'width': dimensions['width'],
'height': dimensions['height']
}
})
# 关闭页面
await page.close()
# 如果是我们创建的浏览器实例,则关闭它
if should_close_browser:
await browser.close()
except Exception as e:
# 确保资源被释放
if page is not None:
try:
await page.close()
except Exception:
pass
if should_close_browser and browser is not None:
try:
await browser.close()
except Exception:
pass
raise # 重新抛出异常以便上层处理