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, project_name: Optional[str] = None): """ 初始化 Step2Converter Args: log_callback: 日志回调函数,用于将日志输出到 GUI css_scheme_id: CSS 样式方案 ID,可选 project_name: 项目名称,可选,用于生成文章标题 """ self.log_callback = log_callback self.css_scheme_id = css_scheme_id self.project_name = project_name 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 "无标题" # 如果提供了项目名称,使用项目名称去掉编号作为标题,并添加作者 if self.project_name: # 去掉编号,如"【A167】xxx" -> "xxx" match = re.search(r'】(.+)', self.project_name) if match: title = match.group(1) else: title = self.project_name # 添加作者 - 通过 publish_to_wechat 传入 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, author="南音" ) 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, author="南音" ) 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()