179 lines
6.8 KiB
Python
179 lines
6.8 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import subprocess
|
|||
|
|
from typing import Optional, Callable
|
|||
|
|
|
|||
|
|
from config_loader import get_config
|
|||
|
|
from image_uploader import get_wechat_access_token, process_html_images
|
|||
|
|
from md_to_wechat import convert_markdown_to_wechat
|
|||
|
|
from wechat_publisher import publish_to_wechat
|
|||
|
|
from markdown_editor import WechatMarkdownEditor, ThemeManager
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _now_datestr() -> str:
|
|||
|
|
from datetime import datetime
|
|||
|
|
return datetime.now().strftime("%Y-%m-%d")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_clipboard_text() -> str:
|
|||
|
|
"""获取剪贴板文本内容"""
|
|||
|
|
result = subprocess.run(
|
|||
|
|
["powershell", "-command", "Get-Clipboard"],
|
|||
|
|
capture_output=True
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
return result.stdout.decode('utf-8').strip()
|
|||
|
|
except UnicodeDecodeError:
|
|||
|
|
try:
|
|||
|
|
return result.stdout.decode('gbk').strip()
|
|||
|
|
except UnicodeDecodeError:
|
|||
|
|
return result.stdout.decode('gb2312', errors='ignore').strip()
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Step2Converter:
|
|||
|
|
"""
|
|||
|
|
Step 2: 获取剪贴板内容,转换为公众号格式,推送到草稿箱
|
|||
|
|
参考: docs/step2.md
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, log_callback: Optional[Callable[[str], None]] = None, css_scheme_id: Optional[str] = None):
|
|||
|
|
"""
|
|||
|
|
初始化 Step2Converter
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
log_callback: 日志回调函数,用于将日志输出到 GUI
|
|||
|
|
css_scheme_id: CSS 样式方案 ID,可选
|
|||
|
|
"""
|
|||
|
|
self.log_callback = log_callback
|
|||
|
|
self.css_scheme_id = css_scheme_id
|
|||
|
|
self.theme_manager = ThemeManager()
|
|||
|
|
|
|||
|
|
def _log(self, message: str):
|
|||
|
|
"""输出日志,支持回调到 GUI"""
|
|||
|
|
print(message)
|
|||
|
|
if self.log_callback:
|
|||
|
|
self.log_callback(message)
|
|||
|
|
|
|||
|
|
def run(self) -> bool:
|
|||
|
|
"""执行 Step 2 流程,成功返回 True"""
|
|||
|
|
|
|||
|
|
# 1) 获取剪贴板内容
|
|||
|
|
self._log("[Step2] 正在获取剪贴板内容...")
|
|||
|
|
md_content = get_clipboard_text()
|
|||
|
|
|
|||
|
|
if not md_content:
|
|||
|
|
self._log("[Step2] 警告:剪贴板为空,无法继续")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
self._log(f"[Step2] 获取到 Markdown 内容,长度: {len(md_content)} 字符")
|
|||
|
|
|
|||
|
|
# 2) 转换为公众号文章格式
|
|||
|
|
self._log("[Step2] 正在转换为公众号 HTML 格式...")
|
|||
|
|
|
|||
|
|
# 如果指定了 CSS 方案,先应用
|
|||
|
|
if self.css_scheme_id:
|
|||
|
|
scheme = self.theme_manager.get_css_scheme(self.css_scheme_id)
|
|||
|
|
if scheme:
|
|||
|
|
self._log(f"[Step2] 应用 CSS 方案: {scheme.name}")
|
|||
|
|
self.theme_manager.apply_css_scheme(self.css_scheme_id)
|
|||
|
|
else:
|
|||
|
|
self._log(f"[Step2] 警告:CSS 方案 {self.css_scheme_id} 不存在,使用默认设置")
|
|||
|
|
|
|||
|
|
# 使用 Markdown 编辑器进行转换(与编辑器中的方式一致)
|
|||
|
|
# 创建编辑器实例,它会自动加载当前配置(包括已应用的 CSS 方案)
|
|||
|
|
editor = WechatMarkdownEditor()
|
|||
|
|
html_content = editor.render(md_content)
|
|||
|
|
|
|||
|
|
# 提取标题(从 HTML 内容中提取 h1 标签)
|
|||
|
|
import re
|
|||
|
|
title_match = re.search(r'<h1[^>]*>(.*?)</h1>', html_content)
|
|||
|
|
title = title_match.group(1) if title_match else "无标题"
|
|||
|
|
|
|||
|
|
html_content = re.sub(r'\n\s*\n+', '\n', html_content)
|
|||
|
|
html_content = re.sub(r'>\s*\n\s*<', '><', html_content)
|
|||
|
|
|
|||
|
|
self._log(f"[Step2] 转换完成,标题: {title}")
|
|||
|
|
self._log(f"[Step2] HTML 内容长度: {len(html_content)} 字符")
|
|||
|
|
|
|||
|
|
# 3) 处理图片 - 将外部图片上传到微信并替换 URL
|
|||
|
|
cover_image_url: Optional[str] = None
|
|||
|
|
config = get_config()
|
|||
|
|
if config.wechat_appid and config.wechat_appsecret:
|
|||
|
|
self._log("[Step2] 正在处理图片,将外部图片上传到微信服务器...")
|
|||
|
|
access_token = get_wechat_access_token(config.wechat_appid, config.wechat_appsecret)
|
|||
|
|
if access_token:
|
|||
|
|
html_content, cover_image_url = process_html_images(html_content, access_token, external_domain="yidaima.cn")
|
|||
|
|
self._log("[Step2] 图片处理完成")
|
|||
|
|
if cover_image_url:
|
|||
|
|
self._log(f"[Step2] 封面图: {cover_image_url}")
|
|||
|
|
else:
|
|||
|
|
self._log("[Step2] 警告:无法获取微信 access_token,跳过图片上传")
|
|||
|
|
|
|||
|
|
# 4) 保存 HTML 到文件
|
|||
|
|
html_dir = os.path.join(".", "data", "html")
|
|||
|
|
os.makedirs(html_dir, exist_ok=True)
|
|||
|
|
html_file = os.path.join(html_dir, "wechat.html")
|
|||
|
|
with open(html_file, "w", encoding="utf-8") as f:
|
|||
|
|
f.write(html_content)
|
|||
|
|
self._log(f"[Step2] HTML 已保存到: {html_file}")
|
|||
|
|
|
|||
|
|
# 5) 推送到公众号草稿箱
|
|||
|
|
self._log("[Step2] 准备推送到公众号草稿箱...")
|
|||
|
|
wechat_appid = config.wechat_appid
|
|||
|
|
wechat_appsecret = config.wechat_appsecret
|
|||
|
|
|
|||
|
|
if wechat_appid and wechat_appsecret:
|
|||
|
|
self._log("[Step2] 正在推送到公众号草稿箱...")
|
|||
|
|
|
|||
|
|
# 优先使用第二张图片作为封面,否则使用默认封面
|
|||
|
|
if cover_image_url:
|
|||
|
|
self._log(f"[Step2] 使用第二张图片作为封面: {cover_image_url}")
|
|||
|
|
success = publish_to_wechat(
|
|||
|
|
title=title,
|
|||
|
|
content=html_content,
|
|||
|
|
appid=wechat_appid,
|
|||
|
|
appsecret=wechat_appsecret,
|
|||
|
|
thumb_image_url=cover_image_url
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
# 使用默认封面图片
|
|||
|
|
thumb_path = r"c:\Users\南音\Desktop\yidaima\assets\img\bg.jpg"
|
|||
|
|
self._log(f"[Step2] 使用默认封面图片: {thumb_path}")
|
|||
|
|
success = publish_to_wechat(
|
|||
|
|
title=title,
|
|||
|
|
content=html_content,
|
|||
|
|
appid=wechat_appid,
|
|||
|
|
appsecret=wechat_appsecret,
|
|||
|
|
thumb_image_path=thumb_path
|
|||
|
|
)
|
|||
|
|
if success:
|
|||
|
|
self._log("[Step2] 公众号草稿推送成功!")
|
|||
|
|
else:
|
|||
|
|
self._log("[Step2] 公众号草稿推送失败!")
|
|||
|
|
else:
|
|||
|
|
self._log("[Step2] 提示:未配置微信公众号,跳过公众号推送")
|
|||
|
|
self._log("[Step2] 如需推送,请编辑 config.yaml 文件:")
|
|||
|
|
self._log("[Step2] wechat:")
|
|||
|
|
self._log("[Step2] appid: \"your_appid\"")
|
|||
|
|
self._log("[Step2] appsecret: \"your_appsecret\"")
|
|||
|
|
self._log("[Step2] 或使用环境变量覆盖:")
|
|||
|
|
self._log("[Step2] set WECHAT_APPID=your_appid")
|
|||
|
|
self._log("[Step2] set WECHAT_APPSECRET=your_appsecret")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""单独运行 Step2"""
|
|||
|
|
step2 = Step2Converter()
|
|||
|
|
try:
|
|||
|
|
step2.run()
|
|||
|
|
print("[INFO] Step2 执行成功!")
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[ERROR] Step2 执行失败: {e}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|