Files
dingtalk-workspace-cli/internal/shortcut/smart/unread_mail.go

157 lines
7.1 KiB
Go
Raw Permalink 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.
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smart
import (
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
)
// UnreadMail: list the current user's unread emails as a compact projection.
//
// Steps:
//
// 1. resolve the mailbox address — use --email when given, otherwise pick the
// current user's first bound mailbox via list_user_mailboxes (reusing
// searchMailFirstMailbox from search_mail.go);
//
// 2. search that mailbox via search_emails with the KQL filter isRead:false,
// matching the isRead field documented in helpers.messageSearch; size is
// passed as a string, defaulting to "20" like the helper's sizeVal;
//
// 3. in Go, project each returned message to {subject, from, date, messageId}
// via the shared searchMail* helpers and print the list with rt.Output so it
// honours --format/--jq/--fields.
//
// Read-only: it only lists and projects, never mutating any mail.
//
// dws mail +unread-mail
// dws mail +unread-mail --email user@company.com
var UnreadMail = shortcut.Shortcut{
Service: "mail",
Command: "+unread-mail",
Product: "mail",
Description: "列出未读邮件并投影列表(主题/发件人/时间/messageId)",
Intent: "当你想快速看自己邮箱里有哪些未读邮件、并只看一份精简清单(主题、发件人、时间、邮件 messageId)而不想翻完整正文时使用;" +
"内部先确定要查的邮箱地址——你可以用 --email 指定,不指定时自动取你绑定的第一个邮箱——" +
"再用 KQL 过滤条件 isRead:false 搜索未读邮件," +
"最后在本地把每封邮件投影成 {subject, from, date, messageId} 打印出来,可配合 --format/--jq/--fields。" +
"这是纯只读操作,只做搜索与本地投影,不会把邮件标记为已读,也不会修改、发送或删除任何邮件;若没有未读邮件则返回空列表。",
Risk: shortcut.RiskRead,
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: corecmd.ContractDecl{
Identity: contract.ToolIdentitySpec{
ProductID: "mail",
Name: "shortcut_unread_mail",
CanonicalPath: "mail.shortcut_unread_mail",
CLIPath: "mail +unread-mail",
PrimaryCLIPath: "mail +unread-mail",
},
Description: "列出未读邮件并投影列表(主题/发件人/时间/messageId)",
Interface: &contract.InterfaceSpec{
Mode: "composite",
Availability: "available",
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
},
Selection: contract.SelectionSpec{
AgentSummary: "列出未读邮件并投影列表(主题/发件人/时间/messageId)",
UseWhen: []string{"当你想快速看自己邮箱里有哪些未读邮件、并只看一份精简清单(主题、发件人、时间、邮件 messageId)而不想翻完整正文时使用;内部先确定要查的邮箱地址——你可以用 --email 指定,不指定时自动取你绑定的第一个邮箱——再用 KQL 过滤条件 isRead:false 搜索未读邮件,最后在本地把每封邮件投影成 {subject, from, date, messageId} 打印出来,可配合 --format/--jq/--fields。这是纯只读操作,只做搜索与本地投影,不会把邮件标记为已读,也不会修改、发送或删除任何邮件;若没有未读邮件则返回空列表。"},
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
Examples: []string{
"dws mail +unread-mail",
"dws mail +unread-mail --email user@company.com",
},
},
},
Flags: []shortcut.Flag{
{Name: "email", Type: shortcut.FlagString, Desc: "要查询的邮箱地址(可选,默认取你绑定的第一个邮箱)", Required: false},
{Name: "size", Type: shortcut.FlagString, Desc: "返回条数上限(可选,默认 20;显式提供时必须是 1-100 之间的整数)", Required: false},
{Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,取自上一页 nextCursor", Required: false},
},
Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"size"}, Description: "显式 --size 必须在 1-100 之间"}},
Validate: func(rt *shortcut.RuntimeContext) error {
return smartMailValidateStringPageSize(rt, "size")
},
Tips: []string{
`dws mail +unread-mail`,
`dws mail +unread-mail --email user@company.com`,
`dws mail +unread-mail --size 50`,
},
Execute: func(rt *shortcut.RuntimeContext) error {
size, err := smartMailStringPageSize(rt, "size", "20")
if err != nil {
return err
}
// Step 1 — resolve the mailbox address.
email := rt.Str("email")
if email == "" {
resolved, err := searchMailFirstMailbox(rt)
if err != nil {
return err
}
email = resolved
}
// Step 2 — search unread mail. email/query/size mirror the search_emails
// call in helpers.messageSearch; the isRead:false KQL filter matches the
// isRead field documented there; size is passed as a string.
args := map[string]any{
"email": email,
"query": "isRead:false",
"size": size,
}
if rt.Changed("cursor") {
args["cursor"] = rt.Str("cursor")
}
data, err := rt.CallMCPData("mail", "search_emails", args)
if err != nil {
return err
}
// Step 3 — project each message to a compact record (shared helpers).
messages, err := smartMailSearchRows(data, "mail/search_emails")
if err != nil {
return err
}
out := make([]map[string]any, 0, len(messages))
for _, m := range messages {
from, err := searchMailFrom(m)
if err != nil {
return err
}
out = append(out, map[string]any{
"subject": searchMailFirstString(m, "subject", "title", "topic"),
"from": from,
"date": searchMailFirstAny(m, "date", "sentDate", "receivedDate", "sentTime", "internalDate", "createTime"),
"messageId": searchMailFirstString(m, "messageId", "id", "mailId", "emailId", "internetMessageId"),
})
}
complete, next, err := smartMailPage(data, "mail/search_emails", "", rt.Str("cursor"))
if err != nil {
return err
}
return smartMailOutputPage(rt, "messages", out, complete, next)
},
}
func init() {
hardenSmartMail(&UnreadMail, "messages", "严格校验的未读邮件摘要")
markSmartMailCompatibilityOnly(&UnreadMail)
shortcut.Register(UnreadMail)
}