Skip to content

文档同步

本页由 plugins/adapters/qq/README.md 自动生成。请修改包内 README 后运行 pnpm sync:adapter-docs

@zhin.js/adapter-qq

Zhin.js QQ 官方机器人适配器,基于 QQ 官方机器人 API 开发,支持频道、群聊和私聊消息。

安装

bash
pnpm add @zhin.js/adapter-qq

配置

基础配置

typescript
import { defineConfig } from 'zhin.js';

export default defineConfig({
  endpoints: [
    {
      context: 'qq',
      name: 'my-qq-bot',
      appid: process.env.QQ_APPID,
      secret: process.env.QQ_SECRET,
      mode: 'middleware',
      application: 'koa',
      webhookPath: '/qq/webhook',
      sandbox: true,
      data_dir: './data',
    },
  ],
  plugins: [
    '@zhin.js/adapter-qq',
  ],
})

完整配置选项

typescript
const config: QQEndpointConfig = {
  context: 'qq',
  name: 'my-qq-bot',
  appid: 'YOUR_APPID',         // 机器人 AppID(必需,小写 appid)
  secret: 'YOUR_SECRET',       // 机器人 Secret(必需)
  mode: 'middleware',        // websocket | webhook | middleware(推荐挂 host-router)
  application: 'koa',          // middleware 模式必填
  webhookPath: '/qq/webhook', // middleware 回调路径(完整 URL: {host}:8086/qq/webhook,无 /api 前缀)
  // mode: 'webhook',         // 独立 HTTP 端口模式
  // port: 8087,
  // path: '/qq/webhook',
  platform: 'qq',              // 'qq' | 'qzone' 平台类型
  intents: [                   // 事件订阅意图(qq-official-bot ^1.2 使用 GROUP_AND_C2C_EVENT)
    'GROUP_AND_C2C_EVENT',     // QQ 群 @ 消息 + C2C 私聊(必需)
    'GUILDS',
    'GUILD_MEMBERS',
    'GUILD_MESSAGES',          // 频道私域 @ 消息(公域用 PUBLIC_GUILD_MESSAGES)
    'DIRECT_MESSAGE',
    'PUBLIC_GUILD_MESSAGES',
  ],
  data_dir: './data',          // 数据目录(可选)
  sandbox: false               // 是否为沙箱环境(可选)
}

入站模式(二选一)

模式配置说明
middleware(推荐)mode: middleware + application: koa + webhookPath挂在 @zhin.js/host-router,与 Host 同端口(如 8086
webhook(独立端口)mode: webhook + port + pathqq-official-bot 自建 HTTP 服务,需单独暴露端口
websocketmode: websocket长连接,无需公网回调

自定义网关(qq-official-bot ^1.2.2)

经代理或私有部署时,可覆盖官方 token / gateway 接口:

yaml
# WebSocket 入站:gatewayUrl 与 accessTokenUrl 均生效
- context: qq
  name: my-qq-bot
  mode: websocket
  appid: ${QQ_APPID}
  secret: ${QQ_SECRET}
  accessTokenUrl: https://your-proxy.example.com/app/getAppAccessToken
  gatewayUrl: https://your-proxy.example.com/gateway/bot
  intents:
    - GROUP_AND_C2C_EVENT
yaml
# middleware / webhook:仅 accessTokenUrl 影响出站鉴权(入站走 HTTP 回调,不用 gateway)
- context: qq
  name: my-qq-bot
  mode: middleware
  application: koa
  webhookPath: /qq/webhook
  accessTokenUrl: https://your-proxy.example.com/app/getAppAccessToken
  • gatewayUrl 可为完整 URL 或相对路径(默认 /gateway/bot,相对 api.sgroup.qq.com baseURL)
  • 连接时日志会输出 op: qq_gateway 及实际使用的地址

middleware 回调地址(test-bot 默认)

  1. 确保 plugins@zhin.js/host-router(test-bot 已启用)

  2. zhin.config.yml 示例见上文 webhookPath: /qq/webhook

  3. QQ 开放平台开发 → 回调配置 填写:

    https://<你的公网域名>/qq/webhook

    本地:http://127.0.0.1:8086/qq/webhook(经 ngrok / cloudflared 暴露公网即可)。

    注意:Host 的 Console/API 走 /api/*(如 /api/stats),但适配器 webhook 直接挂在 Router 根路径,不要/api 前缀;填 /api/qq/webhook 会命中 Bearer 鉴权,QQ 平台回调会 401。

  4. 勾选事件:群事件C2C 私聊频道事件(按需)

  5. 保存时平台会发 SIGN_VERIFY 签名校验,适配器会自动响应

Host 对含 /webhook 的路径免 Bearer 鉴权,由 QQ 的 X-Signature-Ed25519 验签。

webhook 独立端口

yaml
mode: webhook
port: 8087
path: /qq/webhook

回调填 https://<域名>:8087/qq/webhook(或反代到该端口)。

获取配置信息

1. 注册 QQ 机器人

  1. 访问 QQ 开放平台
  2. 登录并创建机器人应用
  3. 在「开发设置」中获取:
    • AppID(配置字段 appid): 机器人应用 ID
    • Secret: 机器人密钥(secret
    • Secret: 机器人密钥

2. 配置权限

在机器人设置中:

  • 配置需要的事件订阅
  • 设置消息接收模式(公域/私域)
  • 添加频道/群聊白名单(如需要)

3. 事件订阅(Intents)

可订阅的事件类型:

  • GUILDS - 频道事件
  • GUILD_MEMBERS - 成员变动
  • GUILD_MESSAGES - 频道消息
  • DIRECT_MESSAGE - 私信消息
  • GROUP_AND_C2C_EVENT - QQ 群 @ 消息 + C2C 私聊(GROUP_AT_MESSAGE_CREATE / C2C_MESSAGE_CREATE
  • INTERACTION - 互动事件

使用示例

基础消息处理

typescript
import { addCommand, MessageCommand } from 'zhin.js'

addCommand(new MessageCommand('hello <name:text>')
  .action(async (message, result) => {
    return `你好,${result.params.name}!`
  })
)

频道消息

typescript
import { onMessage } from 'zhin.js'

onMessage(async (message) => {
  // 仅处理频道消息
  if (message.$channel.type === 'channel') {
    console.log(`频道消息:${message.$raw}`)
  }
})

群聊消息

typescript
import { onGroupMessage } from 'zhin.js'

onGroupMessage(async (message) => {
  console.log(`群聊消息来自:${message.$sender.name}`)
  console.log(`消息内容:${message.$raw}`)
})

私聊消息

typescript
import { onPrivateMessage } from 'zhin.js'

onPrivateMessage(async (message) => {
  await message.$reply('收到你的私信了!')
})

发送不同类型消息

typescript
addCommand(new MessageCommand('card')
  .action(async (message) => {
    // 发送 Ark 模板卡片
    return {
      type: 'ark',
      template_id: 23,
      kv: [
        { key: '#TITLE#', value: '标题' },
        { key: '#DESC#', value: '描述' },
        { key: '#PROMPT#', value: '提示' }
      ]
    }
  })
)

addCommand(new MessageCommand('embed')
  .action(async (message) => {
    // 发送 Embed 消息
    return {
      type: 'embed',
      title: 'Embed 标题',
      prompt: '消息提示',
      thumbnail: { url: 'https://example.com/image.png' },
      fields: [
        { name: '字段1', value: '值1' }
      ]
    }
  })
)

消息类型支持

接收消息类型

  • ✅ 文本消息
  • ✅ @ 提及
  • ✅ 图片消息
  • ✅ 表情消息
  • ✅ Ark 模板消息
  • ✅ Embed 消息
  • ✅ Markdown 消息

发送消息类型

  • ✅ 文本消息
  • ✅ 图片消息
  • ✅ Ark 模板消息
  • ✅ Embed 富文本消息
  • ✅ Markdown 消息
  • ✅ 消息引用(回复)

AI 出站 Markdown(默认开启)

官方已支持全量 Markdown 后,适配器会在 $sendMessage 出站前自动把 AI 的纯文本段转为 msg_type=2 的 Markdown 消息(保留 leading reply)。配置项 outboundMarkdown

行为
auto(默认)正文含 **、列表、代码块等 Markdown 语法时才转换
true纯文本也走 Markdown
false始终发纯文本(与旧行为一致)
yaml
endpoints:
  - context: qq
    name: my-bot
    outboundMarkdown: auto   # 或 true / false

含图片/文件等富媒体的多段消息不会合并,避免破坏分条发送。

API 方法

基础方法

typescript
const endpoint = app.adapters.get('qq')?.endpoints.get('你的机器人ID')

// 发送私信
await endpoint.sendPrivateMessage(userId, '消息内容')

// 发送群消息
await endpoint.sendGroupMessage(groupId, '消息内容')

// 发送频道消息
await endpoint.sendGuildMessage(channelId, '消息内容')

// 撤回消息
await endpoint.$recallMessage(messageId)

消息 ID 格式

本适配器使用特殊的消息 ID 格式来区分不同类型的消息:

  • 私信:private-{userId}:{messageId}
  • 群聊:group-{groupId}:{messageId}
  • 频道:channel-{channelId}:{messageId}
  • 私域频道:direct-{guildId}:{messageId}

注意事项

接收模式

  • 公域模式 (public): 仅接收 @ 机器人的消息
  • 私域模式 (private): 可接收频道内所有消息(需要申请权限)

频率限制

QQ 机器人有严格的频率限制:

  • 主动消息:每个用户每天 5 条
  • 被动消息(回复):无限制
  • 建议在被动模式下使用(用户 @ 后回复)

沙箱环境

开发时可以使用沙箱环境测试:

typescript
{
  context: 'qq',
  sandbox: true,  // 启用沙箱
  // ...其他配置
}

常见问题

Q: 机器人无法收到消息?

A: 检查以下几点:

  1. AppID、Secret 是否正确;intents 必须含 GROUP_AND_C2C_EVENT(不是旧名 GROUP_AT_MESSAGE_CREATE
  2. 沙箱环境:在 QQ 开放平台 → 开发设置 → 沙箱配置,把测试 QQ 群 加入沙箱白名单(私聊能通不代表群已配置)
  3. 机器人是否已被拉入该 QQ 群(日志应出现 qq notice group.add_robot
  4. 群管理员是否在机器人资料页 开启消息接收qq notice group.msg_receive_open);若出现 group.msg_receive_close 则群聊不会推送
  5. 公域/群聊场景必须 @ 机器人 才会推送(GROUP_AT_MESSAGE_CREATE
  6. QQ 频道QQ 群 是两套事件:频道 @ 需要 GUILD_MESSAGESPUBLIC_GUILD_MESSAGES,不是 GROUP_AND_C2C_EVENT

Q: Typing Indicator 群聊报 40034105 主动消息失败, 无权限

A: QQ 群聊不允许无引用的主动消息。框架已对 QQ 群场景的「正在处理中…」自动附带 reply 引用触发消息;若仍失败,可在 Endpoint 配置中关闭群聊 typing:

yaml
typingIndicator:
  enabled: true
  groupConfig:
    type: none

Q: 发送消息失败?

A: 可能的原因:

  1. 超过主动消息频率限制
  2. 没有对应频道/群的发送权限
  3. 消息格式不符合规范
  4. Token 已过期或失效

Q: 如何处理不同平台?

A: 使用 platform 配置:

  • qq: QQ 频道和群聊
  • qzone: QQ 空间(如支持)

相关链接

许可证

MIT License

贡献

欢迎提交 Issue 和 Pull Request!

基于 MIT 许可发布