356 lines
12 KiB
Python
356 lines
12 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
小说内容同步到飞书脚本
|
|||
|
|
将《末日重生-开局囤货十亿物资》同步到飞书云文档
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import re
|
|||
|
|
import json
|
|||
|
|
import time
|
|||
|
|
from pathlib import Path
|
|||
|
|
import subprocess
|
|||
|
|
|
|||
|
|
class NovelToFeishuSync:
|
|||
|
|
def __init__(self):
|
|||
|
|
# 小说目录
|
|||
|
|
self.novel_dir = "/root/.openclaw/workspace/tomato-novel/books/末日重生-开局囤货十亿物资"
|
|||
|
|
self.chapters_dir = os.path.join(self.novel_dir, "chapters")
|
|||
|
|
|
|||
|
|
# 同步状态记录
|
|||
|
|
self.sync_state_file = "/root/.openclaw/workspace/novel_sync_state.json"
|
|||
|
|
self.load_sync_state()
|
|||
|
|
|
|||
|
|
# 飞书文档配置
|
|||
|
|
self.feishu_doc_id = None # 需要创建或指定
|
|||
|
|
|
|||
|
|
def load_sync_state(self):
|
|||
|
|
"""加载同步状态"""
|
|||
|
|
if os.path.exists(self.sync_state_file):
|
|||
|
|
with open(self.sync_state_file, 'r', encoding='utf-8') as f:
|
|||
|
|
self.sync_state = json.load(f)
|
|||
|
|
else:
|
|||
|
|
self.sync_state = {
|
|||
|
|
"last_sync_time": None,
|
|||
|
|
"synced_chapters": [],
|
|||
|
|
"feishu_doc_id": None,
|
|||
|
|
"total_chapters": 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def save_sync_state(self):
|
|||
|
|
"""保存同步状态"""
|
|||
|
|
with open(self.sync_state_file, 'w', encoding='utf-8') as f:
|
|||
|
|
json.dump(self.sync_state, f, ensure_ascii=False, indent=2)
|
|||
|
|
|
|||
|
|
def discover_chapters(self):
|
|||
|
|
"""发现所有章节"""
|
|||
|
|
chapters = []
|
|||
|
|
|
|||
|
|
for file in Path(self.chapters_dir).glob("*.md"):
|
|||
|
|
# 只处理主章节文件,避免备份文件
|
|||
|
|
if not any(x in file.name for x in ["_fixed", "_备份", "_修复", "backup", "_质检前", "_最终", "_原始", "_全面"]):
|
|||
|
|
# 提取章节号
|
|||
|
|
match = re.match(r'(\d{4})_(.*?)\.md', file.name)
|
|||
|
|
if match:
|
|||
|
|
chapter_num = int(match.group(1))
|
|||
|
|
chapter_title = match.group(2)
|
|||
|
|
|
|||
|
|
chapters.append({
|
|||
|
|
"number": chapter_num,
|
|||
|
|
"title": chapter_title,
|
|||
|
|
"file": str(file),
|
|||
|
|
"size": os.path.getsize(file),
|
|||
|
|
"modified": os.path.getmtime(file)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
# 按章节号排序
|
|||
|
|
chapters.sort(key=lambda x: x["number"])
|
|||
|
|
return chapters
|
|||
|
|
|
|||
|
|
def read_chapter_content(self, chapter_file):
|
|||
|
|
"""读取章节内容"""
|
|||
|
|
try:
|
|||
|
|
with open(chapter_file, 'r', encoding='utf-8') as f:
|
|||
|
|
content = f.read()
|
|||
|
|
|
|||
|
|
# 清理内容,确保格式正确
|
|||
|
|
content = content.replace('\r', '')
|
|||
|
|
|
|||
|
|
# 限制内容长度(飞书文档有长度限制)
|
|||
|
|
if len(content) > 15000:
|
|||
|
|
content = content[:15000] + "\n\n【内容过长,已截断】"
|
|||
|
|
|
|||
|
|
return content
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ 读取章节失败: {e}")
|
|||
|
|
return f"读取失败: {e}"
|
|||
|
|
|
|||
|
|
def create_feishu_document(self):
|
|||
|
|
"""创建飞书文档"""
|
|||
|
|
print("📝 创建飞书文档...")
|
|||
|
|
|
|||
|
|
# 生成文档内容
|
|||
|
|
title = "《末日重生-开局囤货十亿物资》完整版"
|
|||
|
|
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
|
|||
|
|
markdown_content = f"""# 《末日重生-开局囤货十亿物资》完整版
|
|||
|
|
|
|||
|
|
## 📖 小说信息
|
|||
|
|
- **书名**: 末日重生:开局囤货十亿物资
|
|||
|
|
- **平台**: 番茄小说
|
|||
|
|
- **类型**: 都市/末世/重生
|
|||
|
|
- **状态**: 连载中
|
|||
|
|
- **总章节**: {self.sync_state.get('total_chapters', 0)} 章
|
|||
|
|
- **最后更新**: {current_time}
|
|||
|
|
- **同步状态**: 自动同步中
|
|||
|
|
|
|||
|
|
## 📋 章节列表
|
|||
|
|
(以下为章节列表,具体内容请查看各章节)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# 创建文档
|
|||
|
|
try:
|
|||
|
|
result = subprocess.run(
|
|||
|
|
[
|
|||
|
|
"python3", "-c",
|
|||
|
|
f"""
|
|||
|
|
import sys
|
|||
|
|
sys.path.insert(0, '/root/.openclaw/workspace')
|
|||
|
|
from tools.feishu_tools import create_doc
|
|||
|
|
result = create_doc(
|
|||
|
|
title='{title}',
|
|||
|
|
content='''{markdown_content}''',
|
|||
|
|
folder_token='root' # 根目录
|
|||
|
|
)
|
|||
|
|
print(result)
|
|||
|
|
"""
|
|||
|
|
],
|
|||
|
|
capture_output=True,
|
|||
|
|
text=True,
|
|||
|
|
encoding='utf-8'
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if result.returncode == 0:
|
|||
|
|
# 解析返回结果获取文档ID
|
|||
|
|
# 这里简化处理,实际需要解析飞书API返回
|
|||
|
|
print("✅ 文档创建成功")
|
|||
|
|
return "dummy_doc_id" # 需要替换为实际文档ID
|
|||
|
|
else:
|
|||
|
|
print(f"❌ 文档创建失败: {result.stderr}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ 创建文档异常: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def update_feishu_document(self, chapters):
|
|||
|
|
"""更新飞书文档"""
|
|||
|
|
if not self.sync_state.get("feishu_doc_id"):
|
|||
|
|
# 创建新文档
|
|||
|
|
doc_id = self.create_feishu_document()
|
|||
|
|
if doc_id:
|
|||
|
|
self.sync_state["feishu_doc_id"] = doc_id
|
|||
|
|
self.save_sync_state()
|
|||
|
|
else:
|
|||
|
|
print("❌ 无法创建飞书文档")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
print(f"📤 更新飞书文档: {self.sync_state['feishu_doc_id']}")
|
|||
|
|
|
|||
|
|
# 构建完整的Markdown内容
|
|||
|
|
full_content = self.build_complete_markdown(chapters)
|
|||
|
|
|
|||
|
|
# 更新文档
|
|||
|
|
try:
|
|||
|
|
result = subprocess.run(
|
|||
|
|
[
|
|||
|
|
"python3", "-c",
|
|||
|
|
f"""
|
|||
|
|
import sys
|
|||
|
|
sys.path.insert(0, '/root/.openclaw/workspace')
|
|||
|
|
from tools.feishu_tools import update_doc
|
|||
|
|
result = update_doc(
|
|||
|
|
doc_id='{self.sync_state["feishu_doc_id"]}',
|
|||
|
|
content='''{full_content}''',
|
|||
|
|
mode='overwrite'
|
|||
|
|
)
|
|||
|
|
print(result)
|
|||
|
|
"""
|
|||
|
|
],
|
|||
|
|
capture_output=True,
|
|||
|
|
text=True,
|
|||
|
|
encoding='utf-8'
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if result.returncode == 0:
|
|||
|
|
print("✅ 文档更新成功")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
print(f"❌ 文档更新失败: {result.stderr}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"❌ 更新文档异常: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def build_complete_markdown(self, chapters):
|
|||
|
|
"""构建完整的Markdown内容"""
|
|||
|
|
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
|
|||
|
|
markdown_lines = [
|
|||
|
|
f"# 《末日重生-开局囤货十亿物资》完整版",
|
|||
|
|
"",
|
|||
|
|
f"## 📖 小说信息",
|
|||
|
|
f"- **书名**: 末日重生:开局囤货十亿物资",
|
|||
|
|
f"- **平台**: 番茄小说",
|
|||
|
|
f"- **类型**: 都市/末世/重生",
|
|||
|
|
f"- **状态**: 连载中",
|
|||
|
|
f"- **总章节**: {len(chapters)} 章",
|
|||
|
|
f"- **最后更新**: {current_time}",
|
|||
|
|
f"- **同步状态**: 自动同步",
|
|||
|
|
"",
|
|||
|
|
"## 📋 章节列表",
|
|||
|
|
""
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# 添加章节链接(实际为锚点)
|
|||
|
|
for chap in chapters:
|
|||
|
|
markdown_lines.append(f"### 第{chap['number']}章 {chap['title']}")
|
|||
|
|
markdown_lines.append(f"字数: {chap['size']} 字符 | 更新时间: {time.strftime('%Y-%m-%d %H:%M', time.localtime(chap['modified']))}")
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
|
|||
|
|
# 读取章节内容(前500字符预览)
|
|||
|
|
try:
|
|||
|
|
with open(chap['file'], 'r', encoding='utf-8') as f:
|
|||
|
|
preview = f.read(500)
|
|||
|
|
preview = preview.replace('\n', ' ').strip()
|
|||
|
|
if len(preview) > 200:
|
|||
|
|
preview = preview[:200] + "..."
|
|||
|
|
|
|||
|
|
markdown_lines.append(f"**内容预览**: {preview}")
|
|||
|
|
except:
|
|||
|
|
markdown_lines.append("**内容预览**: [读取失败]")
|
|||
|
|
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
markdown_lines.append("---")
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
|
|||
|
|
# 添加所有完整章节内容
|
|||
|
|
markdown_lines.append("## 📖 完整章节内容")
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
|
|||
|
|
for chap in chapters:
|
|||
|
|
markdown_lines.append(f"### 第{chap['number']}章 {chap['title']}")
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
|
|||
|
|
content = self.read_chapter_content(chap['file'])
|
|||
|
|
markdown_lines.append(content)
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
markdown_lines.append("---")
|
|||
|
|
markdown_lines.append("")
|
|||
|
|
|
|||
|
|
return "\n".join(markdown_lines)
|
|||
|
|
|
|||
|
|
def sync_to_feishu_table(self, chapters):
|
|||
|
|
"""同步到飞书多维表格(更好的方案)"""
|
|||
|
|
print("🔄 同步到飞书多维表格...")
|
|||
|
|
|
|||
|
|
# 检查是否已经有表格
|
|||
|
|
app_token = "MkRqbphc2afqEksxf6vcJjZVn8O" # 来自记忆的表格token
|
|||
|
|
table_id = "tbllH7wGmGbSCtPD"
|
|||
|
|
|
|||
|
|
# 为每章创建记录
|
|||
|
|
for chap in chapters:
|
|||
|
|
if chap["number"] not in self.sync_state["synced_chapters"]:
|
|||
|
|
print(f"📤 同步第{chap['number']}章: {chap['title']}")
|
|||
|
|
|
|||
|
|
# 读取内容
|
|||
|
|
content = self.read_chapter_content(chap['file'])
|
|||
|
|
|
|||
|
|
# 构建记录数据
|
|||
|
|
record_data = {
|
|||
|
|
"章节号": chap["number"],
|
|||
|
|
"标题": chap["title"],
|
|||
|
|
"字数": chap["size"],
|
|||
|
|
"状态": "已同步",
|
|||
|
|
"更新时间": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(chap["modified"]))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 这里需要调用飞书API添加记录
|
|||
|
|
# 实际实现需要飞书API调用
|
|||
|
|
|
|||
|
|
# 标记为已同步
|
|||
|
|
self.sync_state["synced_chapters"].append(chap["number"])
|
|||
|
|
|
|||
|
|
self.save_sync_state()
|
|||
|
|
print(f"✅ 已同步 {len(self.sync_state['synced_chapters'])} 章到飞书表格")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def run_sync(self, mode="table"):
|
|||
|
|
"""执行同步"""
|
|||
|
|
print("=== 小说内容同步到飞书 ===")
|
|||
|
|
print(f"时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|||
|
|
print(f"小说: 《末日重生-开局囤货十亿物资》")
|
|||
|
|
print(f"目录: {self.chapters_dir}")
|
|||
|
|
print("")
|
|||
|
|
|
|||
|
|
# 发现章节
|
|||
|
|
chapters = self.discover_chapters()
|
|||
|
|
print(f"📚 发现 {len(chapters)} 个章节:")
|
|||
|
|
for chap in chapters[:5]: # 显示前5章
|
|||
|
|
print(f" 第{chap['number']:02d}章: {chap['title']} ({chap['size']} 字符)")
|
|||
|
|
if len(chapters) > 5:
|
|||
|
|
print(f" ... 还有 {len(chapters)-5} 章")
|
|||
|
|
print("")
|
|||
|
|
|
|||
|
|
# 更新状态
|
|||
|
|
self.sync_state["total_chapters"] = len(chapters)
|
|||
|
|
self.sync_state["last_sync_time"] = time.time()
|
|||
|
|
|
|||
|
|
# 选择同步模式
|
|||
|
|
if mode == "table":
|
|||
|
|
success = self.sync_to_feishu_table(chapters)
|
|||
|
|
else:
|
|||
|
|
success = self.update_feishu_document(chapters)
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
print("")
|
|||
|
|
print("✅ 同步完成!")
|
|||
|
|
print(f"📊 统计:")
|
|||
|
|
print(f" - 总章节: {len(chapters)} 章")
|
|||
|
|
print(f" - 已同步: {len(self.sync_state['synced_chapters'])} 章")
|
|||
|
|
print(f" - 新章节: {len(chapters) - len(self.sync_state['synced_chapters'])} 章")
|
|||
|
|
print("")
|
|||
|
|
print("🔗 飞书查看:")
|
|||
|
|
if mode == "table":
|
|||
|
|
print(" https://ecncmdjvm81e.feishu.cn/base/MkRqbphc2afqEksxf6vcJjZVn8O")
|
|||
|
|
elif self.sync_state.get("feishu_doc_id"):
|
|||
|
|
print(f" 文档ID: {self.sync_state['feishu_doc_id']}")
|
|||
|
|
else:
|
|||
|
|
print("❌ 同步失败")
|
|||
|
|
|
|||
|
|
return success
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""主函数"""
|
|||
|
|
import argparse
|
|||
|
|
|
|||
|
|
parser = argparse.ArgumentParser(description="同步小说内容到飞书")
|
|||
|
|
parser.add_argument("--mode", choices=["doc", "table"], default="table",
|
|||
|
|
help="同步模式: doc=文档, table=表格(默认)")
|
|||
|
|
parser.add_argument("--force", action="store_true",
|
|||
|
|
help="强制重新同步所有章节")
|
|||
|
|
|
|||
|
|
args = parser.parse_args()
|
|||
|
|
|
|||
|
|
sync = NovelToFeishuSync()
|
|||
|
|
|
|||
|
|
if args.force:
|
|||
|
|
sync.sync_state["synced_chapters"] = []
|
|||
|
|
|
|||
|
|
sync.run_sync(mode=args.mode)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|