From e9cc07bf561d2a8538910f8986d689016302d1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=B3=B0=E5=A8=9F?= Date: Thu, 2 Apr 2026 20:06:36 +0800 Subject: [PATCH] feat: support user identity for im +chat-create - Add --as user support to +chat-create - Add UserScopes (im:chat:create_by_user) / BotScopes (im:chat:create) - Update skill docs and reference files to reflect user/bot support - Default identity remains bot (first element of AuthTypes) Change-Id: I6be0a160567a0d87a92f176ae12297a11d06dcb1 --- shortcuts/common/testing.go | 5 +++ shortcuts/im/builders_test.go | 20 +++++---- shortcuts/im/im_chat_create.go | 17 +++++--- skills/lark-im/SKILL.md | 2 +- .../lark-im/references/lark-im-chat-create.md | 42 ++++++++++++++----- .../references/lark-im-chat-identity.md | 2 +- 6 files changed, 61 insertions(+), 27 deletions(-) diff --git a/shortcuts/common/testing.go b/shortcuts/common/testing.go index aca00b001..7774acf0d 100644 --- a/shortcuts/common/testing.go +++ b/shortcuts/common/testing.go @@ -22,3 +22,8 @@ func TestNewRuntimeContext(cmd *cobra.Command, cfg *core.CliConfig) *RuntimeCont func TestNewRuntimeContextWithCtx(ctx context.Context, cmd *cobra.Command, cfg *core.CliConfig) *RuntimeContext { return &RuntimeContext{ctx: ctx, Cmd: cmd, Config: cfg} } + +// TestNewRuntimeContextWithIdentity creates a RuntimeContext with a specific identity for testing. +func TestNewRuntimeContextWithIdentity(cmd *cobra.Command, cfg *core.CliConfig, as core.Identity) *RuntimeContext { + return &RuntimeContext{Cmd: cmd, Config: cfg, resolvedAs: as} +} diff --git a/shortcuts/im/builders_test.go b/shortcuts/im/builders_test.go index a4a54de09..81acb0012 100644 --- a/shortcuts/im/builders_test.go +++ b/shortcuts/im/builders_test.go @@ -531,14 +531,18 @@ func TestMessagesSearchPaginationConfig(t *testing.T) { func TestShortcutDryRunShapes(t *testing.T) { t.Run("ImChatCreate dry run includes params and body", func(t *testing.T) { - runtime := newTestRuntimeContext(t, map[string]string{ - "type": "public", - "name": "Team Room", - "users": "ou_1,ou_2", - "owner": "ou_owner", - }, map[string]bool{ - "set-bot-manager": true, - }) + cmd := &cobra.Command{Use: "test"} + for _, name := range []string{"type", "name", "users", "owner"} { + cmd.Flags().String(name, "", "") + } + cmd.Flags().Bool("set-bot-manager", false, "") + _ = cmd.ParseFlags(nil) + _ = cmd.Flags().Set("type", "public") + _ = cmd.Flags().Set("name", "Team Room") + _ = cmd.Flags().Set("users", "ou_1,ou_2") + _ = cmd.Flags().Set("owner", "ou_owner") + _ = cmd.Flags().Set("set-bot-manager", "true") + runtime := common.TestNewRuntimeContextWithIdentity(cmd, nil, "bot") got := mustMarshalDryRun(t, ImChatCreate.DryRun(context.Background(), runtime)) if !strings.Contains(got, `"/open-apis/im/v1/chats"`) || !strings.Contains(got, `"set_bot_manager":true`) || !strings.Contains(got, `"chat_type":"public"`) { t.Fatalf("ImChatCreate.DryRun() = %s", got) diff --git a/shortcuts/im/im_chat_create.go b/shortcuts/im/im_chat_create.go index f0ad606a0..75f1d70c2 100644 --- a/shortcuts/im/im_chat_create.go +++ b/shortcuts/im/im_chat_create.go @@ -19,24 +19,25 @@ import ( var ImChatCreate = common.Shortcut{ Service: "im", Command: "+chat-create", - Description: "Create a group chat with bot identity; bot-only; creates private/public chats, invites users/bots, optionally sets bot manager", + Description: "Create a group chat; user/bot; creates private/public chats, invites users/bots, optionally sets bot manager", Risk: "write", - Scopes: []string{"im:chat:create"}, - AuthTypes: []string{"bot"}, + UserScopes: []string{"im:chat:create_by_user"}, + BotScopes: []string{"im:chat:create"}, + AuthTypes: []string{"bot", "user"}, HasFormat: true, Flags: []common.Flag{ {Name: "name", Desc: "group name (required for public groups, max 60 chars)"}, {Name: "description", Desc: "group description (max 100 chars)"}, {Name: "users", Desc: "comma-separated user open_ids (ou_xxx) to invite, max 50"}, {Name: "bots", Desc: "comma-separated bot app IDs (cli_xxx) to invite, max 5"}, - {Name: "owner", Desc: "owner open_id (ou_xxx); defaults to the bot if not specified"}, + {Name: "owner", Desc: "owner open_id (ou_xxx); defaults to bot (--as bot) or authorized user (--as user)"}, {Name: "type", Default: "private", Desc: "chat type", Enum: []string{"private", "public"}}, - {Name: "set-bot-manager", Type: "bool", Desc: "set the bot that creates this chat as manager"}, + {Name: "set-bot-manager", Type: "bool", Desc: "set the bot that creates this chat as manager (bot identity only)"}, }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body := buildCreateChatBody(runtime) params := map[string]interface{}{"user_id_type": "open_id"} - if runtime.Bool("set-bot-manager") { + if runtime.Bool("set-bot-manager") && runtime.IsBot() { params["set_bot_manager"] = true } return common.NewDryRunAPI(). @@ -45,6 +46,10 @@ var ImChatCreate = common.Shortcut{ Body(body) }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { + if runtime.Bool("set-bot-manager") && !runtime.IsBot() { + return output.ErrValidation("--set-bot-manager is only supported with bot identity (--as bot)") + } + name := runtime.Str("name") chatType := runtime.Str("type") diff --git a/skills/lark-im/SKILL.md b/skills/lark-im/SKILL.md index 72596c48d..e1ed786e8 100644 --- a/skills/lark-im/SKILL.md +++ b/skills/lark-im/SKILL.md @@ -56,7 +56,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli im + [flags]`)。 | Shortcut | 说明 | |----------|------| -| [`+chat-create`](references/lark-im-chat-create.md) | Create a group chat with bot identity; bot-only; creates private/public chats, invites users/bots, optionally sets bot manager | +| [`+chat-create`](references/lark-im-chat-create.md) | Create a group chat; user/bot; creates private/public chats, invites users/bots, optionally sets bot manager | | [`+chat-messages-list`](references/lark-im-chat-messages-list.md) | List messages in a chat or P2P conversation; user/bot; accepts --chat-id or --user-id, resolves P2P chat_id, supports time range/sort/pagination | | [`+chat-search`](references/lark-im-chat-search.md) | Search visible group chats by keyword and/or member open_ids (e.g. look up chat_id by group name); user/bot; supports member/type filters, sorting, and pagination | | [`+chat-update`](references/lark-im-chat-update.md) | Update group chat name or description; user/bot; updates a chat's name or description | diff --git a/skills/lark-im/references/lark-im-chat-create.md b/skills/lark-im/references/lark-im-chat-create.md index fc1dbbb71..2a9fda262 100644 --- a/skills/lark-im/references/lark-im-chat-create.md +++ b/skills/lark-im/references/lark-im-chat-create.md @@ -2,10 +2,13 @@ > **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules. -Create a group chat using **bot identity (TAT)**. You can specify the group name, description, members (users/bots), owner, and chat type (private/public). +Create a group chat. Supports both user identity (`--as user`) and bot identity (`--as bot`). You can specify the group name, description, members (users/bots), owner, and chat type (private/public). This skill maps to the shortcut: `lark-cli im +chat-create` (internally calls `POST /open-apis/im/v1/chats`). +- `--as bot` requires the `im:chat:create` scope. +- `--as user` requires the `im:chat:create_by_user` scope. + ## Commands ```bash @@ -27,12 +30,18 @@ lark-cli im +chat-create --name "My Group" --bots "cli_aaa,cli_bbb" # Invite both users and bots lark-cli im +chat-create --name "My Group" --users "ou_aaa" --bots "cli_aaa" -# Make the creating bot a group manager -lark-cli im +chat-create --name "My Group" --set-bot-manager +# Make the creating bot a group manager (bot identity only) +lark-cli im +chat-create --name "My Group" --set-bot-manager --as bot # JSON output lark-cli im +chat-create --name "My Group" --format json +# Create a group with bot identity +lark-cli im +chat-create --name "My Group" --users "ou_aaa" --as bot + +# Create a group with user identity +lark-cli im +chat-create --name "My Group" --users "ou_aaa,ou_bbb" --as user + # Preview the request without creating anything lark-cli im +chat-create --name "My Group" --dry-run ``` @@ -45,24 +54,25 @@ lark-cli im +chat-create --name "My Group" --dry-run | `--description ` | No | Max 100 characters | Group description | | `--users ` | No | Up to 50, format `ou_xxx` | Comma-separated user open_ids | | `--bots ` | No | Up to 5, format `cli_xxx` | Comma-separated bot app IDs | -| `--owner ` | No | Format `ou_xxx` | Owner open_id (defaults to the bot if not specified) | +| `--owner ` | No | Format `ou_xxx` | Owner open_id (defaults to the bot when using `--as bot`, or the authorized user when using `--as user`) | | `--type ` | No | `private` (default) or `public` | Group type | -| `--set-bot-manager` | No | - | Set the creating bot as a group manager | +| `--set-bot-manager` | No | - | Set the creating bot as a group manager (only effective with `--as bot`) | | `--format json` | No | - | Output as JSON | +| `--as ` | No | `bot` or `user` | Identity type | | `--dry-run` | No | - | Preview the request without executing it | -> **Note:** Only bot identity is supported. - ## AI Usage Guidance -When the user asks to create a group, always use the **two-step flow** below. Do NOT pass other users' open_ids in `--users` during group creation — the bot and target users are often mutually invisible (error 232043). +### When using `--as bot` + +Bot may fail to invite users who are mutually invisible to it during group creation (error 232043). To avoid this, use the **two-step flow** below instead of passing other users' open_ids in `--users`. -1. **Get the current user's open_id:** Run `lark-cli contact +get-user` to retrieve it. +1. **Get the current user's open_id:** Run `lark-cli contact +search-user --query ""` to retrieve it. 2. **Create the group — by default include the current user:** ```bash lark-cli im +chat-create --name "" \ - --users "" + --users "" --as bot ``` **Default behavior:** Always add the current user to the group, unless the user explicitly says "do not add me" or "bot-only group" — only then omit `--users`. @@ -80,6 +90,16 @@ When the user asks to create a group, always use the **two-step flow** below. Do 4. **Check `invalid_id_list`** in the response. If non-empty, report to the user which members could not be added. +### When using `--as user` + +User identity does not have the bot visibility limitation, so you can create the group and invite members in one step: + +```bash +lark-cli im +chat-create --name "" --users "ou_aaa,ou_bbb" --as user +``` + +The authorized user is automatically the group creator and member. + ## Output Fields | Field | Description | @@ -119,7 +139,7 @@ lark-cli im +messages-send --chat-id "$CHAT_ID" --text "Welcome, everyone!" | Symptom | Root Cause | Solution | |---------|---------|---------| -| Permission denied (99991672) | The bot app does not have `im:chat:create` TAT permission enabled | Enable the required permission for the app in the Open Platform console | +| Permission denied (99991672) | The app does not have `im:chat:create` (bot) or `im:chat:create_by_user` (user) permission enabled | Enable the required permission for the app in the Open Platform console | | `--name is required for public groups and must be at least 2 characters` | A public group was created without a name or with a name shorter than 2 characters | Provide a name with at least 2 characters | | `--name exceeds the maximum of 60 characters` | The group name is too long | Shorten the name to 60 characters or fewer | | `--description exceeds the maximum of 100 characters` | The group description is too long | Shorten the description to 100 characters or fewer | diff --git a/skills/lark-im/references/lark-im-chat-identity.md b/skills/lark-im/references/lark-im-chat-identity.md index b2c7ce557..53a9f631a 100644 --- a/skills/lark-im/references/lark-im-chat-identity.md +++ b/skills/lark-im/references/lark-im-chat-identity.md @@ -13,7 +13,7 @@ Group-chat operations support both `--as user` (UAT user identity) and `--as bot | Operation | Recommended Identity | Why | |------|---------|-----------------------------------| -| Create group (`+chat-create`) | Depends on the scenario | Default is bot | +| Create group (`+chat-create`) | Depends on the scenario | Infer from context | | Add members (member-management flow) | `--as user` | Bot visibility is limited and often fails when the target user is mutually invisible to the bot (232024) | | Update group (`+chat-update`) | Owner identity | Permission changes require owner/admin privileges; owner transfer requires owner identity |