diff --git a/shortcuts/mail/draft/service.go b/shortcuts/mail/draft/service.go index e62ab680..34e668fe 100644 --- a/shortcuts/mail/draft/service.go +++ b/shortcuts/mail/draft/service.go @@ -59,8 +59,12 @@ func UpdateWithRaw(runtime *common.RuntimeContext, mailboxID, draftID, rawEML st return err } -func Send(runtime *common.RuntimeContext, mailboxID, draftID string) (map[string]interface{}, error) { - return runtime.CallAPI("POST", mailboxPath(mailboxID, "drafts", draftID, "send"), nil, nil) +func Send(runtime *common.RuntimeContext, mailboxID, draftID, sendTime string) (map[string]interface{}, error) { + var bodyParams map[string]interface{} + if sendTime != "" { + bodyParams = map[string]interface{}{"send_time": sendTime} + } + return runtime.CallAPI("POST", mailboxPath(mailboxID, "drafts", draftID, "send"), nil, bodyParams) } func extractDraftID(data map[string]interface{}) string { diff --git a/shortcuts/mail/helpers.go b/shortcuts/mail/helpers.go index 0b1958d3..841bcc49 100644 --- a/shortcuts/mail/helpers.go +++ b/shortcuts/mail/helpers.go @@ -17,6 +17,7 @@ import ( "regexp" "strconv" "strings" + "time" "github.com/larksuite/cli/extension/fileio" "github.com/larksuite/cli/internal/auth" @@ -1906,6 +1907,27 @@ func checkAttachmentSizeLimit(fio fileio.FileIO, filePaths []string, extraBytes return nil } +// validateSendTime checks that --send-time, if provided, requires --confirm-send, +// is a valid Unix timestamp in seconds, and is at least 5 minutes in the future. +func validateSendTime(runtime *common.RuntimeContext) error { + sendTime := runtime.Str("send-time") + if sendTime == "" { + return nil + } + if !runtime.Bool("confirm-send") { + return fmt.Errorf("--send-time requires --confirm-send to be set") + } + ts, err := strconv.ParseInt(sendTime, 10, 64) + if err != nil { + return fmt.Errorf("--send-time must be a valid Unix timestamp in seconds, got %q", sendTime) + } + minTime := time.Now().Unix() + 5*60 + if ts < minTime { + return fmt.Errorf("--send-time must be at least 5 minutes in the future (minimum: %d, got: %d)", minTime, ts) + } + return nil +} + // validateConfirmSendScope checks that the user's token includes the // mail:user_mailbox.message:send scope when --confirm-send is set. // This scope is not declared in the shortcut's static Scopes (to keep the diff --git a/shortcuts/mail/mail_forward.go b/shortcuts/mail/mail_forward.go index 2ef52034..4a0f35c0 100644 --- a/shortcuts/mail/mail_forward.go +++ b/shortcuts/mail/mail_forward.go @@ -34,6 +34,7 @@ var MailForward = common.Shortcut{ {Name: "attach", Desc: "Attachment file path(s), comma-separated, appended after original attachments (relative path only)"}, {Name: "inline", Desc: "Inline images as a JSON array. Each entry: {\"cid\":\"\",\"file_path\":\"\"}. All file_path values must be relative paths. Cannot be used with --plain-text. CID images are embedded via in the HTML body. CID is a unique identifier, e.g. a random hex string like \"a1b2c3d4e5f6a7b8c9d0\"."}, {Name: "confirm-send", Type: "bool", Desc: "Send the forward immediately instead of saving as draft. Only use after the user has explicitly confirmed recipients and content."}, + {Name: "send-time", Desc: "Scheduled send time as a Unix timestamp in seconds. Must be at least 5 minutes in the future. Use with --confirm-send to schedule the email."}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { messageId := runtime.Str("message-id") @@ -59,6 +60,9 @@ var MailForward = common.Shortcut{ if err := validateConfirmSendScope(runtime); err != nil { return err } + if err := validateSendTime(runtime); err != nil { + return err + } if runtime.Bool("confirm-send") { if err := validateComposeHasAtLeastOneRecipient(runtime.Str("to"), runtime.Str("cc"), runtime.Str("bcc")); err != nil { return err @@ -76,6 +80,7 @@ var MailForward = common.Shortcut{ attachFlag := runtime.Str("attach") inlineFlag := runtime.Str("inline") confirmSend := runtime.Bool("confirm-send") + sendTime := runtime.Str("send-time") mailboxID := resolveComposeMailboxID(runtime) sourceMsg, err := fetchComposeSourceMessage(runtime, mailboxID, messageId) @@ -218,7 +223,7 @@ var MailForward = common.Shortcut{ hintSendDraft(runtime, mailboxID, draftID) return nil } - resData, err := draftpkg.Send(runtime, mailboxID, draftID) + resData, err := draftpkg.Send(runtime, mailboxID, draftID, sendTime) if err != nil { return fmt.Errorf("failed to send forward (draft %s created but not sent): %w", draftID, err) } diff --git a/shortcuts/mail/mail_reply.go b/shortcuts/mail/mail_reply.go index 0a449f9d..9312b938 100644 --- a/shortcuts/mail/mail_reply.go +++ b/shortcuts/mail/mail_reply.go @@ -32,6 +32,7 @@ var MailReply = common.Shortcut{ {Name: "attach", Desc: "Attachment file path(s), comma-separated (relative path only)"}, {Name: "inline", Desc: "Inline images as a JSON array. Each entry: {\"cid\":\"\",\"file_path\":\"\"}. All file_path values must be relative paths. Cannot be used with --plain-text. CID images are embedded via in the HTML body. CID is a unique identifier, e.g. a random hex string like \"a1b2c3d4e5f6a7b8c9d0\"."}, {Name: "confirm-send", Type: "bool", Desc: "Send the reply immediately instead of saving as draft. Only use after the user has explicitly confirmed recipients and content."}, + {Name: "send-time", Desc: "Scheduled send time as a Unix timestamp in seconds. Must be at least 5 minutes in the future. Use with --confirm-send to schedule the email."}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { messageId := runtime.Str("message-id") @@ -56,6 +57,9 @@ var MailReply = common.Shortcut{ if err := validateConfirmSendScope(runtime); err != nil { return err } + if err := validateSendTime(runtime); err != nil { + return err + } return validateComposeInlineAndAttachments(runtime.FileIO(), runtime.Str("attach"), runtime.Str("inline"), runtime.Bool("plain-text"), "") }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { @@ -68,6 +72,7 @@ var MailReply = common.Shortcut{ attachFlag := runtime.Str("attach") inlineFlag := runtime.Str("inline") confirmSend := runtime.Bool("confirm-send") + sendTime := runtime.Str("send-time") inlineSpecs, err := parseInlineSpecs(inlineFlag) if err != nil { @@ -181,7 +186,7 @@ var MailReply = common.Shortcut{ hintSendDraft(runtime, mailboxID, draftID) return nil } - resData, err := draftpkg.Send(runtime, mailboxID, draftID) + resData, err := draftpkg.Send(runtime, mailboxID, draftID, sendTime) if err != nil { return fmt.Errorf("failed to send reply (draft %s created but not sent): %w", draftID, err) } diff --git a/shortcuts/mail/mail_reply_all.go b/shortcuts/mail/mail_reply_all.go index f08c4487..df05616c 100644 --- a/shortcuts/mail/mail_reply_all.go +++ b/shortcuts/mail/mail_reply_all.go @@ -33,6 +33,7 @@ var MailReplyAll = common.Shortcut{ {Name: "attach", Desc: "Attachment file path(s), comma-separated (relative path only)"}, {Name: "inline", Desc: "Inline images as a JSON array. Each entry: {\"cid\":\"\",\"file_path\":\"\"}. All file_path values must be relative paths. Cannot be used with --plain-text. CID images are embedded via in the HTML body. CID is a unique identifier, e.g. a random hex string like \"a1b2c3d4e5f6a7b8c9d0\"."}, {Name: "confirm-send", Type: "bool", Desc: "Send the reply immediately instead of saving as draft. Only use after the user has explicitly confirmed recipients and content."}, + {Name: "send-time", Desc: "Scheduled send time as a Unix timestamp in seconds. Must be at least 5 minutes in the future. Use with --confirm-send to schedule the email."}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { messageId := runtime.Str("message-id") @@ -57,6 +58,9 @@ var MailReplyAll = common.Shortcut{ if err := validateConfirmSendScope(runtime); err != nil { return err } + if err := validateSendTime(runtime); err != nil { + return err + } return validateComposeInlineAndAttachments(runtime.FileIO(), runtime.Str("attach"), runtime.Str("inline"), runtime.Bool("plain-text"), "") }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { @@ -70,6 +74,7 @@ var MailReplyAll = common.Shortcut{ attachFlag := runtime.Str("attach") inlineFlag := runtime.Str("inline") confirmSend := runtime.Bool("confirm-send") + sendTime := runtime.Str("send-time") inlineSpecs, err := parseInlineSpecs(inlineFlag) if err != nil { @@ -195,7 +200,7 @@ var MailReplyAll = common.Shortcut{ hintSendDraft(runtime, mailboxID, draftID) return nil } - resData, err := draftpkg.Send(runtime, mailboxID, draftID) + resData, err := draftpkg.Send(runtime, mailboxID, draftID, sendTime) if err != nil { return fmt.Errorf("failed to send reply-all (draft %s created but not sent): %w", draftID, err) } diff --git a/shortcuts/mail/mail_send.go b/shortcuts/mail/mail_send.go index 0680ce2e..a3965588 100644 --- a/shortcuts/mail/mail_send.go +++ b/shortcuts/mail/mail_send.go @@ -32,6 +32,7 @@ var MailSend = common.Shortcut{ {Name: "attach", Desc: "Attachment file path(s), comma-separated (relative path only)"}, {Name: "inline", Desc: "Inline images as a JSON array. Each entry: {\"cid\":\"\",\"file_path\":\"\"}. All file_path values must be relative paths. Cannot be used with --plain-text. CID images are embedded via in the HTML body. CID is a unique identifier, e.g. a random hex string like \"a1b2c3d4e5f6a7b8c9d0\"."}, {Name: "confirm-send", Type: "bool", Desc: "Send the email immediately instead of saving as draft. Only use after the user has explicitly confirmed recipients and content."}, + {Name: "send-time", Desc: "Scheduled send time as a Unix timestamp in seconds. Must be at least 5 minutes in the future. Use with --confirm-send to schedule the email."}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { to := runtime.Str("to") @@ -62,6 +63,9 @@ var MailSend = common.Shortcut{ if err := validateComposeHasAtLeastOneRecipient(runtime.Str("to"), runtime.Str("cc"), runtime.Str("bcc")); err != nil { return err } + if err := validateSendTime(runtime); err != nil { + return err + } return validateComposeInlineAndAttachments(runtime.FileIO(), runtime.Str("attach"), runtime.Str("inline"), runtime.Bool("plain-text"), runtime.Str("body")) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { @@ -74,6 +78,7 @@ var MailSend = common.Shortcut{ attachFlag := runtime.Str("attach") inlineFlag := runtime.Str("inline") confirmSend := runtime.Bool("confirm-send") + sendTime := runtime.Str("send-time") senderEmail := resolveComposeSenderEmail(runtime) @@ -145,7 +150,7 @@ var MailSend = common.Shortcut{ hintSendDraft(runtime, mailboxID, draftID) return nil } - resData, err := draftpkg.Send(runtime, mailboxID, draftID) + resData, err := draftpkg.Send(runtime, mailboxID, draftID, sendTime) if err != nil { return fmt.Errorf("failed to send email (draft %s created but not sent): %w", draftID, err) } diff --git a/skill-template/domains/mail.md b/skill-template/domains/mail.md index 349cf0d4..012f1320 100644 --- a/skill-template/domains/mail.md +++ b/skill-template/domains/mail.md @@ -42,7 +42,7 @@ 4. **回复** — `+reply` / `+reply-all`(默认存草稿,加 `--confirm-send` 则立即发送) 5. **转发** — `+forward`(默认存草稿,加 `--confirm-send` 则立即发送) 6. **新邮件** — `+send` 存草稿(默认),加 `--confirm-send` 发送 -7. **确认投递** — 发送后用 `send_status` 查询投递状态,向用户报告结果 +7. **确认投递** — 立即发送后用 `send_status` 查询投递状态,定时发送后在预定时间后再查询;取消定时发送用 `cancel_scheduled_send` 8. **编辑草稿** — `+draft-edit` 修改已有草稿。正文编辑通过 `--patch-file`:回复/转发草稿用 `set_reply_body` op 保留引用区,普通草稿用 `set_body` op ### CRITICAL — 首次使用任何命令前先查 `-h` @@ -62,15 +62,17 @@ lark-cli mail user_mailbox.messages -h ### 命令选择:先判断邮件类型,再决定草稿还是发送 -| 邮件类型 | 存草稿(不发送) | 直接发送 | -|----------|-----------------|---------| -| **新邮件** | `+send` 或 `+draft-create` | `+send --confirm-send` | -| **回复** | `+reply` 或 `+reply-all` | `+reply --confirm-send` 或 `+reply-all --confirm-send` | -| **转发** | `+forward` | `+forward --confirm-send` | +| 邮件类型 | 存草稿(不发送) | 直接发送 | 定时发送 | +|----------|-----------------|---------|----------| +| **新邮件** | `+send` 或 `+draft-create` | `+send --confirm-send` | `+send --confirm-send --send-time ` | +| **回复** | `+reply` 或 `+reply-all` | `+reply --confirm-send` 或 `+reply-all --confirm-send` | `+reply --confirm-send --send-time ` 或 `+reply-all --confirm-send --send-time ` | +| **转发** | `+forward` | `+forward --confirm-send` | `+forward --confirm-send --send-time ` | - 有原邮件上下文 → 用 `+reply` / `+reply-all` / `+forward`(默认即草稿),**不要用 `+draft-create`** - **发送前必须向用户确认收件人和内容,用户明确同意后才可加 `--confirm-send`** -- **发送后必须调用 `send_status` 确认投递状态**(详见下方说明) +- **立即发送后必须调用 `send_status` 确认投递状态**;定时发送(`--send-time`)在预定发送时间后再查询,取消定时发送用 `cancel_scheduled_send`(详见下方说明) + +> **定时发送注意事项**:`--send-time` 必须与 `--confirm-send` 配合使用,不能单独使用。`send_time` 为 Unix 时间戳(秒),需至少为当前时间 + 5 分钟。 ### 使用公共邮箱或别名(send_as)发信 @@ -109,7 +111,7 @@ lark-cli mail +send --mailbox me --from alias@example.com \ ### 发送后确认投递状态 -邮件发送成功后(收到 `message_id`),**必须**调用 `send_status` API 查询投递状态并向用户报告: +**立即发送(无 `--send-time`)**:邮件发送成功后(收到 `message_id`),**必须**调用 `send_status` API 查询投递状态并向用户报告: ```bash lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}' @@ -117,6 +119,14 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 返回每个收件人的投递状态(`status`):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":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + ### 撤回邮件 发送成功后,若响应中包含 `recall_available: true`,说明该邮件支持撤回(24 小时内已投递的邮件)。 diff --git a/skills/lark-mail/SKILL.md b/skills/lark-mail/SKILL.md index 292edae0..ce6ce614 100644 --- a/skills/lark-mail/SKILL.md +++ b/skills/lark-mail/SKILL.md @@ -56,7 +56,7 @@ metadata: 4. **回复** — `+reply` / `+reply-all`(默认存草稿,加 `--confirm-send` 则立即发送) 5. **转发** — `+forward`(默认存草稿,加 `--confirm-send` 则立即发送) 6. **新邮件** — `+send` 存草稿(默认),加 `--confirm-send` 发送 -7. **确认投递** — 发送后用 `send_status` 查询投递状态,向用户报告结果 +7. **确认投递** — 立即发送后用 `send_status` 查询投递状态,定时发送后在预定时间后再查询;取消定时发送用 `cancel_scheduled_send` 8. **编辑草稿** — `+draft-edit` 修改已有草稿。正文编辑通过 `--patch-file`:回复/转发草稿用 `set_reply_body` op 保留引用区,普通草稿用 `set_body` op ### CRITICAL — 首次使用任何命令前先查 `-h` @@ -76,15 +76,17 @@ lark-cli mail user_mailbox.messages -h ### 命令选择:先判断邮件类型,再决定草稿还是发送 -| 邮件类型 | 存草稿(不发送) | 直接发送 | -|----------|-----------------|---------| -| **新邮件** | `+send` 或 `+draft-create` | `+send --confirm-send` | -| **回复** | `+reply` 或 `+reply-all` | `+reply --confirm-send` 或 `+reply-all --confirm-send` | -| **转发** | `+forward` | `+forward --confirm-send` | +| 邮件类型 | 存草稿(不发送) | 直接发送 | 定时发送 | +|----------|-----------------|---------|----------| +| **新邮件** | `+send` 或 `+draft-create` | `+send --confirm-send` | `+send --confirm-send --send-time ` | +| **回复** | `+reply` 或 `+reply-all` | `+reply --confirm-send` 或 `+reply-all --confirm-send` | `+reply --confirm-send --send-time ` 或 `+reply-all --confirm-send --send-time ` | +| **转发** | `+forward` | `+forward --confirm-send` | `+forward --confirm-send --send-time ` | - 有原邮件上下文 → 用 `+reply` / `+reply-all` / `+forward`(默认即草稿),**不要用 `+draft-create`** - **发送前必须向用户确认收件人和内容,用户明确同意后才可加 `--confirm-send`** -- **发送后必须调用 `send_status` 确认投递状态**(详见下方说明) +- **立即发送后必须调用 `send_status` 确认投递状态**;定时发送(`--send-time`)在预定发送时间后再查询,取消定时发送用 `cancel_scheduled_send`(详见下方说明) + +> **定时发送注意事项**:`--send-time` 必须与 `--confirm-send` 配合使用,不能单独使用。`send_time` 为 Unix 时间戳(秒),需至少为当前时间 + 5 分钟。 ### 使用公共邮箱或别名(send_as)发信 @@ -123,7 +125,7 @@ lark-cli mail +send --mailbox me --from alias@example.com \ ### 发送后确认投递状态 -邮件发送成功后(收到 `message_id`),**必须**调用 `send_status` API 查询投递状态并向用户报告: +**立即发送(无 `--send-time`)**:邮件发送成功后(收到 `message_id`),**必须**调用 `send_status` API 查询投递状态并向用户报告: ```bash lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}' @@ -131,6 +133,14 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 返回每个收件人的投递状态(`status`):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":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + ### 撤回邮件 发送成功后,若响应中包含 `recall_available: true`,说明该邮件支持撤回(24 小时内已投递的邮件)。 @@ -292,6 +302,7 @@ lark-cli mail [flags] # 调用 API ### user_mailbox.drafts + - `cancel_scheduled_send` — 取消定时发送 - `create` — 创建草稿 - `delete` — 删除指定邮箱账户下的单份邮件草稿。注意:对于草稿状态的邮件,只能使用本接口删除,禁止使用 trash_message;被删除的草稿数据无法恢复,请谨慎使用。 - `get` — 获取草稿详情 @@ -376,6 +387,7 @@ lark-cli mail [flags] # 调用 API | `user_mailboxes.accessible_mailboxes` | `mail:user_mailbox:readonly` | | `user_mailboxes.profile` | `mail:user_mailbox:readonly` | | `user_mailboxes.search` | `mail:user_mailbox.message:readonly` | +| `user_mailbox.drafts.cancel_scheduled_send` | `mail:user_mailbox.message:send` | | `user_mailbox.drafts.create` | `mail:user_mailbox.message:modify` | | `user_mailbox.drafts.delete` | `mail:user_mailbox.message:modify` | | `user_mailbox.drafts.get` | `mail:user_mailbox.message:readonly` | diff --git a/skills/lark-mail/references/lark-mail-forward.md b/skills/lark-mail/references/lark-mail-forward.md index 69165af6..17a271d4 100644 --- a/skills/lark-mail/references/lark-mail-forward.md +++ b/skills/lark-mail/references/lark-mail-forward.md @@ -67,6 +67,7 @@ lark-cli mail +forward --message-id <邮件ID> --to alice@example.com --dry-run | `--attach ` | 否 | 附件文件路径,多个用逗号分隔,追加在原邮件附件之后。相对路径 | | `--inline ` | 否 | 高级用法:手动指定内嵌图片 CID 映射。推荐直接在 `--body` 中使用 ``(自动解析)。仅在需要精确控制 CID 命名时使用此参数。格式:`'[{"cid":"mycid","file_path":"./logo.png"}]'`,在 body 中用 `` 引用。不可与 `--plain-text` 同时使用 | | `--confirm-send` | 否 | 确认发送转发(默认只保存草稿)。仅在用户明确确认后使用 | +| `--send-time ` | 否 | 定时发送时间,Unix 时间戳(秒)。需至少为当前时间 + 5 分钟。配合 `--confirm-send` 使用可定时发送邮件 | | `--dry-run` | 否 | 仅打印请求,不执行 | ## 返回值 @@ -113,6 +114,25 @@ lark-cli mail +forward --message-id <邮件ID> --to bob@example.com --body '

F lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":""}' ``` +### 场景 3:用户说"下午 3 点转发给 Bob"(定时发送) +```bash +# Step 1: 创建转发草稿 +lark-cli mail +forward --message-id <邮件ID> --to bob@example.com --body '

FYI,请查收。

' +# → 返回 draft_id + +# Step 2: 向用户确认 "转发草稿已创建:收件人 bob@example.com,定时 <目标时间> 发送。确认吗?" + +# Step 3: 用户确认后定时发送(send_time 为 Unix 时间戳,需至少当前时间 + 5 分钟) +lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":""}' --data '{"send_time":""}' +``` + +### 场景 4:用户说"等等,先不转发了"(取消定时发送) +```bash +# 取消定时发送(取消后邮件变回草稿) +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` +→ 取消成功后邮件恢复为草稿状态,用户可重新编辑或在之后重新发送。 + ## 转发整个会话 `+forward` 操作的是单封邮件(`--message-id`),但转发整个会话时应 forward **会话中最后一条消息**,因为邮件客户端会将完整的回复链嵌套在最新一条中。典型流程: @@ -138,7 +158,9 @@ lark-cli mail +forward --message-id <最后一条的message_id> --to recipient@e 转发发送成功后: -**1. 确认投递状态**(必须)— 用返回的 `message_id` 查询投递状态: +**1. 确认投递状态**(仅立即发送 — 无 `--send-time` 时必须) + +用返回的 `message_id` 查询投递状态: ```bash lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}' @@ -146,6 +168,18 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 状态码:1=正在投递, 2=投递失败重试, 3=退信, 4=投递成功, 5=待审批, 6=审批拒绝。向用户简要报告投递结果,异常状态需重点提示。 +**1b. 定时发送(指定了 `--send-time`)** + +定时发送不会立即产生 `message_id`,因此 `send_status` 在定时发送成功后会返回"待发送"状态,**不建议在定时发送后立即查询**。可在预定发送时间后再查询。 + +如需取消定时发送: + +```bash +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + **2. 标记已读**(可选)— 询问用户是否需要将原邮件标记为已读。如果用户同意: ```bash diff --git a/skills/lark-mail/references/lark-mail-reply-all.md b/skills/lark-mail/references/lark-mail-reply-all.md index c5f73651..645f758a 100644 --- a/skills/lark-mail/references/lark-mail-reply-all.md +++ b/skills/lark-mail/references/lark-mail-reply-all.md @@ -71,6 +71,7 @@ lark-cli mail +reply-all --message-id <邮件ID> --body '测试' --dry-run | `--attach ` | 否 | 附件文件路径,多个用逗号分隔。相对路径 | | `--inline ` | 否 | 高级用法:手动指定内嵌图片 CID 映射。推荐直接在 `--body` 中使用 ``(自动解析)。仅在需要精确控制 CID 命名时使用此参数。格式:`'[{"cid":"mycid","file_path":"./logo.png"}]'`,在 body 中用 `` 引用。不可与 `--plain-text` 同时使用 | | `--confirm-send` | 否 | 确认发送回复(默认只保存草稿)。仅在用户明确确认后使用 | +| `--send-time ` | 否 | 定时发送时间,Unix 时间戳(秒)。需至少为当前时间 + 5 分钟。配合 `--confirm-send` 使用可定时发送邮件 | | `--dry-run` | 否 | 仅打印请求,不执行 | ## 返回值 @@ -117,6 +118,25 @@ lark-cli mail +reply-all --message-id <邮件ID> --body '

已确认。

' lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":""}' ``` +### 场景 3:用户说"下午 3 点回复全部说已确认"(定时发送) +```bash +# Step 1: 创建回复全部草稿 +lark-cli mail +reply-all --message-id <邮件ID> --body '

已确认。

' +# → 返回 draft_id + +# Step 2: 向用户确认 "回复全部草稿已创建:收件人 alice@, bob@, carol@,内容「已确认。」定时 <目标时间> 发送。确认吗?" + +# Step 3: 用户确认后定时发送(send_time 为 Unix 时间戳,需至少当前时间 + 5 分钟) +lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":""}' --data '{"send_time":""}' +``` + +### 场景 4:用户说"等等,先不回复了"(取消定时发送) +```bash +# 取消定时发送(取消后邮件变回草稿) +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` +→ 取消成功后邮件恢复为草稿状态,用户可重新编辑或在之后重新发送。 + ## 实现说明 - 自动收件人规则:原发件人优先进入 To,原 To/Cc 进入 Cc。 @@ -128,7 +148,9 @@ lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_ 回复发送成功后: -**1. 确认投递状态**(必须)— 用返回的 `message_id` 查询投递状态: +**1. 确认投递状态**(仅立即发送 — 无 `--send-time` 时必须) + +用返回的 `message_id` 查询投递状态: ```bash lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}' @@ -136,6 +158,18 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 状态码:1=正在投递, 2=投递失败重试, 3=退信, 4=投递成功, 5=待审批, 6=审批拒绝。向用户简要报告投递结果,异常状态需重点提示。 +**1b. 定时发送(指定了 `--send-time`)** + +定时发送不会立即产生 `message_id`,因此 `send_status` 在定时发送成功后会返回"待发送"状态,**不建议在定时发送后立即查询**。可在预定发送时间后再查询。 + +如需取消定时发送: + +```bash +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + **2. 标记已读**(可选)— 询问用户是否需要将原邮件标记为已读。如果用户同意: ```bash diff --git a/skills/lark-mail/references/lark-mail-reply.md b/skills/lark-mail/references/lark-mail-reply.md index 2a0485e6..c75902de 100644 --- a/skills/lark-mail/references/lark-mail-reply.md +++ b/skills/lark-mail/references/lark-mail-reply.md @@ -74,6 +74,7 @@ lark-cli mail +reply --message-id <邮件ID> --body '

测试

' --dry-run | `--attach ` | 否 | 附件文件路径,多个用逗号分隔。相对路径 | | `--inline ` | 否 | 高级用法:手动指定内嵌图片 CID 映射。推荐直接在 `--body` 中使用 ``(自动解析)。仅在需要精确控制 CID 命名时使用此参数。格式:`'[{"cid":"mycid","file_path":"./logo.png"}]'`,在 body 中用 `` 引用。不可与 `--plain-text` 同时使用 | | `--confirm-send` | 否 | 确认发送回复(默认只保存草稿)。仅在用户明确确认后使用 | +| `--send-time ` | 否 | 定时发送时间,Unix 时间戳(秒)。需至少为当前时间 + 5 分钟。配合 `--confirm-send` 使用可定时发送邮件 | | `--dry-run` | 否 | 仅打印请求,不执行 | ## 返回值 @@ -120,6 +121,25 @@ lark-cli mail +reply --message-id <邮件ID> --body '

已处理,谢谢。

"}' ``` +### 场景 3:用户说"下午 3 点回复这封邮件说已处理"(定时发送) +```bash +# Step 1: 创建回复草稿 +lark-cli mail +reply --message-id <邮件ID> --body '

已处理,谢谢。

' +# → 返回 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":""}' --data '{"send_time":""}' +``` + +### 场景 4:用户说"等等,先不回复了"(取消定时发送) +```bash +# 取消定时发送(取消后邮件变回草稿) +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` +→ 取消成功后邮件恢复为草稿状态,用户可重新编辑或在之后重新发送。 + ## 实现说明 ### 会话维护 @@ -143,7 +163,9 @@ References: <原邮件references + smtp_message_id> 回复发送成功后: -**1. 确认投递状态**(必须)— 用返回的 `message_id` 查询投递状态: +**1. 确认投递状态**(仅立即发送 — 无 `--send-time` 时必须) + +用返回的 `message_id` 查询投递状态: ```bash lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me","message_id":"<发送返回的 message_id>"}' @@ -151,6 +173,18 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 状态码:1=正在投递, 2=投递失败重试, 3=退信, 4=投递成功, 5=待审批, 6=审批拒绝。向用户简要报告投递结果,异常状态需重点提示。 +**1b. 定时发送(指定了 `--send-time`)** + +定时发送不会立即产生 `message_id`,因此 `send_status` 在定时发送成功后会返回"待发送"状态,**不建议在定时发送后立即查询**。可在预定发送时间后再查询。 + +如需取消定时发送: + +```bash +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + **2. 标记已读**(可选)— 询问用户是否需要将原邮件标记为已读。如果用户同意: ```bash diff --git a/skills/lark-mail/references/lark-mail-send.md b/skills/lark-mail/references/lark-mail-send.md index eca4d300..1b4a4053 100644 --- a/skills/lark-mail/references/lark-mail-send.md +++ b/skills/lark-mail/references/lark-mail-send.md @@ -71,6 +71,7 @@ lark-cli mail +send --to alice@example.com --subject '测试' --body '

test

` | 否 | 附件文件路径,多个用逗号分隔。相对路径 | | `--inline ` | 否 | 高级用法:手动指定内嵌图片 CID 映射。推荐直接在 `--body` 中使用 ``(自动解析)。仅在需要精确控制 CID 命名时使用此参数。格式:`'[{"cid":"mycid","file_path":"./logo.png"}]'`,在 body 中用 `` 引用。不可与 `--plain-text` 同时使用 | | `--confirm-send` | 否 | 确认发送邮件(默认只保存草稿)。仅在用户明确确认收件人和内容后使用 | +| `--send-time ` | 否 | 定时发送时间,Unix 时间戳(秒)。需至少为当前时间 + 5 分钟。配合 `--confirm-send` 使用可定时发送邮件 | | `--dry-run` | 否 | 仅打印请求,不执行 | ## 返回值 @@ -119,8 +120,29 @@ lark-cli mail +send --to alice@example.com --subject '收到' --body '

已收 lark-cli mail user_mailbox.drafts send --params '{"user_mailbox_id":"me","draft_id":""}' ``` +### 场景 3:用户说"下午 3 点给 Alice 发一封周报"(定时发送) +```bash +# Step 1: 创建草稿(定时发送也走草稿流程) +lark-cli mail +send --to alice@example.com --subject '周报' --body '

本周进展如下...

' +# → 返回 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":""}' --data '{"send_time":""}' +``` + +### 场景 4:用户说"等等,先不发那封邮件了"(取消定时发送) +```bash +# 取消定时发送(取消后邮件变回草稿) +lark-cli mail user_mailbox.drafts cancel_scheduled_send --params '{"user_mailbox_id":"me","draft_id":""}' +``` +→ 取消成功后邮件恢复为草稿状态,用户可重新编辑或在之后重新发送。 + ## 发送后跟进 +### 立即发送(无 `--send-time`) + 邮件发送成功后(收到 `message_id`),**必须**调用 `send_status` 查询投递状态: ```bash @@ -129,6 +151,18 @@ lark-cli mail user_mailbox.messages send_status --params '{"user_mailbox_id":"me 状态码: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":""}' +``` + +**取消后邮件会变回草稿**,可继续编辑或在之后重新发送。 + ## 实现说明 - 使用 EML 构建器生成完整 MIME 邮件并 base64url 编码后发送。