QQ Bot
QQ Official Bot v2 messaging with Ed25519 webhook events. Active messages are capped to 4 per month per bot, design for passive replies.
⚠️ Use passive replies, not push, QQ caps active messages at 4 per month per bot. Daily caps: 200 active DMs, 20 active sub-channel messages per channel. Most production interactions must reply to an inbound user message (the bot has 5 seconds to respond, free quota), pushing actively only for critical alerts.
What you get
- Four send surfaces, routed by
channelprefix:c2c:<openid>→/v2/users/{openid}/messages(C2C DMs)group:<openid>→/v2/groups/{openid}/messages(group)channel:<id>(default) →/channels/{channel_id}/messages(public guild channel)dm:<guild_id>→/dms/{guild_id}/messages(private guild DM)
- Inbound webhook at
/webhooks/qqwith Ed25519 signatures. access_tokencached for 7200 seconds with auto-refresh.- Text only. Rich-media messages (images, files, ARK cards) are a future enhancement.
- 4000-char text limit.
Setup
1. Register a bot
- Sign in to QQ Open Platform Bot Console
- Create a Bot. Note the AppID and AppSecret on the bot detail page
- Under "Develop Settings" → "Webhook", set the URL to
https://<your-host>/webhooks/qqand pick "Webhook Mode" - The platform sends a one-time op=13 validation handshake. Minara responds with the signed plain_token automatically.
2. Identify a default target
Pick the most-common destination and encode it with the prefix:
| Scenario | QQ_BOT_DEFAULT_CHANNEL_ID |
|---|---|
| Reply to DMs in a public guild | channel:<channel id> |
| Push to a group | group:<group openid> |
| Push to a single user | c2c:<user openid> |
| Reply to private guild DM | dm:<guild id> |
You can omit the prefix for the public-guild channel case (the most common):
QQ_BOT_DEFAULT_CHANNEL_ID=1234567890Equivalent to channel:1234567890.
3. Configure Minara
minara auth messaging add
# pick `qq`; paste AppID + AppSecret + default target.Or set env vars:
QQ_BOT_APP_ID=12345678
QQ_BOT_APP_SECRET=<opaque secret>
QQ_BOT_TOKEN=<bot token>
QQ_BOT_DEFAULT_CHANNEL_ID=channel:12345678904. Test
minara auth messaging test qqCounts against your monthly active-message quota. Plan around it.
Inbound webhook
QQ signs inbound bodies with Ed25519 over <timestamp><raw_body>.
The public key is not published by the platform; it is derived
from your bot secret:
- take the UTF-8 bytes of
QQ_BOT_APP_SECRET - repeat them until the buffer reaches 32 bytes
- that is the Ed25519 seed; derive keypair
Minara handles this seed-derived keypair internally. A 5-minute replay window is enforced (matches the convention used by Discord and Slack).
Validation handshake: when the platform sends {op: 13, d: {plain_token, event_ts}},
Minara signs event_ts + plain_token with the derived private key
and replies with the signed hex. This is how the platform proves
your endpoint owns the secret.
Inbound event types accepted:
AT_MESSAGE_CREATE, bot was @-mentioned in a public guild channelGROUP_AT_MESSAGE_CREATE, bot was @-mentioned in a groupC2C_MESSAGE_CREATE, DM from a user
Other op=0 dispatch events (channel updates, reactions, etc.) are ignored.
Limits & caveats
- Active-message quotas. 4 / month, 200 / day DMs, 20 / day per sub-channel. Errors with code 130_001 once exhausted; treat them as expected if you push at any volume.
- Passive reply pattern is what's intended. The user @-mentions the bot; within 5 seconds the bot replies. Replies do not count against the active quota.
- Webhook is mandatory for new bots. The WebSocket sandbox mode is deprecated for v2.
Troubleshooting
"Test message returns errcode 130001"
- Monthly active-message quota exhausted. Use passive replies.
"Inbound webhook returns 401"
- Most common cause: clock skew on your host. The 5-minute replay window is strict.
- Less common: you pasted only part of the App Secret. The seed derivation repeats short secrets, but a wrong secret produces a wrong public key.
"Validation handshake (op=13) fails"
QQ_BOT_APP_SECRETis not configured at boot. Without it the seed derivation fails and the platform never finishes binding.
Reference
- Env vars:
QQ_BOT_* - Outbound:
apps/agent/src/messaging/qq.ts - Inbound spec:
apps/agent/src/messaging/inbound/specs/qq.ts - QQ Bot v2 API: bot.q.qq.com / wiki
- Signature recipe: Authentication / Sign