Files
yu-zhi-ran/scripts/publisher.py
T
lt e1ba31afda 版本1.0.4 - 发布前准备
- 修复system.py缩进错误
- 优化前端页面样式(待重构)
- 改进API接口结构
- 完善文档和自动化脚本
- 平台基本功能稳定运行
2026-04-29 09:32:43 +08:00

218 lines
8.1 KiB
Python
Executable File
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.
#!/usr/bin/env python3
"""
多平台内容发布脚本
将「待发布」的文章发布到各平台(知乎/公众号/小红书/B站/头条号)
支持单个 topic 生成发布包模式(--topic-id
"""
import json, datetime, logging, sys, subprocess, time
from pathlib import Path
from typing import Dict, List
import argparse
PROJECT_ROOT = Path('/root/openclaw-workspace/projects/yu-zhi-ran')
sys.path.insert(0, str(PROJECT_ROOT))
DATA_DIR = PROJECT_ROOT / "automation" / "data"
TOPICS_FILE = DATA_DIR / "sustainability_topics.json"
RELEASES_DIR = DATA_DIR / "releases"
LOGS_DIR = PROJECT_ROOT / "automation" / "logs"
TODAY = datetime.datetime.now().strftime("%Y-%m-%d")
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOGS_DIR / f"publisher_{TODAY}.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# 命令行参数
parser = argparse.ArgumentParser(description='发布管理脚本')
parser.add_argument('--topic-id', help='仅处理指定 topic ID')
args = parser.parse_args()
# 平台配置
PLATFORMS = {
"zhihu": {"name": "知乎", "enabled": True, "template": "zhihu.html"},
"wechat": {"name": "微信公众号", "enabled": True, "template": "wechat.html"}, # 需手动授权
"xiaohongshu": {"name": "小红书", "enabled": True, "template": "xiaohongshu.html"},
"bilibili": {"name": "B站", "enabled": False, "template": "bilibili.html"}, # 规划中
"toutiao": {"name": "头条号", "enabled": False, "template": "toutiao.html"} # 规划中
}
def load_topics() -> List[Dict]:
with open(TOPICS_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
def save_topics(topics: List[Dict]):
with open(TOPICS_FILE, 'w', encoding='utf-8') as f:
json.dump(topics, f, ensure_ascii=False, indent=2)
def get_ready_topics() -> List[Dict]:
topics = load_topics()
ready = [t for t in topics if t.get('status') == '待发布']
ready.sort(key=lambda t: t.get('ready_at', ''), reverse=True) # 优先最新
return ready, topics
def publish_to_xiaohongshu(html_path: Path, topic: Dict) -> bool:
"""小红书:复制HTML到发布目录(供手动发布)"""
logger.info(f"准备小红书发布: {topic['id']}")
try:
publish_base = PROJECT_ROOT / "content" / "published"
dest_dir = publish_base / topic['id'] / "手动发布" / "小红书"
dest_dir.mkdir(parents=True, exist_ok=True)
dest_html = dest_dir / "文章.html"
import shutil
shutil.copy2(html_path, dest_html)
logger.info(f"✅ 小红书发布包就绪: {dest_dir}")
return True, str(dest_dir)
except Exception as e:
logger.error(f"小红书发布准备失败: {e}")
return False, None
def publish_to_zhihu(html_path: Path, topic: Dict) -> bool:
"""知乎:复制HTML到发布目录"""
logger.info(f"准备知乎发布: {topic['id']}")
try:
publish_base = PROJECT_ROOT / "content" / "published"
dest_dir = publish_base / topic['id'] / "手动发布" / "知乎"
dest_dir.mkdir(parents=True, exist_ok=True)
dest_html = dest_dir / "文章.html"
import shutil
shutil.copy2(html_path, dest_html)
logger.info(f"✅ 知乎发布包就绪: {dest_dir}")
return True, str(dest_dir)
except Exception as e:
logger.error(f"知乎发布准备失败: {e}")
return False, None
def publish_to_wechat(html_path: Path, topic: Dict) -> bool:
"""微信公众号:复制HTML到发布目录"""
logger.info(f"准备微信公众号发布: {topic['id']}")
try:
publish_base = PROJECT_ROOT / "content" / "published"
dest_dir = publish_base / topic['id'] / "手动发布" / "微信公众号"
dest_dir.mkdir(parents=True, exist_ok=True)
dest_html = dest_dir / "文章.html"
import shutil
shutil.copy2(html_path, dest_html)
logger.info(f"✅ 微信公众号发布包就绪: {dest_dir}")
return True, str(dest_dir)
except Exception as e:
logger.error(f"微信公众号发布准备失败: {e}")
return False, None
def publish_to_platform(platform: str, html_path: Path, topic: Dict) -> (bool, str):
"""生成平台发布包(人工发布)"""
if platform == "xiaohongshu":
return publish_to_xiaohongshu(html_path, topic)
elif platform == "zhihu":
return publish_to_zhihu(html_path, topic)
elif platform == "wechat":
return publish_to_wechat(html_path, topic)
else:
logger.warning(f"平台 {platform} 暂未支持")
return False, None
def main():
logger.info("=== 多平台内容发布包生成开始 ===")
ready, all_topics = get_ready_topics()
if not ready:
logger.info("没有待发布内容")
sys.exit(0)
# 如果指定了 topic-id,只处理该选题
if args.topic_id:
ready = [t for t in ready if t['id'] == args.topic_id]
if not ready:
logger.info(f"未找到指定 topic ID: {args.topic_id}")
sys.exit(0)
results = []
for topic in ready:
tid = topic['id']
title = topic.get('title', '')[:50]
release_date = topic.get('ready_at', TODAY)
release_dir = RELEASES_DIR / release_date
platform_urls = topic.get('platform_urls', {})
for platform, config in PLATFORMS.items():
if not config['enabled']:
continue
# 检查是否已发布过
if platform in platform_urls and platform_urls[platform]:
logger.info(f"跳过已发布: {tid} - {platform}")
continue
html_file = release_dir / platform / f"{platform}_{tid}_{platform}.html"
if not html_file.exists():
logger.warning(f"HTML文件不存在: {html_file}")
continue
# 生成发布包(不自动发布)
success, info = publish_to_platform(platform, html_file, topic)
if success:
results.append((tid, platform, info))
logger.info(f"{tid} 发布包已准备: {platform}")
else:
logger.error(f"{tid} 发布包准备失败: {platform}")
# 汇总报告
summary_file = LOGS_DIR / f"publisher_summary_{TODAY}.json"
summary = {
"date": TODAY,
"total_ready": len(ready),
"packages_generated": len(results),
"details": [{"topic_id": r[0], "platform": r[1], "path": r[2]} for r in results]
}
with open(summary_file, 'w', encoding='utf-8') as f:
json.dump(summary, f, ensure_ascii=False, indent=2)
# === 发送日报通知 ===
try:
# 统计发布数据(按话题去重)
unique_tids = set(r[0] for r in results)
platforms_set = set(r[1] for r in results)
notify_data = {
"task": "daily_summary",
"date": TODAY,
"published_count": len(unique_tids),
"platforms": list(platforms_set),
"publish_dir": str(PROJECT_ROOT / "content" / "published")
}
notify_file = LOGS_DIR / f"publisher_notify_{TODAY}.json"
with open(notify_file, 'w', encoding='utf-8') as f:
json.dump(notify_data, f, ensure_ascii=False, indent=2)
# 调用 notifier
notifier_script = PROJECT_ROOT / "scripts" / "wecom_notifier.py"
if notifier_script.exists():
subprocess.run(
[sys.executable, str(notifier_script), str(notify_file)],
cwd=str(PROJECT_ROOT),
capture_output=True,
text=True,
timeout=30
)
else:
logger.warning("Notifier script not found, skipping notification")
except Exception as e:
logger.error(f"发送日报通知失败: {e}")
# === 通知结束 ===
logger.info(f"📦 发布包生成完成: {len(results)} 个平台发布包已就绪")
print(f"PUBLISH_PACKAGES_READY: {len(results)} packages generated")
sys.exit(0)
if __name__ == "__main__":
main()