# wechaty-puppet-claw-wechat

[![NPM Version](https://img.shields.io/npm/v/wechaty-puppet-claw-wechat)](https://www.npmjs.com/package/wechaty-puppet-claw-wechat)
[![License](https://img.shields.io/npm/l/wechaty-puppet-claw-wechat)](LICENSE)

[Wechaty](https://github.com/wechaty/wechaty) Puppet 实现，通过 [WeChat iLink Bot API](https://ilinkai.weixin.qq.com) 接入微信，支持文本、图片、语音、视频、文件的收发。

## 特性

- ✅ 文本消息收发（自动分块，最大 2000 字）
- ✅ 图片、语音、视频、文件收发（AES-128-ECB CDN 加密）
- ✅ 二维码扫码登录（首次运行打印登录链接）
- ✅ 自动会话续期（token 过期后重新登录）
- ✅ 消息去重（5 分钟 TTL）
- ✅ 联系人缓存持久化（FlashStore）
- ❌ 群组消息（iLink API 仅支持 1:1 私信）

## 安装

```bash
npm install wechaty-puppet-claw-wechat @juzi/wechaty
```

## 快速开始

```typescript
import { WechatyBuilder } from '@juzi/wechaty'
import { PuppetClawWechat } from 'wechaty-puppet-claw-wechat'

const bot = WechatyBuilder.build({
  name: 'my-bot',
  puppet: new PuppetClawWechat(),
})

bot.on('login', (user) => {
  console.log(`登录成功: ${user}`)
})

bot.on('message', async (msg) => {
  if (msg.self()) return
  if (msg.type() === bot.Message.Type.Text) {
    await msg.say(`Echo: ${msg.text()}`)
  }
})

await bot.start()
```

**首次运行**会在终端打印微信扫码登录链接，扫码确认后凭证保存至当前目录的 `.weixin-bot-credentials.json`，后续自动复用。

## 开发

```bash
# 克隆后安装依赖
npm install

# 构建 TypeScript → dist/
npm run dist

# 监听模式
npm run dev

# 类型检查
npx tsc --noEmit

# Lint
npm run lint

# 运行示例 echo bot
npm run example
```

## 凭证与缓存

| 路径 | 说明 |
|------|------|
| `./.weixin-bot-credentials.json` | 登录凭证（运行目录，mode 0600） |
| `~/.wechaty/puppet-claw-wechat-cache/<accountId>/` | 联系人缓存（FlashStore） |

> 如果进程异常退出导致缓存锁未释放：
> ```bash
> rm -f ~/.wechaty/puppet-claw-wechat-cache/*/LOCK
> ```

## 架构

```
WeChat iLink Server (HTTPS long-poll)
  └─ WeixinClientManager        # 单例：QR 登录、长轮询、context_token 缓存
       └─ PuppetClawWechat      # Puppet 核心：消息转换、事件分发
            ├─ CacheManager     # FlashStore（联系人）+ LRU（消息，max 1000）
            └─ MessageDedupeManager  # 消息去重（5 分钟 TTL）
```

### 关键约束

- **context_token**：所有发送消息都需要 `context_token`，由用户首条消息携带并缓存。机器人无法主动发起会话。
- **媒体加密**：CDN 媒体使用 AES-128-ECB + PKCS7 加密，`aes_key` 有两种编码格式（base64 of raw 16 bytes / base64 of hex string）。
- **仅私信**：iLink API 不支持群组，Room 相关方法均返回空或抛出 UnsupportedError。

## License

MIT
