mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-09-05 23:43:58 +08:00
feat: 新增企业微信助手 Agent,并修正 wecom-cli 条目 ref 漂移 (#112)
## 概述 / Overview 两件事:新增「企业微信助手」Agent(自带 15 个技能),并修正 `wecom-cli` 条目钉在 6 月快照的 ref 漂移。 Two changes: adds the **WeCom Assistant** agent (bundling 15 skills), and fixes the `wecom-cli` entry whose pinned ref was stuck on a June snapshot. ## 1. 新增企业微信助手 Agent 覆盖企业微信 **14 类服务、95 个方法**:消息、群聊历史、通讯录、日程、会议、待办、邮件、在线文档、在线表格、智能表格、智能文档、文档管理、微盘、媒体文件。 **采用内联形态 + 自带私有技能**:Agent 安装对 `agents/<id>/` 整目录递归复制且 `skills/` 不在排除集合里,因此装 Agent 即带全部技能,用户无需再单独获取技能合集。 ### 技能集(15 个,约 5000 行) - 基于上游 [wecom-cli](https://github.com/WecomTeam/wecom-cli) 官方 Skill(MIT,© WecomTeam)改写,每个技能末尾保留归属声明 - **新增 `wecom-chat`**:补齐上游零覆盖的群聊历史读取 - 补齐上游未覆盖的 `message.send`、`doc.create`,方法覆盖达 **95/95** - 修正上游三处文档漂移:邮件能力描述与实际相反、会议室参数名已过时、`title_highlight` 字段不存在 ### 相对上游的核心增量:风险治理 - 26 个对外可见或不可逆的方法逐个写明**执行前确认要求** - 4 个条件升级方法给出**参数级判据**,而非按方法名一刀切 - 文档权限扩散两项加重处理,涉及**企业外可见**时单独再确认一次 - 内部标识禁止外露,不因用户索要而放宽 - 拒绝导出可识别到具体自然人的隐私字段 ### 三条真机实测得出、上游未覆盖的硬约束 1. 机器人**只能写入/修改自己创建的数据**,真人创建的只能读 2. 每次响应携带的 `extra_identity_context` **禁止透露给用户** 3. 权限错误(`850002`/`851008`/`853006`)**不得重试**,须将 `help_message` **逐字原样**转给用户 ## 2. 修正 wecom-cli 条目 ref 漂移 `source.ref` 原钉在 2026-06-28 的 `72e14f7`,该快照只有 7 个子技能且用已废弃的旧命名(`msg`/`schedule`)。上游 v1.2.0 已扩展到 **14 个**技能。按旧 ref 安装的用户拿到的是三个月前的快照。 - `source.ref` → `78c514b2afee7c0d3d7be715628478421f37ee63` - `children` 由 `scripts/gen-collection-children.py` 重新生成,**7 → 14** - sidecar 同步 `provenance.content.ref`、`childCount` 与 `children` ## 验证 / Verification **静态** - 215 条示例命令追加 `--dry-run` 实跑,**215/215 退出码 0** - 未知方法 0、未知参数 0、`--json` 未知字段 0、枚举违规 0 - 15 个 `SKILL.md` 的 frontmatter 经客户端 `skillFrontmatterSchema` 校验全部通过 - `validate_catalog_metadata.py --require-complete` 与 `gen-collection-children.py`:**0 error** **真机(在真实企业微信账号上端到端)** - **待办域 6/6 方法全通**(含 2 个 write-high),`items` 必填的隐蔽坑实测证实 - **日程域 5 个方法全通**(含 3 个 write-high) - 消息发送、通讯录解析、微盘列表、邮件搜索、文档搜索、会议列表、智能表格创建均已实测通过 - 测试数据已全部清理,未污染真实账号 **尚未实测**:群聊历史(机器人未开通该品类)。相关文档已明确标注验证状态,未实测的能力不写「实际效果」段落。
This commit is contained in:
382
agents/wecom-assistant/skills/wecom-calendar/SKILL.md
Normal file
382
agents/wecom-assistant/skills/wecom-calendar/SKILL.md
Normal file
@@ -0,0 +1,382 @@
|
||||
---
|
||||
name: wecom-calendar
|
||||
description: >-
|
||||
企业微信日程与会议室管理:预约/查看/搜索/改期/取消日程,查多人共同空闲时段,查办公楼与会议室可订性并预订会议室。
|
||||
当用户说「约个日程 / 明天有什么安排 / 我的日历 / 项目评审是什么时候 / 挪一下时间 / 这个不开了 /
|
||||
张三什么时候有空 / 大家什么时候都有空 / 订个会议室 / 1605 空不空 / 公司有哪些楼」时使用。
|
||||
只负责『日程』——不含在线会议链接的安排(含纯线下面对面碰头);用户要的是含会议号/入会链接的『在线会议』时改用 wecom-meeting。
|
||||
用户只说「开会/约个会/xx 会」而未说明是日程还是会议时,创建场景必须先逐字追问这一句、不得改写:
|
||||
`需要创建日程还是会议?(请回复:日程 / 会议)`(禁止改成「在线会议/视频会议/线下会议/日程安排」等任何变体);
|
||||
查询场景则严禁追问,日程与会议两边都查再合并。
|
||||
不负责:待办事项(wecom-todo)、姓名转 userid(wecom-contact)、发消息通知(wecom-message)。
|
||||
version: 1.0.0
|
||||
type: procedural
|
||||
risk_level: high
|
||||
status: enabled
|
||||
tags:
|
||||
- wecom
|
||||
- calendar
|
||||
- schedule
|
||||
- meeting-room
|
||||
---
|
||||
|
||||
# 企业微信日程与会议室
|
||||
|
||||
帮用户把「什么时候、和谁、在哪儿」这件事落到企业微信日历上:约日程、看安排、找时间、订会议室、改期、取消。
|
||||
|
||||
> **前置**:执行任何 `wecom-cli` 命令前,必须先完成 `wecom-shared` 的前置检查
|
||||
> (CLI 已安装、版本达标、`auth show --status` 返回 `authorized`;具体版本门槛以 `wecom-shared` 为准)。
|
||||
> 未通过前置检查时不得执行本技能任何命令。
|
||||
|
||||
## 能力清单
|
||||
|
||||
| 能力 | 命令 | 风险 |
|
||||
|---|---|---|
|
||||
| 查某段时间的日程列表 | `wecom-cli calendar schedules list` | read |
|
||||
| 按关键词/组织人/参与人搜索日程 | `wecom-cli calendar schedules search` | read |
|
||||
| 按 ID 批量取日程详情 | `wecom-cli calendar schedules get` | read |
|
||||
| 查多人共同空闲时段 | `wecom-cli calendar schedules free list` | read |
|
||||
| 查企业办公楼清单 | `wecom-cli meeting rooms buildings list` | read |
|
||||
| 查会议室可订性 | `wecom-cli meeting rooms search` | read |
|
||||
| 创建日程(可邀请参与人、可占会议室) | `wecom-cli calendar schedules create` | **write-high** |
|
||||
| 更新日程(改时间/地点/人/会议室) | `wecom-cli calendar schedules update` | **write-high** |
|
||||
| 取消(删除)日程 | `wecom-cli calendar schedules cancel` | **write-high** |
|
||||
|
||||
> 会议室与办公楼查询虽然命令前缀是 `meeting`,但**归本技能**(`wecom-meeting` 要订会议室须反向调用本技能)。
|
||||
|
||||
### 三个高风险方法的确认要求
|
||||
|
||||
> ⚠️ **高风险操作**:`calendar schedules create` 带 `attendees` 时会向他人发出日程邀请,对方日历上立刻出现这条安排;传 `meeting_room_id` 时会真实占用会议室。执行前必须向用户复述
|
||||
> 「将创建日程「<主题>」,时间 <开始>-<结束>,邀请 <人名列表>,会议室 <会议室名>」并取得明确同意;用户未明确同意时不得执行。
|
||||
|
||||
> ⚠️ **高风险操作**:`calendar schedules update` 改时间/地点/参与人/会议室会通知全体参与人,且被移除的人会直接失去这条日程。执行前必须向用户复述
|
||||
> 「将把日程「<主题>」的 <改动项> 改为 <新值>,参与人会收到变更通知」并取得明确同意;用户未明确同意时不得执行。
|
||||
|
||||
> ⚠️ **高风险操作**:`calendar schedules cancel` 会删除日程并通知全体参与人,CLI **没有任何恢复接口**。执行前必须向用户复述
|
||||
> 「将取消日程「<主题>」(<时间>),参与人会收到取消通知,且无法撤回」并取得明确同意;用户未明确同意时不得执行。
|
||||
|
||||
## 日程 vs 会议消歧 [CRITICAL|措辞逐字固定]
|
||||
|
||||
企业微信里「会」有两种载体,判据只有一条:
|
||||
|
||||
- **含会议号(`meeting.meeting_code`)/ 入会链接(`meeting.meeting_link`)的是「会议」** → 归 `wecom-meeting`
|
||||
- **不含会议号与入会链接的是「日程」**(包括纯线下面对面碰头、订了会议室的线下会)→ 归本技能
|
||||
|
||||
`search` / `list` / `get` 返回的每条日程都带 `meeting` 字段,**直接读 `meeting.meeting_code` 是否非空即可判定,不需要额外补一次 `get`**。
|
||||
|
||||
### 规则一:创建场景必须逐字追问
|
||||
|
||||
用户只说「开会 / 约个会 / 安排个会 / xx 会 / xx 会议」等而未明确是日程还是会议时,**必须先用文字追问**,问题与选项**逐字固定、不得改写、不得增减、不得翻译**:
|
||||
|
||||
```
|
||||
需要创建日程还是会议?(请回复:日程 / 会议)
|
||||
```
|
||||
|
||||
- 用户答「日程」→ 留在本技能,走「场景:约一个日程」。
|
||||
- 用户答「会议」→ 转 `wecom-meeting` 创建会议(创建会议会自动生成对应日程,**不要**在本技能再建一条)。
|
||||
- 「会议」「会」「开会」这些词**本身不构成「明确」**,禁止因 query 里出现「会议」二字就默认创建日程,也禁止反向默认成会议。
|
||||
- **只给了地点或会议室号**(「在 1605 开会」「订个会议室开会」)**也不构成明确** —— 会议室里同样可能要远程接入,仍须追问。
|
||||
- 只有出现「碰个面 / 创建日程 / 面对面聊」等纯线下信号时才直接留在本技能;出现「入会链接 / 会议号 / 视频会议 / 远程参会 / 外地同事接入」等信号时直接转 `wecom-meeting`,都无需追问。
|
||||
|
||||
### 规则二:查询场景严禁追问,两边都查再合并
|
||||
|
||||
查询场景**严禁**用上面那句话追问(那句话只用于创建)。按两个独立维度处理:
|
||||
|
||||
**维度一 —— 查哪一边**
|
||||
|
||||
| 用户表述 | 动作 |
|
||||
|---|---|
|
||||
| 明确提到「在线会议 / 视频会议 / 入会链接 / 会议号 / 腾讯会议 / 远程参会」 | 只查会议(转 `wecom-meeting`) |
|
||||
| 明确说「日程 / 安排 / 我的安排 / 日历 / 今天有什么安排」且无在线会议特征 | 只查日程(本技能) |
|
||||
| 模糊表述:「会 / xx 会 / xx 会议 / 开会 / 最近有什么会 / 有哪些会 / 找下 xx 会议」 | **日程和会议两边都查**,再合并 |
|
||||
|
||||
**维度二 —— 每一边用 `search` 还是 `list`(与维度一独立,逐边各判)**
|
||||
|
||||
- 有**主题/名称关键词**(「项目评审是什么时候」「找下 xx 会」)→ 该边用 `search`,关键词进 `keywords`。
|
||||
- **只有时间/日期或泛浏览**(「今天有什么安排」「最近有什么会」)→ 该边用 `list`。
|
||||
**禁止把日期当 `keywords` 喂给 `search`。**
|
||||
|
||||
**合并展示**:两边都查时,按是否含在线会议链接分成「(会议)」与「(日程)」两部分(日程中 `meeting.meeting_code` 非空的归「(会议)」),同一场按「主题 + 时间」去重只保留一条,末尾汇总「共 N 场,其中会议 X 场、日程 Y 场」。只有一类时不分部分、不加小标题。
|
||||
|
||||
### 规则三:改约禁止拆成 cancel + create
|
||||
|
||||
「改约 / 改时间 / 挪到 / 顺延 / 重新约」等改期意图,**即使用户说「取消……再约到……」也算改期**,一律走 `update`:
|
||||
|
||||
1. 先 `search` 或 `list` 定位,直接读返回里的 `meeting.meeting_code`。
|
||||
2. `meeting_code` **为空**(纯日程)→ `calendar schedules update` 改时间。
|
||||
3. `meeting_code` **非空**(会议形态日程)→ 转 `wecom-meeting`,把 `meeting.meeting_id` 传给 `meeting update`,**无需再 search 一次**。
|
||||
|
||||
> **根因**:`calendar schedules create` 只能建纯日程、**重建不出会议链接**(能拆不能合)。cancel + create 会让**会议链接永久丢失**,参与人拿到的旧链接全部作废。这条禁令没有例外,不得以「用户自己说要先取消」为由绕过。
|
||||
|
||||
## 场景:约一个日程
|
||||
|
||||
### 步骤
|
||||
|
||||
1. **消歧**(见上文规则一)。确认是「日程」后继续。
|
||||
2. **补必填参数**:`subject` / `begin_time` / `end_time` 缺失,或参与人无法从上下文推断时,用文字询问;其余可选参数(地点、提醒)用户没提就走默认,不专门问。
|
||||
- `end_time` 用户没给 → 默认 `begin_time + 1 小时`,不追问。
|
||||
- 询问时间时候选必须是**精确到分钟的具体时刻**(「明天 14:00」「周六 10:30」),禁止给「上午 / 下午 / 下班前」这类模糊选项。
|
||||
3. **姓名 → userid**:调 `wecom-contact` 解析,多候选时列 2~4 个(姓名 + 部门)让用户选。**禁止**把姓名当 userid 拼接,**禁止**凭记忆编造。
|
||||
4. **查忙闲**(多人时必做):`calendar schedules free list`,把冲突摆给用户拍板。
|
||||
5. **订会议室**(用户提到会议室时必做):见「场景:订会议室」。必须先拿到真实 `meeting_room_id` 再建日程。
|
||||
6. **复述并取得同意**(write-high 确认要求)。
|
||||
7. **执行创建**。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
# 只给自己的日程(无参与人)
|
||||
wecom-cli calendar schedules create \
|
||||
--subject '午餐' \
|
||||
--begin-time '2026-09-01 12:00:00' \
|
||||
--end-time '2026-09-01 13:00:00'
|
||||
|
||||
# 带参与人 —— attendees 是对象数组
|
||||
wecom-cli calendar schedules create --json '{
|
||||
"subject": "产品评审",
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"attendees": [{"userid": "woxxxa"}, {"userid": "woxxxb"}]
|
||||
}'
|
||||
|
||||
# 全天日程
|
||||
wecom-cli calendar schedules create --json '{
|
||||
"subject": "年假",
|
||||
"begin_time": "2026-09-10 00:00:00",
|
||||
"end_time": "2026-09-10 23:59:59",
|
||||
"is_all_day": true
|
||||
}'
|
||||
|
||||
# 建日程 + 原子占用会议室(meeting_room_id 来自 rooms search)
|
||||
wecom-cli calendar schedules create --json '{
|
||||
"subject": "产品评审",
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"attendees": [{"userid": "woxxxa"}],
|
||||
"meeting_room_id": "mrmxxxx"
|
||||
}'
|
||||
|
||||
# 自定义提醒(提前 30 分钟;不传时默认 [-900] 即提前 15 分钟)
|
||||
wecom-cli calendar schedules create --json '{
|
||||
"subject": "客户拜访",
|
||||
"begin_time": "2026-09-02 09:00:00",
|
||||
"end_time": "2026-09-02 10:00:00",
|
||||
"location": "客户现场",
|
||||
"reminders": {"is_remind": true, "reminder_time": [-1800]}
|
||||
}'
|
||||
```
|
||||
|
||||
**创建返回**只有 `schedule_id` 一个字段(内部标识,禁止展示)。需要回显完整信息时用本次入参回显,或用 `schedules get` 补齐。
|
||||
|
||||
### 创建成功后的回复格式
|
||||
|
||||
只输出三行,不加寒暄、不加建议、不展示地点/提醒/`schedule_id`:
|
||||
|
||||
```
|
||||
主题:{subject}
|
||||
时间:{M月D日} {HH:mm}-{HH:mm}
|
||||
参与人:{人名1}、{人名2}
|
||||
```
|
||||
|
||||
## 场景:看看我今天/这周有什么安排
|
||||
|
||||
只给了时间、没有主题关键词 → **走 `list`**。
|
||||
|
||||
```bash
|
||||
# 今天
|
||||
wecom-cli calendar schedules list --begin-time '2026-09-01 00:00:00' --end-time '2026-09-01 23:59:59'
|
||||
|
||||
# 本周
|
||||
wecom-cli calendar schedules list --json '{"begin_time": "2026-08-31 00:00:00", "end_time": "2026-09-06 23:59:59"}'
|
||||
```
|
||||
|
||||
返回 `schedule_list[]`,每条已含 `subject` / `begin_time` / `end_time` / `attendees[].name` / `creator_name` / `repeat_rule` / `meeting` / `meeting_room`,**不必再调 `get`**。
|
||||
|
||||
若用户表述模糊(「最近有什么会」),按消歧规则二**同时**转 `wecom-meeting` 用相同时间范围拉 `meeting list`,合并展示。
|
||||
|
||||
## 场景:项目评审是什么时候(按关键词找日程)
|
||||
|
||||
有主题关键词 → **走 `search`**。`keywords` / `organizer` / `has_attendees` **至少传其一**,三者都不传会失败。
|
||||
|
||||
```bash
|
||||
# 按关键词
|
||||
wecom-cli calendar schedules search --keywords '项目评审'
|
||||
|
||||
# 关键词 + 时间范围
|
||||
wecom-cli calendar schedules search --json '{
|
||||
"keywords": ["周会"],
|
||||
"begin_time": "2026-09-01 00:00:00",
|
||||
"end_time": "2026-09-07 23:59:59"
|
||||
}'
|
||||
|
||||
# 按组织人(organizer 是单值字符串,不是数组)
|
||||
wecom-cli calendar schedules search --json '{"organizer": "woxxx"}'
|
||||
|
||||
# 按参与人(has_attendees 是对象数组)
|
||||
wecom-cli calendar schedules search --json '{"has_attendees": [{"userid": "woxxx"}]}'
|
||||
|
||||
# 翻页
|
||||
wecom-cli calendar schedules search --json '{"keywords": ["周会"], "cursor": "<next_cursor>", "limit": 50}'
|
||||
```
|
||||
|
||||
搜索无结果时给恢复建议:换关键词 / 改按组织人搜 / 改按参与人搜,不要静默失败。
|
||||
|
||||
## 场景:拿到 ID 后补日程详情
|
||||
|
||||
`list` 与 `search` 返回已足够完整,只有在**手上只有 `schedule_id`** 时才用:
|
||||
|
||||
```bash
|
||||
wecom-cli calendar schedules get --json '{"schedule_ids": ["<schedule_id1>", "<schedule_id2>"]}'
|
||||
```
|
||||
|
||||
> `schedule_ids` 是**纯字符串数组**,不是对象数组 —— 与 `attendees` / `meeting_ids` 的形状不同,最容易写错。
|
||||
|
||||
## 场景:大家什么时候都有空
|
||||
|
||||
```bash
|
||||
wecom-cli calendar schedules free list --json '{
|
||||
"userids": [{"userid": "woxxx"}, {"userid": "woyyy"}],
|
||||
"begin_time": "2026-09-01 09:00:00",
|
||||
"end_time": "2026-09-01 18:00:00",
|
||||
"min_duration_minutes": 60,
|
||||
"limit": 5
|
||||
}'
|
||||
```
|
||||
|
||||
- `userids` **是对象数组** `[{"userid": "..."}]`,尽管字段名叫 `userids`。单人合法(退化为「某人什么时候有空」)。
|
||||
- 单次窗口 **≤ 24 小时**;`begin_time` 早于当前时刻的部分会被服务端**自动截断**,传纯历史窗口返回空 `slots`。
|
||||
- 返回 `slots[]`,每项含 `begin_time` / `end_time` / `available_users[]`(含 `name`)/ `available_count` / `busy_users[]`,另有 `total_count`(入参人数)与 `extra_info`(降级提示)。
|
||||
- `available_count < total_count` 说明降级了:告诉用户哪些人冲突、几人能参加,由用户决定是否按降级时段安排。
|
||||
- `slots` 为空 → 引导扩大窗口或减少参与人,**不要在同一窗口反复重试**。
|
||||
- 展示时只用 `available_users[].name` / `busy_users[].name`,**输出正文里绝不允许出现 `wo` 前缀字符串**。
|
||||
|
||||
## 场景:订会议室 / 查会议室空不空 / 公司有哪些楼
|
||||
|
||||
完整编排、返回结构与五条硬性规则见 [`references/meeting-room.md`](references/meeting-room.md)。要点:
|
||||
|
||||
```bash
|
||||
# 列出我可访问的办公楼(无入参)
|
||||
wecom-cli meeting rooms buildings list
|
||||
|
||||
# 查会议室可订性(begin-time / end-time 必填)
|
||||
wecom-cli meeting rooms search --json '{
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"room_name": "1605",
|
||||
"floor_name": "16",
|
||||
"capacity_min": 4
|
||||
}'
|
||||
```
|
||||
|
||||
- **只有用户提到楼名时才调 `buildings list`**;没提楼就跳过,让 `rooms search` 用当前所在楼兜底。
|
||||
- `rooms search` 返回 `target[]`(传了 `room_name` 时的命中项,每项含 `status`:`bookable` / `unavailable` / `not_found`)+ `recommendations[]`(同楼候选)+ `inferred_building`。
|
||||
- 拿 `target[].room.meeting_room_id` 或 `recommendations[].meeting_room_id` 传给 `schedules create` / `schedules update` 才算真正占用。
|
||||
- **会议室名绝不能只写进 `location`** —— 那样不会占用会议室。
|
||||
- `meeting_room_id` 仅工具链流转,对用户只展示会议室 `name` + 楼层 + 容量。
|
||||
|
||||
## 场景:改期 / 改地点 / 加减人 / 换会议室
|
||||
|
||||
先按消歧规则三判定归属,确认是纯日程后:
|
||||
|
||||
```bash
|
||||
# 改时间
|
||||
wecom-cli calendar schedules update --json '{
|
||||
"schedule_id": "<schedule_id>",
|
||||
"begin_time": "2026-09-02 14:00:00",
|
||||
"end_time": "2026-09-02 15:00:00"
|
||||
}'
|
||||
|
||||
# 加人 / 减人(Patch 语义,只传要改的)
|
||||
wecom-cli calendar schedules update --json '{
|
||||
"schedule_id": "<schedule_id>",
|
||||
"add_attendees": [{"userid": "woxxxc"}],
|
||||
"remove_attendees": [{"userid": "woxxxb"}]
|
||||
}'
|
||||
|
||||
# 换会议室(新会议室须先经 rooms search 确认 status=bookable)
|
||||
wecom-cli calendar schedules update --json '{"schedule_id": "<schedule_id>", "meeting_room_id": "mrmyyyy"}'
|
||||
|
||||
# 清空地点/备注:传空字符串
|
||||
wecom-cli calendar schedules update --json '{"schedule_id": "<schedule_id>", "location": "", "description": ""}'
|
||||
```
|
||||
|
||||
- **周期日程(`repeat_rule.is_repeat = true`)不支持更新**,告知用户并引导其到企业微信客户端操作;禁止逐场 `update` 拼凑、禁止 cancel + create 重建。
|
||||
- **不预先按「是不是本人创建」拦截**:直接执行,返回权限错误时再告知用户并建议联系创建人。
|
||||
- 返回 `detail`(更新后的完整 `ScheduleInfo`),可据此回显。
|
||||
|
||||
## 场景:取消日程
|
||||
|
||||
1. 定位(有主题关键词走 `search`,只给时间走 `list`)。
|
||||
2. 读 `repeat_rule.is_repeat` —— **周期日程不支持取消**,引导到客户端。
|
||||
3. 判断用户是真取消还是改期(带「取消」字样也可能是改期,见规则三)。
|
||||
4. 复述并取得同意(write-high 确认要求)。
|
||||
5. 执行:
|
||||
|
||||
```bash
|
||||
wecom-cli calendar schedules cancel --schedule-id '<schedule_id>'
|
||||
```
|
||||
|
||||
成功返回空对象 `{}`;无权限返回错误 —— 此时告知用户并建议联系创建人。
|
||||
|
||||
## 参数速查
|
||||
|
||||
> flag 与 JSON 字段一一对应:`--begin-time` ↔ `begin_time`,`--meeting-room-id` ↔ `meeting_room_id`,其余同理。嵌套结构(`attendees` / `reminders` / `timezone`)建议直接用 `--json`。完整 schema 用 `wecom-cli <service> <method> --help` 或 `--doc` 查。
|
||||
|
||||
| 方法 | 必填 | 关键可选 |
|
||||
|---|---|---|
|
||||
| `calendar schedules create` | `subject`、`begin_time`、`end_time` | `attendees`(对象数组)、`location`、`meeting_room_id`、`description`、`is_all_day`、`allow_self_join`(默认 true)、`reminders`(`{is_remind, reminder_time:[秒]}`,默认 `[-900]`)、`timezone`、`mark_optional_attendees`(**字符串数组**) |
|
||||
| `calendar schedules update` | `schedule_id` | `subject`、`begin_time`、`end_time`、`add_attendees`、`remove_attendees`、`location`(空串=清空)、`description`(空串=清空)、`meeting_room_id`、`is_all_day`、`allow_self_join` |
|
||||
| `calendar schedules cancel` | `schedule_id` | — |
|
||||
| `calendar schedules list` | 无 | `begin_time`(默认当前时间)、`end_time`(默认起点 +30 天) |
|
||||
| `calendar schedules search` | 无(但 `keywords` / `organizer` / `has_attendees` **至少传其一**) | `begin_time`、`end_time`、`limit`(默认 10,最大 1000)、`cursor` |
|
||||
| `calendar schedules get` | `schedule_ids`(**字符串数组**) | — |
|
||||
| `calendar schedules free list` | `begin_time`、`end_time`、`userids`(**对象数组**,≥1) | `min_duration_minutes`(默认 30)、`limit`(默认 10,最大 100)、`strategy`(仅 `max_attendees`) |
|
||||
| `meeting rooms search` | `begin_time`、`end_time` | `room_name`、`building_name`、`city_name`、`floor_name`、`capacity_min`、`expand_to_other_buildings`、`limit`(默认 20,上限 100)、`cursor` |
|
||||
| `meeting rooms buildings list` | 无入参 | — |
|
||||
|
||||
**时间格式**统一 `YYYY-MM-DD HH:MM:SS`,且必须先把「明天」「下周三」解析成具体时刻再传。
|
||||
|
||||
## 输出格式
|
||||
|
||||
- **姓名原样展示**:一律用接口返回的 `attendees[].name`,返回 `zhangsan(张三)` 就展示 `zhangsan(张三)`,不做加工。
|
||||
- **年份**:默认只到月日;跨年时才补 `{YYYY}年M月D日`。
|
||||
- **相对日期**:昨天/今天/明天在月日前加相对词,如 `时间:明天 9月1日 14:00-15:00`。
|
||||
- **列表**:禁止 markdown 表格;按开始时间升序,每条独立条目,只含主题/时间/参与人;超过 10 条只展示前 10 条并告知「还有 N 条,需要查看更多吗?」。
|
||||
- **时区标注**:`timezone.timezone_offset != 28800` 时必须标注,格式 `14:00-15:00(纽约时间 UTC-5)`;`UTC±N = timezone_offset / 3600`,地区中文名由 `timezone_id` 推导,`timezone_id` 为空时只留 `(UTC-5)`。东八区不标注。忙闲 `slots` 不适用。
|
||||
- **禁止展示**:`schedule_id`、`userid`、`meeting_room_id`、`cal_id`、`cursor` / `next_cursor` 等一切内部标识。
|
||||
|
||||
## 不支持的事(直接告知,禁止变通绕过)
|
||||
|
||||
| 不支持 | 正确做法 |
|
||||
|---|---|
|
||||
| 创建 / 更新 / 取消**周期(重复)日程** | 告知不支持,引导到企业微信客户端;禁止用「建多条单次日程」「逐场 update」「cancel + create」变通 |
|
||||
| **RSVP**(接受 / 拒绝 / 待定日程邀请) | 告知不支持,建议在客户端对该邀请操作,或私信发起人 |
|
||||
| 给机器人授予某个日历本权限 | 不存在该能力 |
|
||||
|
||||
## 易错点
|
||||
|
||||
- **消歧措辞不得改写**:创建场景那句问话必须逐字是 `需要创建日程还是会议?(请回复:日程 / 会议)`,不得改成「线上还是线下」「视频会议还是普通日程」等任何变体。
|
||||
- **查询场景严禁追问**,模糊表述必须日程 + 会议两边都查再合并 —— 追问本身就是错误。
|
||||
- **改约禁止 cancel + create**:会议链接不可重建,一旦拆开就永久丢失。
|
||||
- **三种 ID 集合形状各不相同**:`schedules get` 用 `schedule_ids: ["a","b"]`(字符串数组);`attendees` / `add_attendees` / `remove_attendees` / `has_attendees` / `free list` 的 `userids` 用 `[{"userid":"wo..."}]`(对象数组);`organizer` 用单值字符串。写错会静默失败或邀请到错误的人。
|
||||
- **`mark_optional_attendees` 是字符串数组**,不是对象数组 —— 与同一条命令里的 `attendees` 形状相反。
|
||||
- **`schedules search` 三选一**:`keywords` / `organizer` / `has_attendees` 至少传其一;只传时间范围的搜索是无效调用。
|
||||
- **只给时间就用 `list`,别把日期塞进 `keywords`** 去 `search`。
|
||||
- **`schedules list` 窗口限当前时刻前后 30 天**,超出服务端直接不返回(不是报错)。超范围时告知用户重新给一个更短的范围,别自行截断后假装查全了。
|
||||
- **`free list` 窗口 ≤ 24 小时**,且早于当前时刻的部分被自动截断 —— 查「昨天大家什么时候有空」永远返回空。
|
||||
- **时间是日程时区下的墙上时间,后台不做转换**:禁止自行把用户给的时间换算成东八区再传。
|
||||
- **会议室只写 `location` 等于没订**:提到会议室就必须经 `rooms search` 拿 `meeting_room_id` 传入,且订房是 create 的**前置阻塞项**,不能「先建了日程回头补会议室」。
|
||||
- **上游技能的会议室参数名已过时**:上游 `wecomcli-calendar` 写的是 `room_keyword` / `min_capacity` / `building_city`,CLI 1.2.0 实际是 `room_name` / `capacity_min` / `city_name` / `building_name`。照抄上游会直接调用失败。
|
||||
- **指定的会议室查无或被占时,必须先告知、禁止静默替换**,哪怕 `recommendations` 只有 1 个候选也要用户确认。
|
||||
- **`buildings list` 没有 building_id**:下游 `rooms search` 用 `city_name` + `building_name` 引用某栋楼,且这两个值必须逐字取自 `buildings list` 返回,禁止编造。
|
||||
- **判定会议形态不必补 `get`**:`search` / `list` / `get` 都直接返回 `meeting` 字段。
|
||||
- **`schedules create` 只返回 `schedule_id`**:想回显完整内容要么用本次入参,要么再调 `get`,别编造返回字段。
|
||||
- **写操作前的复述确认不可省**:本技能三个写方法全是 write-high,均对外可见或不可逆。
|
||||
|
||||
---
|
||||
|
||||
## 来源
|
||||
|
||||
本技能改写自 [wecom-cli](https://github.com/WecomTeam/wecom-cli) 官方 Skill
|
||||
(MIT License,© WecomTeam),针对 DesireCore 的风险治理与交互约定做了适配。
|
||||
上游对应技能:`wecomcli-calendar`。
|
||||
@@ -0,0 +1,163 @@
|
||||
# 会议室与办公楼查询 — `meeting rooms buildings list` / `meeting rooms search`
|
||||
|
||||
两个方法都是**只读查询**(risk: read),只告诉你「哪间会议室这个时段能订」,**不占用**。
|
||||
真正的占用发生在下游:`calendar schedules create` / `calendar schedules update` /
|
||||
`meeting create` / `meeting update` 传入 `meeting_room_id`。
|
||||
|
||||
> 本文件是会议室查询的唯一信息源。`wecom-meeting` 技能创建/更新会议要订会议室时,
|
||||
> **反向调用本文件**(`wecom-meeting` 自身不含 `rooms.*` 方法)。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 列出我可访问的办公楼(无入参)
|
||||
wecom-cli meeting rooms buildings list
|
||||
|
||||
# 查会议室可订性
|
||||
wecom-cli meeting rooms search --json '{
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"room_name": "1605",
|
||||
"floor_name": "16",
|
||||
"capacity_min": 4
|
||||
}'
|
||||
|
||||
# 指定办公楼查(city_name 与 building_name 同传或同省略)
|
||||
wecom-cli meeting rooms search --json '{
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"city_name": "北京",
|
||||
"building_name": "创新大厦A座",
|
||||
"capacity_min": 6
|
||||
}'
|
||||
|
||||
# 同城跨楼推荐(仅用户明确要求才加)
|
||||
wecom-cli meeting rooms search --json '{
|
||||
"begin_time": "2026-09-01 14:00:00",
|
||||
"end_time": "2026-09-01 15:00:00",
|
||||
"expand_to_other_buildings": true
|
||||
}'
|
||||
```
|
||||
|
||||
> ⚠️ **参数名以 CLI 1.2.0 schema 为准**。上游 `wecomcli-calendar` 的同名文档写的是
|
||||
> `room_keyword` / `min_capacity` / `building_city`,**这三个名字已经过时**,实际是
|
||||
> `room_name` / `capacity_min` / `city_name`。照抄上游会直接调用失败。
|
||||
|
||||
---
|
||||
|
||||
## `meeting rooms buildings list` — 办公楼清单
|
||||
|
||||
**无入参**。返回当前用户可访问的办公楼全量列表。
|
||||
|
||||
### 返回
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `total_count` | 大楼总数 |
|
||||
| `buildings[].name` | 大楼名称(不含城市前缀) |
|
||||
| `buildings[].city` | 城市,展示时拼 `{city} {name}` |
|
||||
| `buildings[].is_current` | 是否为当前办公大楼;无法判断时全为 `false` |
|
||||
|
||||
**没有 `building_id`** —— 下游 `rooms search` 靠 `city_name` + `building_name` 两个字符串引用某栋楼。
|
||||
|
||||
### 用法
|
||||
|
||||
- **仅当用户提到楼名时才调用**。没提楼就跳过,让 `rooms search` 用当前所在楼兜底。
|
||||
- 用户口语楼名(「北京创新 A」)与标准名往往写法不同(简称、漏字、少写 A/B 座、带不带城市前缀),
|
||||
应做**模糊匹配**,不要求逐字相同:
|
||||
- 命中唯一最接近项 → 文字确认一句「你是指【{city} {name}】吗?」,确认后取该条目的 `city` + `name`。
|
||||
- 命中多个相近项 → 只列这几个(展示 `{city} {name}`)让用户选。
|
||||
- 确实匹配不到 → 让用户补充或自由输入楼名。**禁止从全量列表里随机挑几个充数,禁止编造列表里没有的楼名。**
|
||||
- 展示给用户的楼名、以及最终喂给 `rooms search` 的 `city_name` / `building_name`,
|
||||
**必须逐字取自 `buildings list` 的返回条目**。
|
||||
- `buildings` 为空数组 → 提示「暂无可预订办公地点」。
|
||||
|
||||
---
|
||||
|
||||
## `meeting rooms search` — 会议室可订性
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 默认 | 说明 |
|
||||
|---|---|:--:|---|---|
|
||||
| `begin_time` | string | 是 | — | `YYYY-MM-DD HH:MM:SS`,须晚于当前时刻 |
|
||||
| `end_time` | string | 是 | — | 晚于 `begin_time` |
|
||||
| `room_name` | string | 否 | — | 会议室名/号(如 `"1605"`、`"创新室"`)。传了才会有 `target` |
|
||||
| `building_name` | string | 否 | 当前所在楼 | 与 `city_name` 同传或同省略 |
|
||||
| `city_name` | string | 否 | 当前所在楼城市 | 同上 |
|
||||
| `floor_name` | string | 否 | — | 楼层。**直接用用户原始表述**,不做归一化(说「16 楼」就传 `"16 楼"`,说「16F」就传 `"16F"`) |
|
||||
| `capacity_min` | int | 否 | — | 容量下限,取 `参与人数 + 1`(含组织者) |
|
||||
| `expand_to_other_buildings` | bool | 否 | `false` | 同城跨楼推荐,**仅用户明确要求才传** |
|
||||
| `limit` | int | 否 | 20 | 上限 100 |
|
||||
| `cursor` | string | 否 | — | 分页游标 |
|
||||
|
||||
`city_name` / `building_name` 均不传时用当前所在楼兜底。
|
||||
|
||||
> 上游文档声明了三个业务错误码(`current_building_unknown` 兜底失败 / `building_not_found` 楼名无匹配 /
|
||||
> `time_in_past` 起始时间已过),但**这些错误码不在 CLI 的 JSON Schema 里**,属上游声明、未经实测。
|
||||
> 遇到错误时以 CLI 实际返回的 `error.code` 与 `error.message` 为准,不要硬编码上面三个字符串做分支判断。
|
||||
|
||||
### 返回
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `inferred_building.name` / `.city` | 实际查询的办公楼,可展示给用户确认 |
|
||||
| `inferred_building.source` | 来源标记(如兜底 vs 来自入参) |
|
||||
| `target[]` | 传了 `room_name` 时为命中项(**可能多间**);未传时为空数组 |
|
||||
| `target[].status` | `bookable` / `unavailable` / `not_found` |
|
||||
| `target[].room` | 房间元数据;`not_found` 时可能为 null |
|
||||
| `recommendations[]` | 同楼候选,已按「同楼层优先 → 容量恰好够用」排序 |
|
||||
| `*.meeting_room_id` / `name` / `capacity` / `floor` | 房间字段。**`meeting_room_id` 仅工具链流转,禁止出现在回复正文** |
|
||||
| `has_more` / `next_cursor` | 分页 |
|
||||
|
||||
> `meeting_rooms` / `meeting_rooms_count` 在 schema 里标注为**废弃**字段,不要使用。
|
||||
|
||||
### 边界
|
||||
|
||||
- `status = unavailable` 时**不返回占用方信息**,不要告诉用户「被谁占了」。
|
||||
- 同楼无可用时 `recommendations` 为空数组,由你决定是否询问用户开 `expand_to_other_buildings`。
|
||||
- 抢订竞态(查到 bookable、下单时已被抢)发生在 `create` 阶段,按创建返回的错误处理并重新查一轮。
|
||||
|
||||
---
|
||||
|
||||
## 编排
|
||||
|
||||
```
|
||||
├─ 用户提了楼名 → buildings list → 模糊匹配 + 确认 → city_name + building_name
|
||||
│ 用户没提楼 → 跳过(rooms search 用当前所在楼兜底)
|
||||
│
|
||||
└─ rooms search(begin/end + 可选楼 + 可选 room_name + capacity_min = 参与人数 + 1)
|
||||
├─ 用户指定了具体会议室(传了 room_name)→ 看 target:
|
||||
│ ├─ target 中有 status = bookable:
|
||||
│ │ ├─ 仅 1 个 → 直接取其 target[].room.meeting_room_id
|
||||
│ │ └─ 多个 → 用文字让用户选(禁止自动取第一个)
|
||||
│ ├─ target = [](查无此名)→ 先告知「未查到你指定的『xxx』会议室」,
|
||||
│ │ 再让用户决定改订其他会议室或换时间(候选仅 1 个也须确认)
|
||||
│ └─ target 全是 unavailable → 先告知「『xxx』该时段已被占用」,再让用户选替代或换时间
|
||||
├─ 用户未指定具体会议室(target = []):
|
||||
│ ├─ recommendations 多个 → 必须让用户选
|
||||
│ └─ recommendations 仅 1 个 → 可直接使用
|
||||
└─ recommendations = [] → 问是否跨楼(expand_to_other_buildings = true 重查)或换时间
|
||||
```
|
||||
|
||||
`rooms search` 需要**确定的起止时间**。用户只给了「明天下午」这种范围时,
|
||||
先用 `calendar schedules free list` 查共同空闲、让用户选定一个具体时段,再拿该时段查会议室。
|
||||
|
||||
## 五条硬性规则(下游 create / update 必须遵守)
|
||||
|
||||
1. **先查询、后推荐、后创建**:`meeting_room_id` 必须来自本次 `rooms search` 的真实返回值。
|
||||
在拿到真实结果之前,**禁止**凭记忆、上下文、历史会话罗列或推荐任何具体会议室
|
||||
—— 包括回复正文里提到的会议室名 / 房间号 / 楼层 / 容量。
|
||||
2. **多个候选必须让用户选**,禁止自动替用户挑。
|
||||
3. **会议室禁止只写进 `location`**:那样不会真正占用会议室。只要用户提到会议室,
|
||||
就必须经 `rooms search` 拿到 `meeting_room_id` 传入。
|
||||
4. **先订房、后建程/建会**:会议室查询与选择是 create 的**前置阻塞项**,
|
||||
不得以「先把会议建起来、会议室随后补」跳过。事后要换会议室可用 `update` 传新 `meeting_room_id` 改订
|
||||
(须先经 `rooms search` 确认新会议室 `status = bookable`),不必取消重建。
|
||||
5. **查无 / 不可用时必须先告知、禁止静默替换**:即使 `recommendations` 只有 1 个候选也要用户确认。
|
||||
「仅 1 个可直接用」只适用于用户**未指定**具体会议室的情形。
|
||||
|
||||
## 展示约束
|
||||
|
||||
- 给用户的候选 **2~4 个**,展示 `name` + 楼层 + 容量。
|
||||
- **`meeting_room_id` 禁止出现在用户可见的任何文字里**,对用户只说会议室名称。
|
||||
Reference in New Issue
Block a user