Skip to content
Open
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
37 changes: 37 additions & 0 deletions shortcuts/vc/skill_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ func TestVCSearchIdentityDocsMatchAuthTypes(t *testing.T) {
}
}

func TestVCMeetingLeaveDocsMatchBotOnlyAuthTypes(t *testing.T) {
if len(VCMeetingLeave.AuthTypes) != 1 || VCMeetingLeave.AuthTypes[0] != "bot" {
t.Fatalf("VCMeetingLeave.AuthTypes = %v, want [bot]", VCMeetingLeave.AuthTypes)
}

reference := readSkillDoc(t, "skills/lark-meeting/references/lark-vc-agent-meeting-leave.md")
if !strings.Contains(reference, "仅支持 `bot` 身份") {
t.Error("lark-vc-agent-meeting-leave.md must state that +meeting-leave only supports bot identity")
}
}

// TestVCBotShortcutsIdentityDocsMatchAuthTypes pins that the VC shortcuts this
// PR opened to bot (`+detail`, `+recording`, `+meeting-countdown`) are all declared bot-capable in
// code and documented as such in their lark-meeting references.
Expand Down Expand Up @@ -84,6 +95,32 @@ func TestVCBotShortcutsIdentityDocsMatchAuthTypes(t *testing.T) {
}
}

func TestVCMeetingJoinDocsRouteCalendarStart(t *testing.T) {
skill := readSkillDoc(t, "skills/lark-meeting/SKILL.md")
scene := readSkillDoc(t, "skills/lark-meeting/scenes/live-meeting-attend.md")
reference := readSkillDoc(t, "skills/lark-meeting/references/lark-vc-agent-meeting-join.md")

if !strings.Contains(reference, "# vc +meeting-join:发起日程会议或加入会议") {
t.Error("lark-vc-agent-meeting-join.md must identify Calendar meeting starts")
}
for _, want := range []string{
"机器人发起日程会议或参与会议",
"让应用机器人发起日程会议或加入会议",
} {
if !strings.Contains(skill, want) {
t.Errorf("SKILL.md must document %q", want)
}
}
for name, content := range map[string]string{
"live-meeting-attend.md": scene,
"lark-vc-agent-meeting-join.md": reference,
} {
if !strings.Contains(content, "应用机器人发起会议、参会与会中互动") {
t.Errorf("%s must route application-bot meeting starts", name)
}
}
}

func TestMeetingArtifactSceneDelegatesToDomainOwners(t *testing.T) {
scene := readSkillDoc(t, "skills/lark-meeting/scenes/query-meeting-and-artifacts.md")
for _, target := range []string{
Expand Down
5 changes: 1 addition & 4 deletions shortcuts/vc/vc_meeting_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var VCMeetingJoin = common.Shortcut{
Description: "Join a meeting by meeting number (bot join)",
Risk: "write",
Scopes: []string{"vc:meeting.bot.join:write"},
AuthTypes: []string{"user", "bot"},
AuthTypes: []string{"bot"},
HasFormat: true,
Flags: []common.Flag{
{Name: "meeting-number", Required: true, Desc: "meeting number to join"},
Expand All @@ -63,9 +63,6 @@ var VCMeetingJoin = common.Shortcut{
if !validMeetingNumber(mn) {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--meeting-number must be exactly 9 digits, got %q", mn).WithParam("--meeting-number")
}
if meetingJoinAction(runtime) == meetingJoinActionStart && !runtime.As().IsBot() {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--action start requires --as bot").WithParam("--action")
}
return nil
},
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
Expand Down
2 changes: 1 addition & 1 deletion shortcuts/vc/vc_meeting_leave.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var VCMeetingLeave = common.Shortcut{
Description: "Leave a meeting by meeting ID",
Risk: "write",
Scopes: []string{"vc:meeting.bot.join:write"},
AuthTypes: []string{"user", "bot"},
AuthTypes: []string{"bot"},
HasFormat: true,
Flags: []common.Flag{
{Name: "meeting-id", Required: true, Desc: "meeting ID to leave"},
Expand Down
49 changes: 24 additions & 25 deletions shortcuts/vc/vc_meeting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func TestBuildMeetingJoinBody_TrimsCallIDWhitespace(t *testing.T) {
func TestMeetingJoin_Validate_MissingNumber(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, defaultConfig())
// cobra MarkFlagRequired should reject missing --meeting-number
err := mountAndRun(t, VCMeetingJoin, []string{"+meeting-join", "--as", "user"}, f, nil)
err := mountAndRun(t, VCMeetingJoin, []string{"+meeting-join", "--as", "bot"}, f, nil)
if err == nil {
t.Fatal("expected error when --meeting-number is missing")
}
Expand Down Expand Up @@ -266,18 +266,17 @@ func TestMeetingJoin_Validate_Valid(t *testing.T) {
}
}

func TestMeetingJoin_Validate_StartRequiresBot(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, defaultConfig())

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--as", "user",
"--meeting-number", "123456789",
"--action", "start",
"--dry-run",
}, f, nil)

if err == nil || !strings.Contains(err.Error(), "--action start requires --as bot") {
t.Fatalf("start action error = %v", err)
func TestMeetingJoinAndLeave_BotOnly(t *testing.T) {
for _, shortcut := range []struct {
name string
authTypes []string
}{
{name: "+meeting-join", authTypes: VCMeetingJoin.AuthTypes},
{name: "+meeting-leave", authTypes: VCMeetingLeave.AuthTypes},
} {
if len(shortcut.authTypes) != 1 || shortcut.authTypes[0] != "bot" {
t.Errorf("%s AuthTypes = %v, want [bot]", shortcut.name, shortcut.authTypes)
}
}
}

Expand All @@ -289,7 +288,7 @@ func TestMeetingJoin_DryRun(t *testing.T) {
f, stdout, _, _ := cmdutil.TestFactory(t, defaultConfig())
err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "123456789", "--password", "pw123",
"--dry-run", "--as", "user",
"--dry-run", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -434,7 +433,7 @@ func TestMeetingJoin_Execute_Success(t *testing.T) {

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "123456789",
"--format", "json", "--as", "user",
"--format", "json", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -489,7 +488,7 @@ func TestMeetingJoin_Execute_WithPassword_CapturesBody(t *testing.T) {

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "987654321", "--password", "s3cret",
"--format", "json", "--as", "user",
"--format", "json", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -529,7 +528,7 @@ func TestMeetingJoin_Execute_PrettyOutput(t *testing.T) {

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "123456789",
"--format", "pretty", "--as", "user",
"--format", "pretty", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -556,7 +555,7 @@ func TestMeetingJoin_Execute_PrettyOutput_NoMeetingInfo(t *testing.T) {

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "123456789",
"--format", "pretty", "--as", "user",
"--format", "pretty", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -580,7 +579,7 @@ func TestMeetingLeave_Execute_PrettyOutput(t *testing.T) {

err := mountAndRun(t, VCMeetingLeave, []string{
"+meeting-leave", "--meeting-id", "69999999",
"--format", "pretty", "--as", "user",
"--format", "pretty", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -602,7 +601,7 @@ func TestMeetingJoin_Execute_APIError(t *testing.T) {

err := mountAndRun(t, VCMeetingJoin, []string{
"+meeting-join", "--meeting-number", "123456789",
"--as", "user",
"--as", "bot",
}, f, &bytes.Buffer{})
if err == nil {
t.Fatal("expected error for API failure")
Expand All @@ -618,7 +617,7 @@ func TestMeetingJoin_Execute_APIError(t *testing.T) {

func TestMeetingLeave_Validate_MissingID(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, defaultConfig())
err := mountAndRun(t, VCMeetingLeave, []string{"+meeting-leave", "--as", "user"}, f, nil)
err := mountAndRun(t, VCMeetingLeave, []string{"+meeting-leave", "--as", "bot"}, f, nil)
if err == nil {
t.Fatal("expected error when --meeting-id is missing")
}
Expand Down Expand Up @@ -661,7 +660,7 @@ func TestMeetingLeave_DryRun(t *testing.T) {
f, stdout, _, _ := cmdutil.TestFactory(t, defaultConfig())
err := mountAndRun(t, VCMeetingLeave, []string{
"+meeting-leave", "--meeting-id", "69999999",
"--dry-run", "--as", "user",
"--dry-run", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -694,7 +693,7 @@ func TestMeetingLeave_Execute_Success(t *testing.T) {

err := mountAndRun(t, VCMeetingLeave, []string{
"+meeting-leave", "--meeting-id", "69999999",
"--format", "json", "--as", "user",
"--format", "json", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand Down Expand Up @@ -725,7 +724,7 @@ func TestMeetingLeave_Execute_TrimsMeetingID(t *testing.T) {

err := mountAndRun(t, VCMeetingLeave, []string{
"+meeting-leave", "--meeting-id", " 69999999 ",
"--format", "json", "--as", "user",
"--format", "json", "--as", "bot",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
Expand All @@ -750,7 +749,7 @@ func TestMeetingLeave_Execute_APIError(t *testing.T) {
})

err := mountAndRun(t, VCMeetingLeave, []string{
"+meeting-leave", "--meeting-id", "69999999", "--as", "user",
"+meeting-leave", "--meeting-id", "69999999", "--as", "bot",
}, f, &bytes.Buffer{})
if err == nil {
t.Fatal("expected error for API failure")
Expand Down
6 changes: 3 additions & 3 deletions skills/lark-meeting/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: lark-meeting
version: 1.0.0
description: "飞书视频会议:查询会议记录与会议产物(纪要/逐字稿/妙记)、妙记搜索/上传/下载/编辑、机器人参与会议;查询进行中的会议、实时会议内容(发言/聊天/共享文档)问答(会上/会里)、发送会中聊天/表情;基于 meeting_id、meeting_no、event_id、note_id、minute_token、vc-node-id 或妙记 URL 查询相关信息。预约会议、忙闲和会议室管理走 lark-calendar。"
description: "飞书视频会议:查询会议记录与会议产物(纪要/逐字稿/妙记)、妙记搜索/上传/下载/编辑、机器人发起日程会议或参与会议;查询进行中的会议、实时会议内容(发言/聊天/共享文档)问答(会上/会里)、发送会中聊天/表情;基于 meeting_id、meeting_no、event_id、note_id、minute_token、vc-node-id 或妙记 URL 查询相关信息。预约会议、忙闲和会议室管理走 lark-calendar。"
metadata:
requires:
bins: ["lark-cli"]
Expand Down Expand Up @@ -105,7 +105,7 @@ lark-cli vc +meeting-events --as <source_identity> --meeting-id <meeting_id> --p
- [查询妙记及其产物](scenes/query-minutes-and-artifacts.md):已有妙记 URL / `minute_token`,或按标题、所有者、参与者搜索妙记;读取总结、待办、章节、关键词、逐字稿,下载原始音视频,或查询关联智能纪要。
- [生成和修改妙记、管理妙记权限](scenes/create-and-edit-minutes.md):将本地音视频生成妙记、逐字稿、总结、待办或章节;修改妙记标题、总结、待办、关键词或说话人;申请妙记权限,或查看、分配妙记协作者权限。
- [查询智能纪要及关联产物](scenes/query-note-and-artifacts.md):已有 `note_id`、智能纪要 Docx URL/token,或需要查询纪要正文、逐字稿、妙记和共享文档等关联产物。
- [应用机器人参会与会中互动](scenes/live-meeting-attend.md):完整编排应用机器人的活跃会议发现、发起或加入、邀请、事件拉取、会议截图、文本/表情/倒计时互动、结束会议和明确授权后的离会。
- [应用机器人发起会议、参会与会中互动](scenes/live-meeting-attend.md):完整编排应用机器人的活跃会议发现、发起或加入、邀请、事件拉取、会议截图、文本/表情/倒计时互动、结束会议和明确授权后的离会。
- [会中事件与会中互动](scenes/live-meeting-interact.md):在不触发新的入会/离会操作时,使用用户身份或已在会中的应用身份查询活跃会议、查看发言/聊天/共享内容、按需读取当前会议画面,或发送文本/表情、操作倒计时。

## 命令参考
Expand All @@ -121,7 +121,7 @@ lark-cli vc +meeting-events --as <source_identity> --meeting-id <meeting_id> --p
| `vc +meeting-message-send` | 发送会中文本消息或表情 | [lark-vc-meeting-message-send](references/lark-vc-meeting-message-send.md) |
| `vc +meeting-screenshot` | 获取视频会议截图 | [lark-vc-meeting-screenshot](references/lark-vc-meeting-screenshot.md) |
| `vc +meeting-countdown` | 设置、延长、提前结束或关闭会中倒计时 | [lark-vc-meeting-countdown](references/lark-vc-meeting-countdown.md) |
| `vc +meeting-join` | 让应用机器人加入会议 | [lark-vc-agent-meeting-join](references/lark-vc-agent-meeting-join.md) |
| `vc +meeting-join` | 让应用机器人发起日程会议或加入会议 | [lark-vc-agent-meeting-join](references/lark-vc-agent-meeting-join.md) |
| `vc +meeting-invite` | 以应用机器人邀请指定用户或全部合格日程参会人 | [lark-vc-agent-meeting-invite](references/lark-vc-agent-meeting-invite.md) |
| `vc +meeting-end` | 让当前 Host 应用机器人结束会议 | [lark-vc-agent-meeting-end](references/lark-vc-agent-meeting-end.md) |
| `vc +meeting-leave` | 让应用机器人离开会议 | [lark-vc-agent-meeting-leave](references/lark-vc-agent-meeting-leave.md) |
Expand Down
6 changes: 3 additions & 3 deletions skills/lark-meeting/references/lark-vc-agent-meeting-join.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# vc +meeting-join
# vc +meeting-join:发起日程会议或加入会议

通过 9 位会议号让应用机器人加入一场正在进行的视频会议。这是一次**写操作**,会实际让应用机器人加入会议
通过 9 位会议号让应用机器人发起符合条件的日程会议,或加入一场正在进行的视频会议。这是一次**写操作**,会实际让应用机器人发起或加入会议

本 skill 对应 shortcut:`lark-cli vc +meeting-join`(调用 `POST /open-apis/vc/v1/bots/join`)。

Expand Down Expand Up @@ -93,4 +93,4 @@ lark-cli vc +meeting-join --as bot --meeting-number 123456789 --action start
- 执行成功后,立即记录返回的 `meeting.id`,用于后续 `+meeting-leave` / `+meeting-events`。

## 相关场景
- [应用机器人参会与会中互动](../scenes/live-meeting-attend.md)
- [应用机器人发起会议、参会与会中互动](../scenes/live-meeting-attend.md)
4 changes: 2 additions & 2 deletions skills/lark-meeting/references/lark-vc-agent-meeting-leave.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ lark-cli vc +meeting-leave --as bot --meeting-id 69xxxxxxxxxxxxx28

`--meeting-id` 必须是会议的长数字 ID,通常由 `+meeting-join --as bot` 返回体中的 `meeting.id` 提供,也可从应用身份 `+meeting-list-active --as bot --user-id <user_open_id>` 返回体中的 `meeting_id` 获取。**传 9 位会议号会失败**。

### 2. 优先使用 bot 身份
### 2. 仅支持 bot 身份

这是应用机器人离会能力,使用与入会或 active meeting 发现相同的 `--as bot`。只能让当前身份自己离会,无法强制移出其他参会人。
这是应用机器人离会能力,该 shortcut 仅支持 `bot` 身份,必须使用 `--as bot`。只能让当前身份自己离会,无法强制移出其他参会人。

### 3. 当前身份必须在会议中

Expand Down
2 changes: 1 addition & 1 deletion skills/lark-meeting/scenes/live-meeting-attend.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 应用机器人参会与会中互动
# 应用机器人发起会议、参会与会中互动

编排应用机器人的完整会中流程:发现已在参加的会议,或在用户明确授权后发起或加入会议;随后拉取会中事件、发送文本或会中表情、操作倒计时,并仅在用户明确要求时结束会议或离会。

Expand Down
Loading