cleanup: remove old reminder system and unused crontab files
Todo management now lives entirely in skills/calendar/ via VTODO + todoman.
This commit is contained in:
17
MEMORY.md
17
MEMORY.md
@@ -30,17 +30,7 @@ _这份文件记录持续性项目和重要状态,跨会话保留。_
|
|||||||
|
|
||||||
## 🎯 活跃项目
|
## 🎯 活跃项目
|
||||||
|
|
||||||
### 1. 每日待办提醒系统
|
### 1. 邮件自动处理系统
|
||||||
**状态**: 迁移中 → VTODO(见 `skills/calendar/MIGRATION.md`)
|
|
||||||
**创建**: 2026-02-15
|
|
||||||
**旧系统**(待清理):
|
|
||||||
- 脚本: `~/.openclaw/workspace/scripts/reminder_check.py`
|
|
||||||
- 文件: `~/.openclaw/workspace/reminders/active.md`
|
|
||||||
**新系统**: 已合并到日历技能(`skills/calendar/`),使用 VTODO + todoman + CalDAV 同步
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 邮件自动处理系统
|
|
||||||
**状态**: 运行中
|
**状态**: 运行中
|
||||||
**创建**: 2026-02-27
|
**创建**: 2026-02-27
|
||||||
**更新**: 2026-03-05(基于学习机制优化)
|
**更新**: 2026-03-05(基于学习机制优化)
|
||||||
@@ -69,7 +59,7 @@ _这份文件记录持续性项目和重要状态,跨会话保留。_
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4. 工作区自动备份
|
### 2. 工作区自动备份
|
||||||
**状态**: 运行中
|
**状态**: 运行中
|
||||||
**创建**: 2026-03-06
|
**创建**: 2026-03-06
|
||||||
**更新**: 2026-03-12(移除脚本,改用直接 git 命令)
|
**更新**: 2026-03-12(移除脚本,改用直接 git 命令)
|
||||||
@@ -86,7 +76,7 @@ _这份文件记录持续性项目和重要状态,跨会话保留。_
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 5. 日历邀请 + CalDAV 同步
|
### 3. 日历邀请 + CalDAV 同步
|
||||||
**状态**: 运行中
|
**状态**: 运行中
|
||||||
**创建**: 2026-03-18
|
**创建**: 2026-03-18
|
||||||
**配置**:
|
**配置**:
|
||||||
@@ -113,7 +103,6 @@ _这份文件记录持续性项目和重要状态,跨会话保留。_
|
|||||||
| 邮件处理器 | `~/.openclaw/workspace/scripts/email_processor/` |
|
| 邮件处理器 | `~/.openclaw/workspace/scripts/email_processor/` |
|
||||||
| 日历/待办 | `~/.openclaw/workspace/skills/calendar/` |
|
| 日历/待办 | `~/.openclaw/workspace/skills/calendar/` |
|
||||||
| 日历数据 | `~/.openclaw/workspace/calendars/` (home=事件, tasks=待办) |
|
| 日历数据 | `~/.openclaw/workspace/calendars/` (home=事件, tasks=待办) |
|
||||||
| ~~待办提醒~~ | ~~`scripts/reminder_check.py`~~ → 已迁移到 `skills/calendar/` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
12
crontab.txt
12
crontab.txt
@@ -1,12 +0,0 @@
|
|||||||
# OpenClaw 定时任务 - 小鹿
|
|
||||||
# 时区: America/Los_Angeles (PST)
|
|
||||||
SHELL=/bin/bash
|
|
||||||
PATH=/usr/local/bin:/usr/bin:/bin
|
|
||||||
|
|
||||||
# 每日新闻摘要 - 早上 5:00
|
|
||||||
0 5 * * * cd ~/.openclaw/workspace/scripts/news_digest && ./run.sh -v >> ~/.openclaw/workspace/logs/news_digest.log 2>&1 && python3 ~/.openclaw/workspace/scripts/news_digest/send_digest.py >> ~/.openclaw/workspace/logs/news_digest.log 2>&1
|
|
||||||
|
|
||||||
# 每日待办提醒 - 早上 8:00
|
|
||||||
0 8 * * * cd ~/.openclaw/workspace && python3 scripts/reminder_check.py 2>&1 | ~/.local/bin/himalaya message write --to mail@luyx.org --from youlu@luyanxin.com --subject "📋 今日待办清单"
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# 提醒事项表
|
|
||||||
|
|
||||||
## 待办事项(Pending)
|
|
||||||
|
|
||||||
| 事项 | 截止日期 | 优先级 | 状态 | 备注 |
|
|
||||||
|------|----------|--------|------|------|
|
|
||||||
| 给过敏医生打电话 | 2026-02-14 | 高 | done | 问集群过敏针吃的三个药物 |
|
|
||||||
| 给tilles打电话 | 2026-02-17 | 中 | done | 把horizon检测结果发给她,并调整B超时间,跟进治疗进度 |
|
|
||||||
| 跟进iui保险报销 | 2026-03-25 | 中 | pending | 确认iui(人工授精)费用保险报销进度,避免过期 |
|
|
||||||
| 短杖贴上胶布 | 2026-02-28 | 低 | done | 周五提醒:用胶布包裹短杖把手,防滑/保护 |
|
|
||||||
| 打电话给progyny问iui报销 | 2026-04-04 | 中 | pending | 询问iui报销相关事宜 |
|
|
||||||
| 跟进CVS药物报销 | 2026-02-21 | 中 | done | 确认CVS买的药物报销是否到账,核对金额 |
|
|
||||||
| 打电话给progyny问Pacific Bill | 2026-02-20 | 中 | done | 周五前询问Pacific Bill相关事宜 |
|
|
||||||
| 问tilles医生子宫情况 | 2026-02-17 | 高 | done | 问子宫是否已经fixed/修复好,确认是否可以开始备孕/下一步治疗 |
|
|
||||||
| 打电话给Erica wang约超声波 | 2026-02-18 | 中 | done | 预约超声波检查囊肿(确认囊肿情况,是否需要处理) |
|
|
||||||
| 跟accolade确认保险覆盖 | | 中 | done | 确认保险是否cover b超和erica wang的office visit |
|
|
||||||
| 给过敏医生打电话约过敏针 | 2026-02-18 | 中 | done | 约集群过敏针(免疫治疗),需要定期打针建立耐受 |
|
|
||||||
| 打电话给足科医生换时间 | 2026-02-20 | 中 | done | 周五前换预约时间 |
|
|
||||||
|
|
||||||
## 使用说明
|
|
||||||
|
|
||||||
1. **添加事项**:在表格中新增一行
|
|
||||||
2. **截止日期**:格式 YYYY-MM-DD,空着默认为明天
|
|
||||||
3. **优先级**:高/中/低,空着默认为中
|
|
||||||
4. **状态**:pending(待办)/ done(已完成)
|
|
||||||
5. **每天早上8:00自动检查**,到期事项会通知你
|
|
||||||
|
|
||||||
## 已完成归档
|
|
||||||
|
|
||||||
已完成的事项会自动移动到 archive/ 目录
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Daily Reminder Checker
|
|
||||||
Reads reminders from markdown table, filters due items, sends notification
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Paths
|
|
||||||
BASE_DIR = Path.home() / ".openclaw/workspace/reminders"
|
|
||||||
ACTIVE_FILE = BASE_DIR / "active.md"
|
|
||||||
ARCHIVE_DIR = BASE_DIR / "archive"
|
|
||||||
|
|
||||||
# Priority mapping (lower number = higher priority)
|
|
||||||
PRIORITY_MAP = {
|
|
||||||
'高': 0, 'urgent': 0, 'high': 0,
|
|
||||||
'中': 1, 'normal': 1, 'medium': 1,
|
|
||||||
'低': 2, 'low': 2
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse_table(content):
|
|
||||||
"""Parse markdown table into list of dicts"""
|
|
||||||
lines = content.strip().split('\n')
|
|
||||||
reminders = []
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
# Skip header lines and separators
|
|
||||||
if line.startswith('|') and '---' not in line and '事项' not in line:
|
|
||||||
cells = [cell.strip() for cell in line.split('|')[1:-1]]
|
|
||||||
if len(cells) >= 4 and cells[0] and cells[0] != '事项':
|
|
||||||
reminder = {
|
|
||||||
'事项': cells[0],
|
|
||||||
'截止日期': cells[1] if len(cells) > 1 else '',
|
|
||||||
'优先级': cells[2] if len(cells) > 2 else '',
|
|
||||||
'状态': cells[3] if len(cells) > 3 else 'pending',
|
|
||||||
'备注': cells[4] if len(cells) > 4 else ''
|
|
||||||
}
|
|
||||||
reminders.append(reminder)
|
|
||||||
|
|
||||||
return reminders
|
|
||||||
|
|
||||||
def get_default_date():
|
|
||||||
"""Return tomorrow's date as string"""
|
|
||||||
tomorrow = datetime.now() + timedelta(days=1)
|
|
||||||
return tomorrow.strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
def normalize_reminder(reminder):
|
|
||||||
"""Apply defaults and normalize"""
|
|
||||||
# Default priority
|
|
||||||
if not reminder['优先级']:
|
|
||||||
reminder['优先级'] = '中'
|
|
||||||
|
|
||||||
# Default date
|
|
||||||
if not reminder['截止日期']:
|
|
||||||
reminder['截止日期'] = get_default_date()
|
|
||||||
|
|
||||||
# Normalize status
|
|
||||||
reminder['状态'] = reminder['状态'].lower() if reminder['状态'] else 'pending'
|
|
||||||
|
|
||||||
return reminder
|
|
||||||
|
|
||||||
def get_days_until(due_date_str):
|
|
||||||
"""Calculate days until due date"""
|
|
||||||
try:
|
|
||||||
due_date = datetime.strptime(due_date_str, '%Y-%m-%d')
|
|
||||||
today = datetime.now()
|
|
||||||
delta = (due_date.date() - today.date()).days
|
|
||||||
return delta
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_urgency_label(days):
|
|
||||||
"""Get urgency label based on days until due"""
|
|
||||||
if days is None:
|
|
||||||
return "❓ 日期未知"
|
|
||||||
elif days < 0:
|
|
||||||
return f"🔴 逾期 {-days} 天"
|
|
||||||
elif days == 0:
|
|
||||||
return "🔴 今天"
|
|
||||||
elif days == 1:
|
|
||||||
return "🟡 明天"
|
|
||||||
elif days <= 3:
|
|
||||||
return f"🟡 {days} 天后"
|
|
||||||
else:
|
|
||||||
return f"🟢 {days} 天后"
|
|
||||||
|
|
||||||
def sort_reminders(reminders):
|
|
||||||
"""Sort by priority (high first), then by date (earlier first)"""
|
|
||||||
def sort_key(r):
|
|
||||||
priority = PRIORITY_MAP.get(r['优先级'].lower(), 1)
|
|
||||||
try:
|
|
||||||
date = datetime.strptime(r['截止日期'], '%Y-%m-%d')
|
|
||||||
except ValueError:
|
|
||||||
date = datetime.max
|
|
||||||
return (priority, date)
|
|
||||||
|
|
||||||
return sorted(reminders, key=sort_key)
|
|
||||||
|
|
||||||
def format_notification(pending_reminders):
|
|
||||||
"""Format all pending reminders for notification"""
|
|
||||||
if not pending_reminders:
|
|
||||||
return None
|
|
||||||
|
|
||||||
today_str = datetime.now().strftime('%Y-%m-%d')
|
|
||||||
lines = [f"📋 今日待办清单 ({today_str})", "=" * 50]
|
|
||||||
|
|
||||||
# Group by priority
|
|
||||||
groups = {'高': [], '中': [], '低': []}
|
|
||||||
for r in pending_reminders:
|
|
||||||
prio = r['优先级']
|
|
||||||
if prio in groups:
|
|
||||||
groups[prio].append(r)
|
|
||||||
|
|
||||||
# Output high priority
|
|
||||||
if groups['高']:
|
|
||||||
lines.append("\n🔴 高优先级:")
|
|
||||||
for r in groups['高']:
|
|
||||||
days = get_days_until(r['截止日期'])
|
|
||||||
urgency = get_urgency_label(days)
|
|
||||||
note = f" | {r['备注']}" if r['备注'] else ""
|
|
||||||
lines.append(f" • {r['事项']} ({urgency}){note}")
|
|
||||||
|
|
||||||
# Output medium priority
|
|
||||||
if groups['中']:
|
|
||||||
lines.append("\n🟡 中优先级:")
|
|
||||||
for r in groups['中']:
|
|
||||||
days = get_days_until(r['截止日期'])
|
|
||||||
urgency = get_urgency_label(days)
|
|
||||||
note = f" | {r['备注']}" if r['备注'] else ""
|
|
||||||
lines.append(f" • {r['事项']} ({urgency}){note}")
|
|
||||||
|
|
||||||
# Output low priority
|
|
||||||
if groups['低']:
|
|
||||||
lines.append("\n🟢 低优先级:")
|
|
||||||
for r in groups['低']:
|
|
||||||
days = get_days_until(r['截止日期'])
|
|
||||||
urgency = get_urgency_label(days)
|
|
||||||
note = f" | {r['备注']}" if r['备注'] else ""
|
|
||||||
lines.append(f" • {r['事项']} ({urgency}){note}")
|
|
||||||
|
|
||||||
lines.append("\n" + "=" * 50)
|
|
||||||
lines.append("📝 完成事项后请修改状态为 done")
|
|
||||||
lines.append("📁 管理文件: ~/.openclaw/workspace/reminders/active.md")
|
|
||||||
|
|
||||||
return '\n'.join(lines)
|
|
||||||
|
|
||||||
def archive_done_reminders(reminders):
|
|
||||||
"""Move done reminders to archive"""
|
|
||||||
done = [r for r in reminders if r['状态'] == 'done']
|
|
||||||
if not done:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Create archive filename with current quarter
|
|
||||||
now = datetime.now()
|
|
||||||
quarter = (now.month - 1) // 3 + 1
|
|
||||||
archive_file = ARCHIVE_DIR / f"{now.year}-Q{quarter}.md"
|
|
||||||
|
|
||||||
# Append to archive
|
|
||||||
with open(archive_file, 'a', encoding='utf-8') as f:
|
|
||||||
for r in done:
|
|
||||||
f.write(f"| {r['事项']} | {r['截止日期']} | {r['优先级']} | done | {r['备注']} |\n")
|
|
||||||
|
|
||||||
def update_active_file(reminders):
|
|
||||||
"""Rewrite active file without done items"""
|
|
||||||
pending = [r for r in reminders if r['状态'] != 'done']
|
|
||||||
|
|
||||||
with open(ACTIVE_FILE, 'w', encoding='utf-8') as f:
|
|
||||||
f.write("# 提醒事项表\n\n")
|
|
||||||
f.write("## 待办事项(Pending)\n\n")
|
|
||||||
f.write("| 事项 | 截止日期 | 优先级 | 状态 | 备注 |\n")
|
|
||||||
f.write("|------|----------|--------|------|------|\n")
|
|
||||||
|
|
||||||
for r in pending:
|
|
||||||
f.write(f"| {r['事项']} | {r['截止日期']} | {r['优先级']} | {r['状态']} | {r['备注']} |\n")
|
|
||||||
|
|
||||||
f.write("\n## 使用说明\n\n")
|
|
||||||
f.write("1. **添加事项**:在表格中新增一行\n")
|
|
||||||
f.write("2. **截止日期**:格式 YYYY-MM-DD,空着默认为明天\n")
|
|
||||||
f.write("3. **优先级**:高/中/低,空着默认为中\n")
|
|
||||||
f.write("4. **状态**:pending(待办)/ done(已完成)\n")
|
|
||||||
f.write("5. **每天早上8:00自动检查**,到期事项会通知你\n\n")
|
|
||||||
f.write("## 已完成归档\n\n")
|
|
||||||
f.write("已完成的事项会自动移动到 archive/ 目录\n")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main function - show all pending reminders as todo list"""
|
|
||||||
# Check if file exists
|
|
||||||
if not ACTIVE_FILE.exists():
|
|
||||||
print("No reminders file found")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Read and parse
|
|
||||||
with open(ACTIVE_FILE, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
reminders = parse_table(content)
|
|
||||||
|
|
||||||
# Normalize and filter for pending only
|
|
||||||
reminders = [normalize_reminder(r) for r in reminders]
|
|
||||||
pending_reminders = [r for r in reminders if r['状态'] == 'pending']
|
|
||||||
|
|
||||||
if not pending_reminders:
|
|
||||||
# No pending reminders - silent
|
|
||||||
return
|
|
||||||
|
|
||||||
# Sort and format
|
|
||||||
pending_reminders = sort_reminders(pending_reminders)
|
|
||||||
notification = format_notification(pending_reminders)
|
|
||||||
|
|
||||||
if notification:
|
|
||||||
print(notification)
|
|
||||||
|
|
||||||
# Archive done items (optional - uncomment if you want auto-archive)
|
|
||||||
# archive_done_reminders(reminders)
|
|
||||||
# update_active_file(reminders)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# Migration: reminder_check.py → calendar todo
|
|
||||||
|
|
||||||
## What's changing
|
|
||||||
|
|
||||||
| Before | After |
|
|
||||||
|--------|-------|
|
|
||||||
| `scripts/reminder_check.py` | `skills/calendar/scripts/calendar.sh todo` |
|
|
||||||
| `reminders/active.md` (markdown table) | `calendars/tasks/*.ics` (RFC 5545 VTODO) |
|
|
||||||
| Local only, no sync | CalDAV sync to all devices via vdirsyncer |
|
|
||||||
| No native reminders | VALARM triggers on phone/desktop |
|
|
||||||
| Cron reads markdown | Cron reads .ics via todoman |
|
|
||||||
| Custom Python parsing | todoman for robust RFC 5545 VTODO handling |
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- [ ] Install todoman (see below)
|
|
||||||
- [ ] Configure todoman (see below)
|
|
||||||
- [ ] `calendar.sh todo add --dry-run` works
|
|
||||||
- [ ] `~/.openclaw/workspace/calendars/tasks/` directory exists (auto-created by `todo add`)
|
|
||||||
- [ ] vdirsyncer has a `cal/tasks` pair configured
|
|
||||||
- [ ] Live tests 8-12 from `TESTING.md` pass on the Linux machine
|
|
||||||
|
|
||||||
## Step 0: Install and configure todoman
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debian/Ubuntu (the agent's Linux machine)
|
|
||||||
pip install todoman
|
|
||||||
|
|
||||||
# macOS (Homebrew)
|
|
||||||
brew install todoman
|
|
||||||
```
|
|
||||||
|
|
||||||
The CLI command is `todo`.
|
|
||||||
|
|
||||||
### Configure
|
|
||||||
|
|
||||||
Create `~/.config/todoman/config.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
path = "~/.openclaw/workspace/calendars/tasks/"
|
|
||||||
date_format = "%Y-%m-%d"
|
|
||||||
time_format = "%H:%M"
|
|
||||||
default_due = 24
|
|
||||||
default_priority = 0
|
|
||||||
humanize = True
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Should list todos (or show empty list without errors)
|
|
||||||
todo list
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 1: Migrate pending reminders
|
|
||||||
|
|
||||||
Two items are pending in `reminders/active.md`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
SKILL_DIR=~/.openclaw/workspace/skills/calendar
|
|
||||||
|
|
||||||
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
||||||
--summary "跟进iui保险报销" \
|
|
||||||
--due "2026-03-25" \
|
|
||||||
--priority medium \
|
|
||||||
--description "确认iui(人工授精)费用保险报销进度,避免过期"
|
|
||||||
|
|
||||||
$SKILL_DIR/scripts/calendar.sh todo add \
|
|
||||||
--summary "打电话给progyny问iui报销" \
|
|
||||||
--due "2026-04-04" \
|
|
||||||
--priority medium \
|
|
||||||
--description "询问iui报销相关事宜"
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: `todo add` emails each item to `mail@luyx.org`. This is expected.
|
|
||||||
|
|
||||||
Verify:
|
|
||||||
```bash
|
|
||||||
$SKILL_DIR/scripts/calendar.sh todo list
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 2: Set up cron
|
|
||||||
|
|
||||||
Ask the agent to set up a daily cron job via `openclaw cron`:
|
|
||||||
|
|
||||||
> Set up a daily cron at 8:00 AM PST that runs:
|
|
||||||
> `~/.openclaw/workspace/skills/calendar/scripts/calendar.sh todo check`
|
|
||||||
> and emails the output to mail@luyx.org with subject "📋 今日待办清单"
|
|
||||||
|
|
||||||
The old `reminder_check.py` cron should be removed by the agent at the same time.
|
|
||||||
|
|
||||||
## Step 3: Verify
|
|
||||||
|
|
||||||
- [ ] `todo list` (todoman directly) shows both migrated items
|
|
||||||
- [ ] `calendar.sh todo list` shows both items with Chinese priority labels
|
|
||||||
- [ ] `vdirsyncer sync` completes without errors
|
|
||||||
- [ ] Todos appear on phone/CalDAV client
|
|
||||||
- [ ] Next morning's cron email arrives with the new format
|
|
||||||
|
|
||||||
## Step 4: Clean up
|
|
||||||
|
|
||||||
After a few days of successful operation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Remove old reminder system
|
|
||||||
rm scripts/reminder_check.py
|
|
||||||
rm -r reminders/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rollback
|
|
||||||
|
|
||||||
If something goes wrong, the old system is still intact until Step 4:
|
|
||||||
- `reminders/active.md` is unchanged
|
|
||||||
- `scripts/reminder_check.py` still works
|
|
||||||
- Re-add the old cron entry to restore the previous behavior
|
|
||||||
@@ -436,7 +436,7 @@ def _days_until(due_val):
|
|||||||
|
|
||||||
|
|
||||||
def _urgency_label(days):
|
def _urgency_label(days):
|
||||||
"""Urgency label with emoji, matching reminder_check.py style."""
|
"""Urgency label with emoji."""
|
||||||
if days is None:
|
if days is None:
|
||||||
return "❓ 日期未知"
|
return "❓ 日期未知"
|
||||||
elif days < 0:
|
elif days < 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user