npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@trudbot/qqbot-api

v1.0.0

Published

一个极简 TypeScript QQ Bot 私聊工具类:基于 QQ Bot WebSocket Gateway 接收 C2C 私聊消息,并提供私聊文本、流式 Markdown、图片发送 API。

Readme

qqbot-api

一个极简 TypeScript QQ Bot 私聊工具类:基于 QQ Bot WebSocket Gateway 接收 C2C 私聊消息,并提供私聊文本、流式 Markdown、图片发送 API。

特点:

  • 只支持 WebSocket 模式,可接收 QQ 私聊消息,不需要公网 Webhook。
  • 只封装 C2C 私聊能力,避免群聊、频道、DM 等额外复杂度。
  • 不依赖 OpenClaw 仓库代码。
  • 核心文件不 import node:*,浏览器和 Node 都能使用。
  • 对外 API 简洁:start、stop、onMessage、reply、replyImage、replyStream、createReplyStream、sendPrivate、sendPrivateImage、sendPrivateStream、createPrivateStream。

环境要求

  • Node.js 24+ 或现代浏览器
  • QQ Bot 的 appId 和 clientSecret,或后端提供的 accessToken / tokenProvider
  • QQ Bot 已开通 C2C 私聊消息事件权限

安装

npm install

本项目只把 tsx、typescript 和 Node 类型作为开发依赖,核心文件 qqbot-api-tool.ts 本身不依赖第三方运行时包。

配置

复制环境变量示例:

cp .env.example .env

填写:

QQBOT_APP_ID=你的 appId
QQBOT_CLIENT_SECRET=你的 appSecret
QQBOT_USER_OPENID=可选,默认私聊用户 openid

QQBOT_USER_OPENID 不是由 appId / clientSecret 计算出来的。它来自 QQ 私聊消息事件里的 author.user_openid。如果要主动私信,必须先知道目标用户 openid,或者在收到消息后自己保存。

最小接收和回复示例

Node 里可以直接用 appId / clientSecret:

import { QQBotApiTool } from "./qqbot-api-tool.ts";

const bot = new QQBotApiTool({
  appId: process.env.QQBOT_APP_ID!,
  clientSecret: process.env.QQBOT_CLIENT_SECRET!,
});

bot.onMessage(async (event, api) => {
  console.log("收到私聊", event.openid, event.text);

  if (event.text.trim() === "/ping") {
    await api.reply(event, "pong");
  }
});

await bot.start();

浏览器里不要放 clientSecret,应该让你的后端返回 token:

const bot = new QQBotApiTool({
  tokenProvider: async () => {
    const response = await fetch("/api/qqbot-token");
    const data = await response.json();
    return data.accessToken;
  },
});

运行后,程序会主动连接 QQ WebSocket Gateway。QQ 平台会沿这条连接推送私聊消息,所以不需要把本机 localhost 暴露给公网。

回复图片

replyImage 支持公网图片 URL、Blob、ArrayBuffer、Uint8Array 或 base64:

bot.onMessage(async (event, api) => {
  if (event.text.trim() === "/image") {
    await api.replyImage(event, "https://example.com/demo.png");
  }
});

浏览器文件输入示例:

const file = fileInput.files?.[0];
if (file) {
  await bot.sendPrivateImage(file);
}

base64 示例:

await bot.sendPrivateImage({
  base64: "iVBORw0KGgo...",
  filename: "demo.png",
});

主动发送私信

如果构造时传了默认用户 openid:

const bot = new QQBotApiTool({
  appId: process.env.QQBOT_APP_ID!,
  clientSecret: process.env.QQBOT_CLIENT_SECRET!,
  userOpenid: process.env.QQBOT_USER_OPENID,
});

await bot.sendPrivate("你好");

也可以临时指定 openid:

await bot.sendPrivate("你好", "USER_OPENID");

主动发送图片私信

发送公网图片 URL:

await bot.sendPrivateImage("https://example.com/demo.png");

发送浏览器 File / Blob:

await bot.sendPrivateImage(file, "USER_OPENID");

发送 base64:

await bot.sendPrivateImage({ base64: "iVBORw0KGgo...", filename: "demo.png" });

流式回复和发送私信

参考 QQ Bot markdown stream 消息接口实现。

回复当前消息:

bot.onMessage(async (event, api) => {
  if (event.text.trim() === "/stream") {
    await api.replyStream(event, `# 流式回复

你好,这是一条流式 Markdown 回复。`);
  }
});

主动发送:

await bot.sendPrivateStream(`# 流式消息

你好,这是一条流式 Markdown 私信。`);

接 LLM 流式输出时,用 writer API:

bot.onMessage(async (event, api) => {
  const reply = api.createReplyStream(event);

  for await (const chunk of llmStream) {
    await reply.write(chunk);
  }

  await reply.close();
});

主动私信也可以用 writer API:

const stream = bot.createPrivateStream("USER_OPENID");
await stream.write("第一段");
await stream.write("第二段");
await stream.close();

自定义分片大小和间隔:

await bot.sendPrivateStream(markdownText, {
  chunkSize: 50,
  intervalMs: 100,
});

临时指定 openid:

await bot.sendPrivateStream(markdownText, {
  openid: "USER_OPENID",
});

流式发送流程:

  1. 按行切分文本。
  2. 多次发送 state: 1 的 markdown stream 分片。
  3. 最后发送 state: 10、reset: true 的完整文本收尾。

消息事件结构

onMessage 只会收到 C2C 私聊事件:

interface QQBotMessageEvent {
  messageId: string;
  text: string;
  openid: string;
  timestamp?: string;
  attachments: QQBotAttachment[];
  raw: unknown;
}

常用字段:

  • event.text:消息文本。
  • event.openid:私聊用户 openid。
  • event.messageId:回复当前消息时使用。
  • event.attachments:QQ 事件里携带的附件信息。
  • event.raw:QQ 原始事件数据。

API 速览

const bot = new QQBotApiTool(options);

bot.onMessage(handler);
await bot.start();
bot.stop();

await bot.reply(event, "你好");
await bot.replyImage(event, "https://example.com/demo.png");
await bot.replyStream(event, markdownText);

const replyStream = bot.createReplyStream(event);
await replyStream.write("第一段");
await replyStream.close();

await bot.sendPrivate("你好");
await bot.sendPrivateImage(fileOrBlobOrBase64);
await bot.sendPrivateStream(markdownText);

const privateStream = bot.createPrivateStream("USER_OPENID");
await privateStream.write("第一段");
await privateStream.close();

类型检查

npm run typecheck

注意事项

  • openid 不能通过 appId 和 clientSecret 拼接或计算得到。
  • 主动私信需要目标用户 openid。
  • 浏览器里不要暴露 clientSecret,请用后端提供 accessToken 或 tokenProvider。
  • QQ 平台 API 是否允许浏览器直连取决于 CORS;如果被拦截,需要经由你自己的后端代理发送。
  • QQ 平台可能限制主动消息发送频率和场景。
  • 流式消息当前封装的是 C2C 私信 markdown stream。
  • replyStream 会在 stream 消息体里带上当前事件的 msg_id。
  • 图片发送会先调用 /v2/users/{openid}/files 获取 file_info,再发送 msg_type: 7 消息。
  • 本工具类只保留 C2C 私聊能力;群聊、频道、频道私信不在当前封装范围内。