QQ Bot Setup and Configuration for OpenClaw Gateway

This page covers how to install, configure, and use the QQ Bot plugin for OpenClaw via the official QQ Bot API. It is intended for developers and administrators integrating QQ messaging with OpenClaw.

Read this when

  • You want to connect OpenClaw to QQ
  • You need QQ Bot credential setup
  • You want QQ Bot group or private chat support

QQ Bot connects to OpenClaw through the official QQ Bot API using a WebSocket gateway. The primary chat types are C2C private chat and group @ mentions, supporting rich media like images, voice, video, and files. Guild channel messages only work with text and remote URL images. Voice, video, file uploads, and local or Base64 images are not supported in guild channels. Reactions and threads are not available anywhere.

Status: official downloadable plugin.

Install

openclaw plugins install @openclaw/qqbot

Setup

  1. Go to the QQ Open Platform and scan the QR code with your phone QQ to register or log in.
  2. Click Create Bot to create a new QQ bot.
  3. Find AppID and AppSecret on the bot's settings page and copy them.

Note

AppSecret is not stored in plaintext. If you leave the page without saving it, you must regenerate a new one.

  1. Add the channel:
openclaw channels add --channel qqbot --token "AppID:AppSecret"
  1. Restart the Gateway.

Inbound durability

For QQ gateway turn events, OpenClaw stores the raw event before advancing the saved gateway resume sequence. Pending or retryable turns survive a Gateway restart, stay serialized per conversation, and use the provider event ID to prevent duplicate queue entries while the active or retained completion record exists.

If durable admission fails, OpenClaw terminates the current gateway socket without advancing the sequence. The reconnect or resume path can then request the uncommitted event again. Delivery remains at least once across the queue to agent boundary, so a crash during handoff can replay a turn.

Interactive setup:

openclaw channels add

The wizard also provides QR code binding as an alternative to typing AppID and AppSecret manually. Scan the code with the phone app tied to the target QQ Bot to complete binding. OpenClaw persists the returned credentials under the account's config scope.

Configure

Minimal config:

{
  channels: {
    qqbot: {
      enabled: true,
      appId: "YOUR_APP_ID",
      clientSecret: "YOUR_APP_SECRET",
    },
  },
}

Default account env vars (top-level account only):

  • QQBOT_APP_ID
  • QQBOT_CLIENT_SECRET

File backed AppSecret:

{
  channels: {
    qqbot: {
      enabled: true,
      appId: "YOUR_APP_ID",
      clientSecretFile: "/path/to/qqbot-secret.txt",
    },
  },
}

Env SecretRef AppSecret:

{
  channels: {
    qqbot: {
      enabled: true,
      appId: "YOUR_APP_ID",
      clientSecret: { source: "env", provider: "default", id: "QQBOT_CLIENT_SECRET" },
    },
  },
}

Notes:

  • openclaw channels add --channel qqbot --token-file ... sets the AppSecret only. appId must already be set in config or QQBOT_APP_ID.
  • clientSecret accepts a plaintext string, a file path (clientSecretFile), or a structured SecretRef object.
  • Legacy secretref:... or secretref-env:... marker strings are rejected for clientSecret. Use a structured SecretRef object instead.

Streaming

{
  channels: {
    qqbot: {
      streaming: {
        mode: "partial", // block streaming: "partial" (default) or "off"
        nativeTransport: true, // use QQ's official C2C stream_messages API for DMs
      },
    },
  },
}
  • streaming.mode: "off" disables block streaming for the account.
  • streaming.nativeTransport: true streams C2C (DM) replies through QQ's official stream_messages API. Group and channel targets are unaffected.
  • Legacy streaming: true|false scalars and the streaming.c2cStreamApi key migrate to this shape via openclaw doctor --fix.
  • /bot-streaming on|off toggles the same config from a DM.

Access policy

  • allowFrom or groupAllowFrom gate who can chat with the bot in C2C or group contexts. dmPolicy or groupPolicy (open | allowlist | disabled) control the enforcement mode. dmPolicy defaults to allowlist once allowFrom has a concrete (non-wildcard) entry, otherwise open. groupPolicy defaults to allowlist once either groupAllowFrom or allowFrom has a concrete entry, otherwise open.
  • "Auth: allowlist" slash commands require an explicit non-wildcard entry in allowFrom (or groupAllowFrom for group invocations) regardless of dmPolicy or groupPolicy. See Slash commands.

Multi-account setup

Run multiple QQ bots under a single OpenClaw instance:

{
  channels: {
    qqbot: {
      enabled: true,
      appId: "111111111",
      clientSecret: "secret-of-bot-1",
      accounts: {
        bot2: {
          enabled: true,
          appId: "222222222",
          clientSecret: "secret-of-bot-2",
        },
      },
    },
  },
}

Each account owns an isolated WebSocket connection, API client, and token cache, keyed by appId. Log lines are tagged with the owning account id so diagnostics stay separable when you run several bots under one Gateway.

Add a second bot via CLI:

openclaw channels add --channel qqbot --account bot2 --token "222222222:secret-of-bot-2"

Group chats

Group support uses QQ group OpenIDs, not display names. Add the bot to a group, then mention it or configure the group to run without a mention.

{
  channels: {
    qqbot: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["member_openid"],
      groups: {
        "*": {
          requireMention: true,
          commandLevel: "all",
          historyLimit: 50,
          tools: { deny: ["exec", "read", "write"] },
        },
        GROUP_OPENID: {
          name: "Release room",
          requireMention: false,
          ignoreOtherMentions: true,
          commandLevel: "safety",
          historyLimit: 20,
          prompt: "Keep replies short and operational.",
        },
      },
    },
  },
}

groups["*"] sets defaults for every group. A concrete groups.GROUP_OPENID entry overrides those defaults for one group. Group settings:

FieldDefaultDescription
requireMentiontrueRequire an @ mention before the bot replies.
commandLevelallWhich built-in slash commands can run in the group (see below).
ignoreOtherMentionsfalseDrop messages that mention someone else but not the bot.
historyLimit50Recent non-mention messages kept as context for the next mentioned turn. 0 disables history.
tools,Allow or deny tools for the whole group.
toolsBySender,Per-sender tool overrides. See Groups.
nameopenid prefixFriendly label used in logs and group context.
promptbuilt-in defaultPer-group behavior prompt appended to the agent context.

commandLevel accepts:

LevelBehavior
allExisting built-in commands stay available. Some stay hidden from menus but authorized users can still run them in the group.
safety/help, /btw, /stop stay visible in the group. Sensitive commands (/config, /tools, /bash, etc.) must be run in private chat.
strictOnly group-session controls needed for strict operation are allowed. /stop still works so an authorized sender can interrupt an active run.

Old QQBot toolPolicy entries are retired. Run openclaw doctor --fix to migrate them to tools.

Activation modes are mention and always. requireMention: true maps to mention. requireMention: false maps to always. A session-level activation override, when present, wins over config.

The inbound queue is per peer. Group peers get a larger queue cap (50 vs. 20 for direct peers). When full, bot-authored messages are evicted before human ones. Bursts of normal group messages are merged into one attributed turn. Slash commands run one by one, independent of any merge batch.

Voice (STT / TTS)

STT and TTS support two-level configuration with priority fallback:

SettingPlugin-specificFramework fallback
STTchannels.qqbot.sttfirst audio-capable tools.media.models[] entry
TTSchannels.qqbot.tts, channels.qqbot.accounts.<id>.ttstts
{
  channels: {
    qqbot: {
      stt: {
        provider: "your-provider",
        model: "your-stt-model",
      },
      tts: {
        provider: "your-provider",
        model: "your-tts-model",
        voice: "your-voice",
      },
      accounts: {
        "qq-main": {
          tts: {
            providers: {
              openai: { voice: "shimmer" },
            },
          },
        },
      },
    },
  },
}

Set enabled: false on either to disable. Account-level TTS overrides use the same shape as tts and deep-merge over channel and global TTS config.

STT requests time out after 60 seconds by default. Plugin-specific STT uses the selected models.providers.<id>.timeoutSeconds override. Framework audio STT uses the selected audio-capable tools.media.models[] entry's timeoutSeconds, then the selected provider override.

Inbound QQ voice attachments are exposed to agents as audio media metadata while keeping raw voice files out of generic MediaPaths. [[audio_as_voice]] in a plain-text reply synthesizes TTS and sends a native QQ voice message when TTS is configured.

Outbound audio upload and transcode behavior can also be tuned with channels.qqbot.audioFormatPolicy:

  • sttDirectFormats
  • uploadDirectFormats
  • transcodeEnabled

Target formats

FormatDescription
qqbot:c2c:OPENIDPrivate chat (C2C)
qqbot:group:GROUP_OPENIDGroup chat
qqbot:channel:CHANNEL_IDGuild channel

Note

Each bot has its own set of user OpenIDs. An OpenID received by Bot A cannot be used to send messages via Bot B.

Slash commands

Built-in commands intercepted before the AI queue:

CommandAuthScopeDescription
/bot-ping,anyLatency test
/bot-help,anyList all commands
/bot-me,private onlyShow the sender's QQ user ID (openid) for allowFrom or groupAllowFrom setup
/bot-version,private onlyShow the OpenClaw framework version and plugin version
/bot-upgrade,private onlyShow the QQBot upgrade guide link
/bot-approveallowlistprivate onlyManage command-execution approval config (on, off, always, reset, status)
/bot-logsallowlistprivate onlyExport recent gateway logs as a file
/bot-clear-storageallowlistprivate onlyDelete cached downloads under the QQBot media directory
/bot-streamingallowlistprivate onlyToggle C2C streaming replies
/bot-group-allwaysallowlistprivate onlyToggle the default group activation mode (mention-required vs. always-on)

Append ? to any command to see usage help (for example /bot-upgrade ?).

"Auth: allowlist" commands also require the sender's openid in an explicit non-wildcard allowFrom list (groupAllowFrom takes precedence for group-issued commands, falling back to allowFrom). A wildcard allowFrom: ["*"] allows chat but not these commands. Running one of them outside private chat, or without authorization, returns a hint instead of silently dropping the message.

/bot-me, /bot-version, and /bot-upgrade are private-chat-only but do not require the allowlist. Any C2C sender can run them.

When QQ Bot exec approvals use the default same-chat fallback, native approval button clicks follow the same explicit non-wildcard command allowlist. To grant approval-only access without broader command access, configure channels.qqbot.execApprovals.approvers. Native exec approvals are enabled by default.

Media and storage

  • Inbound, outbound, and gateway-bridge media share one payload root under ~/.openclaw/media/qqbot (honoring OPENCLAW_HOME when set), so uploads, downloads, and transcode caches stay under one guarded directory.
  • Rich media delivery for C2C and group targets goes through one sendMedia path. Local files and in-memory buffers of 5 MiB or more use QQ's chunked upload endpoints; smaller payloads and remote-URL/Base64 sources use the one-shot upload API.
  • If a hot upgrade interrupts the Gateway before it finishes writing openclaw.json, the plugin restores the last-known appId / clientSecret for that account from an internal snapshot on the next start (never overwriting an intentional config change), so re-scanning the QR code is not required.

Troubleshooting

  • Gateway does not start / no inbound messages: verify appId and clientSecret are correct and the bot is enabled on the QQ Open Platform. A missing credential surfaces as "QQBot not configured (missing appId or clientSecret)".
  • Setup with --token-file still shows unconfigured: --token-file only sets the AppSecret. appId must still be set in config or QQBOT_APP_ID.
  • Bursty group replies collide: the inbound queue evicts bot-authored messages ahead of human ones when a peer's queue fills up, and merges bursts of normal (non-command) group messages into one attributed turn, so a flood of bot chatter should not starve human messages.
  • Proactive messages not arriving: QQ may block bot-initiated messages if the user has not interacted recently.
  • Voice not transcribed: ensure STT is configured and the provider is reachable.