Skip to content

About

karin QQBotAdapter

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Repository files navigation

@karinjs/adapter-qqbot

Karin 的 QQ 官方机器人适配器。

安装

pnpm add @karinjs/adapter-qqbot

要求:Node.js >= 22、Karin >= 1.15

配置

首次启动没有 QQBot 配置时,使用任意已连接的机器人或在控制台中发送 #QQBot登录 完成扫码授权。

也可以访问 Karin WebUI 可视化编辑 或者 手动编辑:@karinjs-adapter-qqbot/config/config.json。

[
  {
    "name": "我的机器人",
    "appId": "1234567890",
    "secret": "your-secret",
    "qqEnable": true,
    "guildEnable": true,
    "guildMode": 0,
    "event": { "type": 2 }
  }
]
  • event.type: 0 关闭、1 Webhook、2 WebSocket(默认)。
  • guildMode: 0 公域,只收 @ 消息;1 私域,接收全部频道消息。

旧版本里的 markdown.enable 已移除。发送通道现在由平台能力决定,不再需要用户配置: QQ 单聊和群聊的自定义 Markdown 已对所有机器人开放,固定走 Markdown 通道;频道场景仍需向 QQ 申请内邀开通,适配器会先尝试 Markdown,被平台以权限错误拒绝后自动降级为普通消息,并记住这个 机器人,本次运行内不再重试。开通后重启 Karin(或改一次配置)即可恢复。配置文件里残留的 markdown 字段会被忽略,下次保存配置时自动清除。

频道降级为普通消息后按钮无法发送——平台的 keyboard 只能挂在 Markdown 消息上。

按钮

按钮一般和 Markdown 一起发送,常用有三种:

  • 跳转按钮:点击后打开 link。
  • 指令按钮:点击后把 data 发送成一条普通消息。
  • 回调按钮:点击后不会在聊天框发消息,但适配器会下发一条 Karin 消息事件,消息内容就是 data。

下面这个示例先发送一组按钮,然后分别接住“指令按钮”和“回调按钮”的点击结果:

import karin, { segment } from 'node-karin'

export const buttonDemo = karin.command(/^按钮示例$/, async (e) => {
  await e.reply([
    segment.markdown('#### 按钮示例\n请选择一个操作:'),
    segment.button([
      { text: '打开文档', link: 'https://bot.q.qq.com/wiki/' },
      { text: '发送指令', data: '按钮示例 帮助', enter: true },
      { text: '回调确认', callback: true, data: '按钮示例 确认' },
    ]),
  ])
})

export const buttonHelp = karin.command(/^按钮示例 帮助$/, async (e) => {
  await e.reply('这是指令按钮发送出来的消息。')
})

export const buttonConfirm = karin.command(/^按钮示例 确认$/, async (e) => {
  await e.reply(`收到回调按钮:${e.msg}`)
})

如果你希望回复自动带上一组按钮,可以把按钮单独注册出来。下面这个分页示例会根据当前消息生成“上一页 / 下一页”:

import karin, { segment } from 'node-karin'

const getPage = (msg = '') => Number(msg.match(/^菜单(?:\s+(\d+))?$/)?.[1] || 1)

export const menuDemo = karin.command(/^菜单(?:\s+\d+)?$/, async (e) => {
  const page = getPage(e.msg)
  await e.reply(segment.markdown(`#### 菜单\n当前第 ${page} 页`))
})

export const menuKeyboard = karin.button(/^菜单(?:\s+\d+)?$/, (next, args) => {
  const page = getPage(args?.e?.msg)

  return segment.keyboard([
    [
      { text: '上一页', data: `菜单 ${Math.max(1, page - 1)}`, enter: true },
      { text: '下一页', data: `菜单 ${page + 1}`, enter: true },
    ],
  ])
})

简单理解:karin.button 是“自动追加按钮”的规则。适配器回复时会自动用 buttonHandle(e.msg, { e }) 查找匹配规则,所以用户发送 菜单 2,就会命中 /^菜单(?:\s+\d+)?$/,按钮函数再通过 args?.e?.msg 算出当前页。

自己调用 buttonHandle 时只记三点:

  • 参数一是匹配文本,通常填 e.msg,也可以是 '菜单'。
  • 参数二是给按钮函数的上下文,常用 { e };需要状态可以写 { e, page: 2 }。
  • 一个规则里调用 next(),才会继续匹配后面的规则。

回调按钮的 data 会变成一条 Karin 消息内容,所以建议直接写成插件能识别的命令,例如 按钮示例 确认、菜单 下一页。

发送消息与资源文件

适配器会尽量帮你发送图片、视频、语音和文件。推荐配置 fileToUrl 上传处理器,把本地文件、截图、Base64 等资源上传到你的图床、对象存储或 CDN,并返回一个 QQ 能访问的链接。

配置 fileToUrl 后:

  • 本地图片可以正常嵌入 Markdown 消息。
  • 视频、语音和文件会优先使用你返回的链接发送,通常比直接上传给 QQ 更稳定。
  • 如果资源本身已经是 http / https 链接,适配器会直接使用它。

没有配置 fileToUrl 时:

  • 单独发送图片、视频、语音、文件时,适配器会尝试直接交给 QQ 发送。
  • 较大的资源会使用 QQ 的大文件上传流程,优先保证消息能发出去。
  • 较大的图片或视频直接交给 QQ 发送时,QQ 客户端可能会把它显示成群文件,而不是图片或视频卡片;配置 fileToUrl 后通常可以避免这种显示问题。
  • 直接交给 QQ 只是兜底方案,生产环境仍然建议准备自己的文件服务。

注意:Markdown 里的图片依赖 fileToUrl。

Markdown 里的图片只能写成 QQ 能访问的链接,适配器不能把 Markdown 文本里的图片自动当成附件上传。QQ 单聊和群聊有下面提到的内置临时图床可以兜住,频道场景则必须自己配置 fileToUrl。

你可以在自己的 Karin 插件中编写并注册 fileToUrl Handler:

// plugins/karin-plugin-example/fileToUrl.js
import karin, { common } from 'node-karin'
import size from 'image-size'

export const uploadResource = karin.handler('fileToUrl', async (args) => {
  const { file, type, filename } = args
  const buffer = await common.buffer(file)

  // 由你实现:上传到图床/对象存储,返回可公开访问的 URL。
  const url = await uploadToYourStorage(buffer, filename || 'file.bin')

  if (type !== 'image') return { url }

  const dimension = size(buffer) // 获取图片宽高
  return {
    url,
    width: dimension.width || 100,
    height: dimension.height || 100
  }
})

注意:Handler 插件的 key 必须精确为 fileToUrl。

图片必须返回 { url, width, height };其他资源返回 { url }。

建议生产环境都配置 fileToUrl。这样图片、视频和文件都可以先上传到你自己的文件服务,再交给 QQ 发送,成功率和可控性都会更好。

没有自己的图床也不至于完全用不了 Markdown 图片:适配器内置了一个基于 QQ 官方分片上传的临时图床,QQ 单聊和群聊里会自动生效,把本地图片、截图、Base64 图片换成临时直链后嵌进 Markdown。它排在你自己的 fileToUrl 之后,只在你没有配置、或者你的图床这次没接手时才出手,拿到的地址会过期,因此仅供当次发送使用。频道场景和文件类型用不了它,仍然需要自备 fileToUrl。

事件

  • 支持 QQ 单聊、群聊、频道、频道私信和按钮回调。
  • 支持 GROUP_MESSAGE_CREATE 全量群消息和 author.member_role 身份字段。
  • 支持 GROUP_MEMBER_ADD、GROUP_MEMBER_REMOVE 群成员进退群事件。
  • 单聊同一 msg_id 最多回复 4 次;群聊规则保持官方限制。

开发

pnpm install
pnpm build

License

MIT

About

karin QQBotAdapter

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages