diff --git a/internal/app/legacy.go b/internal/app/legacy.go index 12bb540f..25fe605a 100644 --- a/internal/app/legacy.go +++ b/internal/app/legacy.go @@ -332,10 +332,8 @@ func asyncRevalidateRegistry(parent context.Context, store *cache.Store, partiti } } -func newLegacyHiddenCommands(runner executor.Runner) []*cobra.Command { - var commands []*cobra.Command - commands = append(commands, helpers.NewHiddenVendorCommands(runner)...) - return commands +func newLegacyHiddenCommands(_ executor.Runner) []*cobra.Command { + return nil } func mergeTopLevelCommands(commands []*cobra.Command) []*cobra.Command { diff --git a/internal/generator/generator.go b/internal/generator/generator.go index 1d7a4a06..6bf1a60f 100644 --- a/internal/generator/generator.go +++ b/internal/generator/generator.go @@ -123,7 +123,6 @@ var extended22CoverageTargets = []string{ "finance", "law", "docparse", - "credit", } func Generate(catalog ir.Catalog) ([]Artifact, error) { diff --git a/internal/generator/registry.go b/internal/generator/registry.go index 37e84011..67c9d453 100644 --- a/internal/generator/registry.go +++ b/internal/generator/registry.go @@ -67,7 +67,6 @@ var knownRegistryProducts = map[string]struct{}{ "chat": {}, "conference": {}, "contact": {}, - "credit": {}, "devdoc": {}, "ding": {}, "doc": {}, diff --git a/internal/helpers/chat.go b/internal/helpers/chat.go index 02234518..076f09a9 100644 --- a/internal/helpers/chat.go +++ b/internal/helpers/chat.go @@ -60,8 +60,6 @@ func (chatHandler) Command(runner executor.Runner) *cobra.Command { }, } message.AddCommand( - newChatMessageListCommand(runner), - newChatMessageSendCommand(runner), newChatMessageSendByBotCommand(runner), newChatMessageRecallByBotCommand(runner), newChatMessageSendByWebhookCommand(runner), @@ -83,40 +81,6 @@ func (chatHandler) Command(runner executor.Runner) *cobra.Command { return root } -func newChatMessageListCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Short: "拉取会话消息内容", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - params, tool, err := buildChatMessageListInvocation(cmd) - if err != nil { - return err - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - cobracmd.LegacyCommandPath(cmd), - "chat", - tool, - params, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) - }, - } - preferLegacyLeaf(cmd) - - cmd.Flags().String("group", "", "群会话 openconversation_id (群聊必填)") - cmd.Flags().Bool("forward", true, "true=正序拉取, false=倒序拉取") - cmd.Flags().Int("limit", 0, "返回条数,不传为不限制") - cmd.Flags().String("time", "", "起始时间,格式: yyyy-MM-dd HH:mm:ss") - cmd.Flags().String("user", "", "单聊对方 userId (单聊必填)") - return cmd -} - func newChatMessageSendByBotCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "send-by-bot", @@ -289,47 +253,6 @@ func newChatGroupCreateCommand(runner executor.Runner) *cobra.Command { return cmd } -func buildChatMessageListInvocation(cmd *cobra.Command) (map[string]any, string, error) { - group, err := cmd.Flags().GetString("group") - if err != nil { - return nil, "", apperrors.NewInternal("failed to read --group") - } - user, err := cmd.Flags().GetString("user") - if err != nil { - return nil, "", apperrors.NewInternal("failed to read --user") - } - timeValue, err := cmd.Flags().GetString("time") - if err != nil { - return nil, "", apperrors.NewInternal("failed to read --time") - } - if strings.TrimSpace(timeValue) == "" { - return nil, "", apperrors.NewValidation("--time is required") - } - if err := ensureExactlyOneTarget(group, user, "--group", "--user"); err != nil { - return nil, "", err - } - - params := map[string]any{ - "forward": cmd.Flag("forward").Value.String() == "true", - "time": timeValue, - } - limit, err := cmd.Flags().GetInt("limit") - if err != nil { - return nil, "", apperrors.NewInternal("failed to read --limit") - } - if limit > 0 { - params["limit"] = limit - } - - if strings.TrimSpace(group) != "" { - params["openconversation_id"] = group - return params, "list_conversation_message_v2", nil - } - - params["userId"] = user - return params, "list_individual_chat_message", nil -} - func buildChatMessageSendByBotInvocation(cmd *cobra.Command) (map[string]any, string, error) { group, err := cmd.Flags().GetString("group") if err != nil { @@ -509,62 +432,6 @@ func helperResponseContent(result executor.Result) map[string]any { return content } -// ── message send (user identity) ─────────────────────────── - -func newChatMessageSendCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "send", - Short: "以当前用户身份发送消息(--group 群聊 / --user 单聊)", - Long: "--group 指定群会话 ID 发送群消息;--user 指定用户 ID 发送单聊消息。二者只能选其一。", - Example: ` dws chat message send --group "hello" - dws chat message send --user "请查收"`, - Args: cobra.ExactArgs(1), - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - groupID, _ := cmd.Flags().GetString("group") - userID, _ := cmd.Flags().GetString("user") - if err := ensureExactlyOneTarget(groupID, userID, "--group", "--user"); err != nil { - return err - } - text := args[0] - title, _ := cmd.Flags().GetString("title") - if strings.TrimSpace(groupID) != "" { - params := map[string]any{ - "openConversation_id": groupID, - "title": title, - "text": text, - "clawType": "default", - } - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - cobracmd.LegacyCommandPath(cmd), "chat", "send_message_as_user", params, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) - } - params := map[string]any{ - "receiverUserId": userID, - "title": title, - "text": text, - "clawType": "default", - } - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - cobracmd.LegacyCommandPath(cmd), "chat", "send_direct_message_as_user", params, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) - }, - } - preferLegacyLeaf(cmd) - cmd.Flags().String("group", "", "群会话 openconversation_id (群聊必填)") - cmd.Flags().String("user", "", "接收者 userId (单聊必填)") - cmd.Flags().String("title", "Message", "消息标题 (可选, 默认 'Message')") - return cmd -} - // ── message recall-by-bot ────────────────────────────────── func newChatMessageRecallByBotCommand(runner executor.Runner) *cobra.Command { diff --git a/internal/helpers/credit.go b/internal/helpers/credit.go deleted file mode 100644 index e86dc76e..00000000 --- a/internal/helpers/credit.go +++ /dev/null @@ -1,217 +0,0 @@ -// 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 helpers - -import ( - "fmt" - "strings" - - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cobracmd" - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" - "github.com/spf13/cobra" -) - -func init() { - RegisterPublic(func() Handler { - return creditHelper{} - }) -} - -type creditHelper struct{} - -func (creditHelper) Name() string { - return "credit" -} - -func (creditHelper) Command(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "credit", - Short: "Enterprise credit search and risk helpers", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, - } - - cmd.AddCommand( - newCreditSearchCommand(runner), - newCreditRiskCommand(runner), - newCreditEquityCommand(runner), - ) - - return cmd -} - -func newCreditSearchCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "search", - Short: "Enterprise name search", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - name, err := cmd.Flags().GetString("name") - if err != nil { - return err - } - name = strings.TrimSpace(name) - if name == "" { - return fmt.Errorf("--name is required") - } - - params := map[string]any{ - "company_name": name, - } - if cmd.Flags().Changed("page") { - page, err := cmd.Flags().GetInt("page") - if err != nil { - return err - } - params["page"] = page - } - if cmd.Flags().Changed("size") { - size, err := cmd.Flags().GetInt("size") - if err != nil { - return err - } - params["size"] = size - } - - return runHelper(cmd, runner, "credit-ep", "ep_info_search_query", params) - }, - } - cmd.Flags().String("name", "", "Enterprise name keyword") - cmd.Flags().Int("page", 0, "Page offset") - cmd.Flags().Int("size", 0, "Page size") - return cmd -} - -func newCreditRiskCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "risk", - Short: "Enterprise risk information", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, - } - - court := &cobra.Command{ - Use: "court", - Short: "Court notice", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - cert, err := cmd.Flags().GetString("cert") - if err != nil { - return err - } - cert = strings.TrimSpace(cert) - if cert == "" { - return fmt.Errorf("--cert is required") - } - - params := map[string]any{ - "ep_cert_no": cert, - } - if cmd.Flags().Changed("page") { - page, err := cmd.Flags().GetInt("page") - if err != nil { - return err - } - params["page"] = page - } - if cmd.Flags().Changed("size") { - size, err := cmd.Flags().GetInt("size") - if err != nil { - return err - } - params["size"] = size - } - - return runHelper(cmd, runner, "credit-risk", "ep_dossier_courtnotice_query", params) - }, - } - court.Flags().String("cert", "", "Enterprise registration number or credit code") - court.Flags().Int("page", 0, "Page offset") - court.Flags().Int("size", 0, "Page size") - cmd.AddCommand(court) - return cmd -} - -func newCreditEquityCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "equity", - Short: "Enterprise equity information", - Args: cobra.NoArgs, - TraverseChildren: true, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, - } - - shareholder := &cobra.Command{ - Use: "shareholder", - Short: "Shareholder information", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - cert, err := cmd.Flags().GetString("cert") - if err != nil { - return err - } - cert = strings.TrimSpace(cert) - if cert == "" { - return fmt.Errorf("--cert is required") - } - - params := map[string]any{ - "ep_cert_no": cert, - } - if cmd.Flags().Changed("page") { - page, err := cmd.Flags().GetInt("page") - if err != nil { - return err - } - params["page"] = page - } - if cmd.Flags().Changed("size") { - size, err := cmd.Flags().GetInt("size") - if err != nil { - return err - } - params["size"] = size - } - - return runHelper(cmd, runner, "credit-equity", "ep_dossier_shareholder_query", params) - }, - } - shareholder.Flags().String("cert", "", "Enterprise registration number or credit code") - shareholder.Flags().Int("page", 0, "Page offset") - shareholder.Flags().Int("size", 0, "Page size") - cmd.AddCommand(shareholder) - return cmd -} - -func runHelper(cmd *cobra.Command, runner executor.Runner, canonicalProduct, tool string, params map[string]any) error { - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation(cobracmd.LegacyCommandPath(cmd), canonicalProduct, tool, params)) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} diff --git a/internal/helpers/dingtalk_vendor.go b/internal/helpers/dingtalk_vendor.go deleted file mode 100644 index 650bbbd0..00000000 --- a/internal/helpers/dingtalk_vendor.go +++ /dev/null @@ -1,379 +0,0 @@ -// 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 helpers - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cobracmd" - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/i18n" - "github.com/spf13/cobra" -) - -type dingtalkVendorHandler struct { - subtree string -} - -func init() { - for _, subtree := range []string{"discovery", "oa-plus", "ai-sincere-hire"} { - subtree := subtree - RegisterHiddenDingTalk(func() Handler { - return dingtalkVendorHandler{subtree: subtree} - }) - } -} - -func (d dingtalkVendorHandler) Name() string { - return d.subtree -} - -func (d dingtalkVendorHandler) Command(runner executor.Runner) *cobra.Command { - switch d.subtree { - case "discovery": - return newDiscoveryCommand(runner) - case "oa-plus": - return newOAPlusCommand(runner) - case "ai-sincere-hire": - return newAISincereHireCommand(runner) - default: - return newHiddenGroup("dingtalk", "Hidden DingTalk vendor extensions") - } -} - -func registerDingTalkFlags(cmd *cobra.Command) { - cmd.PersistentFlags().String("json", "", "NewsFeedPushRequest JSON payload") - cmd.PersistentFlags().String("source", "", "Crawl source ID") - cmd.PersistentFlags().String("filenames", "", "Comma-separated file names") - cmd.PersistentFlags().String("keyword", "", "Subscription keyword") - cmd.PersistentFlags().String("instance-id", "", "Approval instance ID") - cmd.PersistentFlags().String("process-code", "", "Approval process code") - cmd.PersistentFlags().String("size", "20", "Result size") - cmd.PersistentFlags().String("cursor", "", "Cursor") -} - -func newHiddenGroup(use, short string) *cobra.Command { - return cobracmd.NewHiddenGroupCommand(use, short) -} - -func newVisibleGroup(use, short string) *cobra.Command { - return cobracmd.NewGroupCommand(use, short) -} - -func newDiscoveryCommand(runner executor.Runner) *cobra.Command { - cmd := newVisibleGroup("discovery", "Content discovery") - registerDingTalkFlags(cmd) - media := newVisibleGroup("media", "Media content") - media.AddCommand(&cobra.Command{ - Use: "save", - Short: "Save media content", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runDiscoveryMediaSave(cmd, runner) - }, - }) - media.AddCommand(&cobra.Command{ - Use: "subscribe", - Short: "Subscribe to media source", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runDiscoveryMediaSubscribe(cmd, runner) - }, - }) - - oss := newVisibleGroup("oss", "Upload credentials") - oss.AddCommand(&cobra.Command{ - Use: "get-upload-url", - Short: "Get OSS upload URL", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runDiscoveryOSSGetUploadURL(cmd, runner) - }, - }) - - subscribe := newVisibleGroup("subscribe", "Keyword subscription") - subscribe.AddCommand(&cobra.Command{ - Use: "save", - Short: i18n.T("保存Keyword subscription规则"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runDiscoverySubscribeSave(cmd, runner) - }, - }) - - cmd.AddCommand(media, oss, subscribe) - return cmd -} - -func runDiscoveryMediaSave(cmd *cobra.Command, runner executor.Runner) error { - payload, err := cmd.Flags().GetString("json") - if err != nil { - return errors.NewInternal("failed to read --json") - } - if strings.TrimSpace(payload) == "" { - return errors.NewValidation("--json is required") - } - - var request any - if err := json.Unmarshal([]byte(payload), &request); err != nil { - return errors.NewValidation(fmt.Sprintf("--json must be valid JSON: %v", err)) - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk discovery media save", - "dingtalk-discovery", - "save_video_and_image", - map[string]any{ - "NewsFeedPushRequest": request, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runDiscoveryMediaSubscribe(cmd *cobra.Command, runner executor.Runner) error { - source, err := cmd.Flags().GetString("source") - if err != nil { - return errors.NewInternal("failed to read --source") - } - if strings.TrimSpace(source) == "" { - return errors.NewValidation("--source is required") - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk discovery media subscribe", - "dingtalk-discovery", - "save_media_subscribe_rule", - map[string]any{ - "skillCrawlId": source, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runDiscoveryOSSGetUploadURL(cmd *cobra.Command, runner executor.Runner) error { - filenames, err := cmd.Flags().GetString("filenames") - if err != nil { - return errors.NewInternal("failed to read --filenames") - } - if strings.TrimSpace(filenames) == "" { - return errors.NewValidation("--filenames is required") - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk discovery oss get-upload-url", - "dingtalk-discovery", - "batch_get_oss_temp_upload_url", - map[string]any{ - "filenames": filenames, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runDiscoverySubscribeSave(cmd *cobra.Command, runner executor.Runner) error { - keyword, err := cmd.Flags().GetString("keyword") - if err != nil { - return errors.NewInternal("failed to read --keyword") - } - if strings.TrimSpace(keyword) == "" { - return errors.NewValidation("--keyword is required") - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk discovery subscribe save", - "dingtalk-discovery", - "save_keyword_subscribe_rule", - map[string]any{ - "keyword": keyword, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func newOAPlusCommand(runner executor.Runner) *cobra.Command { - cmd := newVisibleGroup("oa-plus", "OA approval enhanced") - registerDingTalkFlags(cmd) - approval := newVisibleGroup("approval", i18n.T("审批实例管理")) - approval.AddCommand(&cobra.Command{ - Use: "get", - Short: i18n.T("获取审批实例详情"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runOAPlusApprovalGet(cmd, runner) - }, - }) - approval.AddCommand(&cobra.Command{ - Use: "list", - Short: i18n.T("分页查询审批实例"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runOAPlusApprovalList(cmd, runner) - }, - }) - cmd.AddCommand(approval) - return cmd -} - -func runOAPlusApprovalGet(cmd *cobra.Command, runner executor.Runner) error { - instanceID, err := cmd.Flags().GetString("instance-id") - if err != nil { - return errors.NewInternal("failed to read --instance-id") - } - if strings.TrimSpace(instanceID) == "" { - return errors.NewValidation("--instance-id is required") - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk oa-plus approval get", - "dingtalk-oa-plus", - "get_approval_instance", - map[string]any{ - "instanceId": instanceID, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runOAPlusApprovalList(cmd *cobra.Command, runner executor.Runner) error { - processCode, err := cmd.Flags().GetString("process-code") - if err != nil { - return errors.NewInternal("failed to read --process-code") - } - if strings.TrimSpace(processCode) == "" { - return errors.NewValidation("--process-code is required") - } - - size, err := cmd.Flags().GetString("size") - if err != nil { - return errors.NewInternal("failed to read --size") - } - cursor, err := cmd.Flags().GetString("cursor") - if err != nil { - return errors.NewInternal("failed to read --cursor") - } - - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk oa-plus approval list", - "dingtalk-oa-plus", - "list_approval_instances", - map[string]any{ - "processCode": processCode, - "size": size, - "cursor": cursor, - }, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func newAISincereHireCommand(runner executor.Runner) *cobra.Command { - cmd := newVisibleGroup("ai-sincere-hire", i18n.T("AI诚聘")) - registerDingTalkFlags(cmd) - cmd.AddCommand(&cobra.Command{ - Use: "guide", - Short: i18n.T("获取使用指引"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runAISincereHireGuide(cmd, runner) - }, - }) - job := newVisibleGroup("job", i18n.T("岗位查询")) - job.AddCommand(&cobra.Command{ - Use: "list", - Short: i18n.T("查询在招岗位"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runAISincereHireJobList(cmd, runner) - }, - }) - talent := newVisibleGroup("talent", i18n.T("人才查询")) - talent.AddCommand(&cobra.Command{ - Use: "list", - Short: i18n.T("查询入职人才"), - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return runAISincereHireTalentList(cmd, runner) - }, - }) - cmd.AddCommand(job, talent) - return cmd -} - -func runAISincereHireGuide(cmd *cobra.Command, runner executor.Runner) error { - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk ai-sincere-hire guide", - "dingtalk-ai-sincere-hire", - "query_guide_url", - map[string]any{}, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runAISincereHireJobList(cmd *cobra.Command, runner executor.Runner) error { - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk ai-sincere-hire job list", - "dingtalk-ai-sincere-hire", - "query_opening_job_list", - map[string]any{}, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} - -func runAISincereHireTalentList(cmd *cobra.Command, runner executor.Runner) error { - result, err := runner.Run(cmd.Context(), executor.NewHelperInvocation( - "dingtalk ai-sincere-hire talent list", - "dingtalk-ai-sincere-hire", - "query_success_talent_list", - map[string]any{}, - )) - if err != nil { - return err - } - return writeCommandPayload(cmd, result) -} diff --git a/internal/helpers/interfaces.go b/internal/helpers/interfaces.go index d5ef7a9c..9762c8b5 100644 --- a/internal/helpers/interfaces.go +++ b/internal/helpers/interfaces.go @@ -50,15 +50,9 @@ const ( nameMaxLen = 50 ) -type hiddenExtensionFactory struct { - manifest Manifest - factory Factory -} - var ( - registryMu sync.Mutex - publicFactories []Factory - hiddenVendorFactories []hiddenExtensionFactory + registryMu sync.Mutex + publicFactories []Factory ) func RegisterPublic(factory Factory) { @@ -67,99 +61,10 @@ func RegisterPublic(factory Factory) { publicFactories = append(publicFactories, factory) } -func RegisterHiddenVendor(vendor string, factory Factory) { - if factory == nil { - panic("helpers: hidden vendor factory is nil") - } - handler := factory() - if handler == nil { - panic("helpers: hidden vendor handler is nil") - } - - manifest := Manifest{ - Vendor: strings.TrimSpace(vendor), - Name: strings.TrimSpace(handler.Name()), - } - if err := ValidateNaming(manifest.Vendor, manifest.Name); err != nil { - panic(fmt.Sprintf("helpers: invalid hidden vendor extension %s: %v", manifest.FullName(), err)) - } - - registryMu.Lock() - defer registryMu.Unlock() - hiddenVendorFactories = append(hiddenVendorFactories, hiddenExtensionFactory{ - manifest: manifest, - factory: factory, - }) -} - -func RegisterHiddenDingTalk(factory Factory) { - RegisterHiddenVendor("dingtalk", factory) -} - func NewPublicCommands(runner executor.Runner) []*cobra.Command { return buildCommands(publicFactories, runner) } -func NewHiddenVendorCommands(runner executor.Runner) []*cobra.Command { - registryMu.Lock() - factories := append([]hiddenExtensionFactory(nil), hiddenVendorFactories...) - registryMu.Unlock() - - if len(factories) == 0 { - return nil - } - - byVendor := make(map[string][]*cobra.Command) - for _, registered := range factories { - handler := registered.factory() - if handler == nil { - continue - } - command := handler.Command(runner) - if command == nil { - continue - } - byVendor[registered.manifest.Vendor] = append(byVendor[registered.manifest.Vendor], command) - } - - vendors := make([]string, 0, len(byVendor)) - for vendor := range byVendor { - vendors = append(vendors, vendor) - } - sort.Strings(vendors) - - roots := make([]*cobra.Command, 0, len(vendors)) - for _, vendor := range vendors { - commands := byVendor[vendor] - sort.Slice(commands, func(i, j int) bool { - return commands[i].Use < commands[j].Use - }) - root := &cobra.Command{ - Use: vendor, - Short: fmt.Sprintf("Hidden %s vendor extensions", vendor), - Hidden: true, - Args: cobra.NoArgs, - TraverseChildren: true, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, - } - root.AddCommand(commands...) - roots = append(roots, root) - } - return roots -} - -func NewHiddenDingTalkCommand(runner executor.Runner) *cobra.Command { - for _, root := range NewHiddenVendorCommands(runner) { - if root != nil && root.Name() == "dingtalk" { - return root - } - } - return nil -} - func buildCommands(factories []Factory, runner executor.Runner) []*cobra.Command { registryMu.Lock() defer registryMu.Unlock() diff --git a/internal/helpers/interfaces_test.go b/internal/helpers/interfaces_test.go index d3457178..9ce4ccfe 100644 --- a/internal/helpers/interfaces_test.go +++ b/internal/helpers/interfaces_test.go @@ -13,11 +13,7 @@ package helpers -import ( - "testing" - - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" -) +import "testing" func TestValidateNaming(t *testing.T) { t.Parallel() @@ -51,27 +47,3 @@ func TestValidateNaming(t *testing.T) { } } -func TestNewHiddenVendorCommandsIncludesDingTalk(t *testing.T) { - roots := NewHiddenVendorCommands(executor.EchoRunner{}) - if len(roots) == 0 { - t.Fatal("NewHiddenVendorCommands() = empty, want hidden vendor roots") - } - - var dingtalkFound bool - for _, root := range roots { - if root == nil || root.Name() != "dingtalk" { - continue - } - dingtalkFound = true - if !root.Hidden { - t.Fatalf("dingtalk root hidden = false, want true") - } - if len(root.Commands()) == 0 { - t.Fatalf("dingtalk root children = 0, want registered hidden extensions") - } - } - - if !dingtalkFound { - t.Fatal("NewHiddenVendorCommands() missing dingtalk root") - } -} diff --git a/skills/SKILL.md b/skills/SKILL.md index 377f47b3..4f6d5b8b 100644 --- a/skills/SKILL.md +++ b/skills/SKILL.md @@ -1,6 +1,6 @@ --- name: dws -description: 管理钉钉产品能力(AI表格/日历/通讯录/机器人消息/待办/审批/考勤/日志/DING消息/工作台/开放平台文档等)。当用户需要操作表格数据、管理日程会议、查询通讯录、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)时使用。 +description: 管理钉钉产品能力(AI表格/日历/通讯录/群聊与机器人/待办/审批/考勤/日志/DING消息/工作台/开放平台文档等)。当用户需要操作表格数据、管理日程会议、查询通讯录、管理群聊、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)时使用。 cli_version: ">=1.0.0" --- @@ -28,7 +28,7 @@ cli_version: ">=1.0.0" | `approval` | 审批:审批表单/发起实例/审批/撤销 | [simple.md](./references/products/simple.md) | | `attendance` | 考勤:打卡记录/排班查询 | [attendance.md](./references/products/attendance.md) | | `calendar` | 日历:日程/参与者/会议室/闲忙查询 | [calendar.md](./references/products/calendar.md) | -| `chat` | 机器人消息:机器人群发/单聊/撤回/Webhook/机器人搜索/添加机器人到群 | [chat.md](./references/products/chat.md) | +| `chat` | 群聊与机器人:搜索群/建群/群成员管理/改群名/机器人群发/单聊/撤回/Webhook/机器人搜索 | [chat.md](./references/products/chat.md) | | `contact` | 通讯录:用户查询(当前用户/搜索/详情)/部门查询(搜索/子部门/成员列表) | [contact.md](./references/products/contact.md) | | `devdoc` | 开放平台文档:搜索开发文档 | [simple.md](./references/products/simple.md) | | `ding` | DING消息:发送/撤回(应用内/短信/电话) | [ding.md](./references/products/ding.md) | @@ -42,7 +42,7 @@ cli_version: ">=1.0.0" 用户提到"审批/请假/报销/出差/加班" → `approval` 用户提到"考勤/打卡/排班" → `attendance` 用户提到"日程/日历/会议室/约会" → `calendar` -用户提到"机器人发消息/Webhook/机器人群发/机器人单聊/通知" → `chat` +用户提到"群聊/建群/群成员/群管理/机器人发消息/Webhook/机器人群发/机器人单聊/通知" → `chat` 用户提到"通讯录/同事/部门/组织架构" → `contact` 用户提到"开发/API/调用错误 文档" → `devdoc` 用户提到"DING/紧急消息/电话提醒" → `ding` @@ -68,6 +68,7 @@ cli_version: ">=1.0.0" | `calendar` | `event delete` | 删除日程,所有参与者同步取消 | | `calendar` | `participant delete` | 移除日程参与者 | | `calendar` | `room delete` | 取消会议室预定 | +| `chat` | `group members remove` | 移除群成员 | | `todo` | `task delete` | 删除待办 | ### 确认流程 diff --git a/skills/references/error-codes.md b/skills/references/error-codes.md index ae7f87fc..986f49b7 100644 --- a/skills/references/error-codes.md +++ b/skills/references/error-codes.md @@ -52,9 +52,10 @@ ## chat 高频错误 - 参数互斥报错 — `--group` 与 `--users` 同时传入 → 群聊用 `--group`,单聊用 `--users`,二者互斥 -- 群不存在 — openConversationId 不正确 → 确认群会话 ID 来源正确 +- 群不存在 — openConversationId 不正确 → `chat search --query "群名"` 获取正确 ID - 机器人无法添加到群 — 当前用户非群管理员 → 报告给用户,需群管理员操作 - Webhook Token 无效 — token 不正确或已失效 → 确认 Webhook Token 来源正确 +- 添加/移除群成员失败 — userId 不正确或无权限 → 先 `contact user search` 确认 userId,需当前用户为群管理员 --- diff --git a/skills/references/intent-guide.md b/skills/references/intent-guide.md index 929b0a72..5c134863 100644 --- a/skills/references/intent-guide.md +++ b/skills/references/intent-guide.md @@ -13,6 +13,8 @@ | "帮我看看收到的日报" | 日志收件箱 | `report` | `todo` | 钉钉日志系统(日报/周报),不是待办 | | "帮我创建一个待办提醒" | 个人待办 | `todo` | `report` | 个人任务提醒,不是日志汇报 | | "帮我提交请假审批" | 发起审批 | `approval` | — | 审批流程,不是待办或日志 | +| "帮我建一个项目群" | 创建群聊 | `chat group create` | — | 群聊管理,不是日历日程 | +| "把张三拉进群" | 添加群成员 | `chat group members add` | — | 先查 userId,再添加 | | "让机器人在群里发个通知" | 机器人群发 | `chat message send-by-bot` | `chat message send-by-webhook` | 企业内部机器人发消息,需 robotCode | | "通过 Webhook 发告警到群里" | Webhook 告警 | `chat message send-by-webhook` | `chat message send-by-bot` | 自定义机器人 Webhook,需 token | | "给张三发一条机器人单聊消息" | 机器人单聊 | `chat message send-by-bot --users` | — | 机器人批量单聊,先查 userId | diff --git a/skills/references/products/chat.md b/skills/references/products/chat.md index b02a4141..5d0fc22b 100644 --- a/skills/references/products/chat.md +++ b/skills/references/products/chat.md @@ -1,28 +1,93 @@ -# 机器人消息 (chat) 命令参考 +# 群聊与机器人 (chat) 命令参考 ## 命令总览 | 子命令 | 用途 | |-------|------| -| `bot search` | 搜索我的机器人 | +| `search` | 搜索群聊 | +| `group create` | 创建群 | +| `group members` | 查看群成员列表 | +| `group members add` | 添加群成员 | +| `group members remove` | 移除群成员(⚠️ 危险操作) | | `group members add-bot` | 添加机器人到群 | +| `group rename` | 修改群名称 | +| `bot search` | 搜索我的机器人 | | `message send-by-bot` | 机器人发消息(群聊或批量单聊) | | `message recall-by-bot` | 机器人撤回消息(群聊或批量单聊) | | `message send-by-webhook` | 自定义机器人 Webhook 发消息 | --- -## bot search — 搜索我的机器人 +## search — 搜索群聊 ``` Usage: - dws chat bot search [flags] + dws chat search [flags] Example: - dws chat bot search --name "考勤" --format json + dws chat search --query "项目冲刺" --format json Flags: - --name string 机器人名称(模糊搜索) - --page string 页码(默认 1) - --size string 每页数量(默认 10) + --query string 搜索关键词 (必填) + --cursor string 分页游标(首页留空) +``` + +--- + +## group create — 创建群 + +``` +Usage: + dws chat group create [flags] +Example: + dws chat group create --name "Q1 项目冲刺群" --users userId1,userId2,userId3 --format json +Flags: + --name string 群名称 (必填) + --users string 群成员 userId 列表,逗号分隔 (必填) +``` + +> 当前用户自动作为群主加入,无需在 --users 中重复传入。 + +--- + +## group members — 查看群成员列表 + +``` +Usage: + dws chat group members [flags] +Example: + dws chat group members --id --format json +Flags: + --id string 群会话 ID (必填) + --cursor string 分页游标 +``` + +--- + +## group members add — 添加群成员 + +``` +Usage: + dws chat group members add [flags] +Example: + dws chat group members add --id --users userId1,userId2 --format json +Flags: + --id string 群会话 ID (必填) + --users string 要添加的 userId 列表,逗号分隔 (必填) +``` + +--- + +## group members remove — 移除群成员 + +> ⚠️ 危险操作:执行前必须向用户确认,同意后才加 `--yes`。 + +``` +Usage: + dws chat group members remove [flags] +Example: + dws chat group members remove --id --users userId1,userId2 --format json +Flags: + --id string 群会话 ID (必填) + --users string 要移除的 userId 列表,逗号分隔 (必填) ``` --- @@ -41,6 +106,35 @@ Flags: --- +## group rename — 修改群名称 + +``` +Usage: + dws chat group rename [flags] +Example: + dws chat group rename --id --name "新群名" --format json +Flags: + --id string 群会话 ID (必填) + --name string 新群名称 (必填) +``` + +--- + +## bot search — 搜索我的机器人 + +``` +Usage: + dws chat bot search [flags] +Example: + dws chat bot search --name "考勤" --format json +Flags: + --name string 机器人名称(模糊搜索) + --page int 页码(默认 1) + --size int 每页数量(默认 50) +``` + +--- + ## message send-by-bot — 机器人发消息 支持两种模式:群聊发送 和 批量单聊发送,通过 `--group` 和 `--users` 互斥区分。 @@ -68,7 +162,7 @@ Example: --title "通知" --text "会议已取消" --format json Flags: --robot-code string 机器人 code (必填) - --users string 用户 ID 列表,逗号分隔 (必填,与 --group 互斥) + --users string 用户 ID 列表,逗号分隔,最多 20 个 (必填,与 --group 互斥) --title string 消息标题 (必填) --text string 消息内容,支持 Markdown (必填) ``` @@ -130,6 +224,12 @@ Flags: ## 意图判断 +- 用户说"搜索一个群" → `search` +- 用户说"帮我建个群" → `group create` +- 用户说"看看群里有谁" → `group members` +- 用户说"把张三拉进群" → 先 `contact user search` 获取 userId,再 `group members add` +- 用户说"把张三移出群" → 先 `contact user search` 获取 userId,再 `group members remove`(⚠️ 需确认) +- 用户说"改一下群名" → `group rename` - 用户说"让机器人在群里发通知" → `message send-by-bot --group` - 用户说"机器人给张三发消息" → 先 `contact user search` 获取 userId,再 `message send-by-bot --users` - 用户说"通过 Webhook 发告警" / 用户有 Webhook Token → `message send-by-webhook` @@ -141,6 +241,22 @@ Flags: ## 核心工作流 +```bash +# ── 工作流: 建群并添加机器人 ── + +# 1. 搜索同事 userId +dws contact user search --query "张三" --format json + +# 2. 创建群 +dws chat group create --name "项目群" --users , --format json + +# 3. 搜索机器人 +dws chat bot search --format json + +# 4. 添加机器人到群 +dws chat group members add-bot --id --robot-code --format json +``` + ```bash # ── 工作流: 机器人群发消息 ── @@ -164,5 +280,7 @@ dws chat message send-by-webhook --token \ | 操作 | 从返回中提取 | 用于 | |------|-------------|------| +| `search` | openConversationId | `group members` / `group rename` / `group members add` / `send-by-bot --group` | +| `group create` | openConversationId | 同上 | | `bot search` | robotCode | `send-by-bot` / `recall-by-bot` / `add-bot` | | `message send-by-bot` | processQueryKey | `recall-by-bot --keys` |