js-bot 是一個基於 酷 Q Websocket 服務(CoolQ HTTP API 插件)的瀏覽器端聊天機器人框架及開發工具,用 Typescript + React 開發,你能夠在「聊天模式」下與好友/羣聊天,也能夠在其 「控制檯模式」 下輸入 Javascript 代碼運行或調用 js-bot 及 coolq-http 提供的 api ,並註冊消息等事件的響應函數來實現本身的機器人,還能夠在「虛擬聊天模式」下用虛擬帳號向機器人發送消息來測試本身編寫的機器人,全部這一切均可以在 pandolia.net/js-bot 這一個網頁上進行。html
js-bot 控制檯模式效果以下:git
聊天模式效果以下:github
用酷 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
控制檯模式下,能夠直接在輸入框輸入 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/groups 中保存的是 Contact 對象,具備 type/qq/name 屬性和 send 方法,type 爲 BUDDY 表示好友,GROUP 表示羣,另外 VIRTUAL_BUDDY 表示虛擬好友。
在控制檯模式下運行代碼,與在瀏覽器自帶的開發者工具的 Devtool-Console 中運行代碼,有兩點不同:
對於第二點,要注意的是,即使是在控制檯模式下,也不能將 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" 。
爲了測試本身開發的機器人程序,須要利用其它帳號向本帳號發送消息,這顯然很不方便,所以 js-bot 提供 虛擬聊天模式 來快速測試。
在 js-bot 中的 最近 聯繫人列表內,點擊第二個聯繫人 [ yourname ] ,就進入了虛擬聊天模式,此時,用戶扮演 虛擬好友 向本帳號發送消息。在此模式下輸入文本併發送時,機器人會收到一條來自 虛擬好友 的消息, handler.onMessage 一樣會被調用,此時,contact 的 type 屬性爲 cq.VIRTUAL_BUDDY , name 屬性爲 "虛擬好友" 。
例如,對於上一節的 onMessage 函數,在 虛擬聊天 模式下發送 "-hello" ,則機器人會自動回覆 "你好,虛擬好友" 。
若是 js-bot 已鏈接了酷 Q 的 Websocket 服務,那麼 js-bot 的普通聊天模式是可用的,能夠在頁面上點擊好友和羣,而後進行聊天。
當進入到普通聊天模式時, js-bot 會將頁面上的輸入框上方的模式信息文本顏色調得更加明顯,提醒用戶已進入聊天模式,避免發送無關的信息。
若是沒有開啓酷 Q 的 Websocket 服務,普通聊天模式沒法使用,但控制檯模式和虛擬聊天模式仍然是可用的。
本項目採用 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 列表 ,調用時需注意下面兩個問題:
如下列出 js-bot 內部可以使用的常量、變量和方法,禁止直接修改變量,只能經過調用方法來改變 js-bot 的內部狀態。
// 聯繫人類型: 好友/羣/無/控制檯/本身/虛擬好友
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;
複製代碼
// 消息方向 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,
}
複製代碼
class Contact {
// 類型 BUDDY/GROUP/VIRTUAL_BUDDY ,表明 好友/羣/虛擬好友
type: ContactType;
// 帳號
qq: string;
// 名稱
name: string;
// 向本聯繫人發送消息,發送成功返回 null ,發送失敗則拋出 Error 錯誤
send = async (text: string): Promise<null> => { /* */ }
}
複製代碼
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 { /* */ }
}
複製代碼
// 好友列表/羣列表/最近聯繫人列表
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) { /* */ }
複製代碼
感謝 Richard Chien 開發的強大的 coolq-http-api 。