mrkeyoor.com_
Tue 27 Oct 22:53 UTC
npmAI / MLupdated 27 Oct 2026

@tencent-connect/qqbot-nodejs review

A QQ bot that streams answers still needs a working QQ connection before it needs a model. @tencent-connect/qqbot-nodejs supplies that connection: a Node.js client for QQ Open Platform HTTP APIs, incoming events, replies and media uploads. It supports WebSocket and webhook transports. Its streaming helper accepts only C2C targets and requires an incoming message ID. Use it as the messaging layer of your own application, with your own answer generation and business logic.

Verdict

Choose @tencent-connect/qqbot-nodejs when your application needs a QQ messaging client and you can own platform setup and bot logic. Start with one plain-text reply, then add C2C streaming only if private replies meet your product needs.

Answers from our run

@tencent-connect/qqbot-nodejs or qq-guild-bot: which should you use?

qq-guild-bot: Evaluate it for a QQ guild/channel bot: its published README describes a Node.js SDK for that task. Check its own event and API documentation before choosing it over this private-chat and group workflow. Choose @tencent-connect/qqbot-nodejs when your application needs a QQ messaging client and you can own platform setup and bot logic.

When should you not use @tencent-connect/qqbot-nodejs?

Your product requires streaming replies in groups. The streaming helper rejects targets outside C2C.

API stability3/5The version-pinned package exposes a high-level entry and a separate protocol entry, while the README gives concrete examples for events, sends and lifecycle handling. The 1.0.5 release changes the default reconnect budget, so even a patch update can affect supervision policy. This documentation-based score favors pinning and checking lifecycle changes rather than assuming every patch preserves operational behavior.
Docs3/5The English README covers installation, credentials, both transports, middleware, media, streaming and shutdown behavior, with examples you can adapt. It also identifies optional audio dependencies and links to a fuller usage guide. That guide is currently in Chinese, creating extra reference work for an English-speaking reader. The score reflects that language gap and does not imply the examples were executed.
Maintenance3/5The pinned changelog records version 1.0.5 on October 12, 2026, with specific fixes for token refresh across restarts and gateway failures, plus the changed reconnect policy. Earlier entries describe token and streaming corrections. These are useful maintenance signals for this version, but a release log cannot establish future response times or prove that the fixes work in every deployment.
Ecosystem2/5The package targets one messaging platform and provides a protocol sub-entry for code that needs lower-level access. Its README documents middleware helpers and optional voice-related packages, giving a QQ application several integration points. The evidence does not establish a broad third-party integration ecosystem or cross-platform portability, so this scope-based score stays modest without treating unknown adoption as a measurement.

Use it if

  • You already have a QQ Open Platform bot and want to implement its private-chat or group replies in a Node.js service.
  • Your service needs a choice between a long-lived WebSocket connection and an HTTP webhook callback.
  • You want to send incremental answers in private C2C replies and can retain the incoming reply target.
  • You need QQ media sending alongside text, and can handle local files and the platform configuration yourself.
Skip it if

Setup reality

Start with a QQ Open Platform bot's AppID and AppSecret. Pass them into the constructor from your application's secret store. The SDK does not load environment variables itself. The examples below use environment variable names only to show the injection boundary. They are documentation-based illustrations for version 1.0.5, not executed QQ sessions. We did not install the package, authenticate a bot, measure delivery or test webhook deployment.

Use Node.js 20 or newer and ESM. A CommonJS service can load the package with dynamic import(), but it cannot treat the import-only package exports as an ordinary synchronous require(). Keep the package version pinned while you establish one working reply. The default WebSocket mode needs a process that can keep its connection alive. Webhook mode moves incoming events to an HTTP callback, so you must configure the platform callback and a reachable HTTPS endpoint for your deployment. Choosing webhook mode alone does not prove your hosting setup will accept events.

Treat start() as the bot's lifetime, not a readiness check: it resolves when stopped or aborted. Register listeners before starting it. Handle recoverable error events as well as a rejected start(). The 1.0.5 release notes say reconnection now continues until the failure is clearly unrecoverable, replacing the previous finite default. Set reconnect.maxAttempts deliberately when another supervisor owns restarts. A banned or sandbox-only bot needs operator attention rather than an endless restart cycle.

Reply using the incoming msg.replyTarget so its message ID stays attached. The streaming helper rejects group targets and C2C targets without msgId. Each update receives the full text accumulated so far, followed by complete() when finished. This is a private-reply facility, so do not design a group streaming interface around it. Media and voice introduce more setup: the README lists optional silk-wasm and mpg123-decoder packages for voice encoding and MP3 decoding. Keep markdownSupport false unless the bot has that permission.

Rating rubric: these are editorial judgments from the version-pinned documentation, package metadata, implementation and release notes. On the five-point scale, 1 means substantial documented friction, 3 means a usable documented surface with caveats, and 5 requires broad inspectable support for the dimension. Intermediate scores reflect the evidence between those anchors. No score measures delivery speed, adoption, security or production compatibility.

Patterns

Pin the documented release pin-install

npm install --save-exact @tencent-connect/qqbot-nodejs@1.0.5

Unexecuted illustration for 1.0.5. Use Node.js 20 or newer. Set your application to ESM or use an .mjs file for the JavaScript examples.

Construct a client with explicit credentials inject-credentials

import { QQBot } from "@tencent-connect/qqbot-nodejs";

const appId = process.env.QQBOT_APP_ID;
const appSecret = process.env.QQBOT_APP_SECRET;
if (!appId || !appSecret) throw new Error("QQ credentials are required");
const bot = new QQBot({ appId, appSecret, logger: console });

Unexecuted illustration for 1.0.5. Your application reads these variables; the SDK receives their values. Later fragments assume this bot unless they construct another one. Do not print the credentials.

Reply using the incoming target reply-to-message

bot.on("message", async (_ctx, msg) => {
  await bot.sendText(msg.replyTarget, "Message received.");
});

Unexecuted illustration for 1.0.5. Register before starting. Keep replyTarget intact so the reply retains the triggering message ID. This listener is an alternative to the other message listeners below.

Observe connection and error events observe-lifecycle

bot.on("ready", () => console.log("QQ ready"));
bot.on("resumed", () => console.log("QQ resumed"));
bot.on("disconnected", ({ code, willReconnect }) => {
  console.warn("QQ disconnected", code, willReconnect);
});
bot.on("error", (err) => console.error(err));

Unexecuted illustration for 1.0.5. A recoverable error event and a rejected start() have different lifecycle meanings. Event logging does not replace handling the start promise.

Keep the start promise until shutdown run-until-abort

const controller = new AbortController();
process.once("SIGTERM", () => controller.abort());
process.once("SIGINT", () => controller.abort());
try {
  await bot.start(controller.signal);
} catch (err) {
  console.error(err);
  process.exitCode = 1;
}

Unexecuted illustration for 1.0.5. Top-level await requires ESM. start() resolves on stop or abort, so code after it is shutdown code rather than a readiness callback.

Let a supervisor own the restart budget cap-reconnects

const supervisedBot = new QQBot({
  appId, appSecret,
  reconnect: { maxAttempts: 10 },
  logger: console,
});

Unexecuted illustration for 1.0.5. This fragment only constructs the client. Register listeners and run start() as above. Ten attempts is an illustrative policy, not a recommended universal limit; classify terminal failures before restarting.

Construct a webhook client receive-webhooks

const webhookBot = new QQBot({
  appId, appSecret,
  transport: "webhook",
  webhook: { port: 8080, path: "/callback" },
});

Unexecuted illustration for 1.0.5. This is an alternative client configuration. Register its listeners and start it. The local listener settings do not provision public HTTPS or configure the QQ callback.

Measure your handler boundary time-middleware

bot.use(async (ctx, next) => {
  const started = Date.now();
  try {
    await next();
  } finally {
    ctx.log.debug?.(`Handler elapsed: ${Date.now() - started}ms`);
  }
});

Unexecuted illustration for 1.0.5. Register middleware before starting. This measures time across the downstream handler, not QQ delivery latency. The example supplies no benchmark result.

Send cumulative private-chat output stream-private-reply

bot.on("message", async (_ctx, msg) => {
  if (msg.replyTarget.scope !== "c2c") {
    await bot.sendText(msg.replyTarget, "Streaming is available in private chat.");
    return;
  }
  const stream = bot.openStream({ target: msg.replyTarget });
  await stream.update("Preparing");
  await stream.update("Preparing your answer.");
  await stream.complete();
});

Unexecuted illustration for 1.0.5. Use as an alternative message handler. update() takes the full accumulated text, not just the newest fragment. The incoming message supplies msgId. This illustrates one reply and makes no concurrency or cancellation guarantee.

Send an image from your service send-local-image

bot.on("message", async (_ctx, msg) => {
  if (msg.content.trim() === "/diagram") {
    await bot.sendImage(msg.replyTarget, { localPath: "/srv/bot/diagram.png" });
  }
});

Unexecuted illustration for 1.0.5. Use as an alternative message handler. The file must exist on the bot host and be readable. This example does not establish media delivery or QQ permission approval.

Alternatives

PackageRegistryPick it when
qq-guild-botnpmEvaluate it for a QQ guild/channel bot: its published README describes a Node.js SDK for that task. Check its own event and API documentation before choosing it over this private-chat and group workflow.
koishinpmEvaluate it when the main requirement is a cross-platform chatbot framework. QQ connectivity still needs a suitable adapter; this comparison does not establish adapter compatibility.

More ai / ml guides

openai · mcp · huggingface-hub · tiktoken · scikit-learn · tokenizers · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.