OpenPigeon 是一个 TypeScript SDK,通过逆向工程 GamePigeon 的 iMessage 传输协议,让开发者能以纯数据方式读写 11 款游戏的棋盘状态并以 URL 发送操作。

# OpenPigeon
**只需几行 TypeScript,即可将 GamePigeon iMessage 游戏集成到你的应用中。**
以纯数据形式读取棋盘。以 URL 形式发送操作。通过 Linq 和 Photon 工作 —— 无需 AI,无需 SQLite,无需 Mac。
[](LICENSE)
[](https://www.npmjs.com/package/openpigeon)
[](https://nodejs.org)
[](https://www.typescriptlang.org)
[](package.json)
[](docs/GAMES.md)
[](test/)
[](#adding-a-game)

8-Ball — sent via API, rendered natively by GamePigeon
|

Cup Pong — the recipient's own GamePigeon draws the board
|
Real device. Invites are sent through OpenPigeon and render natively — no attached image (Linq ships liveLayoutInfo).
## 原因
GamePigeon 是一个黑盒:游戏以加密的 `imessage_app` 气泡形式搭载 iMessage。
OpenPigeon 对该传输格式进行了逆向工程,让你的机器人能够**理解**游戏
并对其**做出响应** —— 将不透明的气泡转换为普通对象并还原:
```
import * as op from "openpigeon";
const m = op.read(url); // decode any GamePigeon URL
m.game; // "connect4"
m.turn; // 2 (whose turn is next)
m.state; // { grid: [...], move: {...} } ← the board, as data
const reply = m.reply({ botId: "MY-ID", state: m.state }); // your move → a URL
op.invite("sea_battle"); // start a fresh game → { url, id }
```
无需密码、无需 percent-encoding、无需 MSMessage 管道 —— SDK 会处理这一切。
```
npm install openpigeon
```
## 接收 —— 来自 webhook/stream,绝非 SQLite
| Linq (webhook) | Photon (stream) |
|
```
import express from "express";
import * as op from "openpigeon";
const linq = new op.Linq({
token: process.env.LINQ_TOKEN!,
fromNumber: "+1...",
});
app.post("/webhook", async (req, res) => {
res.sendStatus(200);
const inb = op.fromWebhook(req.body);
if (!inb || inb.move.isInvite) return;
const m = inb.move;
// your move logic here
const url = m.reply({ botId: BOT, state: m.state });
await linq.send(url, {
to: inb.replyTo!, chatId: inb.chatId ?? undefined,
});
});
```
|
```
import { customizedMiniApp } from "spectrum-ts/providers/imessage";
import * as op from "openpigeon";
for await (const [space, message] of app.messages) {
const inb = op.fromPhotonMessage(message);
if (!inb || inb.move.isInvite) continue;
const m = inb.move;
// your move logic here
const url = m.reply({
botId: BOT, state: m.state, https: true, // https carrier
});
await space.send(
customizedMiniApp(op.Photon.balloon(url)),
);
}
```
|
完整示例:[`examples/linq-express-bot.ts`](examples/linq-express-bot.ts) · [`examples/photon-bot.ts`](examples/photon-bot.ts) · [`examples/quickstart.ts`](examples/quickstart.ts)
## 查看棋盘
```
import { render } from "openpigeon/games/connect4";
console.log(render(op.read(url).state));
// . . O X X X O
// . . X O . . .
// . . . . . . .
```
## 支持的游戏
所有 11 款游戏都能解码为有文档记录的 `state`,并且在与真实捕获的消息对比时实现**字节精确**的往返转换。
| 游戏 | `game=` token(s) | state 来源 | 你可以读取的内容 |
|---|---|---|---|
| 🎱 **pool** (8球) | `pool` · `pool2` · `pool3` | replay | 击球(瞄准/力度/旋转)+ 每个球 `{x,y,rot,alpha,num,v…,pocketed}` |
| 🎯 **darts** | `darts` | replay | 倒计时分数 + 投掷 `{x,y,points,segment,mult,ring}` |
| ⛳ **mini_golf** | `golf` | replay | 逐洞击球记录 `{power,angle}`(+ 对手的) |
| 🔴 **connect4** | `connect` | replay | 7×6 棋盘网格 + `{col,row,player}` + `render()` |
| 🏹 **archery** | `archery` | replay | 回合状态 + 投掷 `{player,x,y,z}` |
| 🏀 **basketball** | `basketball` | replay ×4 | 跨回合投篮 `{t,arc,spin,made}` |
| 🥤 **cup_pong** | `beer` | replay | 双方剩余杯子 + 投掷记录 |
| 🚢 **sea_battle** | `sea` | envelope+replay | 尺寸、战舰 `{pos,hits,rot}`、射击网格、上一次射击 |
| 🔤 **word_hunt** | `hunt` | envelope | 字母网格、每位玩家的分数 + 找到的单词 |
| 🔀 **anagrams** | `anagrams` | envelope | 字母池、每位玩家的分数 + 找到的单词 |
| 🧩 **wordbites** | `wordbites` | envelope | 字母块、每位玩家的分数 + 找到的单词 |
各游戏的 `state` 结构 → [docs/GAMES.md](docs/GAMES.md) · 添加游戏只需一个文件 → [`src/games/pool.ts`](src/games/pool.ts)。
## 支持的服务提供商
| 提供商 | 发送 | 接收 | 说明 |
|---|:--:|:--:|---|
| **Linq** | ✅ 气泡 + 文本 | ✅ `message.received` webhook | 端到端纯 TypeScript;**原生渲染** (`liveLayoutInfo`) |
| **Photon** | ✅ 通过 `spectrum-ts` | ✅ gRPC 消息流 | 单进程,无需 bridge;操作通过 https 载体传输 |
## 🚫 OpenPigeon *不*做什么
## 工作原理
完整的逆向工程协议 —— 气泡身份标识、基于长度种子的 anagram 密码、state envelope、回合规则 —— 详见 [docs/PROTOCOL.md](docs/PROTOCOL.md)。
## 添加游戏
在 [`src/games/`](src/games) 中添加一个文件。复制 [`pool.ts`](src/games/pool.ts):导出
`parse(replay, env) → state`、`build(state, env) → replay` 和 `register({...})`。
## 开发
```
npm test # codecs (byte-exact vs real captures) + receive paths
node examples/quickstart.ts
npm run build # tsc → dist/
```
Node ≥ 22(通过 type-stripping 直接运行 `.ts`;使用 tsc 5.7+ 构建)。
## 许可证
**MIT** — © 2026 **time-attack**。详情见 [LICENSE](LICENSE)。
你可以自由在自己项目中使用、修改和重新分发 OpenPigeon,
**包括商业用途 —— 但你必须保留版权和许可声明**
(即,必须标明原作者)。MIT 许可证要求上述声明必须
包含在软件的所有副本或大部分内容中。
“GamePigeon” 是其所有者的商标;本项目不附属于他们,也未获得他们的认可。