---
name: schedule-reminder
description: >
  智能日程提醒助手：从 Markdown 日程表中读取事项，自动筛选今日及近期需要提醒的任务，
  检测时间冲突，并发送定时提醒。
  默认通过当前对话界面回复提醒（用户在哪里发消息，提醒就回到哪里），无需任何渠道配置，开箱即用；
  触发场景：用户发送日程表文件、粘贴 Markdown 日程表内容、发送截图/Excel 文件、
  说"帮我看今天的日程"、"设置日程提醒"、"检查日程冲突"、"提醒我今天的会议"等。
  商业场景：团队日报/周报提醒、会议通知、任务截止提醒、个人日程管理。
metadata:
  {
    "openclaw":
      { "emoji": "📅", "requires": { "tools": ["cron"], "bins": ["python3", "jq", "uv"] } },
  }
---

> **内置能力**：本 skill 已内置文件转换功能（基于 `markitdown`），支持直接发送 Excel、CSV、Word、PDF、PowerPoint 等文件作为日程输入，无需单独安装其他 skill。
> **图片/截图 OCR**：依赖 `sophnet-image-ocr` skill，请确保已安装。
> **⭐ 零配置提醒**：cron 提醒任务使用 `--system-event --session main`，直接在当前 agent session 中触发，兼容 Web 界面、钉钉、飞书、企业微信、Telegram 等所有渠道，无需填写任何渠道或 ID。

## 加载时说明

当用户启用此 skill 时，向用户发送以下欢迎语（语气友好，使用 Markdown）：

> 📅 **日程管理助手已就绪！** 支持：
>
> - 用自然语言添加事项（如"明天下午3点开会"、"下周五提交报告"）
> - 上传截图或图片，自动 OCR 识别日程内容
> - 发送表格或直接粘贴内容导入日程

> - 查看今日/近期日程、检测时间冲突、设置定时提醒
> - 提醒直接发到当前对话，无需任何配置
>
> 你想怎么开始？

---

# 日程提醒助手 (Schedule Reminder)

从 Markdown 日程表中读取事项，智能筛选待提醒项，检测冲突，并发送提醒。**用户在哪里对话，提醒就发回哪里，无需额外配置。**

---

## 一、日程表标准格式（入口文件）

用户的日程表存放在一个 Markdown 文件中（通常命名为 `schedule.md` 或 `日程.md`），
格式固定如下：

```markdown
# 我的日程

> 文件路径：/home/node/.openclaw/workspace/日程.md
> 提醒渠道：current（可选：dingtalk / feishu / wecom / telegram）
> 默认提前提醒：15（分钟）

| 日期       | 开始时间 | 结束时间 | 事项         | 地点       | 参与者    | 优先级 | 提前(分钟) | 备注        |
| ---------- | -------- | -------- | ------------ | ---------- | --------- | ------ | ---------- | ----------- |
| 2026-03-02 | 09:00    | 10:00    | 周一全员周会 | 会议室A    | 全员      | 高     | 15         | 带Q1目标PPT |
| 2026-03-02 | 10:30    | 11:30    | 客户方案评审 | 线上       | 张三,李四 | 高     | 30         |             |
| 2026-03-02 | 14:00    | 14:30    | 1on1 与老板  | 老板办公室 | 我,老板   | 中     | 10         |             |
| 2026-03-03 | 09:30    | 10:00    | 产品需求评审 | 会议室B    | 产品组    | 中     | 15         |             |
```

### 字段说明

| 字段       | 必填 | 格式              | 说明                                     |
| ---------- | ---- | ----------------- | ---------------------------------------- |
| 日期       | ✅   | YYYY-MM-DD        | 事项日期                                 |
| 开始时间   | ✅   | HH:MM（24小时制） | 事项开始时间                             |
| 结束时间   | ✅   | HH:MM             | 事项结束时间（用于冲突检测）             |
| 事项       | ✅   | 文字              | 事项名称，简短明了                       |
| 地点       | ❌   | 文字              | 线下填地点，线上填"线上"或会议链接       |
| 参与者     | ❌   | 逗号分隔          | 相关人员，可留空                         |
| 优先级     | ❌   | 高/中/低          | 默认"中"；"高"必须提醒，"低"可跳过       |
| 提前(分钟) | ❌   | 数字              | 提前多少分钟发送提醒，留空则用全局默认值 |
| 备注       | ❌   | 文字              | 额外信息                                 |

---

## 二、Agent 工作流程

### Step 1：读取并解析日程表

当用户提供日程表文件路径或粘贴内容时，调用解析脚本：

```bash
python3 /app/skills/schedule-reminder/scripts/parse_schedule.py \
  --file <schedule_file> \
  --mode today        # today / tomorrow / week / all
```

脚本输出 JSON，包含：

- `events`：解析后的所有事项
- `upcoming`：需要提醒的事项（按时间排序）
- `conflicts`：时间冲突列表

### Step 2：展示筛选结果给用户

解析后，**向用户展示**以下信息：

#### 今日日程预览

用 Markdown 表格展示今日事项（时间升序）：

```
📅 **今日日程（3月2日）**

| 时间          | 事项           | 地点     | 优先级 | 提前提醒 |
|---------------|----------------|----------|--------|----------|
| 09:00–10:00   | 周一全员周会   | 会议室A  | 🔴 高  | 15分钟   |
| 10:30–11:30   | 客户方案评审   | 线上     | 🔴 高  | 30分钟   |
| 14:00–14:30   | 1on1 与老板   | 老板办公室 | 🟡 中 | 10分钟   |
```

#### 冲突提示（如有）

```
⚠️ **发现时间冲突：**
- 09:30–10:30 "产品需求评审" 与 09:00–10:00 "周会" 时间重叠 30 分钟
  建议：调整其中一项，或确认是否同时参加。
```

#### 确认提醒设置

```
以下事项将设置提醒，将在提醒时间直接发回此对话：
- ⏰ 08:45 提醒 → 09:00 周一全员周会（提前15分钟）
- ⏰ 10:00 提醒 → 10:30 客户方案评审（提前30分钟）
- ⏰ 13:50 提醒 → 14:00 1on1（提前10分钟）

是否确认创建提醒？（可调整某项的提前时间或跳过）
```

**等待用户确认**后再执行 Step 3。

### Step 3：创建提醒任务

用户确认后，为每个事项创建 cron 任务：

```bash
bash /app/skills/schedule-reminder/scripts/create_reminders.sh \
  --events-json '/tmp/schedule_upcoming.json' \
  --to "{{sender}}"
```

> `--channel` 和 `--to` 均为可选，不传时默认回到最后一次对话渠道（`channel=last`）。需要推送给其他用户时再显式指定。

---

## 三、支持的提醒渠道

**cron 提醒任务使用 `--system-event + --session main`，在当前对话的 main session 中直接触发，无需任何渠道配置，天然兼容 Web 界面、钉钉、飞书、企业微信、Telegram 等所有渠道。**

> ✅ **为什么用 `--system-event` 而不是 `--message + --announce`？**
> `--message + --announce` 会启动 isolated subagent，结束后需要把结果推送（announce）回目标渠道。
> 但 webchat（Web 界面）不支持被 agent 主动推送，会报 `Unsupported channel: webchat` 错误。
> `--system-event + --session main` 直接在 main session 中注入事件，不走推送流程，所有渠道均可用。

**交互式提醒**（用户发消息触发）：直接在当前对话中回复，零配置。

**cron 定时提醒**（`create_reminders.sh` 创建）：使用 `--system-event --session main`，自动在当前 agent session 中显示提醒，无需指定渠道或收件人。

---

## 四、筛选逻辑

优先筛选以下事项：

1. **今日**所有未过期的事项（当前时间之后的事项）
2. **明日**优先级为"高"的事项
3. 优先级为"高"且**距今 ≤ 24 小时**的所有事项
4. 有**截止时间/提交要求**的任务型事项（备注含"提交"、"截止"、"deadline"关键词）

跳过以下事项：

- 优先级为"低"且用户没有明确要求提醒的
- 已过期（结束时间早于当前时间）的事项

---

## 五、冲突检测规则

两个事项满足以下任一条件即视为**冲突**：

- 同一天，A 的开始时间 < B 的结束时间，且 A 的结束时间 > B 的开始时间（时间重叠）
- 同一天，两个事项开始时间相差 < 5 分钟（紧接场景，没有缓冲时间）

**冲突提示格式**：

```
⚠️ 冲突：[事项A]（09:00-10:00）与 [事项B]（09:30-11:00）重叠 30 分钟
   建议：将 [事项B] 改为 10:00 后开始，或确认是否线上可同时参与。
```

---

## 六、提醒消息模板

发送给用户的提醒消息格式：

```
⏰ 日程提醒

📌 【周一全员周会】即将在 15 分钟后开始
🕐 09:00 – 10:00
📍 会议室A
👥 参与者：全员
📝 带Q1目标PPT

祝会议顺利！
```

---

## 七、一次性 vs 周期性提醒

| 场景                   | 类型   | 说明                                         |
| ---------------------- | ------ | -------------------------------------------- |
| 单次会议/事项          | 一次性 | cron 执行后自动删除（`--delete-after-run`）  |
| 每周固定会议（如周会） | 周期性 | 使用 `* * * * 1`（每周一）等周期 cron 表达式 |
| 每日站会               | 周期性 | 使用 `0 9 * * 1-5`（工作日每天）             |

检测规则：若同一事项在日程表中连续出现超过 2 次且时间规律，自动建议设置为周期性提醒。

---

## 八、主动激活方式（推荐用于商用）

### 方式 A：一次性 Setup → 每天自动运行（推荐）

管理员或用户**运行一次** setup.sh，之后每天自动激活，不需要任何手动触发：

```bash
bash /app/skills/schedule-reminder/scripts/setup.sh
```

交互式引导：

1. 输入日程文件路径（如 `/home/node/.openclaw/workspace/日程.md`）
2. 选择推送渠道（dingtalk / feishu / wecom / telegram，仅在需要推送给特定用户时填写）
3. 输入接收者 ID（可留空，留空则回到最后一次对话渠道）
4. 设置每日激活时间（如 7 表示每天早上 7:00）

Setup 完成后自动注册 `schedule-reminder-daily-activate` cron 任务：

- **每天定时**运行 `daily_activate.sh`
- 自动解析日程文件 → 建今日提醒 → 发日报
- 用户无需操作，提醒准时到达

**带参数快速配置（脚本化部署，需要推送给特定渠道用户时）：**

```bash
bash /app/skills/schedule-reminder/scripts/setup.sh \
  --file /home/node/.openclaw/workspace/日程.md \
  --channel dingtalk \
  --to "user_staff_id" \
  --hour 7 \
  --yes
```

> 不指定 `--channel`/`--to` 时，提醒默认回到最后一次对话渠道，无需填写接收者 ID。

**手动立即触发（测试用）：**

```bash
bash /app/skills/schedule-reminder/scripts/daily_activate.sh
```

**停止每日激活：**

```bash
openclaw cron remove schedule-reminder-daily-activate
```

---

### 方式 B：发送日程截图（sophnet-image-ocr）

用户发送日程截图（手机截图、扫描件、PDF 等），Agent 使用 `sophnet-image-ocr` skill 提取表格文字，再写入日程文件。

#### ⚠️ 图片路径获取

**用户上传图片时，系统会自动对图片进行理解，结果以文字描述（`[Image] Description: ...`）出现在消息上下文中，Agent 可以直接读取描述内容。但如果需要对图片做 OCR（提取表格原文），则必须拿到图片文件的本地路径。**

图片路径的获取策略（按优先级）：

**优先级 1：主动搜索常见位置**

收到图片消息后立即搜索最近修改的图片文件，**不要等用户提供路径**。上传的图片会先下载到 workspace 的 `media/inbound/` 目录，优先搜这里：

```bash
# 1. workspace 的 media/inbound 目录（钉钉/飞书等渠道上传必在此处）
ls -t <workspace>/media/inbound/ 2>/dev/null | grep -iE '\.(jpg|jpeg|png|webp|bmp|gif|pdf)$' | head -3

# 2. 用户常见目录兜底
ls -t ~/Downloads/ ~/Pictures/ ~/Desktop/ 2>/dev/null \
  | grep -iE '\.(jpg|jpeg|png|webp|bmp|gif|pdf)$' | head -3
```

找到候选文件后，展示给用户确认："找到以下文件，是您刚上传的吗？"，确认后再调用 OCR。

**优先级 2：用户补充提供**

若搜索无结果，提示用户直接告知文件路径或粘贴到对话中。

**收到图片消息时，Agent 必须立即执行以下步骤，不要等待用户再次说明：**

1. 按上述优先级确定图片绝对路径
2. 用该路径调用 OCR，输出 Markdown
3. 从 Markdown 中提取日程事项，展示给用户确认

#### 完整流程

**Step 1：确定图片路径，调用 OCR**

```bash
# <image_path> 替换为搜索到的实际绝对路径
uv run /app/skills/sophnet-image-ocr/scripts/ocr.py <image_path>
```

**Step 2：Agent 读取 OCR 输出的 Markdown，理解表格结构，提取日程事项**

列名不标准、版式混乱 → Agent 根据上下文理解，直接提取日期、时间、事项等字段。

**Step 3：展示识别结果，用户确认后逐条写入日程文件**

```bash
# <schedule_file> 替换为用户日程文件的实际路径（优先用用户告知的路径，否则用 workspace 下的 日程.md）
python3 /app/skills/schedule-reminder/scripts/append_event.py \
  --file <schedule_file> \
  --date "..." --start "..." --end "..." --title "..."
```

**支持格式**：JPEG、PNG、WebP、BMP、GIF、PDF（多页自动合并）

### 方式 C：指定本地文件路径

```
帮我看一下 ~/Documents/日程.md 今天的提醒
```

### 方式 D：通过入口文件触发

在工作区根目录放置 `日程.md`，文件第一行包含触发指令：

```markdown
<!-- skill: schedule-reminder -->

# 我的日程

...（表格内容）
```

Agent 检测到此文件时自动激活本 skill。

### 方式 E：发送 Excel / CSV / Word / PDF 文件

用户发送文件后，Agent 执行以下流程：

#### ⚠️ 文件路径获取

上传的文件与图片走同一套机制，渠道（钉钉/飞书等）会将文件下载到 workspace 的 `media/inbound/` 目录，文件名为 `<timestamp>_<random>.<ext>`，**agent 无法提前知道文件名，必须主动搜索**。

**收到文件消息后立即搜索，不要等用户提供路径：**

```bash
# 1. workspace 的 media/inbound 目录（渠道上传文件必在此处）
ls -t <workspace>/media/inbound/ 2>/dev/null \
  | grep -iE '\.(xlsx|xls|csv|docx|pptx|pdf|html)$' | head -3

# 2. 用户常见目录兜底
ls -t ~/Downloads/ ~/Desktop/ 2>/dev/null \
  | grep -iE '\.(xlsx|xls|csv|docx|pptx|pdf|html)$' | head -3
```

找到候选文件后，展示给用户确认是否是刚上传的文件，确认后再转换。若搜索无结果，提示用户告知文件路径。

#### Step 1：文件转 Markdown

```bash
# <file_path> 替换为上面搜索到的实际绝对路径
uvx "markitdown[all]" <file_path> -o /tmp/schedule_converted.md
```

支持格式：Excel（`.xlsx/.xls`）、CSV（`.csv`）、Word（`.docx`）、PowerPoint（`.pptx`）、PDF（`.pdf`）、HTML（`.html`）

> ⚠️ 需要服务器安装 `uv`

#### Step 2：Agent 直接读取 Markdown 内容，提取日程事项

Agent 读取 `/tmp/schedule_converted.md` 的内容，**直接理解表格结构**，无需关心列名是否标准。

从中提取每行事项的：日期、开始时间、结束时间、事项名称、地点、参与者、优先级、备注。

- 列名不标准（如"会期"、"主旨"、"时段开始"）→ Agent 根据上下文理解含义，直接提取
- 缺少某些字段 → 使用默认值（优先级默认"中"，提前提醒默认15分钟）
- 内容模糊 → Agent 在展示确认卡片时标注"待确认"

#### Step 3：展示识别结果，用户确认

```
📋 从文件中识别到以下日程，确认写入日程文件？

| 日期       | 时间          | 事项     | 地点  | 优先级 |
|------------|---------------|----------|-------|--------|
| 2026-03-10 | 10:00–11:30   | 季度复盘 | A101  | 中     |
| 2026-03-11 | 14:00–16:00   | 产品发布 | 线上  | 中     |

✅ 全部写入  ✏️ 修改某项  ❌ 取消
```

#### Step 4：逐条写入日程文件

用户确认后，逐条调用 `append_event.py` 追加到日程文件（路径以用户告知为准，未告知则用 workspace 下的 `日程.md`）：

```bash
# <schedule_file> 替换为实际日程文件路径
python3 /app/skills/schedule-reminder/scripts/append_event.py \
  --file <schedule_file> \
  --date "2026-03-10" \
  --start "10:00" \
  --end "11:30" \
  --title "季度复盘" \
  --location "A101"
```

写入完成后，询问是否为今日/近期事项立即设置提醒。

---

## 九、间接日程：从他人时间表推导用户自己的行动

用户有时发送的是**别人的时间表**（孩子课程表、配偶班次、会议室日历等），但用户真正需要记录的是**自己的关联行动**，不是对方的内容本身。

### 识别特征

以下情况属于"间接日程"，Agent 需要主动推导用户行动，而不是原样照搬表格内容：

- 发送孩子课程表 + 说"我要接孩子"→ 记录的是**接送时间**，不是课程
- 发送他人会议日程 + 说"我要旁听/参与" → 记录的是**用户自己要出席**的事项
- 发送班次/排班表 + 说"这是我老婆的班，我负责接送" → 记录的是接送行动
- 发送活动日程 + 说"这些我都要去" → 逐条转化为用户自己的日程

### 处理规则

**Agent 必须优先理解用户的意图，而不是照搬原表格内容。**

1. **识别用户的行动**：从用户说的话中提取"我要做什么"（接送、旁听、参与、安排等）
2. **推算用户行动的时间**：
   - 接送场景：课程/活动结束时间 → 用户出发/到达时间（默认提前15分钟出发）
   - 旁听/参与场景：直接使用对方事项的开始时间
3. **记录用户自己的事项**，标注来源（如"备注：女儿课程表 周三 9:40-10:20"）
4. **不要把对方的课程/事项原样写入用户日程**，除非用户明确要求存档参考

### 示例

用户发课程表截图，说"我要接女儿，下课后15分钟内到"：

```
📅 根据课程表，为您生成接送日程，请确认：

| 日期    | 出发时间 | 事项              | 备注                    |
|---------|----------|-------------------|-------------------------|
| 每周一  | 10:15    | 接女儿放学        | 课程：语文 8:50-9:30    |
| 每周二  | 9:15     | 接女儿放学        | 课程：数学 8:50-9:30    |
| 每周三  | 14:25    | 接女儿放学        | 课程：体育 10:30-14:10  |
| 每周四  | 10:35    | 接女儿放学        | 课程：科学 10:30-?      |
| 每周五  | 15:45    | 接女儿放学        | 课程：手工 14:50-15:30  |

✅ 全部写入  ✏️ 修改某项  ❌ 取消
```

---

## 十、自然语言快速创建日程

用户可以直接用自然语言描述一个日程，Agent 自动解析并追加到日程文件。

### 触发关键词

用户说的话包含以下任意模式时，进入"创建日程"流程：

- 时间词 + 事项：`明天下午3点开会`、`下周五10点产品评审`
- 截止日期描述：`3月5号截止提交季度报告`
- 明确创建意图：`帮我加一条日程`、`记一下`、`加个提醒`

### 解析规则

| 用户说的内容         | 解析结果                  |
| -------------------- | ------------------------- |
| `明天`               | 明天的日期（当前日期 +1） |
| `下周五`             | 下一个周五的日期          |
| `3月5号` / `3/5`     | 当年对应日期              |
| `上午10点` / `10:00` | 开始时间 10:00            |
| `下午3点`            | 开始时间 15:00            |
| `两小时` / `1小时`   | 推算结束时间              |
| 未说结束时间         | 默认事项时长 1 小时       |
| 未说优先级           | 默认"中"                  |
| 未说地点/参与者      | 留空                      |

### 工作流程

**Step 1：解析用户输入，展示确认卡片**

```
📅 我帮你记录了这条日程，请确认：

| 字段     | 内容              |
|----------|-------------------|
| 日期     | 2026-03-03（明天）|
| 时间     | 15:00 – 16:00     |
| 事项     | 客户会议          |
| 地点     |（未填）           |
| 优先级   | 中                |
| 提前提醒 | 15 分钟           |

✅ 确认写入  ✏️ 修改某项  ❌ 取消
```

**Step 2：用户确认后，追加到日程文件**

将新事项追加到用户的日程文件（优先使用用户告知的路径，未告知则推荐存放在 workspace 下，如 `日程.md`）：

```bash
# <schedule_file> 替换为实际日程文件路径
python3 /app/skills/schedule-reminder/scripts/append_event.py \
  --file <schedule_file> \
  --date "2026-03-03" \
  --start "15:00" \
  --end "16:00" \
  --title "客户会议" \
  --priority "中" \
  --advance 15
```

**Step 3：询问是否立即设置提醒**

```
已写入日程 ✅

要现在就设置提醒吗？
⏰ 14:45 提醒你「客户会议」将在15分钟后开始
```

### 示例对话

**用户**：明天下午3点有个客户会议，提前半小时提醒我

**Bot**：

```
📅 确认日程：
- 日期：2026-03-03（明天）
- 时间：15:00 – 16:00
- 事项：客户会议
- 提前提醒：30 分钟（14:30 提醒）

确认写入日程？
```

---

**用户**：下周五上午10点产品评审，会议室B，高优先级

**Bot**：

```
📅 确认日程：
- 日期：2026-03-06（下周五）
- 时间：10:00 – 11:00
- 事项：产品评审
- 地点：会议室B
- 优先级：🔴 高
- 提前提醒：15 分钟

确认写入日程？
```

---

**用户**：3月5号截止提交季度报告

**Bot**：

```
📋 确认任务截止提醒：
- 截止日期：2026-03-05
- 事项：提交季度报告
- 提前提醒：提前1天（3月4日早9点提醒）

确认写入？
```

---

## 十一、常用操作指令

用户可通过自然语言发出以下指令：

| 用户说                       | Agent 动作                                 |
| ---------------------------- | ------------------------------------------ |
| "明天下午3点客户会议"        | 解析时间事项，确认后写入日程文件并可设提醒 |
| "帮我加一条日程"             | 引导用户输入时间、事项等信息               |
| "帮我看今天的日程"           | 解析日程表，展示今日预览，列出建议提醒     |
| "设置今天所有日程的提醒"     | 确认后批量创建今日提醒 cron 任务           |
| "检查日程有没有冲突"         | 仅运行冲突检测，输出冲突报告               |
| "取消今天的所有提醒"         | 列出并删除今日相关 cron 任务               |
| "提前30分钟提醒我下午的会议" | 修改对应事项的提前时间后重新创建提醒       |
| "帮我看明天的日程"           | 筛选并展示明日事项                         |

---

## 十二、安全与商用注意事项

- 日程表中**不要填写密码、API Key、敏感合同金额**等信息。
- 提醒消息只发送给**当前会话的发送者**（`{{sender}}`），不广播。
- 提醒任务由 OpenClaw cron 管理，可通过 `openclaw cron list` 查看和删除。
- 本 skill 仅负责**读取和调度**，不上传任何数据到第三方服务器。
