QQ Bot Integration: Setup, Configuration, and Usage
Learn how to link OpenClaw to QQ via the official Bot API, covering setup, configuration, and usage. Includes details on supported message types and durability for C2C and group chats.
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 links OpenClaw to the QQ platform through the official Bot API, using the WebSocket gateway. The two main interaction modes are C2C private messaging and group @-mentions, both of which support rich media such as images, voice, video, and files. Guild channel messages handle only text and remote-URL images; voice, video, file uploads, and local or Base64 images do not work there. Reactions and threads are unsupported across all channels.
This plugin is officially downloadable.
Install
openclaw plugins install @tencent-connect/openclaw-qqbot
Setup
- Open the QQ Open Platform and authenticate by scanning the QR code with QQ on your phone.
- Press Create Bot to set up a fresh QQ bot.
- Copy the AppID and AppSecret from the bot's settings page.
Note
The AppSecret is never shown in plaintext. If you navigate away before saving it, a new one must be regenerated.
- Add the channel:
openclaw channels add --channel qqbot --token "AppID:AppSecret"
- Restart the Gateway.
Inbound durability
When QQ gateway turn events arrive, OpenClaw stores the raw event first, then advances the saved gateway resume sequence. Pending or retryable turns survive a Gateway restart, stay serialized per conversation, and rely on the provider event ID to prevent duplicate queue entries while the active or retained completion record is present.
If durable admission fails, OpenClaw closes the current gateway socket without moving the sequence forward. The reconnect/resume flow can then ask for the uncommitted event once more. Delivery remains at least once across the queue-to-agent boundary, so a crash during handoff may replay a turn.
Interactive setup:
openclaw channels add
As an alternative to typing AppID and AppSecret manually, the wizard supports QR-code binding: scan the code with the phone app associated with the target QQ Bot to finish binding. OpenClaw saves 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_IDQQBOT_CLIENT_SECRET
File-backed AppSecret:
{
channels: {
qqbot: {
enabled: true,
appId: "YOUR_APP_ID",
clientSecretFile: "/path/to/qqbot-secret.txt",
},
},
}
Notes:
openclaw channels add --channel qqbot --token-file ...only sets the AppSecret;appIdhas to be present in config already or come fromQQBOT_APP_ID.clientSecrettakes either a plaintext string or a file path (clientSecretFile).- Known limitation: the external
@tencent-connect/openclaw-qqbotpackage lacks support for structured SecretRef objects when used withclientSecret. If your config includes one, relocate the secret to theQQBOT_CLIENT_SECRETenvironment variable (orclientSecretFile) before upgrading.
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"turns off block streaming for the account.streaming.nativeTransport: truesends C2C (DM) replies through QQ's officialstream_messagesAPI; group and channel targets stay unaffected.- Legacy
streaming: true|falsescalars and thestreaming.c2cStreamApikey are migrated to this shape viaopenclaw doctor --fix. /bot-streaming on|offflips the same setting from a DM.
Access policy
allowFrom/groupAllowFromdetermine who can talk to the bot in C2C or group contexts.dmPolicy/groupPolicy(open|allowlist|disabled) set the enforcement mode. OnceallowFromholds a concrete (non-wildcard) entry,dmPolicydefaults toallowlist; otherwise it falls back toopen. Similarly,groupPolicydefaults toallowlistafter eithergroupAllowFromorallowFromgets a concrete entry, else it usesopen.contextVisibilitygoverns the quoted-message text QQ provides as extra context. The default,"all", passes quoted text through unchanged. Choose"allowlist"to include quoted bodies only when the quoted sender meets the configured sender policy, or"allowlist_quote"to preserve explicit quotes while filtering other supplemental context. See Groups.- "Auth: allowlist" slash commands demand an explicit non-wildcard entry in
allowFrom(orgroupAllowFromfor group invocations), independent ofdmPolicy/groupPolicy, as described in Slash commands.
Multi-account setup
Operate several QQ bots from one 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 gets its own isolated WebSocket connection, API client, and token cache, all keyed by appId. Log lines carry the account id that owns them, so diagnostics stay separate when multiple bots run under a single Gateway.
To add a second bot, use the CLI:
openclaw channels add --channel qqbot --account bot2 --token "222222222:secret-of-bot-2"
Group chats
Group support works with QQ group OpenIDs, not display names. Put the bot into a group, then either mention it or set the group to run without requiring 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["*"] applies defaults across all groups; a specific groups.GROUP_OPENID entry overrides those defaults for one group. Group settings:
| Field | Default | Description |
|---|---|---|
requireMention | true | The bot only replies when an @-mention appears. |
commandLevel | all | Which built-in slash commands are permitted in the group (details below). |
ignoreOtherMentions | false | Messages that mention another user but not the bot get dropped. |
historyLimit | 50 | Recent non-mention messages are kept as context for the next mentioned turn. 0 turns history off. |
tools | , | Allow or deny tools across the whole group. |
toolsBySender | , | Per-sender tool overrides; see Groups. |
name | openid prefix | Friendly label shown in logs and group context. |
prompt | built-in default | Per-group behavior prompt appended to the agent context. |
commandLevel accepts these values:
| Level | Behavior |
|---|---|
all | Current built-in commands remain usable. Some are hidden from menus, but authorized users can still invoke them in the group. |
safety | /help, /btw, /stop stay visible in the group; sensitive commands (/config, /tools, /bash, etc.) require private chat. |
strict | Only group-session controls needed for strict operation are allowed. /stop still works so an authorized sender can stop an active run. |
Old QQBot toolPolicy entries are no longer used. Run openclaw doctor --fix to convert them to tools.
Activation modes are mention and always. requireMention: true corresponds to mention; requireMention: false corresponds to always. A session-level activation override, if present, takes precedence over config.
The inbound queue is per peer. Group peers get a larger queue cap (50 vs. 20 for direct peers), evict bot-authored messages before human ones when full, and merge bursts of normal group messages 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:
| Setting | Plugin-specific | Framework fallback |
|---|---|---|
| STT | channels.qqbot.stt | first audio-capable tools.media.models[] entry |
| TTS | channels.qqbot.tts, channels.qqbot.accounts.<id>.tts | tts |
{
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/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/transcode behavior can also be tuned with channels.qqbot.audioFormatPolicy:
sttDirectFormatsuploadDirectFormatstranscodeEnabled
Target formats
| Format | Description |
|---|---|
qqbot:c2c:OPENID | Direct messages (C2C) |
qqbot:group:GROUP_OPENID | Group conversations |
qqbot:channel:CHANNEL_ID | Guild channels |
Note
OpenIDs are unique per bot. An ID from Bot A cannot be used to reach users through Bot B.
Slash commands
Commands handled natively, before anything reaches the AI queue:
| Command | Auth | Scope | Description |
|---|---|---|---|
/bot-ping | , | any | Checks response time |
/bot-help | , | any | Enumerates every available command |
/bot-me | , | private only | Displays the sender's QQ user ID (openid), needed for allowFrom / groupAllowFrom configuration |
/bot-version | , | private only | Reports the OpenClaw framework and plugin versions |
/bot-upgrade | , | private only | Provides the link to the QQBot upgrade guide |
/bot-approve | allowlist | private only | Controls command-execution approval (on / off / always / reset / status) |
/bot-logs | allowlist | private only | Exports recent gateway logs to a downloadable file |
/bot-clear-storage | allowlist | private only | Clears cached downloads from the QQBot media folder |
/bot-streaming | allowlist | private only | Switches C2C streaming replies on or off |
/bot-group-allways | allowlist | private only | Chooses the default group activation mode (mention-required vs. always-on) |
Add ? to any command to see usage details, such as /bot-upgrade ?.
Commands marked "Auth: allowlist" also demand that the sender's openid appear in an explicit non-wildcard allowFrom list (groupAllowFrom overrides for commands issued in groups, with allowFrom as the fallback). A wildcard allowFrom: ["*"] allows chatting but not these commands. Attempting one outside private chat, or without the right authorization, produces a hint instead of silently discarding the message.
/bot-me, /bot-version, and /bot-upgrade work only in private chat but skip the allowlist entirely, so any C2C sender may invoke them.
When QQ Bot exec approvals rely on the default same-chat fallback, clicking native approval buttons follows the same explicit non-wildcard command allowlist. To hand out approval-only access without broader command permissions, set channels.qqbot.execApprovals.approvers. Native exec approvals are on by default.
Media and storage
- All inbound, outbound, and gateway-bridge media share a single payload root at
~/.openclaw/media/qqbot(respectingOPENCLAW_HOMEwhen configured), so uploads, downloads, and transcode caches stay together in one protected location. - Rich media to C2C and group targets travels through a single
sendMediaroute. Local files and in-memory buffers of 5 MiB or larger 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 mid-write of
openclaw.json, the plugin restores the last-knownappId/clientSecretfor that account from an internal snapshot at the next start (never clobbering an intentional config change), so re-scanning the QR code is unnecessary.
Troubleshooting
- Gateway fails to start / no inbound messages: check that
appIdandclientSecretare accurate and the bot is enabled on the QQ Open Platform. A missing credential shows up as "QQBot not configured (missing appId or clientSecret)". - Setup with
--token-filestill reports unconfigured:--token-fileonly sets the AppSecret.appIdmust also be present in config or viaQQBOT_APP_ID. - Bursty group replies collide: when a peer's queue fills, the inbound queue drops bot-authored messages before human ones, and merges bursts of ordinary (non-command) group messages into a single 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: make sure STT is configured and the provider is reachable.