mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 13:38:43 +00:00
[fix] 兼容 linux 的 pdf 生成情况
This commit is contained in:
@@ -278,9 +278,13 @@ class QQGroupDailyAnalysis(Star):
|
|||||||
yield event.plain_result("🔄 开始安装 PDF 功能依赖,请稍候...")
|
yield event.plain_result("🔄 开始安装 PDF 功能依赖,请稍候...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 使用模块化的PDF安装器
|
# 安装 pyppeteer
|
||||||
result = await PDFInstaller.install_pyppeteer(config_manager)
|
result = await PDFInstaller.install_pyppeteer(config_manager)
|
||||||
yield event.plain_result(result)
|
yield event.plain_result(result)
|
||||||
|
|
||||||
|
# 提供系统依赖安装指导
|
||||||
|
system_deps_result = await PDFInstaller.install_system_deps()
|
||||||
|
yield event.plain_result(system_deps_result)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"安装 PDF 依赖失败: {e}", exc_info=True)
|
logger.error(f"安装 PDF 依赖失败: {e}", exc_info=True)
|
||||||
|
|||||||
+107
-35
@@ -11,6 +11,7 @@ from pathlib import Path
|
|||||||
from astrbot.api import logger
|
from astrbot.api import logger
|
||||||
from .templates import HTMLTemplates
|
from .templates import HTMLTemplates
|
||||||
from ..visualization.activity_charts import ActivityVisualizer
|
from ..visualization.activity_charts import ActivityVisualizer
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
class ReportGenerator:
|
class ReportGenerator:
|
||||||
@@ -297,7 +298,7 @@ class ReportGenerator:
|
|||||||
# 尝试启动浏览器,如果 Chromium 不存在会自动下载
|
# 尝试启动浏览器,如果 Chromium 不存在会自动下载
|
||||||
logger.info("启动浏览器进行 PDF 转换")
|
logger.info("启动浏览器进行 PDF 转换")
|
||||||
|
|
||||||
# 配置浏览器启动参数,避免 Chromium 下载问题
|
# 配置浏览器启动参数,提高稳定性,避免意外关闭
|
||||||
launch_options = {
|
launch_options = {
|
||||||
'headless': True,
|
'headless': True,
|
||||||
'args': [
|
'args': [
|
||||||
@@ -307,61 +308,132 @@ class ReportGenerator:
|
|||||||
'--disable-gpu',
|
'--disable-gpu',
|
||||||
'--no-first-run',
|
'--no-first-run',
|
||||||
'--disable-extensions',
|
'--disable-extensions',
|
||||||
'--disable-default-apps'
|
'--disable-default-apps',
|
||||||
|
'--disable-background-timer-throttling',
|
||||||
|
'--disable-backgrounding-occluded-windows',
|
||||||
|
'--disable-renderer-backgrounding',
|
||||||
|
'--disable-features=TranslateUI',
|
||||||
|
'--disable-ipc-flooding-protection',
|
||||||
|
'--disable-background-networking',
|
||||||
|
'--enable-features=NetworkService,NetworkServiceInProcess',
|
||||||
|
'--force-color-profile=srgb',
|
||||||
|
'--metrics-recording-only',
|
||||||
|
'--disable-breakpad',
|
||||||
|
'--disable-component-extensions-with-background-pages',
|
||||||
|
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,AvoidUnnecessaryBeforeUnloadCheckSync',
|
||||||
|
'--enable-automation',
|
||||||
|
'--password-store=basic',
|
||||||
|
'--use-mock-keychain',
|
||||||
|
'--export-tagged-pdf'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 如果是 Windows 系统,尝试使用系统 Chrome
|
# 检测系统 Chrome/Chromium 路径
|
||||||
|
chrome_paths = []
|
||||||
|
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
# 常见的 Chrome 安装路径
|
# Windows 系统 Chrome 安装路径
|
||||||
chrome_paths = [
|
chrome_paths = [
|
||||||
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
||||||
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
|
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
|
||||||
r"C:\Users\{}\AppData\Local\Google\Chrome\Application\chrome.exe".format(os.environ.get('USERNAME', '')),
|
r"C:\Users\{}\AppData\Local\Google\Chrome\Application\chrome.exe".format(os.environ.get('USERNAME', '')),
|
||||||
]
|
]
|
||||||
|
elif sys.platform.startswith('linux'):
|
||||||
|
# Linux 系统 Chrome/Chromium 路径
|
||||||
|
chrome_paths = [
|
||||||
|
'/usr/bin/google-chrome',
|
||||||
|
'/usr/bin/google-chrome-stable',
|
||||||
|
'/usr/bin/chromium',
|
||||||
|
'/usr/bin/chromium-browser',
|
||||||
|
'/snap/bin/chromium',
|
||||||
|
]
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
# macOS 系统 Chrome 路径
|
||||||
|
chrome_paths = [
|
||||||
|
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||||
|
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||||
|
]
|
||||||
|
|
||||||
for chrome_path in chrome_paths:
|
# 查找可用的浏览器
|
||||||
if Path(chrome_path).exists():
|
for chrome_path in chrome_paths:
|
||||||
launch_options['executablePath'] = chrome_path
|
if Path(chrome_path).exists():
|
||||||
logger.info(f"使用系统 Chrome: {chrome_path}")
|
launch_options['executablePath'] = chrome_path
|
||||||
break
|
logger.info(f"使用系统浏览器: {chrome_path}")
|
||||||
|
break
|
||||||
|
|
||||||
browser = await launch(**launch_options)
|
# 尝试启动浏览器,最多重试3次
|
||||||
page = await browser.newPage()
|
max_retries = 3
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
logger.info(f"尝试启动浏览器 (第 {attempt + 1} 次)")
|
||||||
|
browser = await launch(**launch_options)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"第 {attempt + 1} 次启动浏览器失败: {e}")
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
await asyncio.sleep(2) # 等待2秒后重试
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
# 设置页面内容 (pyppeteer 1.0.2 版本的 API)
|
|
||||||
await page.setContent(html_content)
|
|
||||||
# 等待页面加载完成
|
|
||||||
try:
|
try:
|
||||||
await page.waitForSelector('body', {'timeout': 10000})
|
page = await browser.newPage()
|
||||||
except Exception:
|
|
||||||
# 如果等待失败,继续执行(可能页面已经加载完成)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 导出 PDF
|
# 设置页面内容 (pyppeteer 1.0.2 版本的 API)
|
||||||
await page.pdf({
|
await page.setContent(html_content)
|
||||||
'path': output_path,
|
|
||||||
'format': 'A4',
|
# 等待页面加载完成
|
||||||
'printBackground': True,
|
try:
|
||||||
'margin': {
|
await page.waitForSelector('body', {'timeout': 15000})
|
||||||
'top': '10mm',
|
except Exception:
|
||||||
'right': '10mm',
|
# 如果等待失败,继续执行(可能页面已经加载完成)
|
||||||
'bottom': '10mm',
|
logger.warning("等待页面加载超时,继续执行")
|
||||||
'left': '10mm'
|
pass
|
||||||
},
|
|
||||||
'scale': 0.8
|
|
||||||
})
|
|
||||||
|
|
||||||
await browser.close()
|
# 等待额外时间确保页面完全渲染
|
||||||
logger.info(f"PDF 生成成功: {output_path}")
|
await asyncio.sleep(2)
|
||||||
return True
|
|
||||||
|
# 导出 PDF
|
||||||
|
await page.pdf({
|
||||||
|
'path': output_path,
|
||||||
|
'format': 'A4',
|
||||||
|
'printBackground': True,
|
||||||
|
'margin': {
|
||||||
|
'top': '10mm',
|
||||||
|
'right': '10mm',
|
||||||
|
'bottom': '10mm',
|
||||||
|
'left': '10mm'
|
||||||
|
},
|
||||||
|
'scale': 0.8,
|
||||||
|
'displayHeaderFooter': False,
|
||||||
|
'preferCSSPageSize': True
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info(f"PDF 生成成功: {output_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# 确保浏览器被关闭
|
||||||
|
try:
|
||||||
|
await browser.close()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"关闭浏览器时出错: {e}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
if "Chromium downloadable not found" in error_msg:
|
if "Chromium downloadable not found" in error_msg:
|
||||||
logger.error("Chromium 下载失败,建议安装 pyppeteer2 或使用系统 Chrome")
|
logger.error("Chromium 下载失败,建议安装系统 Chrome/Chromium")
|
||||||
|
logger.info("💡 Linux 系统建议: sudo apt-get install chromium-browser 或 sudo yum install chromium")
|
||||||
elif "No usable sandbox" in error_msg:
|
elif "No usable sandbox" in error_msg:
|
||||||
logger.error("沙盒权限问题,已尝试禁用沙盒")
|
logger.error("沙盒权限问题,已尝试禁用沙盒")
|
||||||
|
elif "Connection refused" in error_msg or "connect" in error_msg.lower():
|
||||||
|
logger.error("浏览器连接失败,请检查系统资源或尝试重启")
|
||||||
|
elif "executablePath" in error_msg and "not found" in error_msg:
|
||||||
|
logger.error("未找到系统浏览器,请安装 Chrome 或 Chromium")
|
||||||
|
logger.info("💡 安装建议: sudo apt-get install chromium-browser (Ubuntu/Debian) 或 sudo yum install chromium (CentOS/RHEL)")
|
||||||
|
elif "Browser closed unexpectedly" in error_msg:
|
||||||
|
logger.error("浏览器意外关闭,可能是由于内存不足或系统资源限制")
|
||||||
|
logger.info("💡 建议: 检查系统内存,或重启 AstrBot 后重试")
|
||||||
else:
|
else:
|
||||||
logger.error(f"HTML 转 PDF 失败: {e}")
|
logger.error(f"HTML 转 PDF 失败: {e}")
|
||||||
|
logger.info("💡 可以尝试使用 /安装PDF 命令重新安装依赖,或检查系统日志获取更多信息")
|
||||||
return False
|
return False
|
||||||
@@ -47,6 +47,45 @@ class PDFInstaller:
|
|||||||
logger.error(f"安装 pyppeteer 时出错: {e}")
|
logger.error(f"安装 pyppeteer 时出错: {e}")
|
||||||
return f"❌ 安装过程中出错: {str(e)}"
|
return f"❌ 安装过程中出错: {str(e)}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def install_system_deps():
|
||||||
|
"""安装系统依赖(仅提供指导)"""
|
||||||
|
try:
|
||||||
|
logger.info("提供系统依赖安装指导...")
|
||||||
|
|
||||||
|
if sys.platform.startswith('linux'):
|
||||||
|
return """💡 Linux 系统依赖安装指导:
|
||||||
|
|
||||||
|
1. 安装 Chrome/Chromium 浏览器:
|
||||||
|
Ubuntu/Debian: sudo apt-get install chromium-browser
|
||||||
|
CentOS/RHEL: sudo yum install chromium
|
||||||
|
Arch Linux: sudo pacman -S chromium
|
||||||
|
|
||||||
|
2. 安装完成后,重启 AstrBot
|
||||||
|
|
||||||
|
3. 如果仍然有问题,请检查系统日志"""
|
||||||
|
|
||||||
|
elif sys.platform.startswith('win'):
|
||||||
|
return """💡 Windows 系统依赖安装指导:
|
||||||
|
|
||||||
|
1. 安装 Google Chrome 浏览器
|
||||||
|
2. 重启 AstrBot
|
||||||
|
3. 如果仍然有问题,请检查系统日志"""
|
||||||
|
|
||||||
|
elif sys.platform.startswith('darwin'):
|
||||||
|
return """💡 macOS 系统依赖安装指导:
|
||||||
|
|
||||||
|
1. 安装 Google Chrome 浏览器
|
||||||
|
2. 重启 AstrBot
|
||||||
|
3. 如果仍然有问题,请检查系统日志"""
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "💡 请安装 Chrome 或 Chromium 浏览器,然后重启 AstrBot"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"提供系统依赖指导时出错: {e}")
|
||||||
|
return f"❌ 提供指导时出错: {str(e)}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pdf_status(config_manager) -> str:
|
def get_pdf_status(config_manager) -> str:
|
||||||
"""获取PDF功能状态"""
|
"""获取PDF功能状态"""
|
||||||
|
|||||||
Reference in New Issue
Block a user