---
slug: "pi-qq-integration"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/Star-233/pi-qq-integration@master/README.md"
repo: "https://github.com/Star-233/pi-qq-integration"
source_file: "README.md"
branch: "master"
---
# pi-qq-integration — pi 扩展

在 **QQ 中操控 pi**。安装此扩展后，pi 启动时会自动加载扩展并**默认自动连接** QQ Bot（可在配置中关闭）。连接后即可通过 QQ 向 pi 发消息、查看 session 列表、浏览历史对话；也可随时用 `/qq-connect`、`/qq-disconnect` 手动控制连接。

## 安装

```bash
pi install npm:pi-qq-integration
```

---

## 快速开始

### 1. 注册 QQ Bot

在 [QQ 开放平台](https://q.qq.com) 创建一个机器人应用，获取 **AppID** 和 **AppSecret**。

### 2. 创建配置文件

```json
# /home/nullsky/.pi/agent/qq-integration-config.json
{
  "appId": "你的 AppID",
  "appSecret": "你的 AppSecret"
}
```

### 3. 启动 pi

```bash
pi
```

扩展加载后会**自动连接** QQ Bot（默认行为）。如需关闭自动连接，在配置文件加 `"autoConnect": false` 后重启 pi，再用 `/qq-connect` 手动连接；断开用 `/qq-disconnect`。

现在在 QQ 中给机器人发消息，就能和 pi 对话了。

---

## 配置项（qq-integration-config.json）

配置文件路径：`/home/nullsky/.pi/agent/qq-integration-config.json`（位于 pi 的 agent 数据目录，与扩展代码目录无关）。以下**所有**可配置项均在此文件中：

### 顶层字段

| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `appId` | string | ✅ | — | QQ 开放平台机器人应用的 AppID |
| `appSecret` | string | ✅ | — | QQ 开放平台机器人应用的 AppSecret（**敏感，勿提交 git**） |
| `instanceId` | string | ❌ | `hostname-pid` | 多实例下本实例的唯一 ID，用于注册表区分各实例 |
| `role` | `"auto" \| "leader" \| "follower"` | ❌ | `"auto"` | 多实例角色：`auto` 由文件锁自动选举；`leader` 强制本实例持有 QQ 连接；`follower` 强制本实例经 IPC 接入其他 leader |
| `autoConnect` | boolean | ❌ | `true` | pi 启动时是否自动连接 QQ Bot；设为 `false` 则需手动 `/qq-connect` |

### `settings` 字段（转发设置）

| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `forwardDesktopMessages` | boolean | `false` | 桌面端（pi 终端）输入的消息是否转发到 QQ |
| `forwardToolCalls` | boolean | `false` | 工具调用是否转发到 QQ（与 `lastMessageOnly` 互斥，开启其一会关闭另一个） |
| `lastMessageOnly` | boolean | `false` | 只转发整次 agent 运行的**最后一条** assistant 回复（而非每条逐步回复）；与 `forwardToolCalls` 互斥 |
| `defaultSession` | object \| undefined | `undefined` | 默认 QQ 转发目标（QBSession），桌面端/工具转发未明确来源会话时使用；由 `/qq-target` 或 QQ 内 `#target` 写入 |

> `settings` 内的字段既可在配置文件里静态写死，也可在 QQ 内用 `#settings` 命令动态调整并持久化（见下文「QQ 命令」）。注意 `#settings` 命令对前两个开关使用了简写别名：`forwardMessages` 对应配置键 `forwardDesktopMessages`，`forwardTools` 对应 `forwardToolCalls`。

### 完整示例

```json
{
  "appId": "你的 AppID",
  "appSecret": "你的 AppSecret",
  "autoConnect": true,
  "role": "auto",
  "settings": {
    "forwardDesktopMessages": false,
    "forwardToolCalls": false,
    "lastMessageOnly": false
  }
}
```

### 多实例

同时运行多个 pi 实例时，用文件锁选举唯一的 leader 持有 QQ 连接；其余实例作为 follower 经本地 Unix socket 把 QQ 收发委托给 leader。`autoConnect` 对**所有**实例生效——启动即各自参与选举，最终只有抢到锁的 leader 真正连接 QQ，其余 follower 经 IPC 接入，不会冲突。相关配置：

- `role: "auto"`（默认）：谁先抢到锁谁是 leader，其余自动成为 follower。
- `role: "leader"` / `"follower"`：强制角色（例如固定某台机器做 leader）。
- `instanceId`：一般无需修改；仅在需要固定 ID（如日志/注册表排查）时设置。

---

## 架构

```
QQ 用户
  │
  ├─ 发消息 → QQ Bot 服务器 → WebSocket
  │                                │
  │                     ┌──────────▼──────────┐
  │                     │  pi-qq-integration 扩展  │
  │                     │                      │
  │                     │  ws-client.ts        │
  │                     │    ↕ WebSocket       │
  │                     │  command-handler.ts  │
  │                     │    ↕ /cmd 解析       │
  │                     │  index.ts            │
  │                     │    ↕ sendUserMessage │
  │                     └──────────┬──────────┘
  │                                │
  │                     ┌──────────▼──────────┐
  │                     │      pi 引擎         │
  │                     │   处理 prompt 并回复  │
  │                     └──────────┬──────────┘
  │                                │
  └─────── REST API ←──── 回复内容
```

两个独立通道：
- **WebSocket** — 接收 QQ 消息（长连接，带心跳和断线重连）
- **REST API** — 发送回复到 QQ（`POST /v2/users/{openid}/messages`）

---

## pi Slash 命令

在 pi 终端中使用的命令：

| 命令 | 说明 |
|------|------|
| `/qq-connect` | 手动连接 QQ Bot |
| `/qq-disconnect` | 断开 QQ Bot 连接 |
| `/qq-status` | 查看连接状态概览（锁、WebSocket、Token） |
| `/qq-diagnose` | 查看详细诊断信息（session_id、心跳、重连次数等） |
| `/qq-logs` | 查看最近 30 条日志 |
| `/qq-logs-path` | 查看日志文件路径 |
| `/qq-target` | 设置/查看默认 QQ 转发目标 |

### `/qq-status` 示例

```
🔒 锁: 持有中
🟢 WebSocket: 已连接
⏱ 已运行: 2分35秒
✅ Token: 有效
```

### `/qq-diagnose` 示例

```
🔒 锁状态
  - 持有锁: ✅ 是
  - 锁文件 PID: 12345
  - 本进程 PID: 12345

🌐 WebSocket 连接
  - 状态: 已连接
  - Session ID: xxxx
  - 重连次数: 0

🔑 Access Token
  - 过期时间: 2026-07-21 17:30
  - 剩余时间: 1时58分

⚙️ 配置
  - AppID: 你的 AppID
```

---

## QQ 命令

在 QQ 中给机器人发送的消息，如果不以 `/` 开头，会直接作为 prompt 发给 pi。

| 命令 | 说明 |
|------|------|
| `#help` | 显示帮助 |
| `#sessions` | 列出所有 pi session |
| `#resume <序号/名称>` | 切换到指定 session（在终端中操作） |
| `#new` | 创建新 session（在终端中操作） |
| `#history [N]` | 查看当前 session 最近 N 条消息（默认 5） |
| `#clear` | 清空当前 session（在终端中操作） |
| `#target` | 将当前 QQ 会话设为默认转发目标 |
| `#settings lastMessageOnly on|off` | 只转发整次回复的最后一条 assistant 回复 |

### 示例

```
你: #sessions
Bot: 📋 Pi Sessions
     1. **extensions 07:03** — 2小时前
     2. **learn 05:29** — 2小时前
     ...

你: #history 5
Bot: 📝 最近消息 (extensions 07:03)
     👤 今天天气怎么样？
     🤖 今天天气晴朗...
```

### 桌面端消息转发

开启桌面端转发（`#settings forwardMessages on`）后，桌面端输入的消息会同步转发到 QQ。扩展需要知道“发到哪个 QQ 会话”，目标按以下优先级选择：

1. 最近一条 QQ 消息来源的会话
2. 手动设置的默认目标（`/qq-target` 或 QQ 里的 `#target`）

因此，如果你**先**在桌面端发消息、**后**才收到 QQ 消息，需要预先指定默认目标：

```bash
# 在 pi 终端设置默认目标（以私聊为例）
/qq-target c2c <用户openid>

# 或设置群聊
/qq-target group <群openid>

# 查看当前默认目标
/qq-target

# 清除
/qq-target clear
```

也可以在 QQ 里发送 `#target`，把当前会话设为默认目标。

### `#settings`

转发设置在 `/reload` 后永久保存：

```
你: #settings
Bot: ⚙️ QQ Bot 设置
     | 选项 | 状态 | 说明 |
     | forwardMessages | ❌ 关 | 桌面端消息转发到 QQ |
     | forwardTools | ✅ 开 | 工具调用转发到 QQ |
     | lastMessageOnly | ❌ 关 | 只转发整次回复的最后一条 assistant 回复 |

你: #settings forwardTools on
Bot: ✅ 工具调用转发已开启

你: #settings forwardMessages off
Bot: ❌ 桌面消息转发已关闭

你: #settings lastMessageOnly on
Bot: ✅ 只转发最后一条回复已开启，assistant 整次运行仅发送一条最终回复；forwardTools 已自动关闭。
```

---

## 文件结构

```
pi-qq-integration/
├── index.ts              # 入口：初始化、事件注册、slash 命令
├── config.ts             # 读取 qq-integration-config.json
├── auth.ts               # QQ Bot Access Token 获取 + 自动刷新
├── lock.ts               # 文件锁（多实例防冲突）
├── ws-client.ts          # WebSocket 客户端（连接、鉴权、心跳、重连）
├── api-client.ts         # REST API 客户端（发送消息）
├── session-manager.ts    # Pi session 浏览
├── command-handler.ts    # QQ 消息中的 /cmd 命令解析
├── types.ts              # 类型定义
├── package.json          # 依赖（ws）
└── README.md
```

---

## 多实例处理

如果同时启动多个 pi 实例，扩展使用**文件锁**机制确保只有一个实例连接 QQ Bot：

```
~/.pi/agent/qq-integration.lock
  ├── PID: 持有者进程 ID
  ├── 获取时间
  └── 心跳时间（每 30 秒更新）
```

- 第一个启动的 pi 获取锁并连接 Bot
- 后续实例检测到锁被持有，跳过连接
- 持有锁的实例崩溃后，锁文件中的 PID 失效，后续实例自动接管

---

## 日志

所有调试日志写入文件：

```
/home/nullsky/.pi/agent/qq-integration.log
```

在 pi 中可用 `/qq-logs` 查看最近 30 条，用 `/qq-logs-path` 查看文件路径。
日志文件达到 5MB 会自动截断。

## 注意事项

1. **Token 安全** — `access_token` 有效期 2 小时，扩展会自动提前刷新
2. **消息频率** — QQ Bot 主动消息每月每用户/群限 4 条，被动回复较宽松
3. **Session 管理** — session 切换（`/new`、`/resume`）需在 pi 终端中操作
4. **`#settings` 持久化** — 设置保存在 `qq-integration-config.json` 中，`/reload` 不丢失
4. **群聊消息** — 仅接收 @机器人的群消息（`GROUP_AT_MESSAGE_CREATE`）
5. **配置文件** — 含 AppSecret，注意不要提交到 git

---

## 开发

```bash
cd ~/.pi/agent/extensions/qq-integration
npm install          # 安装依赖
# 编辑代码后 /reload 即可热重载
```
