ooodc a7883dbed9 refactor(todo): 重构待办事项管理逻辑及更新状态规则
- 移除 TodoItem 中的 priority、created_at 和 updated_at 字段
- 强制每个任务都必须有唯一 id,且由用户负责生成
- 修改合并模式逻辑,merge=true 下保留未提及的旧任务
- 支持已完成和已取消任务重新激活(状态改回 pending 或 in_progress)
- 禁止 in_progress 状态退回到 pending,必须标记为 completed 或 cancelled
- 优化状态转换校验,允许特定状态间合法切换
- 简化任务变更消息,移除详细的新增/更新/移除统计
- 更新文档和示例,明确 id 必须由用户生成和使用
- 修复和补充测试,增强状态转换和合并模式验证
- 调整任务时间戳生成逻辑,统一使用当前时间及索引
- 该变更提供更合理的任务状态机械及管理模式,提升稳定性和易用性
2026-06-13 09:22:33 +08:00

220 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# mail +send
> **前置条件:** 先阅读 [`../../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
发送新邮件,支持:
- 纯文本或 HTML 正文
- 抄送/密送
- 本地文件附件(`--attach`
- 内嵌图片(`--inline`CID 可用随机字符串)
本 skill 对应 shortcut`lark-cli mail +send`
## CRITICAL — 发送工作流(必须遵循)
**CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
此命令默认**只保存草稿**,不会发送邮件。需要发送时,有两种合规方式:
**方式 A推荐** — 先创建草稿,再确认发送:
```bash
lark-cli mail +send --to <收件人> --subject '<主题>' --body '<正文>'
```
→ 返回 `draft_id`
向用户展示邮件摘要(收件人、主题、正文预览);如果用户想先看效果,可引导其去飞书邮件里打开该草稿查看详情。
用户明确同意后,发送该草稿:
```bash
lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":"<Step 1 返回的 draft_id>"}'
```
**方式 B允许** — 用户已经明确确认收件人和内容时,可直接使用 `--confirm-send` 立即发送:
```bash
lark-cli mail +send --to <收件人> --subject '<主题>' --body '<正文>' --confirm-send
```
**禁止在用户未明确同意的情况下执行发送,无论是发送草稿还是直接使用 `--confirm-send`。**
## 命令
```bash
# 保存为草稿(默认行为,不发送)— HTML 格式推荐
lark-cli mail +send --to alice@example.com --subject '周报' \
--body '<p>本周进展:</p><ul><li>完成 A 模块</li><li>修复 3 个 bug</li></ul>'
# 保存为草稿并抄送
lark-cli mail +send --to alice@example.com --cc bob@example.com --subject '状态更新' --body '<b>已完成</b>'
# 确认发送(仅在用户明确确认后使用)
lark-cli mail +send --to alice@example.com --subject '周报' \
--body '<p>本周进展如下...</p>' --confirm-send
# 保存带附件的草稿
lark-cli mail +send --to alice@example.com --subject '请查收' --body '<p>见附件</p>' --attach ./report.pdf,./logs.zip
# 保存带内嵌图片的草稿(推荐:直接用相对路径,自动解析)
lark-cli mail +send --to alice@example.com --subject '预览图' --body '<img src="./logo.png" />'
# 纯文本邮件(仅在内容极简时使用)
lark-cli mail +send --to alice@example.com --subject '确认' --body '收到,谢谢'
# Dry Run仅打印请求不执行
lark-cli mail +send --to alice@example.com --subject '测试' --body '<p>test</p>' --dry-run
```
## 参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `--to <emails>` | 是 | 收件人邮箱,多个用逗号分隔 |
| `--subject <text>` | 是 | 邮件主题 |
| `--body <text>` | 二选一 | 邮件正文。推荐使用 HTML 获得富文本排版;也支持纯文本(自动检测)。使用 `--plain-text` 可强制纯文本模式。支持 `<img src="./local.png" />` 相对路径自动解析为内嵌图片(仅支持相对路径,不支持绝对路径)。与 `--body-file` 互斥 |
| `--body-file <path>` | 二选一 | 从文件读取邮件正文 HTML相对路径仅限 cwd 子树)。与 `--body` 互斥。文件大小上限 32 MB |
| `--from <email>` | 否 | 发件人邮箱地址EML From 头。使用别名send_as发信时设为别名地址并配合 `--mailbox` 指定所属邮箱。默认读取邮箱主地址 |
| `--mailbox <email>` | 否 | 邮箱地址,指定草稿所属的邮箱(默认回退到 `--from`,再回退到 `me`)。当发件人(`--from`)与邮箱不同时使用。可通过 `accessible_mailboxes` 查询可用邮箱 |
| `--cc <emails>` | 否 | 抄送邮箱,多个用逗号分隔 |
| `--bcc <emails>` | 否 | 密送邮箱,多个用逗号分隔 |
| `--plain-text` | 否 | 强制纯文本模式,忽略 HTML 自动检测。不可与 `--inline` 同时使用 |
| `--attach <paths>` | 否 | 附件文件路径,多个用逗号分隔。相对路径。当附件导致 EML 总大小超过 25 MB 时超出部分自动上传为超大附件HTML 邮件插入下载卡片,纯文本邮件追加下载链接),单个文件上限 3 GB |
| `--inline <json>` | 否 | 高级用法:手动指定内嵌图片 CID 映射。推荐直接在 `--body` 中使用 `<img src="./path" />`(自动解析)。仅在需要精确控制 CID 命名时使用此参数。格式:`'[{"cid":"mycid","file_path":"./logo.png"}]'`,在 body 中用 `<img src="cid:mycid">` 引用。不可与 `--plain-text` 同时使用 |
| `--signature-id <id>` | 否 | 签名 ID。附加邮箱签名到正文末尾。运行 `mail +signature` 查看可用签名。不可与 `--plain-text` 同时使用 |
| `--priority <level>` | 否 | 邮件优先级:`high``normal``low`。省略或 `normal` 时不设置优先级 |
| `--event-summary <text>` | 否 | 日程标题。设置此参数即在邮件中嵌入日程邀请text/calendar。需同时设置 `--event-start``--event-end` |
| `--event-start <time>` | 条件必填 | 日程开始时间ISO 8601`2026-04-20T14:00+08:00` |
| `--event-end <time>` | 条件必填 | 日程结束时间ISO 8601 |
| `--event-location <text>` | 否 | 日程地点 |
| `--confirm-send` | 否 | 确认发送邮件(默认只保存草稿)。仅在用户明确确认收件人和内容后使用 |
| `--send-time <timestamp>` | 否 | 定时发送时间Unix 时间戳(秒)。需至少为当前时间 + 5 分钟。配合 `--confirm-send` 使用可定时发送邮件 |
| `--request-receipt` | 否 | 请求已读回执RFC 3798 Message Disposition Notification。在出站 EML 里写 `Disposition-Notification-To: <sender>` 头。收件人的邮件客户端**可能**弹出提示询问是否回执、可能自动发送、也可能忽略——送达不保证 |
| `--dry-run` | 否 | 仅打印请求,不执行 |
### 日程邀请约束
使用 `--event-*` 时需满足以下条件:
- `--event-summary``--event-start``--event-end` 必须同时出现或同时不出现
-`--send-time` 互斥,不可同时使用(日程邀请必须立即发送,否则收件人可能在日程开始后才收到)
- 不可与 `--bcc` 同时使用日程参会人ATTENDEE仅来自 To 和 CcBcc 收件人不在参会人列表中、无法 RSVP且该组合将导致邮件发送失败。需要邀请某人参加日程请用 `--to``--cc`;如只想告知而不邀请,请单独发一封无日程的邮件
## 返回值
**草稿模式(默认):**
```json
{
"ok": true,
"data": {
"draft_id": "草稿ID",
"tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'"
}
}
```
草稿模式下,只要结果不是直接发信而是产出了草稿,就应给用户展示草稿打开链接。当前应以 `create` / `edit` / `send` 链路返回的链接信息为准,不要把 `user_mailbox.drafts get` 当作拿草稿打开链接的来源。如果返回中带有 `reference`,应把链接与 `draft_id` 一并返回;当前没有链接时,静默处理,不要伪造链接。
**发送模式(`--confirm-send`**
```json
{
"ok": true,
"data": {
"message_id": "邮件ID",
"thread_id": "会话ID"
}
}
```
可选字段:
- `automation_send_disable_reason`:发送被邮箱自动化设置拦截时返回的原因
- `automation_send_disable_reference`:发送被拦截时的草稿打开链接
字段语义:
- 若返回中包含 `automation_send_disable_reason` / `automation_send_disable_reference`,说明邮件未真正发出,而是被邮箱设置拦截。此时应直接向用户展示原因和草稿打开链接,不要继续假设已经发送成功
## 典型场景
### 场景 1用户说"帮我写一封邮件给 Alice"(只创建草稿)
```bash
lark-cli mail +send --to alice@example.com --subject '周报' --body '<p>本周进展如下...</p>'
```
→ 返回草稿结果时,如输出中带有草稿打开链接,则一起展示给用户;如果当前输出没有链接,则静默处理。如果用户想先看效果,可去飞书邮件 UI 中打开草稿查看详情。
### 场景 2用户说"发邮件给 Alice 说收到了"(需要发送)
```bash
# 方式 A: 创建草稿
lark-cli mail +send --to alice@example.com --subject '收到' --body '<p>已收到,谢谢!</p>'
# → 返回 draft_id
# 向用户确认 "当前收件人 alice@example.com主题「收到」。如果你想先看效果也可以先去飞书邮件里打开草稿查看详情。确认发送吗"
# 用户确认后发送
lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":"<draft_id>"}'
# 方式 B: 用户已明确确认时,直接发送
lark-cli mail +send --to alice@example.com --subject '收到' --body '<p>已收到,谢谢!</p>' --confirm-send
```
### 场景 3用户说"下午 3 点给 Alice 发一封周报"(定时发送)
```bash
# Step 1: 创建草稿(定时发送也走草稿流程)
lark-cli mail +send --to alice@example.com --subject '周报' --body '<p>本周进展如下...</p>'
# → 返回 draft_id
# Step 2: 向用户确认 "邮件草稿已创建:收件人 alice@example.com主题「周报」定时 <目标时间> 发送。确认吗?"
# Step 3: 用户确认后定时发送send_time 为 Unix 时间戳,需至少当前时间 + 5 分钟)
lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":"<draft_id>"}' --data '{"send_time":"<unix_timestamp>"}'
```
### 场景 4用户说"等等,先不发那封邮件了"(取消定时发送)
```bash
# 取消定时发送(取消后邮件变回草稿)
lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":"<draft_id>"}'
```
→ 取消成功后邮件恢复为草稿状态,用户可重新编辑或在之后重新发送。
## 发送后跟进
邮件发送后,分两种情况处理:
- 若返回中有 `automation_send_disable_reason` / `automation_send_disable_reference`:说明发送被邮箱设置拦截,应直接告诉用户原因并提供草稿打开链接,**不要**调用 `send_status`
### 立即发送(无 `--send-time`
若返回非空 `message_id`,调用:
```bash
lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}'
```
状态码1=正在投递, 2=投递失败重试, 3=退信, 4=投递成功, 5=待审批, 6=审批拒绝。向用户简要报告各收件人投递结果,异常状态需重点提示。
### 定时发送(指定了 `--send-time`
定时发送不会立即产生 `message_id`,因此 `send_status` 在定时发送成功后会返回"待发送"状态,**不建议在定时发送后立即查询**。可在预定发送时间后再查询投递状态。
如需取消定时发送,可在预定时间前调用取消接口:
```bash
lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":"<draft_id>"}'
```
**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。
## 实现说明
- 使用 EML 构建器生成完整 MIME 邮件并 base64url 编码后发送。
- `--attach` 作为普通附件添加。相对路径。
- `--inline` 接受 JSON 数组,每项需提供 `cid`(唯一标识符,可用随机十六进制字符串)和 `file_path`(相对路径),作为 inline part 嵌入邮件。
- **超大附件**:当附件导致 EML 总大小headers + body + inline images + attachmentsbase64 编码后)超过 25 MB 时,超出的文件自动通过 `medias/upload_*` API 上传到云端。HTML 邮件插入与飞书客户端一致的下载卡片;纯文本邮件追加包含文件名、大小和下载链接的文本块。单个文件上限 3 GB总附件数量上限 250 个。
## 相关命令
- `lark-cli mail +reply` — 回复邮件
- `lark-cli mail +reply-all` — 回复全部
- `lark-cli mail +forward` — 转发邮件
- `lark-cli mail user_mailbox.messages list` — 列出邮件