# @ericzhaozzz/wechat-channel

> 一个 Pi Agent 扩展，把你的私人微信变成 AI 助手的遥控器——手机上和 pi 对话，微信里
> 实时收到回答、进度和文件，基于腾讯 iLink Bot API。

[English](README.md) | **中文**

## 功能特性

- **微信里对话，得到连贯回答** — 消息转发给 pi 的 Agent，回复保留 Markdown；连续短消息
  会合并成一次带编号（`[1]`、`[2]`…）的调用，长回答分块送达、不乱序。
- **看得见的处理过程** — "正在输入"贯穿整轮，进度短语（`⏳ 正在执行：npm test`）显示
  Agent 在做什么；消息排队、掉线补处理、回答未能送达都会明确告知。
- **双向富媒体** — 图片、文件、语音、视频自动下载，收到的图片直接以图像内容进入模型
  上下文；Agent 也能在回答完成前主动把截图、图表、生成的文件推给你。
- **不在电脑前也能控制** — `#status`、`#queue`、`#clear`、`#cancel`、`#help` 直接发微信
  即可。
- **一人一会话，安全** — 只有被绑定的所有者可用，其他人被静默丢弃；频道同时只归一个
  pi 会话所有。
- **经得起折腾** — 自动重连、自动重新登录，掉线消息补处理，只排队不丢弃，状态跨重启、
  崩溃存活。

## 安装

```bash
# 通过 npm 安装（推荐）— 自带编译好的 dist/，无需其他步骤
pi install npm:@ericzhaozzz/wechat-channel
pi

# 或从源码构建后复制到 pi 扩展目录 — pi 加载的是编译产物
# （pi.extensions 指向 ./dist/index.js）
npm install && npm run build          # 在 wechat-channel 源码目录内执行
cd .. && cp -r wechat-channel ~/.pi/agent/extensions/
cd ~/.pi/agent/extensions/wechat-channel
npm install --omit=dev                # 只需要微信 SDK，运行时不再需要 tsx

# 重启 pi（或执行 /reload）
pi
```

## 快速开始

1. 启动 pi 并执行 `/wechat login`
2. 用手机微信扫描二维码，并在手机上确认登录
3. **用你自己的微信向机器人发送一条消息** — 第一个发送者被绑定为所有者
4. 你的消息会转发给 pi；回复会自动返回给你

## 使用方法

### 微信侧指令

不在电脑前时，所有者可以直接发这些指令（前缀由 `channelCommandPrefix` 配置，默认 `#`，
设为空字符串即禁用）：

| 指令 | 说明 |
|------|------|
| `#cancel` / `#停` | 中止当前正在处理的一轮 |
| `#status` / `#状态` | 连接状态、是否正忙、排队数、上下文占用 |
| `#queue` / `#队列` | 排队中的消息数 |
| `#clear` / `#清空` | 清空排队（不影响正在处理的那一轮） |
| `#help` / `#帮助` | 显示以上说明 |

只有**完全匹配**的指令名才算指令——拼错的 `#cancle` 会照常发给 Agent。

### 终端命令

`/wechat login`、`/wechat status`、`/wechat config`、`/wechat history`、
`/wechat log --tail`、`/wechat owner`、`/wechat send` / `send-media`、`/wechat start` /
`stop`、`/wechat qr`、`/wechat cancel`、`/wechat clean`、`/wechat prefix` 覆盖登录、状态、
归属、发送与媒体（子命令自动补全，输入 `/wechat st` 后按 Tab）。pi 底部状态栏常驻一行
频道状态（`wechat: ✓ busy q2`）；掉线、回答被丢弃等会以 pi 通知的形式出现。

## 配置

所有可调项都在 `~/.pi/wechat/config.json`，加载时读取一次，改动需 `/reload` 后生效；
没有该文件时行为与默认一致：

```json
{
  "ownerUserId": "wxid_xxx@im.wechat",
  "aiPrefix": "",
  "autoLogin": true,
  "preserveMarkdown": true,
  "mergeDebounceMs": 1500,
  "maxMergeMessages": 10,
  "rateLimitMax": 20,
  "rateLimitWindowMs": 60000,
  "agentTimeoutMs": 3600000,
  "maxDeliveryAttempts": 3,
  "deliveryParkTimeoutMs": 600000,
  "progressUpdates": true,
  "progressIntervalMs": 45000,
  "notifyFailures": true,
  "channelCommandPrefix": "#",
  "inlineImages": true,
  "maxInlineImageBytes": 5242880,
  "steerWhileBusy": false
}
```

常用项：`channelCommandPrefix`（微信侧指令前缀）、`ownerUserId`（预先固定所有者）、
`mergeDebounceMs` / `maxMergeMessages`（短消息合并）、`steerWhileBusy`（处理中收到的新
消息插入当前轮次而不是排队）、`inlineImages`、`progressUpdates`、`notifyFailures`。
读不出来或键名写错会保留默认值，并由 `/wechat config` 列出。

## 安全与归属

任何能接触到机器人的人理论上都可以通过 pi 读取文件或执行命令，因此非所有者的消息会被
**静默丢弃**。所有者首次使用即绑定并持久化到 `~/.pi/wechat/owner.json`，也可通过
`ownerUserId` 预先固定；用 `/wechat owner` 查看或修改。

一个 iLink 账号只支持一个长轮询者，因此频道同时只归一个 pi 会话所有。不持有频道的会话
完全不启动 worker，并在持有者关闭后约 30 秒内自动接管；`/wechat start --takeover` 立刻
抢占。被 SIGKILL 的 pi 会在租约过期（约 90 秒）后释放频道。

## 与 Hermes Agent 对比

| 特性 | Hermes Agent | Pi WeChat 扩展 |
|---------|:-----------:|:------------------:|
| 长轮询 | ✅ asyncio Gateway | ✅ 子进程 |
| QR 登录 | ✅ 内置 | ✅ 内置 |
| Markdown 保留 | ✅ | ✅ |
| 发送者授权 | ✅ 按用户策略 | ✅ 单一所有者（单白名单） |
| 语音转文字/引用回复上下文 | ✅ | ✅ 透传给 Agent |
| 接收媒体（自动下载） | ✅ 完整 | ✅ 图片/文件/视频/语音（仅首个附件） |
| 发送媒体 | ✅ 完整 | ✅ 通过 `/wechat send-media`（路径/URL/Base64） |
| 多用户会话 | ✅ 按用户隔离 | ⚠️ 单会话桥接 |
| 7×24 守护进程 | ✅ systemd/launchd | ⚠️ 需要 pi 在前台运行 |
| 正在输入指示 | ✅ | ✅ |
| 消息去重 | ✅ | ✅ |
| Context Token 持久化 | ✅ | ✅ |
| 智能分块 | ✅ | ✅ |
| SSRF 防护 | ✅ | ❌ 未来计划 |
| 投递账本 | ✅ | ✅ 有界重试 + 持久化 |
