Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions shortcuts/common/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}
20 changes: 12 additions & 8 deletions shortcuts/im/builders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 11 additions & 6 deletions shortcuts/im/im_chat_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand All @@ -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")

Expand Down
2 changes: 1 addition & 1 deletion skills/lark-im/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Comment thread
shifengjuan-dev marked this conversation as resolved.
| [`+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 |
Expand Down
42 changes: 31 additions & 11 deletions skills/lark-im/references/lark-im-chat-create.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```
Expand All @@ -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`) |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| `--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`

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 "<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
```

Comment thread
greptile-apps[bot] marked this conversation as resolved.
**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`.
Expand All @@ -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 |
Expand Down Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion skills/lark-im/references/lark-im-chat-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
Loading