# Wecom App Ops

> wecom-app 运维/使用规范（本地技能）

- Skill: `bytepioneer-ai/wecom-app-ops` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add bytepioneer-ai/wecom-app-ops`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bytepioneer-ai/wecom-app-ops/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: BytePioneer-AI (https://skillmd.com/u/bytepioneer-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bytepioneer-ai/wecom-app-ops

---


# wecom-app 运维/使用规范（本地技能）

本技能针对你这套 OpenClaw + 企业微信自建应用（wecom-app）环境，提供可复用的“怎么做”步骤。

## 0) 快速判断：你要做哪一类事？

- **A. 回发媒体（图片/录音/文件）**：需要拿到本地路径 + 正确的 `target`（通常 `user:<name>`）
- **B. 从消息里拿 saved: 路径做 OCR/二次处理**：使用 `saved:/.../inbound/YYYY-MM-DD/...` 的稳定路径
- **C. 修复“找不到图片/自动删除”**：检查 wecom-app 的 `inboundMedia.keepDays/dir/maxBytes`
- **D. 语音发不出去**：优先 `.amr/.speex`，或开启 `voiceTranscode.enabled`

---

## 1) target 与 replyTo（最容易踩坑）

### 1.1 target 是什么
使用 `message.send` 向 wecom-app 主动发消息时，必须提供可解析 target（否则会报 `Action send requires a target.`）。

常见可用形式（以本环境为准）：
- `target: "wecom-app:user:<userId>"`
- `target: "user:<userId>"`
- `target: "<userId>"`（裸 ID，插件会按用户 ID 处理）
- `target: "user:<userId>@<accountId>"`（多账号场景）

> 注意：当前 wecom-app 出站实现以用户私聊为主，运维侧不要依赖 `chatid:<id>` 作为通用 target 格式。

### 1.2 replyTo 怎么用
- 如果你要“回复当前对话”，优先使用消息的 `message_id` 作为 `replyTo`。
- `replyTo` 不是 target；target 仍然要填。

### 1.3 Unknown target 怎么办（排查步骤）
- 显示名（比如 `CaiHongYu`）不一定能被解析。
- 优先尝试：`target: "user:CaiHongYu"`

若仍报 `Unknown target`：
1) 先确认是 **私聊** 还是 **群聊**：
  - 私聊：使用 `user:<userId>`（推荐）
  - 群聊：本技能默认不走该路径（请先确认插件是否已实现对应目标解析）
2) 让对方（或你自己）在企业微信里再发一条消息（任意内容），以便系统记录真实标识。
3) 从以下任意来源抓取真实的 `userId/chatId`：
   - OpenClaw / wecom-app 插件日志（`~/.openclaw/logs/`）
   - 或把“收到消息时的原始字段/报错日志”贴出来，我来反推。
4) 拿到真实值后再发：
  - `target: "user:<userId>"`（或 `wecom-app:user:<userId>`）

---

## 2) 媒体文件在哪里？（图片/录音/文件）

### 2.1 入站媒体（推荐稳定路径）
wecom-app 现在会把入站媒体归档到：
- `inboundMedia.dir/YYYY-MM-DD/`
- 默认（跨平台）：`~/.openclaw/media/wecom-app/inbound/YYYY-MM-DD/`

消息正文会出现：
- `[image] saved:/.../inbound/YYYY-MM-DD/img_...jpg`
- `[voice] saved:/.../inbound/YYYY-MM-DD/voice_...amr`

这条 saved 路径用于：OCR、回发、复用。

### 2.2 临时目录（不建议依赖）
- `/tmp/wecom-app-media/` 只作为下载中转，不保证长期存在。
- Windows 也同理：临时目录仅中转，不作为业务引用路径。

---

## 3) 回发图片/文件/录音（标准做法）

### 3.1 回发图片（注意：必须是真图片格式）
使用 `message` 工具：
- `channel: "wecom-app"`
- `target: "user:<name>"`
- `path: "<本地文件路径>"`
- `replyTo: "<message_id>"`（可选但推荐）

**格式要求（踩坑高发）：**
- 优先使用真正的图片格式：`.png` / `.jpg` / `.jpeg`
- `.svg` 在企业微信里通常不会按“图片消息”展示。
  - 新版本插件会把 `.svg` **按文件发送**，避免误走图片通道。
  - 若你需要“图片预览”：请先把 svg 转 png 再发（可选工具：`rsvg-convert` / ImageMagick `convert` / 或用脚本生成 PNG 兜底）。

### 3.2 回发录音（语音格式坑）
- **推荐格式**：`.amr`（你收到的入站语音也通常是 amr）
- `.wav/.mp3` 在企业微信自建应用里经常无法作为“语音消息(voice)”发送。

**自动兜底（新功能）**
- 你可以开启 `voiceTranscode.enabled=true`：
  - 系统存在 `ffmpeg` 时：遇到 wav/mp3 会 **自动转码为 amr** 再按 voice 发送
  - 没有 `ffmpeg` 时：会 **自动降级为 file 发送**（保证可达）
  - 若 `mediaUrl` 是远程 URL，当前实现会直接走 file 降级（不做“下载后再转码”）

配置示例（openclaw.json）：
```jsonc
{
  "channels": {
    "wecom-app": {
      "voiceTranscode": {
        "enabled": true,
        "prefer": "amr"
      }
    }
  }
}
```

**手动转码示例（ffmpeg）**
```bash
ffmpeg -i in.wav -ar 8000 -ac 1 -c:a amr_nb out.amr
```

- 其他流程同图片。

### 3.3 回发 README/文本为“文件形式”
- 先写到临时文件（如 `/tmp/xxx.md`）
- 再用 `message.send` 的 `path` 作为附件发送。

---

## 4) 发送失败的排障清单

### 4.1 `Action send requires a target`
- 说明 target 缺失：补 `target:"user:..."`。

### 4.2 `Unknown target` / 发送 ok=false
- 优先确认 target 是否能解析（`user:<name>` vs 内部 id）。
- 尝试 `wecom-app:user:<id>` / `user:<id>` / `<id>` 三种格式。
- 多账号时追加 `@accountId`，例如：`user:alice@default`。
- 检查附件路径是否存在（文件是否被清理）。

### 4.3 `Account not configured for active sending`
- 缺少 `corpId` / `corpSecret` / `agentId` 任一项时会出现。
- 这类错误先修配置，不是 target 问题。

### 4.4 图片发不出去但文件存在
- 核对文件大小是否超出渠道限制。
- 若是 inbound 归档文件：确保 OpenClaw 进程对该文件可读。

---

## 5) 入站媒体保留策略（产品级默认）

- `inboundMedia.keepDays`：默认 7 天（延迟清理，不会“回复后立刻删”）
- `inboundMedia.dir`：可自定义归档目录
- `inboundMedia.maxBytes`：单个媒体大小限制（默认 10MB）

要修改：编辑 `openclaw.json` 的 `channels.wecom-app.inboundMedia`。

可复制模板：
```json
{
  "channels": {
    "wecom-app": {
      "inboundMedia": {
        "enabled": true,
        "keepDays": 7,
        "maxBytes": 10485760
      }
    }
  }
}
```

---

## 6) 文本与流式发送行为（实现对齐）

- wecom-app webhook 会先返回 stream 占位，再在后台推送最终内容。
- 推送前会做 Markdown 降级（`stripMarkdown`）。
- 企业微信单条文本限制 2048 bytes，超出会自动分段发送。

---

## 7) MCP OCR（识别图片文字）

当用户说“记得调用 mcp 识别图片”，用 `mcporter` 调用：
- `zai-mcp-server.extract_text_from_screenshot(image_source: <saved-path>, prompt: <说明>)`

前提：必须拿到**真实存在的文件路径**（建议用 inbound saved 路径）。

---

## 参考
- 详细示例与常用模板见：`references/wecom-app-examples.md`

