Documentation
¶
Overview ¶
Package application provides shortcuts for Open Platform app self-management (slash commands of the current bound app).
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var SlashCommandCreate = common.Shortcut{ Service: "application", Command: "+slash-command-create", Description: "Register a slash command (/ command) on the current bound Open Platform app; --force converts a name collision into an update (idempotent re-run)", Risk: "write", Scopes: []string{"application:app_slash_command:write"}, ConditionalScopes: []string{ "application:app_slash_command:read", }, AuthTypes: []string{"bot", "user"}, Flags: []common.Flag{ {Name: "command", Desc: "command name WITHOUT the leading slash (server enforces uniqueness per app; max 100 commands)", Required: true}, {Name: "description", Desc: "default description shown in the client command panel (description.default_value)", Required: true}, {Name: "description-i18n", Type: "string_array", Desc: "localized description, repeatable, format <lang>=<text> (e.g. zh_cn=发送问候); language codes are passed through to the server"}, {Name: "icon-key", Desc: "icon key (server default: skill_outlined; invalid keys are rejected server-side with code 40000031)"}, {Name: "force", Type: "bool", Desc: "on name collision, resolve the existing command by name and update it in place"}, }, Tips: []string{ `lark-cli application +slash-command-create --command greet --description "say hi" --description-i18n zh_cn=问候 --as bot`, "changes take ~5 minutes to appear in clients (client-side cache); the server updates immediately", "user identity needs explicit authorization first: lark-cli auth login --scope application:app_slash_command:write", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { if err := validateCommandName(runtime.Str("command"), "--command"); err != nil { return err } if len(strings.TrimSpace(runtime.Str("description"))) == 0 { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--description must not be blank").WithParam("--description") } if _, err := parseDescriptionI18n(runtime.StrArray("description-i18n")); err != nil { return err } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { i18n, err := parseDescriptionI18n(runtime.StrArray("description-i18n")) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } name := strings.TrimSpace(runtime.Str("command")) body := buildSlashCommandBody(name, runtime.Str("description"), i18n, runtime.Str("icon-key")) d := common.NewDryRunAPI(). Desc("Create a slash command on the current bound app"). POST(slashCommandBasePath). Body(body) if runtime.Bool("force") { d.Desc("--force: on 'command already exists' (code 40000000), GET list to resolve command_id then PATCH the same body") } return d }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { name := strings.TrimSpace(runtime.Str("command")) i18n, err := parseDescriptionI18n(runtime.StrArray("description-i18n")) if err != nil { return err } body := buildSlashCommandBody(name, runtime.Str("description"), i18n, runtime.Str("icon-key")) data, err := runtime.CallAPITyped("POST", slashCommandBasePath, nil, body) action := "created" if err != nil { if !isCommandExists(err) { return err } if !runtime.Bool("force") { p, _ := errs.ProblemOf(err) rewrapped := errs.NewAPIError(errs.SubtypeAlreadyExists, "slash command %q already exists", name). WithHint("rerun with --force to update it, or use `lark-cli application +slash-command-update --command %q`", name). WithCause(err) if p.Code != 0 { rewrapped = rewrapped.WithCode(p.Code) } if p.LogID != "" { rewrapped = rewrapped.WithLogID(p.LogID) } return rewrapped } id, rerr := resolveCommandID(runtime, name) if rerr != nil { return rerr } patchBody := buildSlashCommandBody("", runtime.Str("description"), i18n, runtime.Str("icon-key")) data, err = runtime.CallAPITyped("PATCH", slashCommandBasePath+"/"+encodeCommandIDPathSegment(id), nil, patchBody) if err != nil { return err } action = "updated" } if data == nil { data = map[string]interface{}{} } data["action"] = action fmt.Fprintln(runtime.IO().ErrOut, clientCacheHint) runtime.OutFormat(data, nil, func(w io.Writer) { fmt.Fprintf(w, "%s /%v (command_id: %v)\n", action, data["command"], data["command_id"]) }) return nil }, }
SlashCommandCreate registers a new slash command on the current bound app.
View Source
var SlashCommandDelete = common.Shortcut{ Service: "application", Command: "+slash-command-delete", Description: "Delete a slash command from the current bound app (high-risk: irreversible; recreating the same name yields a new command_id)", Risk: "high-risk-write", Scopes: []string{"application:app_slash_command:write"}, ConditionalScopes: []string{ "application:app_slash_command:read", }, AuthTypes: []string{"bot", "user"}, Flags: []common.Flag{ {Name: "command-id", Desc: "target command_id; mutually exclusive with --command"}, {Name: "command", Desc: "target command name WITHOUT leading slash (resolved via live list, needs read scope); mutually exclusive with --command-id"}, }, Tips: []string{ "lark-cli application +slash-command-delete --command greet --yes --as bot", "deleted commands may linger in clients for ~5 minutes (client cache)", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { id := strings.TrimSpace(runtime.Str("command-id")) name := strings.TrimSpace(runtime.Str("command")) if (id == "") == (name == "") { return errs.NewValidationError(errs.SubtypeInvalidArgument, "provide exactly one of --command-id or --command").WithParam("--command-id") } if name != "" { return validateCommandName(name, "--command") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { d := common.NewDryRunAPI().Desc("HIGH-RISK: delete a slash command (irreversible; same-name recreate gets a NEW command_id)") target := strings.TrimSpace(runtime.Str("command-id")) if target == "" { name := strings.TrimSpace(runtime.Str("command")) d.GET(slashCommandBasePath). Desc(fmt.Sprintf("resolve command_id by name %q via GET list first", name)) target = "<resolved_command_id>" } else { target = encodeCommandIDPathSegment(target) } return d.DELETE(slashCommandBasePath + "/" + target) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { id := strings.TrimSpace(runtime.Str("command-id")) name := strings.TrimSpace(runtime.Str("command")) if id == "" { resolved, err := resolveCommandID(runtime, name) if err != nil { return err } id = resolved } if _, err := runtime.CallAPITyped("DELETE", slashCommandBasePath+"/"+encodeCommandIDPathSegment(id), nil, nil); err != nil { return err } out := map[string]interface{}{"action": "deleted", "command_id": id} if name != "" { out["command"] = name } fmt.Fprintln(runtime.IO().ErrOut, clientCacheHint) fmt.Fprintln(runtime.IO().ErrOut, "note: recreating the same command name will yield a NEW command_id.") runtime.OutFormat(out, nil, func(w io.Writer) { fmt.Fprintf(w, "deleted command_id %s\n", id) }) return nil }, }
SlashCommandDelete removes a slash command (irreversible; command_id is not reused - recreating the same name yields a NEW id).
View Source
var SlashCommandList = common.Shortcut{ Service: "application", Command: "+slash-command-list", Description: "List all slash commands (/ commands) registered on the currently bound Open Platform app; source of command_id for update/delete", Risk: "read", Scopes: []string{"application:app_slash_command:read"}, AuthTypes: []string{"bot", "user"}, Tips: []string{ "lark-cli application +slash-command-list --as bot", "user identity needs explicit authorization first: lark-cli auth login --scope application:app_slash_command:read", "the upstream API returns all commands at once (max 100 per app, no pagination)", }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { return common.NewDryRunAPI(). Desc("List all slash commands of the current bound app (read-only)"). GET(slashCommandBasePath) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { data, err := runtime.CallAPITyped("GET", slashCommandBasePath, nil, nil) if err != nil { return err } items, _ := data["items"].([]interface{}) if items == nil { items = []interface{}{} } out := map[string]interface{}{"items": items, "count": len(items)} runtime.OutFormat(out, nil, func(w io.Writer) { fmt.Fprintf(w, "%d slash command(s)\n", len(items)) for _, it := range items { m, ok := it.(map[string]interface{}) if !ok { continue } desc := "" if d, ok := m["description"].(map[string]interface{}); ok { desc, _ = d["default_value"].(string) } fmt.Fprintf(w, " /%v\t%v\t%s\n", m["command"], m["command_id"], desc) } }) return nil }, }
SlashCommandList lists all slash commands of the current bound app.
View Source
var SlashCommandUpdate = common.Shortcut{ Service: "application", Command: "+slash-command-update", Description: "Update description / localized descriptions / icon of a slash command on the current bound app, addressed by --command-id or by name via --command", Risk: "write", Scopes: []string{"application:app_slash_command:write"}, ConditionalScopes: []string{ "application:app_slash_command:read", }, AuthTypes: []string{"bot", "user"}, Flags: []common.Flag{ {Name: "command-id", Desc: "target command_id (from +slash-command-list or create output); mutually exclusive with --command"}, {Name: "command", Desc: "target command name WITHOUT leading slash; resolved via live list (needs read scope); mutually exclusive with --command-id"}, {Name: "description", Desc: "new default description (description.default_value)"}, {Name: "description-i18n", Type: "string_array", Desc: "localized description, repeatable <lang>=<text>; REPLACES the whole i18n map (missing languages are dropped); requires --description"}, {Name: "icon-key", Desc: "new icon key (invalid keys rejected server-side with code 40000031)"}, }, Tips: []string{ `lark-cli application +slash-command-update --command greet --description "new text" --as bot`, "PATCH is field-level partial: fields you do not pass are preserved server-side", "the command NAME itself cannot be changed (API limitation): rename = delete + create (new command_id)", }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { return validateUpdateTarget(runtime) }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { i18n, err := parseDescriptionI18n(runtime.StrArray("description-i18n")) if err != nil { return common.NewDryRunAPI().Set("error", err.Error()) } body := buildSlashCommandBody("", runtime.Str("description"), i18n, runtime.Str("icon-key")) d := common.NewDryRunAPI() target := strings.TrimSpace(runtime.Str("command-id")) if target == "" { name := strings.TrimSpace(runtime.Str("command")) d.GET(slashCommandBasePath). Desc(fmt.Sprintf("resolve command_id by name %q via GET list first", name)) target = "<resolved_command_id>" } else { target = encodeCommandIDPathSegment(target) } return d.PATCH(slashCommandBasePath + "/" + target). Desc("Update a slash command by command_id"). Body(body) }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { id := strings.TrimSpace(runtime.Str("command-id")) if id == "" { resolved, err := resolveCommandID(runtime, strings.TrimSpace(runtime.Str("command"))) if err != nil { return err } id = resolved } i18n, err := parseDescriptionI18n(runtime.StrArray("description-i18n")) if err != nil { return err } body := buildSlashCommandBody("", runtime.Str("description"), i18n, runtime.Str("icon-key")) data, err := runtime.CallAPITyped("PATCH", slashCommandBasePath+"/"+encodeCommandIDPathSegment(id), nil, body) if err != nil { return err } if data == nil { data = map[string]interface{}{} } data["action"] = "updated" fmt.Fprintln(runtime.IO().ErrOut, clientCacheHint) runtime.OutFormat(data, nil, func(w io.Writer) { fmt.Fprintf(w, "updated /%v (command_id: %v)\n", data["command"], data["command_id"]) }) return nil }, }
SlashCommandUpdate updates description/i18n/icon of an existing slash command.
Functions ¶
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.