# 易秒办即时通讯（im）

`im` 是 E10 易秒办即时通讯的聚合命令，覆盖**消息（msg）**、**必达（ding）**、**群组（group）**、**会话（session）**、**系统消息（sysmsg）**，以及支撑能力人员解析（person）、文件上传/预览/下载（file）、用户状态与免打扰设置（user）、i18n 标签（i18n）。

Skill 名与命令名不同：Skill 目录是 `skills/weaver-e10-yimiaoban`，CLI 命令后缀是 `im`（`weaver-work-cli im ...`）。

```text
weaver-work-cli im --help
weaver-work-cli im schema
weaver-work-cli --json im run <operation> --input-json '{"key":"value"}'
```

`schema` 是能力清单的唯一事实来源，包含每个 operation 的 `inputSchema`、`risk`、`requiresConfirmation` 和 `deprecatedInputAliases`。

## 实现位置

| 路径 | 职责 |
| --- | --- |
| `src/shortcuts/im/index.ts` | 命令注册与 JSON envelope |
| `src/shortcuts/im/manifest.ts` | `schema` 契约 |
| `src/shortcuts/im/host.ts` | E10 会话桥接、认证头、executeIm/文件上传、hrm 人员解析、i18n、媒体 URL |
| `src/shortcuts/im/continuation.ts` | 写操作确认链的 HMAC-SHA256 token |
| `src/shortcuts/im/operations/` | 按业务域拆分的 operation 实现 |
| `src/shortcuts/im/operations/registry.ts` | operation 注册表与去重校验 |
| `skills/weaver-e10-yimiaoban/` | Agent Skill（入口 + 按能力拆分的 reference） |

## 认证

复用 `src/internal/e10` 的 E10 会话（`requireE10Session`），请求自动携带 `Cookie`、`eteamsid` 与 `X-Requested-With: XMLHttpRequest`。IM 接口前缀 `/api/em/msg/executeIm`，必达例外走 `/api/em/msg/createDing`。业务代码不接触认证目录，路径强制以 `/api/` 开头。

## 红线（模块内强制）

- 请求体禁止传操作人字段（`user`/`sender`/`creator`/`operator`），操作者由 `eteamsid` 识别。
- 本人 IM 身份（`imCid`/`imUid`）只用于本机组装搜索条件、撤回/必达消息体、建群自检和「我」的判定（`teamsCheck` 解析，进程内缓存），**绝不入请求体**，也不接受调用方传入。
- 人名一律走 hrm：`/api/hrm/common/getEmployeeByIds` 解析，禁止用 IM 群成员 `name` 字段展示姓名。
- uid/cid/群 id 是 uint64，一律字符串传输且非 0。
- 过滤全部服务端完成；有界区间自动翻页（最多 100 页），无界区间只拉一页。
- 时间范围查询换算为消息 id 区间（`MakeMinAndMaxIdByTm`），跨度 > 7 天自动收敛为该范围最后 7 个自然日并在响应里给出提示。
- 全空白文本不算内容（拒绝发送空白消息）；普通请求 15 秒超时即失败不重试（上传 15/60 秒）。
- 媒体只给需登录态的 URL，真要看内容用 `im.file.download` 落地到本地（父目录须存在、拒绝覆盖）。
- 消息内容完整透出，不截断；展示规则见 Skill `references/safety-boundaries.md`。

## 消息拉取与展示

`im.msg.sync.single`（对方 `fromUid`+`fromCid` 必填）、`im.msg.sync.group`（`groupId` 必填，type=0 固定）为倒序拉取：

- 每页默认 50 条、上限 50；**有界区间**（`from`/`to`、缺省当天或显式 `end`/`endId`）自动循环翻页拉完再返回；**无界区间**（`latest:true`）只拉一页，用 `nextStart` 续拉。
- `actionMsg.code=1303` 表示无聊天记录，按成功空列表处理，并返回 `empty:true` + `emptyReason`（区分当天无消息/时间范围无消息/会话无历史）。
- 响应带 `pages`/`autoPaged`/`pageSize`/`exhausted`/`hasMore`/`nextStart`/`range`/`rangeShrink`；消息按消息 id 从新到旧排序。

返回的 `messages[]` 已按 msgParser 语义解析为结构化行：

- `msgid`；`sender`（自己显示「我」）；`time`；`typeName`；`content` 完整正文（@ 提及已还原、媒体显示 `[图片]名称`/`[视频]名称`/`[文件]名称`）；`must` 必达标记。
- `fileName`：媒体消息原始文件名；`media`：图片/视频/文件的预览与下载 URL（需登录态）。

## 系统消息

`im.sysmsg.query` 与聊天消息同一分页口径；`start` 为排除边界（返回 id `< start`），返回 `dealMode` 表示该分组以处理状态（`已处理/未处理`）还是阅读状态（`已读/未读`）为准。分组/类型 id 来自 `im.sysmsg.groupSearch` / `im.sysmsg.typeSync`。

## 写协议与 continuation

所有写操作（send/withdraw/top.set/ding/group 系列）统一 `prepare → apply`：

- `prepare`：只读校验（成员解析、源消息读取、接收人计算），返回 `summary` + `continuation`（HMAC-SHA256 签名，10 分钟有效，含 baseUrl/profile/userId 指纹与 snapshot 指纹）。
- `apply`：必须 `confirm:true` + 有效 continuation；校验租户/账号一致（`context_mismatch`）与目标指纹（`target_changed`）；执行后不确定结果返回 `partial/write_uncertain`，禁止自动重试。

会话提醒设置（`im.user.setRemind`，接口 `POST /api/em/msg/executeIm/user/setMsgRemind`）走同一确认链：`sessionType` 取 `1` 单聊会话（`toUid`+`toCid`）、`2` 群聊会话（`groupId`）、`4` 系统会话（`group`）；`reminder` 取 `0` 消息接收提醒 / `1` 消息接收但不提醒（免打扰）。操作者身份由 CLI 用当前登录人填充，调用方不能自带。

## 分组速览（以 schema 为准）

| 前缀 | 覆盖 |
| --- | --- |
| `im.person.*` | resolve / resolveByIds |
| `im.msg.sync.*` / `im.msg.top.sync` / `im.msg.read.*` | 消息拉取/置顶/阅读状态 |
| `im.msg.send.*` / `im.msg.withdraw` / `im.msg.top.set` | 发送/撤回/置顶（写，两段式） |
| `im.ding.send.*` | 必达/转必达（写，两段式） |
| `im.group.search/info/users/admins/exist/userExist/announce.*` | 群读 |
| `im.group.create/invite/kick/exit/destroy/join/modify/rename/announce.modify` | 群写（两段式） |
| `im.session.list` / `im.sysmsg.*` | 会话与系统消息 |
| `im.file.upload/preview/download` / `im.user.state` / `im.user.setRemind` / `im.i18n.labels` | 文件/状态/免打扰/翻译 |

## 平台示例

Windows PowerShell：

```powershell
weaver-work-cli --json im run im.msg.sync.group --input-json '{"groupId":"1788334281700000004","num":50,"latest":true}'
weaver-work-cli --json im run im.group.users --input-json '{"groupId":"1788334281700000004"}'
weaver-work-cli --json im run im.session.list --input-json '{"num":20}'
```

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json im run im.msg.sync.group --input-json '{"groupId":"1788334281700000004","num":50,"latest":true}'
weaver-work-cli --json im run im.group.users --input-json '{"groupId":"1788334281700000004"}'
weaver-work-cli --json im run im.session.list --input-json '{"num":20}'
```

## 关联文档

- Agent 路由入口：`skills/weaver-e10-yimiaoban/SKILL.md`
- 条件化细节：`skills/weaver-e10-yimiaoban/references/`
- 源资料清单与影响索引：`skills/weaver-e10-yimiaoban/references/source-manifest.json`
