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'
]*>(.*?)
', 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()