js-bot:用 Typescrpit + React + CoolQ + cqhttp 插件打造的網頁端聊天機器人

1、介紹

js-bot 是一個基於 酷 Q Websocket 服務(CoolQ HTTP API 插件)的瀏覽器端聊天機器人框架及開發工具,用 Typescript + React 開發,你能夠在「聊天模式」下與好友/羣聊天,也能夠在其 「控制檯模式」 下輸入 Javascript 代碼運行或調用 js-bot 及 coolq-http 提供的 api ,並註冊消息等事件的響應函數來實現本身的機器人,還能夠在「虛擬聊天模式」下用虛擬帳號向機器人發送消息來測試本身編寫的機器人,全部這一切均可以在 pandolia.net/js-bot 這一個網頁上進行。html

js-bot 控制檯模式效果以下:git

console-mode.png

聊天模式效果以下:github

chat-mode.png

2、系統需求

用酷 Q 登陸帳號後,啓用 cqhttp 插件,以後退出酷 Q ,找到 「data\app\io.github.richardchien.coolqhttpapi\config」 下相應帳號的配置文件,將 websocket 相關的配置修改以下:web

{
    "use_ws": true,
    "ws_host": "127.0.0.1",
    "ws_port": 6700,
    "access_token": "mytoken",
}

複製代碼

再次登陸,等酷 Q Websocket 服務啓動後,用瀏覽器打開 pandolia.net/js-bot 網址,就可使用 js-bot 盡情的玩耍了,此時用其餘帳號給本帳號發送 "-joke",本帳號會自動回覆一則笑話。typescript

若是酷 Q Websocket 服務是在其餘機器上部署的,能夠在 url 參數中指定其地址及 token ,例如:pandolia.net/js-bot/?ws_…npm

對於 IE/Edge ,因爲瀏覽器默認禁止 Javascript 代碼鏈接不一樣主機的 WebSocket 服務,解決方案爲:Internet選項 -> 安全 -> 本地Internet -> 站點,把全部勾選取消。或者下載本項目代碼,build 以後將 html 文件部署在本地再訪問。json

若是不開啓酷 Q Websocket 服務, js-bot 的 「控制檯模式」 和 「虛擬聊天」 模式仍然是可使用的,但與 QQ 相關的功能全都不可用。api

3、控制檯模式

控制檯模式下,能夠直接在輸入框輸入 Javascript 代碼運行,例如:數組

// 打印文本
>>> print('hello')
hello

// 查找帳號爲 3497303033 的好友
>>> buddies.get('3497303033')
[ 好友 feng,3497303033 ]

// ans 中保存上一次命令的運行結果
>>> ans
[ 好友 feng,3497303033 ]

// 向好友發送消息 "hello"
>>> ans.send('hello')
null

// 調用 ai.joke() 生成一個笑話
// 注意: ai.joke() 返回的是一個 Promise 對象,js-bot 解釋器會
// 等待其 fullfilled ,將結果保存到 ans 中再返回
>>> ai.joke()
中午去存錢,排隊時一美女在後面問我:「存錢是嗎?」「恩!」「我正好要取
錢,反正你要存,不如把錢給我,咋倆就不用排隊了。」我想一想以爲有理,
因而把錢給她了!

// 向好友發送笑話
>>> buddies.get('3497303033').send(ans)
null
複製代碼

控制檯模式下,可以使用 js-bot 提供的內部變量和方法:瀏覽器

  • buddies: 好友列表,ContactTable 對象,具備 type/name/length 屬性及 get/map/forEach/filter/find 方法
  • groups: 羣列表,ContactTable 對象
  • ans: 上一次命令的運行結果
  • print/clr/debug/info/warn/error: 打印、清屏及日誌方法
  • showModal/popMoal: 顯示信息框,例如: showModal("xxx")
  • BUDDY/GROUP/NOTYPE/CONSOLE/MYSELF/VIRTUAL_BUDDY: 常量,表示聯繫人的類型
  • handler.onMessage/handler.onCqEvent: 消息事件及其餘事件的響應函數,能夠改寫這兩個屬性

buddies/groups 中保存的是 Contact 對象,具備 type/qq/name 屬性和 send 方法,type 爲 BUDDY 表示好友,GROUP 表示羣,另外 VIRTUAL_BUDDY 表示虛擬好友。

在控制檯模式下運行代碼,與在瀏覽器自帶的開發者工具的 Devtool-Console 中運行代碼,有兩點不同:

  • 1) 以上所述的 js-bot 提供的變量,在 Devtool-Console 中是不可見的,須要加 cq. 才能訪問,例如: cq.buddies
  • 2) 控制檯模式下,若是命令返回的是一個 Promise 對象,js-bot 解釋器會等待其 fullfilled 再返回,而 Devtool-Console 則不會,例如,對於 ai.joke() ,在 Devtool-Console 下若是須要打印其返回結果,則應這樣調用: cq.ai.joke().then(function (t) { console.log(t) })

對於第二點,要注意的是,即使是在控制檯模式下,也不能將 ai.joke() 的結果直接傳遞給 send 方法,而應該這樣調用: ai.joke().then(function (t) { buddies.get('3497303033').send(t) })

能夠在控制檯模式下對 handler.onMessage 和 handler.onCqEvent 進行從新賦值,從而實現本身的聊天機器人,例如:

>>>
handler.onMessage = function (contact, message) {
    if (message.content === '-hello') {
        contact.send('你好,' + contact.name)
            .then(function() {
                popModal('發消息成功');
            });
    }
}
複製代碼

在控制檯中運行以上代碼後,當 本帳號 收到內容爲 "-hello" 的消息時,會自動回覆: "你好,xx"

4、虛擬聊天模式

爲了測試本身開發的機器人程序,須要利用其它帳號向本帳號發送消息,這顯然很不方便,所以 js-bot 提供 虛擬聊天模式 來快速測試。

在 js-bot 中的 最近 聯繫人列表內,點擊第二個聯繫人 [ yourname ] ,就進入了虛擬聊天模式,此時,用戶扮演 虛擬好友 向本帳號發送消息。在此模式下輸入文本併發送時,機器人會收到一條來自 虛擬好友 的消息, handler.onMessage 一樣會被調用,此時,contact 的 type 屬性爲 cq.VIRTUAL_BUDDY , name 屬性爲 "虛擬好友"

例如,對於上一節的 onMessage 函數,在 虛擬聊天 模式下發送 "-hello" ,則機器人會自動回覆 "你好,虛擬好友" 。

5、普通聊天模式

若是 js-bot 已鏈接了酷 Q 的 Websocket 服務,那麼 js-bot 的普通聊天模式是可用的,能夠在頁面上點擊好友和羣,而後進行聊天。

當進入到普通聊天模式時, js-bot 會將頁面上的輸入框上方的模式信息文本顏色調得更加明顯,提醒用戶已進入聊天模式,避免發送無關的信息。

若是沒有開啓酷 Q 的 Websocket 服務,普通聊天模式沒法使用,但控制檯模式和虛擬聊天模式仍然是可用的。

6、開發模式

本項目採用 Typescript + React 開發,能夠下載本項目源碼,運行 npm install 和 npm start 啓動本項目,並按本身的須要進行開發和擴展。建議採用 Vs Code (須要安裝 Eslint 和 Tslint 插件)。

開發和擴展 js-bot 時,修改 src/myhandler-ts.ts 文件就能夠了,在此文件中導出兩個事件函數:

import Contact from './cq/Contact';
import cq from './cq';

export default {
    onMessage: async (contact: Contact, message: IMessage) => {
        if (message.content !== '-joke') {
            return;
        }

        const joke = await cq.ai.joke();
        await contact.send(joke);
        cq.popModal('發送笑話成功.');
    },

    onCqEvent: async (data: any) => {
        return;
    },
};
複製代碼

若是不會 Typescript ,也能夠用 Javascript 開發,修改 src/myhandler-js.js 文件就能夠了,須要在 src/index.tsx 文件中改成:import handler from './myhandler.js'

本項目中的其餘文件,建議不要修改,若是確實須要修改,請在 項目 github 主頁 上發 issue 或 pull-request 。

好友消息和羣消息以外的其餘事件會被傳遞給 onCqEvent 函數,支持的事件列表及各事件的字段說明詳見 cqhttp 事件列表

在事件函數中,除了發送消息,也能夠用 cq.api 方法調用 cqhttp 提供的 api (例如:發送好友贊等),示例以下:

await cq.api('send_like', { user_id: 158297369 });
複製代碼

其餘 cqhttp-api 見 cqhttp API 列表 ,調用時需注意下面兩個問題:

  • 第一個參數爲 cqhttp-api 名稱,前面不含斜槓 "/"
  • 第二個參數爲 cqhttp-api 參數,與用戶 id 相關的字段所有采用 number 類型(js-bot 內部採用的是 string 類型)

7、 js-bot API

如下列出 js-bot 內部可以使用的常量、變量和方法,禁止直接修改變量,只能經過調用方法來改變 js-bot 的內部狀態。

1. 常量(src/cq/CqStore.tsx)

// 聯繫人類型: 好友/羣/無/控制檯/本身/虛擬好友
export const BUDDY = 0;
export const GROUP = 1;
export const NOTYPE = 2;
export const CONSOLE = 3;
export const MYSELF = 4;
export const VIRTUAL_BUDDY = 5;

// 消息方向,LEFT 表明消息畫在左邊,RIGHT 表明消息畫在右邊
export const LEFT = 0;
export const RIGHT = 1;

// 日誌級別
export const DEBUG = 0;
export const INFO = 1;
export const WARN = 2;
export const ERROR = 3;

// 每一個聯繫人保存的消息總數最大值
export const MAX_MESSAGES_SIZE = 400;

// 環境(在 .env 文件內定義),項目名稱, CQ-WEBSOCKET 參數, github 地址
export const PROJECT_NAME: string;
export const DEFAULT_WS_HOST: string;
export const DEFAULT_TOKEN: string;
export const DEFAULT_RECENTS: string;
export const GITHUB_URL: string;
複製代碼

2. 類型及接口(src/types.d.ts)

// 消息方向 LEFT/RIGHT
type DirectionType = 0 | 1;

// 消息接口( onMessage 的第二個參數爲 IMessage 對象)
interface IMessage {
    readonly id: string;
    readonly direction: DirectionType;
    readonly from: string;
    readonly content: string;
}

// 聯繫人類型 BUDDY ~ VIRTUAL_BUDDY
type ContactType = 0 | 1 | 2 | 3 | 4 | 5;

// 日誌級別 DEBUG ~ ERROR
type LogLevel = 0 | 1 | 2 | 3;

// 事件處理接口
interface IHandler {
    onMessage: (c: Contact, m: Message) => any,
    onCqEvent: (data: any) => any,
}
複製代碼

3. Contact 類(src/cq/Contact.tsx)

class Contact {

    // 類型 BUDDY/GROUP/VIRTUAL_BUDDY ,表明 好友/羣/虛擬好友
    type: ContactType;
    
    // 帳號
    qq: string;

    // 名稱
    name: string;

    // 向本聯繫人發送消息,發送成功返回 null ,發送失敗則拋出 Error 錯誤
    send = async (text: string): Promise<null> => { /* */ }

}
複製代碼

4. ContactTable 類(src/cq/ContactTable)

class ContactTable {

    // 類型 BUDDY/GROUP/NOTYPE 表明 好友列表/羣列表/最近聯繫人列表
    type: ContactType;

    // 名稱
    get name() { /* */ }

    // 分別以數組和字典保存全部聯繫人,請勿訪問這兩個屬性
    _list: Contact[] = [];
    _dict: Map<string, Contact> = new Map();

    // 聯繫人個數
    get length() { return this._list.length; }

    // 遍歷、查找聯繫人
    map = this._list.map.bind(this._list);
    forEach = this._list.forEach.bind(this._list);
    filter = this._list.filter.bind(this._list);
    find = this._list.find.bind(this._list);

    // 查詢聯繫人
    // get('3497303033') 返回 qq 爲 '3497303033' 的 Contact 對象
    // get(0) 返回第一個 Contact 對象
    // 對象不存在時返回 undefined
    get(qqOrIndex: string | number): Contact | undefined { /* */ }
}
複製代碼

5. 全局 cq 對象(src/cq/CqStore.tsx)

// 好友列表/羣列表/最近聯繫人列表
export const buddies = new ContactTable(BUDDY);
export const groups = new ContactTable(GROUP);
export const recents = new ContactTable(NOTYPE);

// 事件處理對象
export let handler: IHandler;

// 控制檯上次命令運行結果
export let ans;

// 打印、清屏
export function print(line = '') { /* */ }
export function clr() { /* */ }

// 日誌
export let level: LogLevel;
export function setLogLevel(_level: LogLevel) { /* */ }
export function debug(_level: LogLevel, msg: any) { /* */ }
export function info(_level: LogLevel, msg: any) { /* */ }
export function warn(_level: LogLevel, msg: any) { /* */ }
export function error(_level: LogLevel, msg: any) { /* */ }

// 模態對話框
export async function showModal(msg: any) { /* */ } // 展現
export function closeModal() { /* */ } // 關閉
export function popModal(msg: any, t = 2500) { /* */ } // 展現,t 毫秒後關閉

// cqhttp 服務地址及 token
export const ws_host: string;
export const token: string;

// ai (見 src/ai/index.tsx 目前只有 ai.joke )
export const ai;

// api
export function api(action: string, params: any = null): Promise<any> { /* */ }

// 退出並重啓 js-bot
export function abort(msg: string) { /* */ }

// 重置 cqhttp 服務地址
export function reset(w = DEFAULT_WS_HOST, t = DEFAULT_TOKEN, r = DEFAULT_RECENTS) { /* */ }
複製代碼

8、特別緻謝

感謝 Richard Chien 開發的強大的 coolq-http-api

相關文章
相關標籤/搜索