Files
dingtalk-workspace-cli/internal/helpers/ding.go
T
qinze a65d6f23ec fix: resolve real-machine QA findings across CLI, scripts and skill docs
Fix CLI command bugs surfaced by full real-machine QA:
- aitable: make chart/dashboard share update --enabled a string flag so
  "--enabled false" disables instead of silently enabling (bool flag +
  space-syntax help example inverted the action); clarify chart update
  requires --config; make form get filter by view-id client-side so it
  returns a single form; drop inline // comments from chart JSON examples
- chat: resolve conversation-info --user to openDingTalkId, register
  --id/--conversation-id/--chat aliases; cap list-all-conversations
  --limit at 100 and reject larger values instead of silent truncation;
  detect webhook errcode failures instead of wrapping them as success;
  remove duplicate group/members subcommand registration in help
- contact: register --dept/--depts as the primary dept flags to match
  the RunE parsing (were only registered as --id/--ids)
- sheet: emit clean JSON for media-upload and export under --format json,
  suppressing progress lines that leaked onto stdout
- wiki: correct node create --type enum (drop unsupported asheet, add
  axls/able/appt/adraw/amind)
- ding: default message list --type to ALL since the server rejects an
  empty type

Fix helper scripts (mono and multi):
- aitable import/export flag names and tableId length regex
- mail search --limit, contact dept response keys and userInfo nesting
- attendance_my_record whoami compatibility, calendar_schedule_meeting
  event id unwrapping, drive_tree_list recursion via fileId, report
  scripts migrated off deprecated report list/detail

Sync skill docs (mono and multi) to real-machine behavior across all
products: command indexes, flag names, enums, return-structure keys, and
cross-product intent routing; annotate genuinely server-side limitations
and the no-op global --jq/--fields flags.
2026-07-07 23:38:23 +08:00

240 lines
11 KiB
Go
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.
package helpers
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
)
// ──────────────────────────────────────────────────────────
// dws ding — DING 消息
// ──────────────────────────────────────────────────────────
// remindType: 服务端 API 1=应用内 2=短信 3=电话
var dingRemindTypeMap = map[string]int{"app": 1, "sms": 2, "call": 3}
func newDingCommand() *cobra.Command {
root := &cobra.Command{
Use: "ding",
Short: "DING 消息 / 发送 / 撤回",
Long: `发送和撤回 DING 消息(应用内/短信/电话)。预发环境可用。`,
RunE: groupRunE,
}
dingMessageCmd := &cobra.Command{Use: "message", Short: "DING 消息管理", RunE: groupRunE}
dingMessageSendCmd := &cobra.Command{
Use: "send",
Short: "发送 DING 消息",
Long: `发送 DING 消息。类型:
app = 应用内 DING (默认)
sms = 短信 DING (有成本)
call = 电话 DING (有成本)`,
Example: ` # 查询 userId: dws contact user search --keyword "姓名"
dws ding message send --robot-code <robot-code> --users userId1,userId2 --content "请查看"
dws ding message send --robot-code <robot-code> --type call --users userId1 --content "紧急告警"`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "users", "content"); err != nil {
return err
}
robotCode := mustGetFlag(cmd, "robot-code")
if robotCode == "" {
robotCode = os.Getenv("DINGTALK_DING_ROBOT_CODE")
}
if robotCode == "" {
return fmt.Errorf("flag --robot-code is required (or set DINGTALK_DING_ROBOT_CODE env var)")
}
typeStr := mustGetFlag(cmd, "type")
remindType, ok := dingRemindTypeMap[typeStr]
if !ok {
remindType = 1 // 默认应用内
}
toStr := mustGetFlag(cmd, "users")
var receiverUserIdList []string
for _, uid := range strings.Split(toStr, ",") {
if u := strings.TrimSpace(uid); u != "" {
receiverUserIdList = append(receiverUserIdList, u)
}
}
return callMCPTool("send_ding_message", map[string]any{
"robotCode": robotCode,
"remindType": remindType,
"receiverUserIdList": receiverUserIdList,
"content": mustGetFlag(cmd, "content"),
})
},
}
dingMessageRecallCmd := &cobra.Command{
Use: "recall",
Short: "撤回 DING 消息",
Example: ` dws ding message recall --robot-code <robot-code> --id <open-ding-id>`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "id"); err != nil {
return err
}
robotCode := mustGetFlag(cmd, "robot-code")
if robotCode == "" {
robotCode = os.Getenv("DINGTALK_DING_ROBOT_CODE")
}
if robotCode == "" {
return fmt.Errorf("flag --robot-code is required (or set DINGTALK_DING_ROBOT_CODE env var)")
}
return callMCPTool("recall_ding_message", map[string]any{
"robotCode": robotCode,
"openDingId": mustGetFlag(cmd, "id"),
})
},
}
dingMessageListCmd := &cobra.Command{
Use: "list",
Short: "查询 DING 消息历史",
Long: `查询当前用户的 DING 消息列表,支持按类型过滤。
--type 支持: ALL(全部)、UNREAD(未读)、SEND(已发)、NEW_COMMENT(新评论)、DELETED(已删除)。
--type 为服务端必填字段,空值会报「type不能为空」;不传时 CLI 默认按 ALL 查询。`,
Example: ` dws ding message list # 默认 --type ALL
dws ding message list --type UNREAD
dws ding message list --type SEND --cursor 10`,
RunE: func(cmd *cobra.Command, args []string) error {
toolArgs := map[string]any{}
if v, _ := cmd.Flags().GetInt64("cursor"); v > 0 {
toolArgs["cursor"] = v
}
// type 是服务端必填,空值会报错;不传或传空时兜底为 ALL。
t, _ := cmd.Flags().GetString("type")
if t == "" {
t = "ALL"
}
toolArgs["type"] = t
return callMCPToolOnServer("im", "list_ding_messages", toolArgs)
},
}
dingMessageReceiverStatusCmd := &cobra.Command{
Use: "receiver-status",
Short: "查看 DING 接收状态",
Long: `查看指定 DING 消息的接收者状态(已读/未读等)。`,
Example: ` dws ding message receiver-status --ding-id <openDingId>
# 查询 dingId: dws ding message list`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "ding-id"); err != nil {
return err
}
return callMCPToolOnServer("im", "list_ding_receiver_status", map[string]any{
"openDingId": mustGetFlag(cmd, "ding-id"),
})
},
}
// ── send-personal: 以用户身份发送 DING ──────────────────────
dingMessageSendPersonalCmd := &cobra.Command{
Use: "send-personal",
Short: "以用户身份发送 DING",
Long: `以当前用户身份(非机器人)发送 DING 消息。提醒类型:
app = 应用内 DING (默认)
sms = 短信 DING (有成本)
call = 电话 DING (有成本)`,
Example: ` # 查询 openDingTalkId: dws contact user search --query "姓名"
dws ding message send-personal --users openDingTalkId1,openDingTalkId2 --content "请查看"
dws ding message send-personal --type call --users openDingTalkId1 --content "紧急告警"`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "users", "content"); err != nil {
return err
}
users := parseCSVValues(mustGetFlag(cmd, "users"))
toolArgs := map[string]any{
"receiverOpenDingTalkIds": users,
"content": mustGetFlag(cmd, "content"),
"remindType": mustGetFlag(cmd, "type"),
}
if v, _ := cmd.Flags().GetString("uuid"); v != "" {
toolArgs["uuid"] = v
}
return callMCPToolOnServer("im", "send_personal_ding", toolArgs)
},
}
// ── send-by-message: 消息转 DING ─────────────────────────────
dingMessageSendByMessageCmd := &cobra.Command{
Use: "send-by-message",
Short: "消息转 DING(将聊天消息转为 DING 通知)",
Long: `将指定聊天消息转为 DING 发送给指定接收者。提醒类型:
app = 应用内 DING (默认)
sms = 短信 DING (有成本)
call = 电话 DING (有成本)`,
Example: ` # 查询 openDingTalkId: dws contact user search --query "姓名"
# 查询 openConversationId: dws chat search --keyword "群名"
dws ding message send-by-message --group <openConversationId> --message-id <openMessageId> --users id1,id2
dws ding message send-by-message --group <openConversationId> --message-id <openMessageId> --users id1 --type sms`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "group", "message-id", "users"); err != nil {
return err
}
users := parseCSVValues(mustGetFlag(cmd, "users"))
toolArgs := map[string]any{
"openConversationId": mustGetFlag(cmd, "group"),
"openMessageId": mustGetFlag(cmd, "message-id"),
"receiverOpenDingTalkIds": users,
"remindType": mustGetFlag(cmd, "type"),
}
if v, _ := cmd.Flags().GetString("uuid"); v != "" {
toolArgs["uuid"] = v
}
return callMCPToolOnServer("im", "send_ding_by_message", toolArgs)
},
}
// ── recall-personal: 以用户身份撤回 DING ────────────────────
dingMessageRecallPersonalCmd := &cobra.Command{
Use: "recall-personal",
Short: "以用户身份撤回 DING",
Long: `以当前用户身份撤回已发送的 DING 消息。需要提供发送时返回的 openDingId。`,
Example: ` dws ding message recall-personal --id <openDingId>
# 查询 openDingId: dws ding message list`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "id"); err != nil {
return err
}
return callMCPToolOnServer("im", "recall_personal_ding", map[string]any{
"openDingId": mustGetFlag(cmd, "id"),
})
},
}
dingMessageSendCmd.Flags().String("robot-code", "", "机器人 ID,发 DING 的机器人编码 (必填,可从 应用管理→机器人 获取,或设 DINGTALK_DING_ROBOT_CODE)")
dingMessageSendCmd.Flags().String("type", "app", "提醒类型: app/sms/call (默认 app)")
dingMessageSendCmd.Flags().String("users", "", "接收人 userId 列表 (必填)")
dingMessageSendCmd.Flags().String("content", "", "消息内容 (必填)")
dingMessageRecallCmd.Flags().String("robot-code", "", "机器人 ID (必填,或设 DINGTALK_DING_ROBOT_CODE)")
dingMessageRecallCmd.Flags().String("id", "", "DING 消息 ID (必填)")
dingMessageListCmd.Flags().Int64("cursor", 0, "分页游标(首次传 0,翻页传返回的 nextCursor)")
dingMessageListCmd.Flags().String("type", "ALL", "消息类型: ALL / UNREAD / SEND / NEW_COMMENT / DELETED(必填,服务端不接受空值;默认 ALL 全部)")
dingMessageReceiverStatusCmd.Flags().String("ding-id", "", "DING 消息 openDingId (必填)")
_ = dingMessageReceiverStatusCmd.MarkFlagRequired("ding-id")
dingMessageSendPersonalCmd.Flags().String("users", "", "接收者 openDingTalkId 列表,逗号分隔 (必填)")
_ = dingMessageSendPersonalCmd.MarkFlagRequired("users")
dingMessageSendPersonalCmd.Flags().String("content", "", "DING 内容 (必填)")
_ = dingMessageSendPersonalCmd.MarkFlagRequired("content")
dingMessageSendPersonalCmd.Flags().String("type", "app", "提醒类型: app/sms/call (默认 app)")
dingMessageSendPersonalCmd.Flags().String("uuid", "", "幂等唯一标识(可选,不传由服务端生成)")
dingMessageRecallPersonalCmd.Flags().String("id", "", "DING 消息 openDingId (必填)")
_ = dingMessageRecallPersonalCmd.MarkFlagRequired("id")
dingMessageSendByMessageCmd.Flags().String("group", "", "原消息所在会话 openConversationId (必填)")
_ = dingMessageSendByMessageCmd.MarkFlagRequired("group")
dingMessageSendByMessageCmd.Flags().String("message-id", "", "原消息 openMessageId (必填)")
_ = dingMessageSendByMessageCmd.MarkFlagRequired("message-id")
dingMessageSendByMessageCmd.Flags().String("users", "", "接收者 openDingTalkId 列表,逗号分隔 (必填)")
_ = dingMessageSendByMessageCmd.MarkFlagRequired("users")
dingMessageSendByMessageCmd.Flags().String("type", "app", "提醒类型: app/sms/call (默认 app)")
dingMessageSendByMessageCmd.Flags().String("uuid", "", "幂等唯一标识(可选,不传由服务端生成)")
dingMessageCmd.AddCommand(dingMessageSendCmd, dingMessageRecallCmd, dingMessageListCmd, dingMessageReceiverStatusCmd, dingMessageSendPersonalCmd, dingMessageRecallPersonalCmd, dingMessageSendByMessageCmd)
root.AddCommand(dingMessageCmd)
return root
}