飞书会议室偏好初始化与预定。适用于:初始化/刷新个人会议室列表、创建会议并预定会议室、 给已有会议补订会议室。使用本地状态文件保存首选会议室列表;第一版只维护一个 base 地(默认城市,内部字段为 default_city) 的会议室列表,跨城市时只创建会议不自动订房。
Scanned 9/9/2026
Install to Claude Code
npx -y skills add Lord1Egypt/awesome-skill-forge --skill feishu-meeting-room-book --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Feishu Meeting Room Book?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lord1egypt-feishu-meeting-room-book)More formats (shields.io, HTML) on the badges page.
---
name: feishu-meeting-room-book
description: |
飞书会议室偏好初始化与预定。适用于:初始化/刷新个人会议室列表、创建会议并预定会议室、
给已有会议补订会议室。使用本地状态文件保存首选会议室列表;第一版只维护一个 base 地(默认城市,内部字段为 default_city)
的会议室列表,跨城市时只创建会议不自动订房。
user-invocable: true
---
# Feishu Meeting Room Book
基于飞书日历事件学习个人常用会议室,并按本地偏好列表预定会议室。
## Paths
- State file: `state/feishu-meeting-room-book.json`
- Helper script: `skills/feishu-meeting-room-book/scripts/meeting_room_booker.py`
- Refresh plan file: `tmp/meeting-room-refresh-plan.json`
## Dependencies And Permissions
- Required OpenClaw plugin:
- `openclaw-lark` or any equivalent Feishu/Lark plugin that exposes:
- `feishu_calendar_event`
- `feishu_calendar_event_attendee`
- `feishu_calendar_freebusy`
- Required local runtime:
- `python3`
- the bundled helper script uses Python standard library only and does not require extra pip packages
- Local file access:
- persistent state is written to `state/feishu-meeting-room-book.json`
- temp working files may be written under `tmp/meeting-room-events-*.json` and `tmp/meeting-room-candidates-*.json`
- Required Feishu OAuth scopes:
- `calendar:calendar.event:read` for reading recent calendar events during init/refresh
- `calendar:calendar.event:create` for creating new calendar events
- `calendar:calendar.event:update` for attaching meeting rooms and updating event attendees
- `calendar:calendar.free_busy:read` for checking room availability
- Booking model:
- this skill does not call a separate meeting-room booking service
- room booking is performed by creating or updating Feishu calendar events and attaching the room as a `resource attendee`
## Required Tools
- `feishu_calendar_event`
- `feishu_calendar_event_attendee`
- `feishu_calendar_freebusy`
If any Feishu tool returns auth or scope errors, finish the OAuth flow first and retry. The normal scopes for this skill are:
- `calendar:calendar.event:read`
- `calendar:calendar.event:create`
- `calendar:calendar.event:update`
- `calendar:calendar.free_busy:read`
## Product Rules
- Manual refresh only. Never auto-refresh cached rooms.
- The cached room list is single-city v1. Store one base location (default city) and one ordered room list. The internal state field name remains `default_city`.
- On first use, if `state/feishu-meeting-room-book.json` does not exist and the user asked to create/book a room or book an existing event, do not ask the user to initialize separately. Tell the user you are starting first-time initialization, run init immediately, and continue the original request after the default room order has been saved.
- During init/refresh, the extracted candidate order is the default priority order. Do not reshuffle it unless the user explicitly changes it.
- When creating a new event, use defaults instead of asking follow-up questions for common omissions:
- missing title: use summary `会议`
- missing attendees: create a self-only event with no extra attendees
- missing duration/end time: default to 1 hour
- If the user does not specify a city, use the saved base location (default city) from `default_city`.
- If the user specifies a different city from the saved base location (default city), create the event but do not auto-book a room. Explain that the current cache only covers the base location (default city), and tell the user to seed that city manually and run refresh.
- When no preferred room is free:
- `create_and_book`: still create the event without a room
- `book_existing`: keep the event unchanged and tell the user to refresh after manually seeding more rooms
## Updating Candidate Rooms
Use these rules when the user asks how to update the candidate room list.
- To add a new candidate room:
1. Ask the user to create any calendar event within the next 7 days.
2. Tell the user to attach the desired meeting room to that event as a `resource attendee`.
3. Then tell the user to say `刷新会议室列表`, `更新会议室列表`, `重新学习会议室`, or `更新会议室候选表`.
4. Default refresh scans only a narrow seeding window and includes that room if it appears in the event payload.
- To remove a room from the candidate list:
- Run refresh and keep only the rooms the user still wants.
- Or keep the current list and overwrite the state with a smaller subset and a new order.
- To change room priority:
- Run refresh and let the user reply with the final room order.
- Or keep the current rooms and overwrite the state with a reordered `--room-id` list.
- Window limitation:
- Only events in the chosen scan window affect candidates.
- Default refresh uses `now - 1 day` to `now + 7 days`.
- First-use init uses `now - 7 days` to `now + 7 days`.
- Only explicit full rebuild uses `now - 20 days` to `now + 20 days - 1 minute`.
- A seeding event outside the current scan window will not update the candidate list.
## Init Or Refresh
Use this flow when the user says:
- "初始化会议室偏好"
- "初始化会议室预定技能"
- "刷新会议室列表"
- "更新会议室列表"
- "重新学习会议室"
- "更新会议室候选表"
- "全量刷新会议室列表"
- "重建会议室列表"
- or the state file does not exist
When init is triggered implicitly because the state file does not exist, explicitly tell the user:
- this is the first use of the skill
- you are learning their recent meeting room history now
- you will save a default room priority order first
- you will continue the current booking request automatically after init finishes
Steps:
1. Choose one scan mode before calling any Feishu tool:
- `default_refresh`: when state already exists and the user says `刷新会议室列表`, `更新会议室列表`, `重新学习会议室`, or `更新会议室候选表`
- scan window: `now - 1 day` to `now + 7 days`
- save behavior: incremental update; keep existing rooms and order, append newly discovered rooms, refresh metadata for rooms seen again
- `init`: when the state file does not exist or the user explicitly says `初始化会议室偏好` or `初始化会议室预定技能`
- scan window: `now - 7 days` to `now + 7 days`
- save behavior: rebuild the state from the extracted candidates
- `full_rebuild`: only when the user explicitly says `全量刷新会议室列表` or `重建会议室列表`
- scan window: `now - 20 days` to `now + 20 days - 1 minute`
- save behavior: rebuild the state from the extracted candidates
2. Build the refresh plan with the helper script.
Never compute the slice boundaries manually:
```bash
python3 skills/feishu-meeting-room-book/scripts/meeting_room_booker.py plan-refresh \
--mode <default_refresh|init|full_rebuild> \
--output tmp/meeting-room-refresh-plan.json
```
3. Read `tmp/meeting-room-refresh-plan.json`.
Use exactly the `slices[].start_time`, `slices[].end_time`, `slices[].event_file`, and `slices[].candidate_file` values from that plan.
Do not invent your own temp filenames or time windows.
4. For each slice from the plan:
- call `feishu_calendar_event` with `action=list`
- immediately save only that slice result to the exact `event_file` from the plan
- immediately run extract on that single slice:
```bash
python3 skills/feishu-meeting-room-book/scripts/meeting_room_booker.py extract \
--input <event_file_from_plan> \
--output <candidate_file_from_plan>
```
- If `extract` fails with `invalid JSON in <event_file>` or any other parse error, treat that slice file as damaged or truncated.
- Stop immediately. Tell the user refresh failed because one slice result was not valid JSON.
- Do not continue to later slices and do not run `finalize-refresh`.
5. After all slices succeed, finalize refresh with one helper command.
This command is the only allowed way to write `state/feishu-meeting-room-book.json` during refresh:
```bash
python3 skills/feishu-meeting-room-book/scripts/meeting_room_booker.py finalize-refresh \
--input <candidate_file_from_plan_1> \
--input <candidate_file_from_plan_2> \
--mode <default_refresh|init|full_rebuild> \
--state state/feishu-meeting-room-book.json \
--output tmp/meeting-room-candidates.json
```
6. Read `tmp/meeting-room-candidates.json`.
If the helper command failed or returned `saved != true`, stop, tell the user refresh failed, and keep the previous state unchanged.
7. Show the candidate rooms to the user with explicit priority numbers. Tell the user:
- this is the current default priority order
- the current base location (default city) is `<default_city>`
- if this was `default_refresh`, explain that the existing list was preserved and newly discovered rooms were appended
- it has already been saved as the fallback order
- if the user does not reply, future bookings will use this default order
- if this init was triggered by a booking request, the current request will proceed with this default order
- if the user wants to adjust it, reply with:
- the base location (default city) if they want to change it
- which rooms to keep
- the final room order
8. If the user replies with a custom order or subset, overwrite the state with:
```bash
python3 skills/feishu-meeting-room-book/scripts/meeting_room_booker.py save \
--input tmp/meeting-room-candidates.json \
--state state/feishu-meeting-room-book.json \
--default-city "<city>" \
--output tmp/meeting-room-candidates-confirmed.json \
--room-id omm_first \
--room-id omm_second
```
Rules:
- Learn only from resource attendees already attached to real events.
- Prefer resource attendees with `rsvp_status=accept`.
- If `rsvp_status` is missing in the event payload, keep the room candidate.
- The helper script's candidate order is already the default priority order.
- If the user gives no reordering instructions, keep the saved default order unchanged.
- `default_refresh` is incremental:
- keep existing rooms even if they did not appear in the current narrow scan window
- keep the existing order for already saved rooms
- append newly discovered rooms after the existing list
- only refresh metadata such as `display_name`, `score`, `last_seen_at`, and `location_hints` for rooms that were seen again
- Room removal is not part of incremental refresh. To remove rooms, the user must explicitly reorder/trim the list or run `full_rebuild` and then confirm a smaller subset.
- The current official `feishu_calendar_event` tool does not expose reliable paging controls for `list`, so keep each time slice small enough that a single slice result remains manageable.
- Never fetch a whole multi-week scan window in one tool call.
- Never widen the scan window beyond `now - 20 days` to `now + 20 days - 1 minute`.
- Never use `write`, `edit`, or any other direct file-writing tool on `state/feishu-meeting-room-book.json`.
- During refresh, `write` is allowed only for raw per-slice temp files under `tmp/meeting-room-events-*.json`.
- Never use shell redirection like `>` with `meeting_room_booker.py` JSON commands. Use the helper's `--output` flag so it writes atomically.
- If any `event_file` is truncated or malformed, `extract` will fail. Treat that as the real failure cause; do not continue with an empty or partially written candidate file.
- Never hand-copy, paraphrase, or manually reconstruct the `feishu_calendar_event` JSON into another file.
- Never invent candidate rooms or scores from memory if parsing fails.
- The only valid persistent state file for this skill is `state/feishu-meeting-room-book.json`.
- Never write or read `state/meeting-room-preferences.json` or any other alternate state filename.
- If writing any slice result or running `extract` / `plan-refresh` / `finalize-refresh` / `save` fails, stop immediately, tell the user refresh failed, and keep the previous state unchanged.
- Never overwrite the state with a guessed list when no new candidates were extracted.
## Create And Book
Use this flow when the user wants to create a new event and also book a meeting room.
Steps:
1. If `state/feishu-meeting-room-book.json` is missing, run init first.
- Tell the user you detected first use and are initializing meeting room preferences before booking.
- After init saves the default order, continue this create-and-book flow automatically.
2. Load the state:
```bash
python3 skills/feishu-meeting-room-book/scripts/meeting_room_booker.py show --state state/feishu-meeting-room-book.json --json
```
3. Parse:
- title
- start time
- end time or duration
- attendees
- city
4. Apply defaults before asking any follow-up:
- If title is missing, set it to `会议`.
- If attendees are missing, use no extra attendees. The event is still created for the current user because `user_open_id` is always passed.
- If both end time and duration are missing, set end time to `start_time + 1 hour`.
5. If city is missing, use the saved base location (default city) from `default_city`.
6. If city is not the saved base location (default city), create the event without a room and explain the single-city v1 limitation.
7. Otherwise, iterate the cached rooms in rank order. For each room, call `feishu_calendar_freebusy` with `action=list` and `room_id=<room_id>`.
8. Pick the first room whose `freebusy_items` is empty.
9. Create the event with `feishu_calendar_event` `action=create`.
- Always pass `user_open_id`
- Put normal attendees in `attendees` only when the user explicitly provided them
- Do not put the room into the initial create call
10. If a free room was found, attach it with `feishu_calendar_event_attendee` `action=create`.
11. Immediately call `feishu_calendar_event_attendee` `action=list` once to inspect the room attendee status.
Do not tell the user that topic, attendees, and duration are required. The user can simply give a start time; the defaults above fill the rest.
Interpretation:
- `accept`: room booked successfully
- `needs_action`: event created and room booking submitted; tell the user it is pending
- `decline` or attendee-create failure: keep the event and tell the user no room was booked
## Book Existing Event
Use this flow when the user wants to add a room to an existing event.
Steps:
1. If `state/feishu-meeting-room-book.json` is missing, run init first.
- Tell the user you detected first use and are initializing meeting room preferences before booking.
- After init saves the default order, continue this booking flow automatically.
2. Resolve the target event:
- Prefer an explicit `event_id`
- Otherwise search by title/time with `feishu_calendar_event` `action=search` or `action=list`
- If multiple plausible matches remain, ask the user to choose before mutating
3. Read attendees first with `feishu_calendar_event_attendee` `action=list`.
4. If the event already has a resource attendee in `accept` or `needs_action`, stop and report the current room state.
5. If the requested city is different from the saved base location (default city), do not try booking; explain the single-city v1 limitation.
6. Otherwise, iterate cached rooms with `feishu_calendar_freebusy` and attach the first free room with `feishu_calendar_event_attendee`.
7. Re-read attendees once and report the resulting room RSVP state.
## Local State Shape
The helper script writes:
```json
{
"version": 1,
"default_city": "Shanghai",
"updated_at": "2026-03-17T20:00:00+08:00",
"rooms": [
{
"room_id": "omm_xxx",
"display_name": "A5-01",
"rank": 1,
"score": 18,
"last_seen_at": "2026-03-16T10:00:00+08:00",
"location_hints": ["Shanghai HQ"]
}
]
}
```
This file is renewable runtime state. Keep it in `state/`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!