feat: support user identity for im +chat-create (#242)

- 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
This commit is contained in:
shifengjuan-dev
2026-04-03 16:35:28 +08:00
committed by GitHub
parent 8645d26d09
commit a641fdd5e6
6 changed files with 61 additions and 27 deletions

View File

@@ -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}
}

View File

@@ -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)

View File

@@ -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")

View File

@@ -56,7 +56,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli im +<verb> [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 |

View File

@@ -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 <text>` | No | Max 100 characters | Group description |
| `--users <ids>` | No | Up to 50, format `ou_xxx` | Comma-separated user open_ids |
| `--bots <ids>` | No | Up to 5, format `cli_xxx` | Comma-separated bot app IDs |
| `--owner <open_id>` | No | Format `ou_xxx` | Owner open_id (defaults to the bot if not specified) |
| `--owner <open_id>` | No | Format `ou_xxx` | Owner open_id (defaults to the bot when using `--as bot`, or the authorized user when using `--as user`) |
| `--type <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 <identity>` | 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`
1. **Get the current user's open_id:** Run `lark-cli contact +get-user` to retrieve it.
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 +search-user --query "<name or email>"` to retrieve it.
2. **Create the group — by default include the current user:**
```bash
lark-cli im +chat-create --name "<group name>" \
--users "<current user open_id>"
--users "<current user open_id>" --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 "<group 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 |

View File

@@ -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 |