📚 小说内容: - 《末日重生-开局囤货十亿物资》33章 - 完整的状态文件、记忆索引、钩子系统 🛠️ 系统配置: - 版本控制管理系统 - 自动化脚本系统 - 质量监控系统 🧠 固化记忆: - 长期记忆文件 - 系统配置文档 - 恢复流程指南 💾 数据安全: - 本地备份系统 - Git版本控制 - 远程同步机制 同步时间: 2026-03-30 16:25:35 系统状态: inkos正常运行中 (PID: 1433309) 创作进度: 第33章《油粮》创作中
338 lines
12 KiB
Python
338 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
新章节监控脚本
|
||
实时监控《末日重生:开局囤货十亿物资》是否有新章节创建
|
||
"""
|
||
|
||
import os
|
||
import json
|
||
import time
|
||
import hashlib
|
||
from datetime import datetime
|
||
import subprocess
|
||
import sys
|
||
import threading
|
||
from watchdog.observers import Observer
|
||
from watchdog.events import FileSystemEventHandler
|
||
|
||
# 配置
|
||
CONFIG = {
|
||
"novel_path": "/root/.openclaw/workspace/tomato-novel/books/末日大战",
|
||
"chapters_dir": "chapters",
|
||
"monitor_dir": "/root/.openclaw/workspace/tomato-novel/books/末日大战/chapters",
|
||
"state_file": "/root/.openclaw/workspace/feishu_sync_system/sync_state.json",
|
||
"log_file": "/root/.openclaw/feishu_sync_system/monitor_log.txt",
|
||
"check_interval": 300, # 检查间隔(秒),5分钟
|
||
"last_check_time": None,
|
||
"new_chapters_found": [],
|
||
}
|
||
|
||
def load_sync_state():
|
||
"""加载同步状态"""
|
||
if os.path.exists(CONFIG["state_file"]):
|
||
try:
|
||
with open(CONFIG["state_file"], 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
except:
|
||
pass
|
||
return {"synced_chapters": {}}
|
||
|
||
def log_info(message):
|
||
"""记录信息日志"""
|
||
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
log_entry = f"[MONITOR] {timestamp} - {message}\n"
|
||
print(log_entry.strip())
|
||
|
||
os.makedirs(os.path.dirname(CONFIG["log_file"]), exist_ok=True)
|
||
with open(CONFIG["log同步_system_log.txt", 'a', encoding='utf-8') as f:
|
||
f.write(log_entry)
|
||
|
||
def log_error(message):
|
||
"""记录错误日志"""
|
||
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
log_entry = f"[MONITOR.ERROR] {timestamp} - {message}\n"
|
||
print(log_entry.strip())
|
||
|
||
os.makedirs(os.path.dirname(CONFIG["log_file"]), exist_ok=True)
|
||
with open(CONFIG["log_file"], 'a', encoding='utf-8') as f:
|
||
f.write(log_entry)
|
||
|
||
def get_chapter_info(chapter_path):
|
||
"""获取章节信息"""
|
||
try:
|
||
with open(chapter_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
# 从文件名提取章节号
|
||
filename = os.path.basename(chapter_path)
|
||
# 格式如:0001_冰点记忆.md
|
||
if '_' in filename:
|
||
chapter_num_str = filename.split('_')[0]
|
||
try:
|
||
chapter_num = int(chapter_num_str)
|
||
except:
|
||
chapter_num = 0
|
||
else:
|
||
chapter_num = 0
|
||
|
||
# 提取标题
|
||
title = ""
|
||
lines = content.split('\n', 10)
|
||
for line in lines:
|
||
if line.startswith('# '):
|
||
title = line[2:].strip()
|
||
break
|
||
if not title:
|
||
title = filename.replace('.md', '').replace(f"{chapter_num_str}_", "")
|
||
|
||
# 计算哈希值
|
||
content_hash = hashlib.md5(content.encode('utf-8')).hexdigest()
|
||
|
||
return {
|
||
"number": chapter_num,
|
||
"title": title,
|
||
"path": chapter_path,
|
||
"hash": content_hash,
|
||
"size": len(content),
|
||
"word_count": len(content) // 3,
|
||
"modified_time": os.path.getmtime(chapter_path)
|
||
}
|
||
except Exception as e:
|
||
log_error(f"读取章节失败 {chapter_path}: {e}")
|
||
return None
|
||
|
||
def find_new_chapters():
|
||
"""查找新章节"""
|
||
chapters_dir = os.path.join(CONFIG["novel_path"], CONFIG["chapters_dir"])
|
||
if not os.path.exists(chapters_dir):
|
||
log_info(f"章节目录不存在: {chapters_dir}")
|
||
return []
|
||
|
||
# 加载当前状态
|
||
state = load_sync_state()
|
||
synced_chapters = state.get("synced_chapters", {})
|
||
|
||
# 查找所有章节
|
||
chapters_dir = os.path.join(CONFIG["novel_path"], CONFIG["chapters_dir"])
|
||
all_chapters = []
|
||
for filename in os.listdir(chapters_dir):
|
||
if filename.endswith('.md') and not filename.startswith('0000_'):
|
||
# 排除备份文件和报告文件
|
||
if not any(x in filename for x in ['_backup', '_report', '_fix', '_修复', '_质检']):
|
||
chapter_path = os.path.join(chapters_dir, filename)
|
||
all_chapters.append(chapter_path)
|
||
|
||
# 检查哪些章节是新的或已修改
|
||
new_chapters = []
|
||
for chapter_path in all_chapters:
|
||
chapter_info = get_chapter_info(chapter_path)
|
||
if not chapter_info:
|
||
continue
|
||
|
||
chapter_num = str(chapter_info["number"])
|
||
chapter_hash = chapter_info["hash"]
|
||
|
||
# 检查是否已经同步过
|
||
if chapter_num not in synced_chapters:
|
||
# 新章节
|
||
new_chapters.append(chapter_info)
|
||
log_info(f"发现新章节: 第{chapter_num}章 - {chapter_info['title']}")
|
||
else:
|
||
synced_hash = synced_chapters[chapter_num].get("hash", "")
|
||
if synced_hash != chapter_hash:
|
||
# 章节内容已修改
|
||
log_info(f"章节内容已修改: 第{chapter_num}章 - {chapter_info['title']}")
|
||
new_chapters.append(chapter_info)
|
||
|
||
return new_chapters
|
||
|
||
class ChapterChangeHandler(FileSystemEventHandler):
|
||
"""监控文件变化的处理器"""
|
||
def __init__(self):
|
||
self.last_chapters = set()
|
||
self.new_chapters = []
|
||
super().__init__()
|
||
|
||
def on_created(self, event):
|
||
if event.is_directory:
|
||
return
|
||
|
||
if event.src_path.endswith('.md'):
|
||
# 检查是否是章节文件
|
||
filename = os.path.basename(event.src_path)
|
||
if not filename.startswith('0000_') and not any(x in filename for x in ['_backup', '_report', '_fix']):
|
||
log_info(f"检测到新章节文件: {filename}")
|
||
self.new_chapters.append(event.src_path)
|
||
|
||
# 检查是否是新的章节
|
||
chapter_info = get_chapter_info(event.src_path)
|
||
if chapter_info:
|
||
log_info(f"新章节: 第{chapter_info['number']}章 - {chapter_info['title']}")
|
||
# 触发同步
|
||
trigger_sync()
|
||
|
||
def check_for_new_chapters():
|
||
"""定期检查新章节"""
|
||
log_info("开始监控新章节")
|
||
|
||
last_chapters = set()
|
||
|
||
while True:
|
||
try:
|
||
log_info(f"检查新章节...")
|
||
|
||
# 查找所有章节
|
||
chapters_dir = os.path.join(CONFIG["novel_path"], CONFIG["chapters_dir"])
|
||
if not os.path.exists(chapters_dir):
|
||
log_info("章节目录不存在,等待创建...")
|
||
time.sleep(CONFIG["check_interval"])
|
||
continue
|
||
|
||
current_chapters = set()
|
||
for filename in os.listdir(chapters_dir):
|
||
if filename.endswith('.md') and not filename.startswith('0000_'):
|
||
if not any(x in filename for x in ['_backup', '_report', '_fix']):
|
||
current_chapters.add(filename)
|
||
|
||
# 检测新章节
|
||
new_chapters = current_chapters - last_chapters
|
||
if new_chapters:
|
||
for chapter_file in new_chapters:
|
||
log_info(f"发现新章节: {chapter_file}")
|
||
# 获取章节信息
|
||
chapter_path = os.path.join(chapters_dir, chapter_file)
|
||
chapter_info = get_chapter_info(chapter_path)
|
||
if chapter_info:
|
||
log_info(f"新章节: 第{chapter_info['number']}章 - {chapter_info['title']}")
|
||
# 触发同步
|
||
trigger_sync()
|
||
|
||
last_chapters = current_chapters
|
||
log_info(f"当前章节数: {len(current_chapters)}")
|
||
time.sleep(CONFIG["check_interval"])
|
||
|
||
except Exception as e:
|
||
log_error(f"监控出错: {e}")
|
||
time.sleep(60) # 出错后等待60秒再重试
|
||
|
||
def trigger_sync():
|
||
"""触发同步"""
|
||
log_info("触发章节同步")
|
||
|
||
try:
|
||
# 运行同步脚本
|
||
script_path = os.path.join(os.path.dirname(__file__), "sync_chapters.py")
|
||
if os.path.exists(script_path):
|
||
# 使用子进程运行同步脚本
|
||
result = subprocess.run([sys.executable, script_path],
|
||
capture_output=True, text=True)
|
||
|
||
if result.returncode == 0:
|
||
log_info("同步脚本执行成功")
|
||
log_info(f"输出:\n{result.stdout}")
|
||
else:
|
||
log_error(f"同步脚本执行失败: {result.stderr}")
|
||
else:
|
||
log_error(f"同步脚本不存在: {script_path}")
|
||
|
||
except Exception as e:
|
||
log_error(f"触发同步失败: {e}")
|
||
|
||
def watch_changes():
|
||
"""实时监控文件变化"""
|
||
log_info("启动实时文件监控")
|
||
|
||
event_handler = ChapterChangeHandler()
|
||
observer = Observer()
|
||
|
||
try:
|
||
observer.schedule(event_handler, CONFIG["monitor_dir"], recursive=True)
|
||
observer.start()
|
||
|
||
log_info("文件监控已启动,正在监控章节目录...")
|
||
|
||
# 等待监控
|
||
while True:
|
||
time.sleep(1)
|
||
|
||
except KeyboardInterrupt:
|
||
observer.stop()
|
||
log_info("监控停止")
|
||
except Exception as e:
|
||
log_error(f"实时监控失败: {e}")
|
||
finally:
|
||
observer.stop()
|
||
observer.join()
|
||
|
||
def get_chapter_list():
|
||
"""获取当前所有章节列表"""
|
||
chapters_dir = os.path.join(CONFIG["novel_path"], CONFIG["chapters_dir"])
|
||
if not os.path.exists(chapters_dir):
|
||
return []
|
||
|
||
chapters = []
|
||
for filename in os.listdir(chapters_dir):
|
||
if filename.endswith('.md') and not filename.startswith('0000_'):
|
||
if not any(x in filename for x in ['_backup', '_report', '_fix', '_修复', '_质检']):
|
||
chapters.append(filename)
|
||
chapters.sort()
|
||
return chapters
|
||
|
||
def monitor_changes():
|
||
"""定期监控变化"""
|
||
log_info("启动定期监控")
|
||
|
||
last_chapters = get_chapter_list()
|
||
log_info(f"初始章节数: {len(last_chapters)}")
|
||
|
||
while True:
|
||
try:
|
||
current_chapters = get_chapter_list()
|
||
|
||
# 检测新章节
|
||
if len(current_chapters) > len(last_chapters):
|
||
new_chapters = [chap for chap in current_chapters if chap not in last_chapters]
|
||
for chapter_file in new_chapters:
|
||
log_info(f"发现新章节: {chapter_file}")
|
||
# 触发同步
|
||
trigger_sync()
|
||
|
||
# 检测章节号更新
|
||
if len(current_chapters) == len(last_chapters):
|
||
for i in range(len(current_chapters)):
|
||
if current_chapters[i] != last_chapters[i]:
|
||
log_info(f"章节文件更新: {current_chapters[i]}")
|
||
|
||
last_chapters = current_chapters
|
||
time.sleep(CONFIG["check_interval"])
|
||
|
||
except Exception as e:
|
||
log_error(f"监控出错: {e}")
|
||
time.sleep(60) # 出错后等待60秒再重试
|
||
|
||
def main():
|
||
"""主函数"""
|
||
log_info("=" * 50)
|
||
log_info("新章节监控系统启动")
|
||
log_info(f"监控目录: {CONFIG['monitor_dir']}")
|
||
log_info(f"检查间隔: {CONFIG['check_interval']}秒")
|
||
log_info("=" * 50)
|
||
|
||
try:
|
||
# 检查路径
|
||
if not os.path.exists(CONFIG["novel_path"]):
|
||
log_error(f"小说路径不存在: {CONFIG['novel_path']}")
|
||
return
|
||
|
||
# 启动监控
|
||
monitor_changes()
|
||
|
||
except KeyboardInterrupt:
|
||
log_info("监控系统被用户中断")
|
||
except Exception as e:
|
||
log_error(f"监控系统运行失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
if __name__ == "__main__":
|
||
main() |