skill-h-meeting-sync

【会议状态同步后台守护任务】每25分钟由 OpenClaw cron 触发一次。负责同步腾讯会议与各 Gitea 仓库之间的会议状态一致性,处理会议取消、改期、新增三种场景,并定期归档过期会议目录。不处理会议创建(用 skill-a)、会前简报(用 skill-b)、会后纪要(用 skill-c)等场景。

Audits

Warn

Install

openclaw skills install skill-h-meeting-sync

Skill-H: meeting_sync — 会议状态同步后台守护

定位

本技能是 cron 每 25 分钟触发的后台守护任务

职责分工:

  • OpenClaw(本次对话/cron 任务)

    • 触发 scan,获取 Gitea 侧全部活跃会议
    • 调用 tencent-meeting-skill 获取腾讯会议侧未来 7 天会议列表
    • 做三向对比分析,判断每场会议属于哪种情况(取消 / 改期 / 新增 / 已结束或已过期 / 无变化)
    • 根据分析结果,调用对应的 Skill-H 命令执行 Gitea 侧操作
    • 调用 imap-smtp-email 发送通知邮件
    • 在每次 cron 最后调用 archive 做归档清理
  • Skill-H(本技能)

    • scan:汇总所有 Gitea 活跃会议(status ∈ {scheduled, brief-sent}),返回供对比的数据
    • cancel:将“尚未开始但腾讯会议中已不存在”的会议标记为已取消,写日志,返回取消通知邮件参数
    • reschedule:更新旧目录状态为 rescheduled,创建新时间目录,写日志,返回改期通知邮件参数
    • create-pending:在 aifusion-meta 创建 repo:pending 的会议占位记录
    • archive:将 cancelled/rescheduled 状态超过 30 天的目录移入 meetings/archive/
  • tencent-meeting-skill

    • 查询腾讯会议侧未来 7 天会议列表(get_user_meetings
    • 查询当天已结束会议(get_user_ended_meetings),用于辅助排除“其实已经开完”的情况
  • imap-smtp-email

    • 发送取消通知、改期通知、新会议待确认通知邮件

配置要求

配置文件:~/.config/skill-h-meeting-sync/.env

首次使用前运行:

bash setup.sh

严格工作流(每次 cron 触发必须按顺序执行)


第一步:scan,获取 Gitea 侧所有活跃会议

node main.js scan

返回 JSON,gitea_meetings 字段包含所有 status ∈ {scheduled, brief-sent} 的会议,每条包含:

  • repo:仓库全名
  • meeting_dir:会议目录名
  • meeting_id:腾讯会议内部 ID(用于对比)
  • meeting_code:9 位会议号
  • topic:会议主题
  • scheduled_time:会议时间(ISO8601)
  • status:当前状态
  • organizer:组织者用户名
  • attendees:参会人用户名列表
  • attendee_emails:参会人邮箱列表
  • category:single | cross-project

第二步:OpenClaw 调用 tencent-meeting-skill 获取腾讯会议列表

调用 get_user_meetings 获取即将开始及进行中的会议,同时调用 get_user_ended_meetings 补充当天已结束会议。

将结果整理为两个字典:

  1. future_meetings_by_id

    • key:meeting_id
    • value:会议信息(含 scheduled_time
  2. ended_meetings_by_id

    • key:meeting_id
    • value:会议信息(含 scheduled_time 或结束标记)

判断"未来 7 天内":只保留 scheduled_time 在 now ~ now+7d 范围内的腾讯会议记录用于“未来会议”对比。
get_user_ended_meetings 只用于排除“会议其实已经结束”的误判,不用于触发 cancel。


第三步:OpenClaw 做三向对比分析

meeting_id 作为唯一标识,对 Gitea 列表、腾讯未来会议列表、腾讯已结束会议列表进行对比,产生以下五种情况:

情况判断条件处理方式
无变化Gitea 有,腾讯未来会议也有,且时间差 < 1 分钟跳过
已改期Gitea 有,腾讯未来会议也有,但时间差 ≥ 1 分钟→ reschedule
已结束或已过期Gitea 有,腾讯未来会议无,且(腾讯已结束列表中有该 meeting_id,或 Gitea scheduled_time <= now跳过,不调用 cancel
已取消Gitea 有,腾讯未来会议无,腾讯已结束列表也无,且 Gitea scheduled_time > now→ cancel
新增腾讯未来会议有,Gitea 无对应 meeting_id→ create-pending

关键修正:

  • “腾讯会议中没有这场会” 不等于 “已取消”
  • 它也可能表示:会议已正常结束,或者会议时间已经过去
  • 因此只有“尚未开始 + 腾讯未来列表中不存在 + 腾讯已结束列表中也不存在”时,才允许判定为取消

时间对比说明:

  • 腾讯会议返回的是秒级时间戳,需先转为北京时间再与 Gitea 的 scheduled_time 对比
  • 时间差 小于 1 分钟 视为无变化(仅用于避免秒级精度误差)
  • 时间差 大于等于 1 分钟 视为改期,应触发 reschedule

第四步:对"已取消"的会议,调用 cancel

node main.js cancel \
  --repo "HKU-AIFusion/dexterous-hand" \
  --meeting-dir "2026-04-22-1500" \
  --attendee-emails "email1@163.com,email2@163.com" \
  [--cancel-reason "腾讯会议中已不存在,且会议时间尚未到达"]

返回 JSON,可能有两种结果:

结果 A:真正取消成功

  • success: true
  • new_status: "cancelled"
  • cancel_email.to / .subject / .html

此时 OpenClaw 调用 imap-smtp-email 发送取消通知给全体参会人。

结果 B:被 Skill-H 安全拦截

  • success: true
  • skipped: true
  • skip_reason: "meeting_time_passed"

表示:

  • 会议时间已过
  • Skill-H 判断它可能是“已经正常开完”或“已过期未清理”
  • 不会写 cancelled
  • 不会返回取消邮件参数
  • OpenClaw 应记录日志后直接跳过

第五步:对"已改期"的会议,调用 reschedule

node main.js reschedule \
  --repo "HKU-AIFusion/dexterous-hand" \
  --old-meeting-dir "2026-04-22-1500" \
  --new-time "2026-04-23T15:00:00+08:00" \
  --new-meeting-id "yyy" \
  --new-meeting-code "987654321" \
  --new-join-url "https://meeting.tencent.com/..." \
  --attendee-emails "email1@163.com,email2@163.com"

本命令会:

  1. 将旧目录 meta.yaml status → rescheduled,写入 rescheduled_to 字段
  2. 在同仓库创建新目录 meetings/YYYY-MM-DD-HHMM/,继承旧会议的 topic / attendees / organizer / category,写入 rescheduled_from 字段,status = scheduled
  3. 在新目录创建 agenda.md(注明"本次会议由 YYYY-MM-DD-HHMM 改期而来")

返回 JSON,关键字段:

  • success
  • new_meeting_dir:新目录名
  • reschedule_email.to / .subject / .html:改期通知邮件参数

收到返回后,OpenClaw 调用 imap-smtp-email 发送改期通知给全体参会人。


第六步:对"新增"的会议(腾讯有,Gitea 无),调用 create-pending

node main.js create-pending \
  --meeting-id "zzz" \
  --meeting-code "111222333" \
  --topic "未知主题" \
  --time "2026-04-24T10:00:00+08:00" \
  --join-url "https://meeting.tencent.com/..." \
  [--duration 60]

本命令会:

  1. aifusion-meta/meetings/YYYY-MM-DD-HHMM/ 创建占位目录
  2. 写入 meta.yaml(status=scheduled,repo=pending)

返回 JSON,关键字段:

  • success
  • meeting_dir:创建的目录名
  • notify_email.to / .subject / .html:发给组织者(ADVISOR)的待确认通知邮件参数

收到返回后,OpenClaw 调用 imap-smtp-email 向组织者发送待确认通知。

通知内容应说明:"腾讯会议中发现一场新会议,尚未关联到 Gitea 仓库,请在 aifusion-meta 中修改 repo 字段或在 OpenClaw 中说明归属仓库。"


第七步:archive 归档清理(每次 cron 最后执行)

node main.js archive

本命令会:

  1. 遍历所有受管仓库(包括 aifusion-meta)
  2. 找出 status ∈ {cancelled, rescheduled} 且 scheduled_time 距今超过 30 天的会议目录
  3. 将这些目录下所有文件复制到同仓库的 meetings/archive/YYYY-MM-DD-HHMM/
  4. 删除原目录下所有文件(Gitea API 逐文件删除)
  5. 写日志,返回归档摘要

错误处理规则

错误场景处理方式
scan 失败打印错误,本次 cron 中止
tencent-meeting-skill 调用失败跳过本次同步,不修改任何 Gitea 状态
cancel / reschedule 写 Gitea 失败记录错误,下次 cron 重试
create-pending 失败记录错误,下次 cron 检测到同一会议时重试
邮件发送失败记录错误,不影响 Gitea 状态更新
archive 单个文件迁移失败跳过该文件,继续处理其他文件,日志记录警告

幂等性说明

  • scan 只返回 status ∈ {scheduled, brief-sent} 的会议
  • cancel 成功后 status → cancelled,下次 cron 不再扫描到
  • 若会议时间已过,cancel 会返回 skipped=true,不会误写为 cancelled
  • reschedule 成功后旧目录 status → rescheduled,新目录 status=scheduled;下次 cron 只处理新目录
  • create-pending 用 meeting_id 作为 meta.yaml 的唯一标识;下次 cron 的 scan 会包含它(repo=pending 的目录也被 scan 返回以便追踪);待组织者确认归属后人工处理
  • archive 只处理超过 30 天的目录,不会误归档活跃会议