node-cq-websocket

A Node SDK for developing QQ chatbots based on WebSocket, which is depending on CoolQ and CQHTTP API plugin.

View project on GitHub

SDK 介紹

核心概念

CQWebSocket SDK 是基於 CQHTTP API 插件之 WebSocket 通訊,底層封裝了兩個 socket,分別為 /api/event (詳細功能描述可見 coolq-http-api/websocket)。

CQWebSocket SDK 使開發者能夠更專心於機器人應用的開發,SDK 為開發者提供底層連線的維護、斷線重連等功能,並第一手先處理了上報事件,依照不同事件類型,將事件文本分發至各事件監聽器處理。

自動獲取機器人QQ號

若機器人配置 enableAPI 為 true, 且沒有通過 qq 項配置機器人ID的話, 連線建立成功後會主動發送 API 請求向 CQHTTP API 取得酷Q正登錄的QQ號作為機器人QQ號。

此操作為異步操作, 在API響應之前, @.me 事件均不會發布。

除非真的有人QQ號是 -1, 哪尼口雷 Σ(*゚д゚ノ)ノ

事件傳播

事件具有向上傳播的機制,一個事件上報之後,該事件之所有親事件也會依序上報。 事件名稱以 . 相互連接,形成具有繼承關係的結構,如 message (任意消息事件) 為 message.group (群消息) 的親事件。 關於事件親子關係的構成,可參考事件樹

舉個例子,群消息有人@某機器人,該機器人則會首先上報 message.group.@.me 事件,該事件之親事件由下而上依序為 message.group.@, message.groupmessage ,則這幾個事件也會依照這個順序上報,這樣稱為一次事件傳播

快速響應

message 及其子事件支援此機制。

message 及其子事件監聽器的第一個參數: CQEvent 類別實例,在這個機制中扮演重要的角色。 透過 CQEvent 實例,所有監聽器皆可在自己的運行期間調用 CQEvent #stopPropagation() 方法聲明自己的處理權,以截獲事件並阻斷後續監聽器的調用,並立即以該事件返回之文字訊息(或透過調用 CQEvent #setMessage(msg) 設定之文字訊息,也可以透過 Promise 對象 resolve 之文字訊息)作為響應,送回至 CQHTTP API 插件端。

由於在一次事件傳播中的所有監聽器都會收到同一個 CQEvent 實例,因此對於響應的決定方式,除了 CQEvent #stopPropagation() 所提供的事件截獲機制之外,也可以採取協議式的方式,就是透過每個監聽器調用 CQEvent #getMessage() CQEvent #setMessage(msg) 協議出一個最終的響應訊息。

範例: 協議式響應

假設機器人具有以下代碼。

// app.js

const { CQWebSocket } = require('cq-websocket')
const bot = CQWebSocket()

const plugins = [
  require('./pluginA'),
  require('./pluginB')
]

plugins.forEach(plugin => {
  bot.on('message.private', plugin)
})

// pluginA.js
module.exports = function (e) {
  if (e.hasMessage()) {
    e.appendMessage(' world!')
  } else {
    e.setMessage('Hello')
  }
}
// pluginB.js
module.exports = function (e) {
  if (e.hasMessage()) {
    e.appendMessage(' CQWebSocket!')
  } else {
    e.setMessage('Hi')
  }
}

此時若機器人收到了一則私人消息,則會響應 "Hello CQWebSocket!"

我們也可以藉此機制,加入消息過濾的功能。 首先在機器人的 app.js 加入以下代碼。

// app.js
const pluginGuard = require('./pluginGuard')

// 於最上層親事件檢查所有響應訊息
bot.on('message', pluginGuard)
// pluginGuard.js
module.exports = function (e) {
  if (e.hasMessage()) {
    e.stopPropagation()

    // 關鍵字過濾
    e.setMessage(
      e.getMessage().replace(/關鍵字/g, '')
    )
  }
}

發送結果追蹤

不論是快速響應或是 API 調用均有此機制。

追蹤快速響應之結果,可通過 CQEvent #onResponse() 設置結果監聽器, 並透過 CQEvent #onError() 處理響應的錯誤。 若沒有 CQEvent #onError() 進行錯誤處理, 發生響應錯誤時會觸發 error 事件

追蹤 API 調用之結果,可以利用 bot(method[, params[, options]]) 返回的 Promise 對象進行追蹤。

消息格式

上報消息

message 事件中,監聽器第三個參數為一個 CQTag 的數組,關於該數組內可能出現的元素,可以參考CQ 碼相關類別

※除了已定義的 CQTag 之外,其餘的內容都會當作 CQText 的字符串處理。

舉例,上報消息中含有一個不在定義中的 CQ 碼 "[CQ:unknown,key=value]",則此 CQ 碼會被 Parser 判定為一個 CQText 實例,等同 new CQText('[CQ:unknown,key=value]')

發送消息

不論是快速響應或是 API 調用發送消息,均可使用以下消息格式。

  1. 字符串格式
  2. 數組格式

其中,數組格式在本 SDK 的擴增下,支持了將本 SDK 的 CQ 碼相關類別直接作為數組的內容。

首先可以先了解一下 CQHTTP API 所提供的消息段對象,此消息段在這邊我們姑且稱之為 CQHTTPMessage

快速響應或是 API 調用發送消息時,可以使用一個含有 CQHTTPMessage CQTag string 的數組作為消息文本。 (CQTag 可參考 CQ 碼相關類別)

舉個快速響應的例子:

bot.on('message', () => {
  return [
    {
      type: 'at',
      data: {
        qq: '123'
      }
    },
    '你好~ ',
    new CQEmoji(129303) // 🤗
  ]
})

每當機器人收到消息時,便會回應 "[CQ:at,qq=123]你好~ 🤗"

延伸閱讀