Skill: hawkvan/notion-sync-obsidian (version 1.0.0)

--- name: notion-sync-obsidian description: 自动将Notion文章同步到本地Obsidian目录，支持定时检查、完整内容导出、智能标题提取。 homepage: https://github.com/openclaw/openclaw metadata: {"clawdbot":{"emoji":"📝"}} --- # notion-sync-obsidian 自动将Notion文章同步到本地Obsidian目录的完整解决方案。支持定时检查、完整内容导出、智能标题提取、移动端优化通知。 ## ✨ 功能特性 ### ✅ 核心功能 - **自动同步**: 定时检查Notion更新并同步到本地Obsidian目录 - **完整导出**: 导出文章标题、元数据、完整内容 - **智能标题**: 自动识别文章原始标题（非摘要内容） - **移动通知**: 移动端优化格式，只在有更新时通知 ### ✅ 高级特性 - **定时检查**: 可配置检查频率（默认15分钟） - **安静时段**: 支持配置安静时段（默认00:00-08:30） - **强制检查**: 支持用户手动触发检查，忽略安静时段 - **增量同步**: 基于时间戳避免重复导出 - **错误处理**: 完善的错误处理和日志记录 ### ✅ 技术特性 - **Python支持**: 使用Python requests库进行完整API调用 - **环境兼容**: 自动处理Python依赖安装 - **配置灵活**: 支持自定义API密钥、导出目录、检查频率 - **跨平台**: 支持Linux/macOS/Windows（容器环境已验证） ## 🚀 快速开始 ### 1. 前置要求 - Notion API密钥（从 https://notion.so/my-integrations 获取） - Obsidian目录路径 - Python 3.6+（可选，用于完整内容导出） ### 2. 基础配置 ```bash # 1. 进入skill目录 cd ~/.openclaw/workspace/skills/notion-sync-obsidian # 2. 编辑配置文件 nano config.json ``` 配置文件示例 (`config.json`): ```json { "notion": { "api_key": "ntn_your_api_key_here", "api_version": "2022-06-28" }, "obsidian": { "root_dir": "/path/to/your/obsidian/notion" }, "sync": { "check_interval_minutes": 15, "quiet_hours_start": "00:00", "quiet_hours_end": "08:30", "enable_notifications": true } } ``` ### 3. 启动同步系统 ```bash # 启动定时同步（每15分钟检查一次） ./scripts/start_timer.sh # 手动检查（忽略安静时段） FORCE_CHECK=1 ./scripts/simple_checker.sh # 查看状态 ./scripts/status_timer.sh # 停止同步 ./scripts/stop_timer.sh ``` ## 📁 目录结构 ``` notion-sync-obsidian/ ├── SKILL.md # 技能说明文档 ├── config.json # 配置文件模板 ├── scripts/ # 核心脚本 │ ├── real_notion_checker.py # 完整Python检查器 │ ├── simple_checker.sh # 简化Shell检查器 │ ├── timer_checker.sh # 定时检查器 │ ├── start_timer.sh # 启动定时器 │ ├── stop_timer.sh # 停止定时器 │ ├── status_timer.sh # 查看状态 │ ├── list_recent_articles.sh # 列出最近文章 │ └── debug_page_structure.py # 调试页面结构 ├── references/ # 参考文档 │ └── NOTION_API_GUIDE.md # Notion API使用指南 └── examples/ # 示例文件 └── exported_article.md # 导出文件示例 ``` ## 🔧 详细配置 ### Notion API配置 1. 访问 https://notion.so/my-integrations 创建集成 2. 复制API密钥（以 `ntn_` 开头） 3. 将集成分享到你的Notion工作空间 4. 在 `config.json` 中配置API密钥 ### 导出目录配置 - **默认路径**: [REDACTED_SECRET] - **组织结构**: 按年月分目录 `YYYY-MM/` - **文件命名**: 使用文章原始标题，特殊字符自动过滤 ### 定时检查配置 - **检查频率**: 默认15分钟，可配置 - **安静时段**: 默认00:00-08:30，避免夜间打扰 - **强制模式**: `FORCE_CHECK=1` 环境变量忽略所有限制 ## 🛠️ 脚本说明 ### 核心脚本 - **`real_notion_checker.py`**: 完整Python检查器，导出完整内容 - **`simple_checker.sh`**: 简化Shell检查器，快速检查更新 - **`timer_checker.sh`**: 定时检查器，管理定时任务 ### 管理脚本 - **`start_timer.sh`**: 启动定时同步系统 - **`stop_timer.sh`**: 停止定时同步系统 - **`status_timer.sh`**: 查看系统状态和日志 - **`list_recent_articles.sh`**: 列出Notion最近文章 ### 调试脚本 - **`debug_page_structure.py`**: 调试Notion页面结构 - **`test_title_fix.py`**: 测试标题提取修复 ## 📊 系统状态 ### 查看状态 ```bash ./scripts/status_timer.sh ``` 输出示例: ``` 📊 Notion定时同步状态检查 检查时间: 2026-02-24 15:44:53 时区: Asia/Shanghai ======================================== 🟢 状态: 运行中 进程PID: 4538 运行时间: 10:29 📋 日志信息: 日志文件: ./sync_timer.log 日志行数: 1179 📁 目录状态: 文章目录: /hellox/openclaw/obsidian/notion 文章数量: 78 ⏰ 下次检查时间: 15:59 (15分钟后) ``` ### 查看日志 ```bash tail -f sync_timer.log ``` ## 🔍 故障排除 ### 常见问题 **Q: 同步失败，提示API错误** A: 检查API密钥是否正确，确保集成已分享到工作空间 **Q: 文件名使用摘要而非标题** A: 这是已知问题，已修复。确保使用最新版本的 `real_notion_checker.py` **Q: Python依赖安装失败** A: 容器环境可能需要特殊处理，脚本已包含自动安装逻辑 **Q: 定时器不运行** A: 检查进程状态，可能需要重启定时器 ### 调试步骤 1. 运行 `./scripts/debug_page_structure.py` 检查API连接 2. 运行 `FORCE_CHECK=1 ./scripts/simple_checker.sh` 手动测试 3. 检查 `sync_timer.log` 日志文件 4. 验证配置文件路径和权限 ## 📈 高级用法 ### 自定义导出格式 修改 `real_notion_checker.py` 中的 `export_page_to_markdown` 函数来自定义Markdown格式。 ### 集成到其他系统 脚本输出标准化格式，可轻松集成到： - CI/CD流水线 - 其他自动化工具 - 自定义监控系统 ### 扩展功能 1. **标签同步**: 同步Notion标签到Obsidian标签 2. **图片下载**: 自动下载文章中的图片 3. **双向同步**: 支持从Obsidian同步回Notion 4. **多数据库**: 支持同步多个Notion数据库 ## 🤝 贡献指南 欢迎贡献代码、报告问题或提出建议！ ### 开发环境 ```bash # 克隆仓库 git clone https://github.com/your-username/notion-sync-obsidian.git # 安装依赖 pip install -r requirements.txt # 运行测试 python -m pytest tests/ ``` ### 代码规范 - 遵循PEP 8 Python代码规范 - 添加适当的注释和文档 - 编写单元测试 - 更新CHANGELOG.md ## 📄 许可证 MIT License - 详见 LICENSE 文件 ## 🙏 致谢 - Notion官方API文档 - OpenClaw社区 - 所有贡献者和用户 --- **版本**: 1.0.0 **最后更新**: 2026-02-24 **维护者**: kk (你的私人助理) **状态**: ✅ 生产就绪

path: INSTALL.md
content: # 安装和使用指南

## 🚀 快速安装

### 方法 1: 从 ClawHub 安装（推荐）
```bash
# 安装 ClawHub CLI（如果尚未安装）
curl -fsSL https://clawhub.com/install.sh | bash

# 搜索技能
clawhub search notion-sync

# 安装技能
clawhub install notion-sync-obsidian
```

### 方法 2: 手动安装
```bash
# 1. 下载技能
git clone https://github.com/your-username/notion-sync-obsidian.git
# 或直接复制文件到技能目录

# 2. 复制到 OpenClaw 技能目录
cp -r notion-sync-obsidian ~/.openclaw/workspace/skills/
```

## ⚙️ 配置步骤

### 步骤 1: 获取 Notion API 密钥
1. 访问 [Notion Integrations](https://notion.so/my-integrations)
2. 创建新集成
3. 复制 API 密钥（以 `ntn_` 开头）
4. 将集成分享到你的工作空间

### 步骤 2: 配置技能
```bash
# 进入技能目录
cd ~/.openclaw/workspace/skills/notion-sync-obsidian

# 复制配置文件模板
cp config.json.example config.json

# 编辑配置文件
nano config.json
```

### 步骤 3: 修改配置文件
编辑 `config.json`:
```json
{
  "notion": {
    "api_key": "ntn_your_actual_api_key_here",
    "api_version": "2022-06-28"
  },
  "obsidian": {
    "root_dir": "/path/to/your/obsidian/vault",
    "organize_by_month": true
  },
  "sync": {
    "check_interval_minutes": 15,
    "quiet_hours_start": "00:00",
    "quiet_hours_end": "08:30"
  }
}
```

**重要配置项**:
- `api_key`: 你的 Notion API 密钥
- `root_dir`: Obsidian 笔记库的根目录
- `check_interval_minutes`: 检查频率（分钟）

## 🧪 测试安装

### 测试 API 连接
```bash
cd ~/.openclaw/workspace/skills/notion-sync-obsidian
./scripts/simple_checker.sh
```

**预期输出**:
```
✅ API连接成功
   用户: Your Name
   工作空间: Your Workspace
✅ 创建测试文件: Notion同步测试_20260224_154322.md
```

### 测试完整同步
```bash
# 运行完整 Python 检查器
python3 ./scripts/real_notion_checker.py
```

## 🚀 启动定时同步

### 启动定时器
```bash
./scripts/start_timer.sh
```

**输出示例**:
```
🚀 启动Notion定时同步...
启动时间: 2026-02-24 15:45:37
时区: Asia/Shanghai
检查间隔: 15 分钟
日志文件: sync_timer.log
✅ 定时同步已启动
📋 定时进程PID: 4538
```

### 查看状态
```bash
./scripts/status_timer.sh
```

### 停止定时器
```bash
./scripts/stop_timer.sh
```

## 📱 使用技巧

### 手动同步（忽略安静时段）
```bash
FORCE_CHECK=1 ./scripts/simple_checker.sh
```

### 查看最近文章
```bash
./scripts/list_recent_articles.sh
```

### 调试页面结构
```bash
python3 ./scripts/debug_page_structure.py
```

### 查看日志
```bash
# 实时查看日志
tail -f sync_timer.log

# 查看最后100行
tail -n 100 sync_timer.log
```

## 🔧 故障排除

### 常见问题 1: API 连接失败
**症状**: "API连接失败" 或 "未找到数据库"
**解决方案**:
```bash
# 1. 检查 API 密钥
grep api_key config.json

# 2. 测试 API 连接
curl -s -X GET "https://api.notion.com/v1/users/me" \
  -H "[REDACTED_SECRET]" \
  -H "Notion-Version: 2022-06-28"

# 3. 确认集成已分享到工作空间
```

### 常见问题 2: Python 依赖缺失
**症状**: "ModuleNotFoundError: No module named 'requests'"
**解决方案**:
```bash
# 安装 Python 依赖
pip install requests pytz

# 或使用系统包管理器
apt-get install python3-requests python3-pytz
```

### 常见问题 3: 权限问题
**症状**: "Permission denied" 或无法创建文件
**解决方案**:
```bash
# 检查目录权限
ls -la /path/to/your/obsidian

# 修改权限（如果需要）
chmod 755 /path/to/your/obsidian
```

## 📊 监控和维护

### 定期检查
1. **查看状态**: `./scripts/status_timer.sh`
2. **检查日志**: `tail -f sync_timer.log`
3. **验证导出**: 检查 Obsidian 目录中的文件

### 清理操作
```bash
# 清理旧日志（保留最近7天）
find . -name "*.log" -mtime +7 -delete

# 清理 PID 文件（如果进程异常）
rm -f sync_timer.pid
```

### 备份配置
```bash
# 备份配置文件
cp config.json config.json.backup

# 备份日志
cp sync_timer.log sync_timer.log.backup
```

## 🔄 更新技能

### 检查更新
```bash
# 如果从 ClawHub 安装
clawhub update notion-sync-obsidian

# 如果手动安装
cd ~/.openclaw/workspace/skills/notion-sync-obsidian
git pull origin main
```

### 更新后操作
1. 停止定时器: `./scripts/stop_timer.sh`
2. 备份配置: `cp config.json config.json.backup`
3. 更新文件
4. 恢复配置: `cp config.json.backup config.json`
5. 启动定时器: `./scripts/start_timer.sh`

## 🎯 高级配置

### 自定义检查频率
```json
{
  "sync": {
    "check_interval_minutes": 30,  // 每30分钟检查
    "quiet_hours_start": "23:00",
    "quiet_hours_end": "07:00"
  }
}
```

### 自定义导出目录
```json
{
  "obsidian": {
    "root_dir": "/mnt/obsidian/vault",
    "organize_by_month": false,  // 不按月份组织
    "subdirectory": "imports/notion"  // 自定义子目录
  }
}
```

### 启用详细日志
```json
{
  "logging": {
    "log_level": "DEBUG",
    "max_log_size_mb": 50,
    "backup_count": 10
  }
}
```

## 🤝 获取帮助

### 查看文档
- `SKILL.md` - 技能详细说明
- `references/NOTION_API_GUIDE.md` - API 使用指南
- `examples/` - 示例文件

### 社区支持
- [OpenClaw Discord](https://discord.com/invite/clawd)
- [GitHub Issues](https://github.com/your-username/notion-sync-obsidian/issues)
- [ClawHub 社区](https://clawhub.com)

### 报告问题
1. 描述具体问题
2. 提供相关日志
3. 说明复现步骤
4. 附上配置文件（隐藏敏感信息）

---

**技能版本**: v1.0.0  
**最后更新**: 2026-02-24  
**维护者**: OpenClaw 社区  
**状态**: ✅ 生产就绪

path: README.md
content: # notion-sync-obsidian

![Version](https://img.shields.io/badge/version-1.0.0-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![OpenClaw](https://img.shields.io/badge/OpenClaw-Skill-orange)

自动将 Notion 文章同步到本地 Obsidian 目录的完整解决方案。

## ✨ 特性

### ✅ 核心功能
- **自动同步**: 定时检查 Notion 更新并同步到本地
- **完整导出**: 导出文章标题、元数据、完整内容
- **智能标题**: 正确提取文章原始标题（非摘要）
- **移动通知**: 移动端优化格式，智能通知

### ⚙️ 高级功能
- **可配置检查频率** (默认15分钟)
- **安静时段支持** (默认00:00-08:30)
- **强制检查模式** (用户手动触发)
- **增量同步** (避免重复导出)
- **错误处理和日志记录**

## 🚀 快速开始

### 安装
```bash
# 从 ClawHub 安装
clawhub install notion-sync-obsidian

# 或手动安装
git clone https://github.com/your-username/notion-sync-obsidian.git
cp -r notion-sync-obsidian ~/.openclaw/workspace/skills/
```

### 配置
1. 获取 Notion API 密钥: https://notion.so/my-integrations
2. 复制配置文件: `cp config.json.example config.json`
3. 编辑配置: 设置 API 密钥和 Obsidian 目录

### 使用
```bash
# 测试连接
./scripts/simple_checker.sh

# 启动定时同步
./scripts/start_timer.sh

# 查看状态
./scripts/status_timer.sh
```

## 📁 目录结构

```
notion-sync-obsidian/
├── SKILL.md                    # 技能详细文档
├── README.md                   # 项目说明
├── INSTALL.md                  # 安装指南
├── config.json                 # 配置文件
├── config.json.example         # 配置示例
├── scripts/                    # 核心脚本
│   ├── real_notion_checker.py  # 完整Python检查器
│   ├── simple_checker.sh       # 简化Shell检查器
│   ├── timer_checker.sh        # 定时检查器
│   ├── start_timer.sh          # 启动定时器
│   ├── stop_timer.sh           # 停止定时器
│   ├── status_timer.sh         # 查看状态
│   ├── list_recent_articles.sh # 列出最近文章
│   └── debug_page_structure.py # 调试页面结构
├── references/                 # 参考文档
│   └── NOTION_API_GUIDE.md     # Notion API指南
├── examples/                   # 示例文件
│   └── exported_article.md     # 导出文件示例
└── LICENSE                     # 许可证文件
```

## 🔧 技术细节

### 标题提取算法
```python
# 智能标题提取，避免使用摘要内容
preferred_title_names = ['标题', 'Title', '名称', 'Name']
# 1. 按属性名优先查找
# 2. 按类型查找 (title类型)
# 3. 使用页面ID作为后备
```

### 同步逻辑
- **增量同步**: 只同步最近24小时编辑的页面
- **错误处理**: 完善的异常处理和日志记录
- **重试机制**: 网络错误自动重试

### 文件管理
- **安全命名**: 特殊字符自动过滤
- **冲突处理**: 自动添加序号避免冲突
- **目录组织**: 按年月自动组织文件

## 📊 系统要求

### 最低要求
- **操作系统**: Linux, macOS, Windows (WSL)
- **Python**: 3.6+ (用于完整功能)
- **Notion**: 有效API密钥和工作空间
- **Obsidian**: 本地笔记库目录

### 推荐配置
- **内存**: 512MB+
- **存储**: 100MB+ 可用空间
- **网络**: 稳定的互联网连接

## 🤝 贡献

欢迎贡献代码、报告问题或提出建议！

### 开发流程
1. Fork 仓库
2. 创建功能分支
3. 提交更改
4. 创建 Pull Request

### 代码规范
- 遵循 PEP 8 Python 代码规范
- 添加适当的注释和文档
- 编写单元测试
- 更新 CHANGELOG.md

## 📄 许可证

本项目基于 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。

## 🙏 致谢

- Notion 官方 API 团队
- OpenClaw 社区
- 所有贡献者和用户
- 测试和反馈的早期用户

## 🔗 相关链接

- [OpenClaw 官网](https://openclaw.ai)
- [ClawHub 技能市场](https://clawhub.com)
- [Notion API 文档](https://developers.notion.com)
- [GitHub 仓库](https://github.com/your-username/notion-sync-obsidian)

## 📞 支持

### 文档
- [安装指南](INSTALL.md)
- [API 指南](references/NOTION_API_GUIDE.md)
- [技能文档](SKILL.md)

### 社区
- [OpenClaw Discord](https://discord.com/invite/clawd)
- [GitHub Issues](https://github.com/your-username/notion-sync-obsidian/issues)

### 问题排查
1. 查看日志文件 `sync_timer.log`
2. 运行调试脚本 `debug_page_structure.py`
3. 检查配置文件 `config.json`

---

**版本**: 1.0.0  
**最后更新**: 2026-02-24  
**维护者**: OpenClaw 社区  
**状态**: ✅ 生产就绪

path: config.json
content: {
  "notion": {
    "api_key": "ntn_your_api_key_here",
    "api_version": "2022-06-28",
    "workspace_id": "your_workspace_id_optional"
  },
  "obsidian": {
    "root_dir": "/path/to/your/obsidian/notion",
    "organize_by_month": true,
    "file_naming": "title",
    "max_filename_length": 100
  },
  "sync": {
    "check_interval_minutes": 15,
    "quiet_hours_start": "00:00",
    "quiet_hours_end": "08:30",
    "enable_notifications": true,
    "notification_format": "mobile",
    "force_check_env_var": "FORCE_CHECK"
  },
  "export": {
    "include_metadata": true,
    "include_content": true,
    "include_images": false,
    "date_format": "%Y-%m-%d %H:%M:%S",
    "timezone": "Asia/Shanghai"
  },
  "logging": {
    "log_file": "sync_timer.log",
    "log_level": "INFO",
    "max_log_size_mb": 10,
    "backup_count": 5
  },
  "advanced": {
    "python_path": "python3",
    "use_python_checker": true,
    "fallback_to_shell": true,
    "auto_install_deps": true
  }
}

path: examples/exported_article.md
content: ---
title: OpenClaw 多 Agent 玩法，我的AI 团队越来越壮大了！
notion_id: 311d3ed9-dd9a-8122-80c3-d48a12f578ae
created_time: 2026-02-24T06:40:00.000Z
last_edited_time: 2026-02-24T06:40:00.000Z
original_url: https://www.notion.so/OpenClaw-Agent-AI-311d3ed9dd9a812280c3d48a12f578ae
export_time: 2026-02-24T15:43:37.620852+08:00
---

# OpenClaw 多 Agent 玩法，我的AI 团队越来越壮大了！

一句话总结：介绍了OpenClaw的多Agent方案，包括其与多实例的差异、隔离机制、实操配置、交互方式及模型分配建议。

## 要点

1. OpenClaw的多Agent方案可在单个Telegram Bot下按任务拆分Agent，实现记忆物理隔离，避免上下文串扰和记忆臃肿。
2. 多实例容灾能力强但资源占用多，单实例多Agent轻量但进程挂则全Agent失效，建议结合使用，多实例做容灾，单实例内用多Agent细分任务。
3. OpenClaw中每个Agent有独立工作目录、状态目录和对话历史，记忆物理隔离，不会互相污染。
4. 配置Agent只需建Telegram群拉进Bot并告知群ID，剩余可由龙虾完成，也可用命令行向导，同时需注意关闭Bot的Group Privacy模式或设为群管理员。
5. 与子Agent交互有对应群直聊和主Agent委派两种方式，可按场景选择。
6. 多Agent可按任务分配不同大模型以节省成本，建议从拆分写作助手开始逐步扩编。
7. Agent的Workspace可选择共享或独立，共享可复用素材，独立可避免文件干扰。

## 详细内容

### 多Agent vs 多实例

**多实例方案**：
- 每个实例独立进程、独立Bot
- 容灾能力强，一个挂不影响其他
- 资源占用多，管理复杂

**多Agent方案**：
- 单进程内多个Agent
- 轻量级，易于管理
- 进程挂则全Agent失效

### 实操配置

1. **创建Telegram群组**
   - 每个Agent对应一个群
   - 拉入OpenClaw Bot
   - 获取群ID

2. **配置Agent**
   ```bash
   # 使用命令行向导
   openclaw agent create
   
   # 或手动配置
   # 编辑 openclaw.json
   ```

3. **权限设置**
   - 关闭Bot的Group Privacy模式
   - 或设置为群管理员

### 交互方式

**直接交互**：
- 在对应Agent群直接聊天
- 适合专用任务

**委派交互**：
- 在主Agent群委派任务
- 适合复杂工作流

### 模型分配策略

| 任务类型 | 推荐模型 | 理由 |
|---------|---------|------|
| 写作助手 | 小模型 | 成本低，响应快 |
| 代码分析 | 大模型 | 需要强推理能力 |
| 日常问答 | 中等模型 | 平衡成本效果 |

### Workspace策略

**共享Workspace**：
- 优点：素材复用，协作方便
- 缺点：可能文件干扰

**独立Workspace**：
- 优点：完全隔离，避免干扰
- 缺点：素材无法共享

## 最佳实践

### 起步建议
1. 从拆分写作助手开始
2. 逐步添加专用Agent
3. 根据使用频率调整

### 监控维护
1. 定期检查Agent状态
2. 清理无用Agent
3. 优化模型分配

### 故障处理
1. 进程重启
2. 日志分析
3. 社区求助

## 总结

OpenClaw的多Agent方案为AI助手团队化提供了灵活高效的解决方案。通过合理的任务拆分和资源配置，可以构建强大的AI助手团队，提升工作效率的同时控制成本。

---

*本文档由 notion-sync-obsidian 技能自动导出*
*导出时间: 2026-02-24 15:43:37*
*技能版本: v1.0.0*

path: references/NOTION_API_GUIDE.md
content: # Notion API 使用指南

## 📋 概述

本文档提供 Notion API 的基本使用指南，帮助配置和使用 notion-sync-obsidian 技能。

## 🔑 获取 API 密钥

### 步骤 1: 创建集成
1. 访问 [Notion Integrations](https://notion.so/my-integrations)
2. 点击 "New integration"
3. 填写集成信息:
   - **Name**: 例如 "Obsidian Sync"
   - **Logo** (可选): 上传图标
   - **Associated workspace**: 选择你的工作空间
4. 点击 "Submit"

### 步骤 2: 获取 API 密钥
创建成功后，你会看到:
- **Internal Integration Token**: 以 `ntn_` 或 `secret_` 开头的字符串
- 这就是你的 API 密钥

### 步骤 3: 分享集成到工作空间
1. 在 Notion 中打开要同步的页面或数据库
2. 点击右上角的 "..." 菜单
3. 选择 "Add connections" 或 "Connect to"
4. 找到你创建的集成并连接

## 📊 API 版本说明

### 当前使用的版本: `2022-06-28`
这是最稳定和广泛支持的版本。

### 关键特性:
- 支持页面和数据库查询
- 支持内容块获取
- 支持富文本格式
- 稳定的 API 端点

## 🔧 配置技能

### 配置文件 (`config.json`)
```json
{
  "notion": {
    "api_key": "ntn_your_api_key_here",
    "api_version": "2022-06-28"
  },
  "obsidian": {
    "root_dir": "/path/to/your/obsidian",
    "organize_by_month": true
  },
  "sync": {
    "check_interval_minutes": 15,
    "quiet_hours_start": "00:00",
    "quiet_hours_end": "08:30"
  }
}
```

### 配置说明
1. **api_key**: 从 Notion Integrations 获取的密钥
2. **root_dir**: Obsidian 笔记库的根目录
3. **check_interval_minutes**: 检查更新的频率（分钟）
4. **quiet_hours**: 安静时段，避免夜间打扰

## 🚀 快速测试

### 测试 API 连接
```bash
cd /path/to/skill
./scripts/simple_checker.sh
```

### 手动同步
```bash
# 忽略安静时段
FORCE_CHECK=1 ./scripts/simple_checker.sh

# 使用完整 Python 检查器
python3 ./scripts/real_notion_checker.py
```

## 🔍 调试技巧

### 查看页面结构
```bash
python3 ./scripts/debug_page_structure.py
```

### 列出最近文章
```bash
./scripts/list_recent_articles.sh
```

### 查看日志
```bash
tail -f sync_timer.log
```

## ⚠️ 常见问题

### 问题 1: API 连接失败
**症状**: "API连接失败" 或 "未找到数据库"
**解决方案**:
1. 检查 API 密钥是否正确
2. 确认集成已分享到工作空间
3. 检查网络连接

### 问题 2: 标题提取错误
**症状**: 文件名使用摘要而非标题
**解决方案**:
1. 运行调试脚本查看页面结构
2. 确认页面有 "标题" 或 "Title" 属性
3. 修改 `real_notion_checker.py` 中的 `get_page_title` 函数

### 问题 3: 权限不足
**症状**: "没有找到数据库"
**解决方案**:
1. 将集成分享到所有需要同步的数据库
2. 检查数据库是否在集成的工作空间内

## 📈 API 限制

### 速率限制
- 普通用户: 约 3 请求/秒
- 集成: 可能有额外限制

### 数据限制
- 页面内容: 可能有限制（通常足够）
- 数据库查询: 分页支持

## 🔄 同步逻辑

### 增量同步
- 只同步最近 24 小时内编辑的页面
- 避免重复导出相同内容
- 基于 `last_edited_time` 时间戳

### 文件命名
- 使用页面标题作为文件名
- 特殊字符自动过滤
- 避免文件名冲突

### 目录结构
```
obsidian_root/
└── notion/
    └── YYYY-MM/
        ├── 文章标题1.md
        ├── 文章标题2.md
        └── ...
```

## 🛠️ 高级配置

### 自定义标题提取
修改 `scripts/real_notion_checker.py` 中的 `get_page_title` 函数:
```python
# 添加你的特定属性名
preferred_title_names = ['标题', 'Title', '名称', 'Name', '你的属性名']
```

### 自定义导出格式
修改 `save_page_as_markdown` 函数来自定义 Markdown 格式。

### 扩展功能
1. 图片下载
2. 标签同步
3. 双向同步
4. 多数据库支持

## 📚 参考资源

### 官方文档
- [Notion API Documentation](https://developers.notion.com)
- [API Reference](https://developers.notion.com/reference)
- [Integration Guide](https://developers.notion.com/docs/getting-started)

### 社区资源
- [OpenClaw 社区](https://discord.com/invite/clawd)
- [ClawHub 技能市场](https://clawhub.com)
- [GitHub 仓库](https://github.com/openclaw/openclaw)

## 🆘 技术支持

### 获取帮助
1. 查看日志文件 `sync_timer.log`
2. 运行调试脚本
3. 联系 OpenClaw 社区

### 报告问题
1. 描述具体问题
2. 提供相关日志
3. 说明复现步骤

---

**最后更新**: 2026-02-24  
**版本**: notion-sync-obsidian v1.0.0  
**维护者**: OpenClaw 社区

path: scripts/debug_page_structure.py
content: #!/usr/bin/env python3
"""
调试Notion页面结构 - 查看页面属性和内容结构
"""

import os
import json
import sys
import requests

# 添加当前目录到Python路径
sys.path.append(os.path.dirname(__file__))

def load_config():
    """加载配置文件"""
    config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        
        notion_config = config.get('notion', {})
        return {
            'NOTION_API_KEY': notion_config.get('api_key', ''),
            'NOTION_VERSION': notion_config.get('api_version', '2022-06-28')
        }
    except Exception as e:
        print(f"❌ 加载配置文件失败: {e}")
        return {
            'NOTION_API_KEY': '',
            'NOTION_VERSION': '2022-06-28'
        }

# 加载配置
CONFIG = load_config()
NOTION_API_KEY = CONFIG['NOTION_API_KEY']
NOTION_VERSION = CONFIG['NOTION_VERSION']

# 请求头
HEADERS = {
    'Authorization': f'Bearer {NOTION_API_KEY}',
    'Notion-Version': NOTION_VERSION,
    'Content-Type': 'application/json'
}

def search_pages():
    """搜索页面并显示属性结构"""
    print("🔍 调试Notion页面结构")
    print("=" * 60)
    
    # 检查API密钥
    if not NOTION_API_KEY or NOTION_API_KEY.startswith('ntn_your_api_key'):
        print("❌ 请先在config.json中配置正确的Notion API密钥")
        return
    
    print("✅ 使用配置的Notion API密钥")
    print("=" * 60)
    
    try:
        response = requests.post(
            "https://api.notion.com/v1/search",
            headers=HEADERS,
            json={
                "filter": {"value": "page", "property": "object"},
                "sort": {"direction": "descending", "timestamp": "last_edited_time"},
                "page_size": 3
            },
            timeout=30
        )
        response.raise_for_status()
        
        data = response.json()
        pages = data.get('results', [])
        
        print(f"✅ 找到 {len(pages)} 个页面")
        print("=" * 60)
        
        for i, page in enumerate(pages):
            page_id = page['id']
            print(f"\n📄 页面 {i+1}: {page_id}")
            print(f"   创建时间: {page.get('created_time')}")
            print(f"   最后编辑: {page.get('last_edited_time')}")
            print(f"   URL: {page.get('url', '')}")
            
            # 显示所有属性
            properties = page.get('properties', {})
            print(f"   属性数量: {len(properties)}")
            
            for prop_name, prop_value in properties.items():
                prop_type = prop_value.get('type', 'unknown')
                print(f"   - {prop_name} ({prop_type}):")
                
                if prop_type == 'title':
                    title_items = prop_value.get('title', [])
                    if title_items:
                        title_text = ''.join([item.get('plain_text', '') for item in title_items])
                        print(f"      标题内容: {title_text[:50]}...")
                
                elif prop_type == 'rich_text':
                    text_items = prop_value.get('rich_text', [])
                    if text_items:
                        text_content = ''.join([item.get('plain_text', '') for item in text_items])
                        print(f"      文本内容: {text_content[:50]}...")
                
                elif prop_type == 'multi_select':
                    options = prop_value.get('multi_select', [])
                    if options:
                        option_names = [opt.get('name', '') for opt in options]
                        print(f"      选项: {', '.join(option_names)}")
                
                elif prop_type == 'date':
                    date_value = prop_value.get('date', {})
                    if date_value:
                        print(f"      日期: {date_value.get('start')}")
                
                elif prop_type == 'url':
                    url_value = prop_value.get('url', '')
                    if url_value:
                        print(f"      URL: {url_value}")
                
                elif prop_type == 'relation':
                    relation_items = prop_value.get('relation', [])
                    if relation_items:
                        print(f"      关联数量: {len(relation_items)}")
            
            print("-" * 40)
            
            # 显示前几个内容块
            print("   内容块预览:")
            try:
                blocks_response = requests.get(
                    f"https://api.notion.com/v1/blocks/{page_id}/children",
                    headers=HEADERS,
                    params={"page_size": 3},
                    timeout=30
                )
                blocks_response.raise_for_status()
                
                blocks_data = blocks_response.json()
                blocks = blocks_data.get('results', [])
                
                for j, block in enumerate(blocks[:2]):  # 只显示前2个块
                    block_type = block.get('type')
                    print(f"     [{j+1}] {block_type}")
                    
                    if block_type == 'paragraph':
                        rich_text = block.get('paragraph', {}).get('rich_text', [])
                        if rich_text:
                            text = ''.join([item.get('plain_text', '') for item in rich_text])
                            print(f"        文本: {text[:50]}...")
                    
                    elif block_type == 'heading_1':
                        rich_text = block.get('heading_1', {}).get('rich_text', [])
                        if rich_text:
                            text = ''.join([item.get('plain_text', '') for item in rich_text])
                            print(f"        标题: {text[:50]}...")
            
            except Exception as e:
                print(f"     获取内容块失败: {e}")
            
            print("=" * 60)
    
    except Exception as e:
        print(f"❌ 搜索失败: {e}")
        print("请检查:")
        print("1. API密钥是否正确")
        print("2. 集成是否已分享到Notion工作空间")
        print("3. 网络连接是否正常")

def main():
    """主函数"""
    search_pages()
    
    print("\n📝 调试信息总结:")
    print("=" * 60)
    print("1. 检查页面是否有 '标题' 或 'Title' 属性")
    print("2. 确认属性类型为 'title' (不是 'rich_text')")
    print("3. 检查 '摘要' 或其他文本属性是否干扰标题提取")
    print("4. 确保Python检查器使用正确的属性名提取标题")
    print("=" * 60)
    print("\n🔧 修复建议:")
    print("如果标题提取不正确，请:")
    print("1. 修改 real_notion_checker.py 中的 get_page_title 函数")
    print("2. 添加你的特定属性名到 preferred_title_names 列表")
    print("3. 重新启动定时同步系统")
    print("=" * 60)

if __name__ == "__main__":
    main()

path: scripts/list_recent_articles.sh
content: #!/bin/bash
# 列出Notion中的最近文章

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$SKILL_DIR/config.json"

# 加载配置
if [ ! -f "$CONFIG_FILE" ]; then
    echo "❌ 配置文件不存在: $CONFIG_FILE"
    exit 1
fi

# 从配置读取API密钥
[REDACTED_SECRET] -o '"api_key": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
TIMEZONE=$(grep -o '"timezone": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)

# 设置默认值
[ -z "$TIMEZONE" ] && TIMEZONE="Asia/Shanghai"

# 检查API密钥
if [ -z "$API_KEY" ] || [ "$API_KEY" = "ntn_your_api_key_here" ]; then
    echo "❌ 请先在config.json中配置正确的Notion API密钥"
    exit 1
fi

echo "📋 列出Notion最近文章"
echo "========================================"
echo "时区: $TIMEZONE"
echo "API密钥: ${[REDACTED_SECRET]"
echo "========================================"
echo ""

# 搜索最近的文章
echo "🔍 搜索最近编辑的文章..."
RESPONSE=$(curl -s -X POST "https://api.notion.com/v1/search" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Notion-Version: 2022-06-28" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
      "value": "page",
      "property": "object"
    },
    "sort": {
      "direction": "descending",
      "timestamp": "last_edited_time"
    },
    "page_size": 10
  }')

# 检查响应
if echo "$RESPONSE" | grep -q '"object":"list"'; then
    echo "✅ 成功获取文章列表"
    echo ""
    
    # 提取文章信息
    echo "📄 最近10篇文章:"
    echo ""
    
    # 使用jq解析JSON（如果可用），否则使用grep
    if command -v jq >/dev/null 2>&1; then
        echo "$RESPONSE" | jq -r '.results[] | "\(.last_edited_time) | \(.id) | \(.properties["标题"]?.title[0]?.plain_text // .properties["Title"]?.title[0]?.plain_text // "未命名页面")"' | while read -r line; do
            # 解析时间
            TIME_PART=$(echo "$line" | cut -d'|' -f1 | xargs)
            PAGE_ID=$(echo "$line" | cut -d'|' -f2 | xargs)
            TITLE=$(echo "$line" | cut -d'|' -f3- | xargs)
            
            # 转换时间格式
            LOCAL_TIME=$(TZ="$TIMEZONE" date -d "$TIME_PART" '+%Y-%m-%d %H:%M' 2>/dev/null || echo "$TIME_PART")
            
            echo "  📝 $TITLE"
            echo "     🆔 ${PAGE_ID:0:8}..."
            echo "     🕒 $LOCAL_TIME"
            echo ""
        done
    else
        # 简单解析
        echo "$RESPONSE" | grep -E '"last_edited_time":"[^"]+"|"id":"[^"]+"|"plain_text":"[^"]+"' | \
        while read -r line1 && read -r line2 && read -r line3; do
            TIME=$(echo "$line1" | cut -d'"' -f4)
            ID=$(echo "$line2" | cut -d'"' -f4)
            TITLE=$(echo "$line3" | cut -d'"' -f4)
            
            LOCAL_TIME=$(TZ="$TIMEZONE" date -d "$TIME" '+%Y-%m-%d %H:%M' 2>/dev/null || echo "$TIME")
            
            echo "  📝 $TITLE"
            echo "     🆔 ${ID:0:8}..."
            echo "     🕒 $LOCAL_TIME"
            echo ""
        done
    fi
    
    # 统计总数
    TOTAL=$(echo "$RESPONSE" | grep -o '"has_more":[^,]*' | head -1 | grep -o '[0-9]*')
    if [ -n "$TOTAL" ] && [ "$TOTAL" -gt 10 ]; then
        echo "📊 提示: 还有更多文章（共 $TOTAL+ 篇）"
    fi
    
else
    echo "❌ 获取文章列表失败"
    echo "响应:"
    echo "$RESPONSE" | head -5
fi

echo ""
echo "========================================"
echo "📋 使用完整Python检查器获取更多信息:"
echo "   python3 $SCRIPT_DIR/real_notion_checker.py"
echo "========================================"

path: scripts/real_notion_checker.py
content: #!/usr/bin/env python3
"""
真实Notion API文章检查器
使用真实的Notion API检查文章更新并导出到Obsidian
"""

import os
import json
import sys
import requests
from datetime import datetime, timedelta
import pytz
from pathlib import Path

# 添加当前目录到Python路径
sys.path.append(os.path.dirname(__file__))

def load_config():
    """加载配置文件"""
    config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        
        # 设置默认值
        notion_config = config.get('notion', {})
        obsidian_config = config.get('obsidian', {})
        sync_config = config.get('sync', {})
        export_config = config.get('export', {})
        logging_config = config.get('logging', {})
        
        return {
            'NOTION_API_KEY': notion_config.get('api_key', ''),
            'NOTION_VERSION': notion_config.get('api_version', '2022-06-28'),
            'OBSIDIAN_ROOT': obsidian_config.get('root_dir', '/path/to/your/obsidian'),
            'CHECK_INTERVAL': sync_config.get('check_interval_minutes', 15),
            'QUIET_START': sync_config.get('quiet_hours_start', '00:00'),
            'QUIET_END': sync_config.get('quiet_hours_end', '08:30'),
            'TIMEZONE': export_config.get('timezone', 'Asia/Shanghai'),
            'LOG_FILE': logging_config.get('log_file', 'sync_timer.log')
        }
    except Exception as e:
        print(f"❌ 加载配置文件失败: {e}")
        # 返回默认配置
        return {
            'NOTION_API_KEY': '',
            'NOTION_VERSION': '2022-06-28',
            'OBSIDIAN_ROOT': '/path/to/your/obsidian',
            'CHECK_INTERVAL': 15,
            'QUIET_START': '00:00',
            'QUIET_END': '08:30',
            'TIMEZONE': 'Asia/Shanghai',
            'LOG_FILE': 'sync_timer.log'
        }

# 加载配置
CONFIG = load_config()

# 配置常量
NOTION_API_KEY = CONFIG['NOTION_API_KEY']
NOTION_VERSION = CONFIG['NOTION_VERSION']
OBSIDIAN_ROOT = CONFIG['OBSIDIAN_ROOT']
TIMEZONE = pytz.timezone(CONFIG['TIMEZONE'])

# 请求头
HEADERS = {
    'Authorization': f'Bearer {NOTION_API_KEY}',
    'Notion-Version': NOTION_VERSION,
    'Content-Type': 'application/json'
}

def get_notion_databases():
    """获取Notion中的所有数据库"""
    try:
        print("📋 获取Notion数据库列表...")
        response = requests.post(
            'https://api.notion.com/v1/search',
            headers=HEADERS,
            json={
                'filter': {'property': 'object', 'value': 'database'},
                'sort': {'direction': 'descending', 'timestamp': 'last_edited_time'}
            },
            timeout=30
        )
        response.raise_for_status()
        databases = response.json().get('results', [])
        print(f"✅ 找到 {len(databases)} 个数据库")
        return databases
    except Exception as e:
        print(f"❌ 获取数据库失败: {e}")
        return []

def get_page_title(page):
    """从页面属性中提取标题（修复版本）"""
    properties = page.get('properties', {})
    
    # 优先查找常见的标题属性名（中文和英文）
    preferred_title_names = ['标题', 'Title', '名称', 'Name', 'title', '名称', '標題']
    
    # 1. 首先按属性名查找
    for prop_name, prop_value in properties.items():
        if prop_name in preferred_title_names:
            prop_type = prop_value.get('type')
            if prop_type == 'title':
                title_items = prop_value.get('title', [])
                if title_items:
                    title_text = ''.join([item.get('plain_text', '') for item in title_items])
                    if title_text.strip():
                        return title_text
    
    # 2. 然后按类型查找（title类型）
    for prop_name, prop_value in properties.items():
        prop_type = prop_value.get('type')
        if prop_type == 'title':
            title_items = prop_value.get('title', [])
            if title_items:
                title_text = ''.join([item.get('plain_text', '') for item in title_items])
                if title_text.strip():
                    return title_text
    
    # 3. 如果没有找到合适的标题，使用页面ID
    return f"未命名页面_{page['id'][:8]}"

def get_page_blocks(page_id):
    """获取页面的所有内容块"""
    try:
        print(f"    获取页面内容块 {page_id[:8]}...")
        all_blocks = []
        has_more = True
        start_cursor = None
        
        while has_more:
            params = {}
            if start_cursor:
                params['start_cursor'] = start_cursor
            
            response = requests.get(
                f'https://api.notion.com/v1/blocks/{page_id}/children',
                headers=HEADERS,
                params=params,
                timeout=30
            )
            response.raise_for_status()
            
            data = response.json()
            all_blocks.extend(data.get('results', []))
            has_more = data.get('has_more', False)
            start_cursor = data.get('next_cursor')
        
        print(f"    获取到 {len(all_blocks)} 个内容块")
        return all_blocks
    except Exception as e:
        print(f"❌ 获取内容块失败: {e}")
        return []

def extract_rich_text(rich_text):
    """提取富文本内容并应用格式"""
    if not rich_text:
        return ''
    
    text_parts = []
    for text_item in rich_text:
        text_content = text_item.get('plain_text', '')
        annotations = text_item.get('annotations', {})
        
        # 应用文本格式
        if annotations.get('bold'):
            text_content = f'**{text_content}**'
        if annotations.get('italic'):
            text_content = f'*{text_content}*'
        if annotations.get('strikethrough'):
            text_content = f'~~{text_content}~~'
        if annotations.get('code'):
            text_content = f'`{text_content}`'
        
        # 处理链接
        if text_item.get('href'):
            text_content = f'[{text_content}]({text_item["href"]})'
        
        text_parts.append(text_content)
    
    return ''.join(text_parts)

def convert_block_to_markdown(block, indent_level=0):
    """将单个Notion块转换为Markdown"""
    block_type = block.get('type')
    block_content = block.get(block_type, {})
    
    # 缩进
    indent = '  ' * indent_level
    
    if block_type == 'paragraph':
        text = extract_rich_text(block_content.get('rich_text', []))
        return f"{indent}{text}\n" if text else ''
    
    elif block_type == 'heading_1':
        text = extract_rich_text(block_content.get('rich_text', []))
        return f"{indent}# {text}\n\n" if text else ''
    
    elif block_type == 'heading_2':
        text = extract_rich_text(block_content.get('rich_text', []))
        return f"{indent}## {text}\n\n" if text else ''
    
    elif block_type == 'heading_3':
        text = extract_rich_text(block_content.get('rich_text', []))
        return f"{indent}### {text}\n\n" if text else ''
    
    elif block_type == 'bulleted_list_item':
        text = extract_rich_text(block_content.get('rich_text', []))
        bullet = f"{indent}* "
        return f"{bullet}{text}\n" if text else ''
    
    elif block_type == 'numbered_list_item':
        text = extract_rich_text(block_content.get('rich_text', []))
        number = f"{indent}1. "
        return f"{number}{text}\n" if text else ''
    
    elif block_type == 'to_do':
        text = extract_rich_text(block_content.get('rich_text', []))
        checked = block_content.get('checked', False)
        checkbox = '[x]' if checked else '[ ]'
        return f"{indent}{checkbox} {text}\n" if text else ''
    
    elif block_type == 'code':
        text = extract_rich_text(block_content.get('rich_text', []))
        language = block_content.get('language', '')
        return f"{indent}```{language}\n{indent}{text}\n{indent}```\n\n" if text else ''
    
    elif block_type == 'quote':
        text = extract_rich_text(block_content.get('rich_text', []))
        return f"{indent}> {text}\n\n" if text else ''
    
    elif block_type == 'divider':
        return f"{indent}---\n\n"
    
    elif block_type == 'image':
        image_url = block_content.get('external', {}).get('url') or block_content.get('file', {}).get('url')
        if image_url:
            return f"{indent}![Image]({image_url})\n\n"
    
    elif block_type == 'bookmark':
        url = block_content.get('url', '')
        if url:
            return f"{indent}[Bookmark]({url})\n\n"
    
    return ''

def save_page_as_markdown(page, markdown_content):
    """将页面保存为Markdown文件"""
    try:
        # 创建年-月目录
        now = datetime.now(TIMEZONE)
        year_month = now.strftime('%Y-%m')
        
        # 创建notion子目录
        target_dir = Path(OBSIDIAN_ROOT) / 'notion' / year_month
        target_dir.mkdir(parents=True, exist_ok=True)
        
        # 获取页面信息
        page_id = page['id']
        page_title = get_page_title(page)
        created_time = page.get('created_time', '')
        last_edited_time = page.get('last_edited_time', '')
        url = page.get('url', '')
        
        # 生成安全的文件名
        safe_title = ''.join(c for c in page_title if c.isalnum() or c in (' ', '-', '_', '，', '。')).strip()
        safe_title = safe_title[:80] or 'untitled'
        
        filename = f"{safe_title}.md"
        filepath = target_dir / filename
        
        # 避免文件名冲突
        counter = 1
        while filepath.exists():
            filename = f"{safe_title}_{counter}.md"
            filepath = target_dir / filename
            counter += 1
        
        # 创建完整的Markdown内容
        full_content = f"""---
title: {page_title}
notion_id: {page_id}
created_time: {created_time}
last_edited_time: {last_edited_time}
original_url: {url}
export_time: {now.isoformat()}
---

# {page_title}

{markdown_content}
"""
        
        # 保存文件
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(full_content)
        
        print(f"✅ 保存: {page_title} -> {filepath}")
        return str(filepath)
        
    except Exception as e:
        print(f"❌ 保存文件失败: {e}")
        return None

def main():
    """主函数"""
    print("🚀 真实Notion API文章检查器")
    print("=" * 50)
    
    # 检查API密钥
    if not NOTION_API_KEY or NOTION_API_KEY.startswith('ntn_your_api_key'):
        print("❌ 请先在config.json中配置正确的Notion API密钥")
        return
    
    print(f"✅ 使用配置的Notion API密钥")
    print(f"时区: {TIMEZONE}")
    print(f"导出目录: {OBSIDIAN_ROOT}/notion/")
    print(f"当前时间: {datetime.now(TIMEZONE).strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 50)
    
    # 获取数据库
    databases = get_notion_databases()
    if not databases:
        print("❌ 没有找到数据库，检查API权限")
        return
    
    exported_files = []
    
    # 检查每个数据库
    for db in databases:
        db_id = db['id']
        db_title = db.get('title', [{}])[0].get('plain_text', '未命名数据库')
        
        print(f"\n📁 检查数据库: {db_title}")
        
        # 查询最近编辑的页面（最近24小时）
        last_check_time = datetime.now(TIMEZONE) - timedelta(hours=24)
        
        try:
            response = requests.post(
                f'https://api.notion.com/v1/databases/{db_id}/query',
                headers=HEADERS,
                json={
                    'filter': {
                        'timestamp': 'last_edited_time',
                        'last_edited_time': {
                            'after': last_check_time.isoformat()
                        }
                    }
                },
                timeout=30
            )
            response.raise_for_status()
            pages = response.json().get('results', [])
        except Exception as e:
            print(f"❌ 查询数据库失败: {e}")
            continue
        
        print(f"  找到 {len(pages)} 个最近编辑的页面")
        
        if not pages:
            print("  没有最近编辑的页面")
            continue
        
        # 处理每个页面
        for page in pages:
            page_id = page['id']
            page_title = get_page_title(page)
            
            print(f"  📄 处理页面: {page_title}")
            
            # 获取页面内容块
            blocks = get_page_blocks(page_id)
            if not blocks:
                print(f"    页面没有内容块")
                continue
            
            # 转换为Markdown
            markdown_content = []
            for block in blocks:
                content = convert_block_to_markdown(block)
                if content:
                    markdown_content.append(content)
            
            if not markdown_content:
                print(f"    页面内容为空")
                continue
            
            full_markdown = ''.join(markdown_content)
            
            # 保存为文件
            filepath = save_page_as_markdown(page, full_markdown)
            if filepath:
                exported_files.append((page_title, filepath))
    
    # 输出结果
    print("\n" + "=" * 50)
    print("📊 检查完成")
    print(f"处理数据库: {len(databases)} 个")
    print(f"导出文章: {len(exported_files)} 篇")
    
    if exported_files:
        print("\n📁 导出的文章:")
        for title, path in exported_files:
            print(f"  ✅ {title}")
        
        # 发送移动端优化通知
        print("\n📱 移动端通知格式:")
        for title, path in exported_files:
            print(f"""
📱 Notion文章更新通知

📄 文章标题: {title}
📁 保存位置: {path}
🕒 更新时间: {datetime.now(TIMEZONE).strftime('%Y-%m-%d %H:%M')}
🔗 原始链接: [Notion页面]

✅ 文章已成功导出到Obsidian
""")
    else:
        print("📭 没有发现新的文章更新")
    
    print("=" * 50)

if __name__ == "__main__":
    main()

path: scripts/simple_checker.sh
content: #!/bin/bash
# 简化版Notion检查器 - 快速测试API连接和基本功能

echo "🧪 Notion同步系统 - 简化检查器"
echo "========================================"

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$SKILL_DIR/config.json"

# 加载配置
if [ ! -f "$CONFIG_FILE" ]; then
    echo "❌ 配置文件不存在: $CONFIG_FILE"
    echo "请先复制 config.json.example 为 config.json 并配置"
    exit 1
fi

# 从配置读取参数
[REDACTED_SECRET] -o '"api_key": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
OBSIDIAN_ROOT=$(grep -o '"root_dir": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
TIMEZONE=$(grep -o '"timezone": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)

# 设置默认值
[ -z "$TIMEZONE" ] && TIMEZONE="Asia/Shanghai"
[ -z "$OBSIDIAN_ROOT" ] && OBSIDIAN_ROOT="/path/to/your/obsidian"

# 获取当前时间
CURRENT_DATETIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')
YEAR_MONTH=$(TZ="$TIMEZONE" date '+%Y-%m')

# 创建目录
NOTION_DIR="$OBSIDIAN_ROOT/notion"
mkdir -p "$NOTION_DIR/$YEAR_MONTH"

echo "✅ 时区: $TIMEZONE"
echo "✅ 当前时间: $CURRENT_DATETIME"
echo "✅ 导出目录: $NOTION_DIR/$YEAR_MONTH"
echo "========================================"

# 检查API密钥
if [ -z "$API_KEY" ] || [ "$API_KEY" = "ntn_your_api_key_here" ]; then
    echo "❌ 请先在config.json中配置正确的Notion API密钥"
    echo "   获取地址: https://notion.so/my-integrations"
    exit 1
fi

echo "✅ API密钥: ${[REDACTED_SECRET]"

# 测试API连接
echo ""
echo "🔍 测试Notion API连接..."
TEST_RESPONSE=$(curl -s -X GET "https://api.notion.com/v1/users/me" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Notion-Version: 2022-06-28" \
  -H "Content-Type: application/json" 2>/dev/null)

if echo "$TEST_RESPONSE" | grep -q '"object":"user"'; then
    USER_NAME=$(echo "$TEST_RESPONSE" | grep -o '"name":"[^"]*' | cut -d'"' -f4)
    WORKSPACE=$(echo "$TEST_RESPONSE" | grep -o '"workspace_name":"[^"]*' | cut -d'"' -f4)
    echo "✅ API连接成功"
    echo "   用户: ${USER_NAME:-未知}"
    echo "   工作空间: ${WORKSPACE:-未知}"
    
    # 创建测试文件
    echo ""
    echo "📝 创建测试文件..."
    
    TEST_TITLE="Notion同步测试_$(date +%Y%m%d_%H%M%S)"
    SAFE_TITLE=$(echo "$TEST_TITLE" | tr -cd '[:alnum:]_-')
    FILENAME="$NOTION_DIR/$YEAR_MONTH/${SAFE_TITLE}.md"
    
    cat > "$FILENAME" << EOF
---
title: $TEST_TITLE
notion_id: test_$(date +%s)
created_time: $CURRENT_DATETIME
last_edited_time: $CURRENT_DATETIME
export_time: $CURRENT_DATETIME
export_version: notion-sync-obsidian-v1.0
---

# $TEST_TITLE

这是Notion同步系统的测试文章。

## 系统信息

- **测试时间**: $CURRENT_DATETIME
- **时区**: $TIMEZONE
- **API用户**: ${USER_NAME:-未知}
- **工作空间**: ${WORKSPACE:-未知}
- **系统版本**: notion-sync-obsidian v1.0

## 功能特性

### ✅ 核心功能
1. **自动同步** - 定时检查Notion更新
2. **完整导出** - 导出文章标题和内容
3. **智能标题** - 正确提取文章原始标题
4. **移动通知** - 移动端优化格式

### ⚙️ 配置选项
- 检查频率: 可配置 (默认15分钟)
- 安静时段: 可配置 (默认00:00-08:30)
- 导出目录: 可自定义
- 通知格式: 移动端优化

## 使用说明

### 启动定时同步
\`\`\`bash
cd $SKILL_DIR
./scripts/start_timer.sh
\`\`\`

### 手动检查 (忽略安静时段)
\`\`\`bash
FORCE_CHECK=1 ./scripts/simple_checker.sh
\`\`\`

### 查看状态
\`\`\`bash
./scripts/status_timer.sh
\`\`\`

## 技术支持

技能名称: notion-sync-obsidian
版本: 1.0.0
维护者: OpenClaw社区

---
*测试完成时间: $CURRENT_DATETIME*
*系统状态: 正常 ✅*
EOF
    
    echo "✅ 创建测试文件: $(basename "$FILENAME")"
    echo "📁 文件路径: $FILENAME"
    
    echo ""
    echo "📱 移动端通知示例:"
    echo ""
    echo "📱 Notion同步系统测试成功"
    echo ""
    echo "📄 测试标题: $TEST_TITLE"
    echo "👤 API用户: ${USER_NAME:-未知}"
    echo "🏢 工作空间: ${WORKSPACE:-未知}"
    echo "📁 保存位置: $YEAR_MONTH/$(basename "$FILENAME")"
    echo "🕒 测试时间: $CURRENT_DATETIME"
    echo ""
    echo "✅ 系统连接正常"
    echo "🚀 可以开始使用定时同步功能"
    
else
    echo "❌ API连接失败"
    echo "请检查:"
    echo "1. API密钥是否正确"
    echo "2. 集成是否已分享到Notion工作空间"
    echo "3. 网络连接是否正常"
    echo ""
    echo "响应详情:"
    echo "$TEST_RESPONSE" | head -5
fi

echo ""
echo "========================================"
echo "🎉 简化检查器完成"
echo "📊 下一步: 配置定时同步"
echo "   ./scripts/start_timer.sh"
echo "========================================"

path: scripts/start_timer.sh
content: #!/bin/bash
# 启动Notion定时同步

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
PID_FILE="$SKILL_DIR/sync_timer.pid"
LOG_FILE="$SKILL_DIR/sync_timer.log"

# 检查是否已经在运行
if [ -f "$PID_FILE" ]; then
    PID=$(cat "$PID_FILE")
    if ps -p "$PID" > /dev/null 2>&1; then
        echo "⚠️  定时同步已经在运行 (PID: $PID)"
        echo "   使用 ./scripts/stop_timer.sh 停止"
        echo "   使用 ./scripts/status_timer.sh 查看状态"
        exit 1
    else
        echo "🔄 清理旧的PID文件..."
        rm -f "$PID_FILE"
    fi
fi

# 从配置读取检查间隔
CONFIG_FILE="$SKILL_DIR/config.json"
if [ -f "$CONFIG_FILE" ]; then
    CHECK_INTERVAL=$(grep -o '"check_interval_minutes":[^,}]*' "$CONFIG_FILE" | grep -o '[0-9]*')
    [ -z "$CHECK_INTERVAL" ] && CHECK_INTERVAL=15
else
    CHECK_INTERVAL=15
fi

# 获取当前时间
TIMEZONE="Asia/Shanghai"
START_TIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')

echo "🚀 启动Notion定时同步..."
echo "启动时间: $START_TIME"
echo "时区: $TIMEZONE"
echo "检查间隔: $CHECK_INTERVAL 分钟"
echo "日志文件: $LOG_FILE"

# 启动定时检查器
cd "$SKILL_DIR"
nohup bash "$SCRIPT_DIR/timer_checker.sh" > "$LOG_FILE" 2>&1 &
TIMER_PID=$!

# 保存PID
echo "$TIMER_PID" > "$PID_FILE"

# 等待进程启动
sleep 2

# 检查是否启动成功
if ps -p "$TIMER_PID" > /dev/null 2>&1; then
    echo "✅ 定时同步已启动"
    echo "📋 定时进程PID: $TIMER_PID"
    echo ""
    echo "📋 使用以下命令管理:"
    echo "   查看日志: tail -f $LOG_FILE"
    echo "   停止同步: ./scripts/stop_timer.sh"
    echo "   查看状态: ./scripts/status_timer.sh"
    echo "   手动检查: FORCE_CHECK=1 ./scripts/simple_checker.sh"
else
    echo "❌ 定时同步启动失败"
    rm -f "$PID_FILE"
    exit 1
fi

path: scripts/status_timer.sh
content: #!/bin/bash
# 查看Notion定时同步状态

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
PID_FILE="$SKILL_DIR/sync_timer.pid"
LOG_FILE="$SKILL_DIR/sync_timer.log"
CONFIG_FILE="$SKILL_DIR/config.json"

# 获取当前时间
TIMEZONE="Asia/Shanghai"
CHECK_TIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')

echo "📊 Notion定时同步状态检查"
echo "检查时间: $CHECK_TIME"
echo "时区: $TIMEZONE"
echo "========================================"

# 检查配置文件
if [ -f "$CONFIG_FILE" ]; then
    echo "📁 配置文件: $CONFIG_FILE"
    
    # 读取配置信息
    CHECK_INTERVAL=$(grep -o '"check_interval_minutes":[^,}]*' "$CONFIG_FILE" | grep -o '[0-9]*')
    QUIET_START=$(grep -o '"quiet_hours_start": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
    QUIET_END=$(grep -o '"quiet_hours_end": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
    OBSIDIAN_ROOT=$(grep -o '"root_dir": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
    
    [ -z "$CHECK_INTERVAL" ] && CHECK_INTERVAL=15
    [ -z "$QUIET_START" ] && QUIET_START="00:00"
    [ -z "$QUIET_END" ] && QUIET_END="08:30"
    [ -z "$OBSIDIAN_ROOT" ] && OBSIDIAN_ROOT="/path/to/your/obsidian"
    
    echo "⏰ 检查间隔: $CHECK_INTERVAL 分钟"
    echo "🤫 安静时段: $QUIET_START - $QUIET_END"
    echo "📁 Obsidian目录: $OBSIDIAN_ROOT/notion/"
else
    echo "⚠️  配置文件不存在: $CONFIG_FILE"
fi

echo ""

# 检查进程状态
if [ -f "$PID_FILE" ]; then
    PID=$(cat "$PID_FILE")
    
    if ps -p "$PID" > /dev/null 2>&1; then
        # 获取进程运行时间
        START_TIME=$(ps -o lstart= -p "$PID" 2>/dev/null | xargs)
        if [ -n "$START_TIME" ]; then
            START_EPOCH=$(date -d "$START_TIME" +%s 2>/dev/null || echo 0)
            CURRENT_EPOCH=$(date +%s)
            RUN_SECONDS=$((CURRENT_EPOCH - START_EPOCH))
            
            # 转换为可读格式
            RUN_DAYS=$((RUN_SECONDS / 86400))
            RUN_HOURS=$(( (RUN_SECONDS % 86400) / 3600 ))
            RUN_MINUTES=$(( (RUN_SECONDS % 3600) / 60 ))
            RUN_SECONDS=$((RUN_SECONDS % 60))
            
            RUN_TIME=""
            [ $RUN_DAYS -gt 0 ] && RUN_TIME="${RUN_TIME}${RUN_DAYS}天 "
            [ $RUN_HOURS -gt 0 ] && RUN_TIME="${RUN_TIME}${RUN_HOURS}小时 "
            [ $RUN_MINUTES -gt 0 ] && RUN_TIME="${RUN_TIME}${RUN_MINUTES}分钟 "
            RUN_TIME="${RUN_TIME}${RUN_SECONDS}秒"
        else
            RUN_TIME="未知"
        fi
        
        echo "🟢 状态: 运行中"
        echo "进程PID: $PID"
        echo "运行时间: $RUN_TIME"
    else
        echo "🔴 状态: PID文件存在但进程未运行"
        echo "进程PID: $PID (进程不存在)"
        echo "⚠️  建议清理: rm -f $PID_FILE"
    fi
else
    echo "🔴 状态: 未运行"
    echo "进程PID: 无"
fi

echo ""

# 检查日志文件
if [ -f "$LOG_FILE" ]; then
    echo "📋 日志信息:"
    echo "日志文件: $LOG_FILE"
    
    # 统计日志行数
    LOG_LINES=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
    echo "日志行数: $LOG_LINES"
    
    # 获取日志大小
    LOG_SIZE=$(du -h "$LOG_FILE" 2>/dev/null | cut -f1)
    echo "日志大小: $LOG_SIZE"
    
    echo ""
    echo "最近5条记录:"
    echo ""
    tail -n 10 "$LOG_FILE" | while IFS= read -r line; do
        echo "  $line"
    done
else
    echo "📭 日志文件不存在"
fi

echo ""

# 检查导出目录
if [ -f "$CONFIG_FILE" ] && [ -n "$OBSIDIAN_ROOT" ] && [ "$OBSIDIAN_ROOT" != "/path/to/your/obsidian" ]; then
    NOTION_DIR="$OBSIDIAN_ROOT/notion"
    
    if [ -d "$NOTION_DIR" ]; then
        echo "📁 目录状态:"
        echo "文章目录: $NOTION_DIR"
        
        # 统计文章数量
        ARTICLE_COUNT=$(find "$NOTION_DIR" -name "*.md" -type f 2>/dev/null | wc -l)
        echo "文章数量: $ARTICLE_COUNT"
        
        # 获取最新文章
        echo ""
        echo "最新3篇文章:"
        find "$NOTION_DIR" -name "*.md" -type f -exec ls -lt --time-style="+%b %d %H:%M" {} + 2>/dev/null | head -3 | while read -r line; do
            # 提取文件名和修改时间
            FILE_INFO=$(echo "$line" | awk '{print $6, $7, $8}')
            FILE_PATH=$(echo "$line" | awk '{print $9}')
            FILE_NAME=$(basename "$FILE_PATH")
            
            echo "  $FILE_INFO $FILE_NAME"
        done
    else
        echo "📁 目录状态:"
        echo "文章目录: $NOTION_DIR (不存在)"
    fi
fi

echo ""

# 计算下次检查时间（如果正在运行）
if [ -f "$PID_FILE" ] && ps -p "$(cat "$PID_FILE")" > /dev/null 2>&1 && [ -n "$CHECK_INTERVAL" ]; then
    NEXT_CHECK=$(TZ="$TIMEZONE" date -d "+$CHECK_INTERVAL minutes" '+%H:%M')
    echo "⏰ 下次检查时间:"
    echo "   $NEXT_CHECK (${CHECK_INTERVAL}分钟后)"
fi

echo ""
echo "========================================"
echo "📋 管理命令:"
echo "  启动: ./scripts/start_timer.sh"
echo "  停止: ./scripts/stop_timer.sh"
echo "  状态: ./scripts/status_timer.sh"
echo "  手动检查: FORCE_CHECK=1 ./scripts/simple_checker.sh"
echo "========================================"

path: scripts/stop_timer.sh
content: #!/bin/bash
# 停止Notion定时同步

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
PID_FILE="$SKILL_DIR/sync_timer.pid"
LOG_FILE="$SKILL_DIR/sync_timer.log"

# 获取当前时间
TIMEZONE="Asia/Shanghai"
STOP_TIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')

echo "🛑 停止Notion定时同步..."
echo "停止时间: $STOP_TIME"

# 检查PID文件
if [ ! -f "$PID_FILE" ]; then
    echo "📭 没有找到PID文件，定时同步可能未运行"
    echo "   使用 ./scripts/status_timer.sh 查看状态"
    exit 0
fi

# 读取PID
PID=$(cat "$PID_FILE")

# 检查进程是否存在
if ps -p "$PID" > /dev/null 2>&1; then
    echo "🔍 找到运行进程: PID $PID"
    
    # 停止进程
    echo "⏹️  停止进程..."
    kill "$PID"
    
    # 等待进程停止
    sleep 2
    
    # 检查是否已停止
    if ps -p "$PID" > /dev/null 2>&1; then
        echo "⚠️  进程仍在运行，强制停止..."
        kill -9 "$PID"
        sleep 1
    fi
    
    # 最终检查
    if ps -p "$PID" > /dev/null 2>&1; then
        echo "❌ 无法停止进程 $PID"
        echo "   请手动检查: ps -p $PID"
        exit 1
    else
        echo "✅ 停止进程: $PID"
        
        # 清理PID文件
        rm -f "$PID_FILE"
        
        # 记录停止日志
        echo "[$STOP_TIME] 🛑 定时同步已停止" >> "$LOG_FILE"
        
        echo "✅ 定时同步已停止 - $STOP_TIME"
    fi
else
    echo "📭 进程 $PID 不存在，清理PID文件"
    rm -f "$PID_FILE"
    echo "✅ 已清理"
fi

path: scripts/timer_checker.sh
content: #!/bin/bash
# 定时检查器 - 管理定时同步任务

SCRIPT_DIR="$(dirname "$0")"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_FILE="$SKILL_DIR/config.json"
LOG_FILE="$SKILL_DIR/sync_timer.log"
PID_FILE="$SKILL_DIR/sync_timer.pid"

# 加载配置
if [ ! -f "$CONFIG_FILE" ]; then
    echo "❌ 配置文件不存在: $CONFIG_FILE"
    exit 1
fi

# 从配置读取参数
CHECK_INTERVAL=$(grep -o '"check_interval_minutes":[^,}]*' "$CONFIG_FILE" | grep -o '[0-9]*')
QUIET_START=$(grep -o '"quiet_hours_start": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
QUIET_END=$(grep -o '"quiet_hours_end": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
TIMEZONE=$(grep -o '"timezone": "[^"]*' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)

# 设置默认值
[ -z "$CHECK_INTERVAL" ] && CHECK_INTERVAL=15
[ -z "$QUIET_START" ] && QUIET_START="00:00"
[ -z "$QUIET_END" ] && QUIET_END="08:30"
[ -z "$TIMEZONE" ] && TIMEZONE="Asia/Shanghai"

# 计算检查间隔（秒）
CHECK_INTERVAL_SECONDS=$((CHECK_INTERVAL * 60))

echo "⏰ Notion定时同步检查器启动"
echo "========================================"
echo "时区: $TIMEZONE"
echo "检查间隔: $CHECK_INTERVAL 分钟"
echo "安静时段: $QUIET_START - $QUIET_END"
echo "日志文件: $LOG_FILE"
echo "========================================"
echo ""

# 记录启动时间
START_TIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')
echo "[$START_TIME] 🔄 定时同步启动" >> "$LOG_FILE"
echo "[$START_TIME] ⏰ 检查间隔: $CHECK_INTERVAL 分钟" >> "$LOG_FILE"
echo "[$START_TIME] 🤫 安静时段: $QUIET_START - $QUIET_END" >> "$LOG_FILE"

# 主循环
while true; do
    CURRENT_DATETIME=$(TZ="$TIMEZONE" date '+%Y-%m-%d %H:%M:%S')
    CURRENT_TIME=$(TZ="$TIMEZONE" date '+%H:%M')
    
    # 检查是否在安静时段
    IN_QUIET_HOURS=0
    if [[ "$CURRENT_TIME" > "$QUIET_START" ]] && [[ "$CURRENT_TIME" < "$QUIET_END" ]]; then
        IN_QUIET_HOURS=1
    fi
    
    # 检查强制检查标志
    FORCE_CHECK=${FORCE_CHECK:-0}
    
    echo "[$CURRENT_DATETIME] 🔍 检查时间..." | tee -a "$LOG_FILE"
    
    if [ "$IN_QUIET_HOURS" -eq 1 ] && [ "$FORCE_CHECK" -eq 0 ]; then
        echo "[$CURRENT_DATETIME] 🤫 安静时段 ($QUIET_START-$QUIET_END)，跳过检查" | tee -a "$LOG_FILE"
    else
        if [ "$FORCE_CHECK" -eq 1 ]; then
            echo "[$CURRENT_DATETIME] ⚡ 强制检查模式，忽略安静时段" | tee -a "$LOG_FILE"
        fi
        
        # 选择检查脚本
        CHECK_SCRIPT="$SCRIPT_DIR/real_notion_checker.py"
        
        # 运行检查脚本
        if [ -f "$CHECK_SCRIPT" ]; then
            echo "[$CURRENT_DATETIME] 🚀 运行检查脚本..." | tee -a "$LOG_FILE"
            
            # 运行Python检查器
            OUTPUT=$(cd "$SCRIPT_DIR" && python3 "$CHECK_SCRIPT" 2>&1)
            echo "$OUTPUT" >> "$LOG_FILE"
            
            # 检查是否有更新
            if echo "$OUTPUT" | grep -q "✅ 保存:"; then
                echo "[$CURRENT_DATETIME] 📱 发现更新，已导出" | tee -a "$LOG_FILE"
                
                # 提取更新的文章标题
                UPDATED_ARTICLES=$(echo "$OUTPUT" | grep "✅ 保存:" | sed 's/✅ 保存: //' | head -3)
                if [ -n "$UPDATED_ARTICLES" ]; then
                    echo "[$CURRENT_DATETIME] 📄 更新文章:" | tee -a "$LOG_FILE"
                    echo "$UPDATED_ARTICLES" | while read -r article; do
                        echo "[$CURRENT_DATETIME]   - $article" | tee -a "$LOG_FILE"
                    done
                fi
            elif echo "$OUTPUT" | grep -q "📭 没有发现新的文章更新"; then
                echo "[$CURRENT_DATETIME] 📭 没有发现新的文章更新" | tee -a "$LOG_FILE"
            else
                echo "[$CURRENT_DATETIME] ⚠️  检查完成，但可能遇到问题" | tee -a "$LOG_FILE"
            fi
        else
            echo "[$CURRENT_DATETIME] ❌ 检查脚本不存在: $CHECK_SCRIPT" | tee -a "$LOG_FILE"
        fi
        
        # 重置强制检查标志
        FORCE_CHECK=0
    fi
    
    # 计算下次检查时间
    NEXT_CHECK=$(TZ="$TIMEZONE" date -d "+$CHECK_INTERVAL minutes" '+%H:%M')
    echo "[$CURRENT_DATETIME] ⏳ 等待 $CHECK_INTERVAL 分钟..." | tee -a "$LOG_FILE"
    echo "[$CURRENT_DATETIME] 🕐 下次检查: $NEXT_CHECK" | tee -a "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    
    # 等待下次检查
    sleep "$CHECK_INTERVAL_SECONDS"
done

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)