# @tencent-connect/qqbot-nodejs
> **English** (this file) · [简体中文](./README.zh-CN.md)
[](https://www.npmjs.com/package/@tencent-connect/qqbot-nodejs)
[](https://github.com/tencent-connect/qqbot-nodejs/actions/workflows/ci.yml)
[](./LICENSE)
[](https://nodejs.org)
Tencent QQ Open Platform Node.js SDK. Provides every protocol-layer capability
needed to integrate with the QQ Bot Open Platform: HTTP REST, dual WebSocket /
Webhook transport, a Koa-style middleware pipeline, messaging, media upload
(including chunked upload for large files), and C2C streaming messages
(`stream_messages`).
## Requirements
- Node.js **>= 20** (uses global `fetch` / `AbortController`)
- Pure ESM (`"type": "module"`); CJS consumers should use dynamic `import()`
## Install
```bash
pnpm add @tencent-connect/qqbot-nodejs
# or
npm install @tencent-connect/qqbot-nodejs
```
## Quick Start
```ts
import { QQBot } from "@tencent-connect/qqbot-nodejs";
const bot = new QQBot({
appId: process.env.QQBOT_APP_ID!,
appSecret: process.env.QQBOT_APP_SECRET!,
logger: console,
});
bot.on("message", async (ctx, msg) => {
// ctx — Koa-style MiddlewareContext carrying data injected by middleware
await bot.sendText(msg.replyTarget, `Echo: ${msg.content}`);
});
await bot.start();
```
See the complete examples under [examples/](./examples/):
| Example | Description |
|------|------|
| [playground](./examples/playground/) | Core capabilities demo (text, streaming, media, commands) |
| [middleware](./examples/middleware/) | Full 13-layer Koa-style middleware pipeline |
| [webhook](./examples/webhook/) | Webhook (HTTP callback) transport |
| [send-plain-100](./examples/send-plain-100/) | Plain-text send baseline |
| [send-streaming-100](./examples/send-streaming-100/) | Streaming-message send baseline |
> Examples read `QQBOT_APP_ID` / `QQBOT_APP_SECRET` from `process.env` **only
> for demo convenience**. The SDK itself never reads environment variables —
> credentials are always injected via `new QQBot({ appId, appSecret })`.
## Features
### 1. Dual transport (WebSocket / Webhook)
```ts
// WebSocket (default) — long-lived connection + heartbeat + RESUME
const bot = new QQBot({ appId, appSecret });
// Webhook — HTTP callback, suited to Serverless / horizontal scaling
const bot = new QQBot({
appId, appSecret,
transport: "webhook",
webhook: { port: 8080, path: "/callback" },
});
```
Middleware, event listeners and send APIs are identical in both modes.
### 2. Koa-style middleware
The SDK uses an onion-model middleware chain; `bot.on("message")` is the
innermost downstream:
```ts
import { errorHandler, messageFilter, contentSanitizer, mentionGate } from "@tencent-connect/qqbot-nodejs";
bot.use(errorHandler());
bot.use(messageFilter({ skipSelfEcho: true, dedup: { windowMs: 5000 } }));
bot.use(contentSanitizer({ stripBotMention: true }));
bot.use(mentionGate({ requireMentionInGroup: true }));
// Custom middleware
bot.use(async (ctx, next) => {
const start = Date.now();
await next();
ctx.log.debug?.(`elapsed: ${Date.now() - start}ms`);
});
```
Built-in middleware:
| Middleware | Purpose |
|--------|------|
| `errorHandler` | Unified error capture + friendly reply |
| `messageFilter` | Bot echo filtering + message dedup |
| `rateLimiter` | Three-tier rate limiting (sender / group / global) |
| `concurrencyGuard` | Serial processing per user / group (avoids stream races) |
| `accessPolicy` | Allow / block lists |
| `contentSanitizer` | Strip @markers / face tags / whitespace |
| `mentionGate` | Group @bot gating |
| `quoteRef` | Message index recording + quoted-message resolution |
| `historyBuffer` | Group history buffer |
| `envelopeFormatter` | Assemble LLM prompt context (XML tags) |
| `typingIndicator` | Automatic C2C typing |
| `slashCommand` | /cmd command framework |
### 3. Text messages
```ts
await bot.sendText(target, "hello");
```
### 4. Files / images / voice
`sendImage` / `sendVoice` / `sendVideo` / `sendFile` are convenience wrappers
over `sendMedia`. Sources ≥ 5 MB switch to chunked upload automatically.
```ts
await bot.sendImage(target, { localPath: "/tmp/cat.jpg" });
await bot.sendFile(
target,
{ buffer: bigBuffer },
{
fileName: "report.pdf",
onProgress: (uploaded, total) => console.log(`${uploaded}/${total}`),
},
);
```
### 5. C2C streaming messages
```ts
const stream = bot.openStream({ target });
for (const partial of generator) {
await stream.update(partial);
}
await stream.complete();
```
QQ Open Platform constraint: `stream_messages` is available for C2C (private chat) only.
### 6. Events
```ts
bot.on("ready", () => console.log("connected"));
bot.on("resumed", () => console.log("reconnected"));
bot.on("disconnected", ({ code, willReconnect }) => console.log("closed", code, willReconnect));
bot.on("error", (err) => console.error(err)); // recoverable failures; never ends start()
bot.on("message", (ctx, msg) => { /* C2C / Group / Guild / DM */ });
bot.on("interaction", (ctx, event) => { /* button click etc. */ });
```
### 7. Lifecycle
WebSocket mode reconnects forever by default. `start()` resolves on `stop()` /
abort, and rejects — after releasing the connection and token refresher — only
when it cannot recover:
| Rejection | Cause | Handling |
|---|---|---|
| `GatewayError` `GATEWAY_FATAL_CLOSE` | 4914 offline / sandbox-only, 4915 banned | Needs operator action; don't restart |
| `GatewayError` `GATEWAY_RETRY_EXHAUSTED` | Only with a finite `reconnect.maxAttempts` | Restart with backoff |
| `Error` | Startup token fetch failed (default `tokenPrefetch: "sync"`) | Check credentials / network |
```ts
import { GatewayErrorCode, QQBot } from "@tencent-connect/qqbot-nodejs";
// Set a limit only when an outer supervisor owns process-level restarts
const bot = new QQBot({ appId, appSecret, reconnect: { maxAttempts: 10 } });
try {
await bot.start(signal);
} catch (err) {
if ((err as { code?: string }).code === GatewayErrorCode.FATAL_CLOSE) markBlocked(err);
else scheduleRestart(err);
}
```
`bot.stop(); bot.start();` restarts in place. See [USAGE.md §7](./USAGE.md#7-生命周期管理)
for close-code handling.
### 8. Protocol-level access
```ts
import {
ApiClient,
TokenManager,
GatewayConnection,
withRetry,
} from "@tencent-connect/qqbot-nodejs/protocol";
```
## Documentation
- **[USAGE.md](./USAGE.md)** — complete usage guide (currently in Chinese;
contributions to translate are very welcome).
## Optional dependencies
These `peerDependencies` are declared as optional. Install them only if you
need the feature:
| Package | Enables |
| ----------------- | ------------------------------------------ |
| `silk-wasm` | Sending voice messages (SILK encoding) |
| `mpg123-decoder` | Decoding MP3 audio to PCM before encoding |
## Contributing
Bug reports, feature proposals and pull requests are welcome. Please read:
- [CONTRIBUTING.md](./CONTRIBUTING.md) — dev workflow & commit convention
- [SECURITY.md](./SECURITY.md) — how to report vulnerabilities privately
## Module structure
```
src/
├── QQBot.ts ← High-level facade
├── streaming.ts ← C2C streaming-message controller
├── index.ts ← Public API
├── middleware/ ← Koa-style middleware
│ ├── types.ts Middleware types + onion runner
│ ├── error-handler.ts Unified error capture
│ ├── message-filter.ts Echo filtering + dedup
│ ├── rate-limiter.ts Three-tier rate limiting
│ ├── concurrency-guard.ts Serial processing per user / group
│ ├── access-policy.ts Allow / block lists
│ ├── content-sanitizer.ts Content sanitizing
│ ├── mention-gate.ts @bot gating
│ ├── quote-ref.ts Quote resolution + message index
│ ├── history-buffer.ts Group history buffer
│ ├── envelope-formatter.ts LLM prompt assembly
│ ├── typing-indicator.ts Typing state
│ └── slash-command.ts Command framework
└── protocol/ ← Protocol layer
├── api/
│ ├── api-client.ts HTTP client
│ ├── token.ts access_token management
│ ├── messages.ts Message sending
│ ├── media.ts Small-file upload
│ ├── media-chunked.ts Chunked large-file upload
│ ├── retry.ts Retry engine
│ └── routes.ts Route templates
├── gateway/
│ ├── constants.ts opcode / intent / close code
│ ├── codec.ts Message decoding
│ ├── reconnect.ts Reconnect state machine + reconnect policy
│ ├── errors.ts GatewayError (terminal start() errors)
│ ├── event-dispatcher.ts Event → InboundMessage
│ └── gateway-connection.ts WebSocket lifecycle
├── transport/
│ ├── types.ts EventTransport interface
│ ├── webhook.ts Webhook transport
│ ├── webhook-verify.ts Ed25519 signature verification
│ └── webhook-server-node.ts Built-in node:http adapter
├── utils/
│ ├── format.ts Formatting helpers
│ ├── file-utils.ts File helpers
│ └── upload-cache.ts file_info TTL cache
├── types.ts All public types + errors
└── index.ts protocol sub-entry
```
## Contributors
Thanks to everyone who has contributed to this project!
Contributor avatars are rendered by [contrib.rocks](https://contrib.rocks).
## License
[MIT](./LICENSE) © Tencent