diff --git a/.gitignore b/.gitignore index d192865..68d7939 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ ssl *.key *.pem sessions +/test \ No newline at end of file diff --git a/Readme.md b/Readme.md index 73f21b7..9b60aae 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,40 @@ -# node.js开发酷Q机器人封装 +# node-coolq-robot -作者:草梅友仁 +### 作者:草梅友仁 -本项目基于cq-websocket 项目进行了二次封装,用async/await重新封装函数,使调用api更加方便。 +本项目基于cq-robot、coolq-http-api、cq-websocket ,按照官方SDK风格重新封装了事件函数和api函数 + +## 项目特色 + +1. 仿官方SDK风格,熟悉易语言版的很快就可以上手。 +3. 仿酷Q目录设计,可以像原生酷Q插件那样载入插件,也便于插件的开发 +3. 可以使用JavaScript和TypeScript进行开发,具有一定的跨平台性 + +## 快速开始 + +1. clone本项目 + +2. 修改src/conf/setting.jsonc中的accessToken等,若不需要修改可以默认 +3. 酷Q安装coolq-http-api,修改 酷Q Pro\data\app\io.github.richardchien.coolqhttpapi\config 下的相关配置,其中access_token与上方accessToken保持一致 +4. 执行 npm run build 生成dist文件 +5. 执行dist/index.js,开发环境下可以使用nodemon,生产环境可以使用pm2 +6. 在控制台查看效果 + +## 安装插件 + +1. 所有插件放在src/app目录下,新建一个由appId命名的文件夹(下称为应用根目录),入口文件必须为index.js/index.ts(编译后均为index.js),配置项为index.json或index.jsonc【关于jsonc需要特别解释下,就是支持注释的json,在VScode中为JSON with Comments格式】 + +2. 应用所有数据需存放在 [appId]/data/ 目录下,以免给用户造成困扰 +3. 考虑到插件可能会有自己的node_modules依赖,将依赖也装在 项目根目录 下即可(注意,如果安装在项目根目录有问题,也可安装在 应用根目录 ,只不过src和dist目录下均需安装) +4. 注意:最终执行的文件为dist目录下的内容,因此如果有除了json/jsonc格式以外的文件需要从src目录下复制,请修改gulpfile.js文件 + +## 插件开发 + +对于开发者,本人也提供了demo, + +## 项目依赖 + +**node-cq-websocket : https://github.com/momocow/node-cq-websocket** + +**coolq-http-api : https://github.com/richardchien/coolq-http-api** diff --git a/dist/app/com.example.demo/index.js b/dist/app/com.example.demo/index.js new file mode 100644 index 0000000..e6494b1 --- /dev/null +++ b/dist/app/com.example.demo/index.js @@ -0,0 +1,250 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +// import * as CQ from '../../bin/CQ.old' +const cq_robot_1 = require("cq-robot"); +class App extends cq_robot_1.CQApp { + constructor() { + super('com.example.demo', __dirname); + this.CQ.setDebug(false); + this.isEnable = true; //注意,只有isEnable为true的插件才会载入,可以将isEnable置为false不载入某插件 + } + debug() { + // console.log('debug()方法只会在debug模式下执行') + this.privateMsg('test', 1, 996881204, '这是一条私聊消息', 1); + this.groupMsg('test', 1, 947983200, 996881204, '', '这是一条群消息', 1); + this.discussMsg('test', 1, 580771123, 996881204, '这是一条讨论组消息', 1); + } + /** + * 本函数会在连接建立前执行,可以在此执行初始化代码 + * + * @author CaoMeiYouRen + * @date 2019-07-15 + * @returns {0} + * @memberof App + */ + startup() { + return 0; + } + /** + *本函数会在连接断开后执行,请在此执行插件退出代码 + * + * @author CaoMeiYouRen + * @date 2019-07-15 + * @returns {0} + * @memberof App + */ + exit() { + return 0; + } + /** + * 本函数会在连接建立后立刻执行,可以在此执行初始化代码 + * + * @author CaoMeiYouRen + * @date 2019-07-15 + * @returns {0} + * @memberof App + */ + enable() { + return 0; + } + /** + * 本函数会在连接断开前执行,可以在此执行插件退出代码 + * + * @author CaoMeiYouRen + * @date 2019-07-15 + * @returns {0} + * @memberof App + */ + disable() { + this.isEnable = false; + return 0; + } + /** + * + * Type=21 私聊消息 + * @param {string} subType 消息子类型,friend:来自好友、group:来自群聊、discuss:来自讨论组、other:其他来源 + * @param {number} msgId 消息ID + * @param {number} fromQQ 来源QQ + * @param {string} msg 消息内容 + * @param {number} font 字体 + * @returns {number} 返回值*不能*直接返回文本 如果要回复消息,请调用api发送。 * 这里 返回 1 | CQMsg.MSG_INTERCEPT - 截断本条消息,不再继续处理 + * 注意:应用优先级设置为"最高"(10000)时,不得使用本返回值。 + * 如果不回复消息,交由之后的应用/过滤器处理,这里 返回 0 | CQMsg.MSG_IGNORE - 忽略本条消息 + */ + privateMsg(subType, msgId, fromQQ, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + if (fromQQ === 996881204) { + this.CQ.sendPrivateMsg(fromQQ, `这是${this.APP_ID},你发送了:${msg}`); + } + return cq_robot_1.CQMsg.MSG_INTERCEPT; + }); + } + /** + * + * Type=2 群消息 + * @param {string} subType 消息子类型,normal:正常消息,anonymous:匿名消息,notice:系统提示 + * @param {number} msgId + * @param {number} fromGroup 来源群号 + * @param {number} fromQQ + * @param {string} fromAnonymous 来源匿名者 + * @param {string} msg + * @param {number} font 字体 + * @returns {number} 关于返回值说明, 见 privateMsg 私聊消息 方法 + * + */ + groupMsg(subType, msgId, fromGroup, fromQQ, fromAnonymous, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + if (fromQQ === 996881204) { + this.CQ.sendGroupMsg(fromGroup, `这是${this.APP_ID},你发送了:${msg}`); + } + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=4 讨论组消息 + * @param {string} subType 子类型,目前固定为discuss + * @param {number} msgId + * @param {number} fromDiscuss 来源讨论组 + * @param {number} fromQQ + * @param {string} msg + * @param {number} font + * @returns {number} + * + */ + discussMsg(subType, msgId, fromDiscuss, fromQQ, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + if (fromQQ === 996881204) { + this.CQ.sendDiscussMsg(fromDiscuss, `这是${this.APP_ID},你发送了:${msg}`); + } + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * + * Type=11 群文件上传事件 + * @param {string} subType 子类型,目前固定为group_upload + * @param {number} sendTime 发送时间(时间戳) + * @param {number} fromGroup 来源群号 + * @param {number} fromQQ 来源QQ号 + * @param {CQFile} file 上传文件的信息 + * @returns {number} + */ + groupUpload(subType, sendTime, fromGroup, fromQQ, file) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + *Type=101 群事件-管理员变动 + * @param {string} subType 子类型,set:设置管理员,unset:取消管理员 + * @param {number} sendTime 发送时间(时间戳) + * @param {number} fromGroup 来源群号 + * @param {number} beingOperateQQ 被操作QQ + * @returns {number} + * + */ + groupAdmin(subType, sendTime, fromGroup, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=102 群事件-群成员减少 + * @param {string} subType 子类型,leave:主动退群、kick:成员被踢、kick_me:登录号被踢 + * @param {number} sendTime + * @param {number} fromGroup + * @param {number} fromQQ 操作者QQ(仅子类型为2时存在) + * @param {number} beingOperateQQ 被操作QQ + * @returns {number} + * + */ + groupDecrease(subType, sendTime, fromGroup, fromQQ, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=103 群事件-群成员增加 + * @param {string} subType 子类型,approve:管理员已同意入群、invite:管理员邀请入群 + * @param {number} sendTime 发送时间(时间戳) + * @param {number} fromGroup + * @param {number} fromQQ 操作者QQ(即管理员QQ) + * @param {number} beingOperateQQ 被操作QQ(即加群的QQ) + * @returns {number} + * + */ + groupIncrease(subType, sendTime, fromGroup, fromQQ, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=201 好友事件-好友已添加 + * @param {string} subType 子类型,目前固定为friend_add + * @param {number} sendTime 发送时间(时间戳) + * @param {number} fromQQ 来源QQ + * @returns {number} + * + */ + friendAdd(subType, sendTime, fromQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=301 请求-好友添加 + * @param {number} subType 子类型,目前固定为request_add_friend + * @param {number} sendTime + * @param {number} fromQQ 来源QQ + * @param {string} msg 附言 + * @param {string} responseFlag 反馈标识(处理请求用) + * @returns {number} + * + */ + requestAddFriend(subType, sendTime, fromQQ, msg, responseFlag) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + /** + * + * Type=302 请求-群添加 + * @param {number} subType 请求子类型,add:加群请求、invite:邀请登录号入群 + * @param {number} sendTime + * @param {number} fromGroup + * @param {number} fromQQ + * @param {string} msg 附言 + * @param {string} responseFlag 反馈标识(处理请求用) + * @returns {number} + * + */ + requestAddGroup(subType, sendTime, fromGroup, fromQQ, msg, responseFlag) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } +} +const app = new App(); //类名可以随意 +exports.app = app; +/** + *仅在debug模式下执行,若不需要也可注释掉 + *请注意,因为debug的内容在此处就会执行,因此是最先执行的内容! + */ +if (app.CQ.getDebug()) { + app.debug(); +} diff --git a/dist/app/com.example.js.demo/index.js b/dist/app/com.example.js.demo/index.js new file mode 100644 index 0000000..3884f15 --- /dev/null +++ b/dist/app/com.example.js.demo/index.js @@ -0,0 +1,102 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const cq_robot_1 = require("cq-robot"); +class App extends cq_robot_1.CQApp { + constructor() { + super('com.example.js.demo', __dirname); + this.CQ.setDebug(false); + } + debug() { + //本函数里面的内容仅会在debug模式下执行 + this.privateMsg('test', 1, 996881204, '这是一条私聊消息', 1); + this.groupMsg('test', 1, 947983200, 996881204, '', '这是一条群消息', 1); + this.discussMsg('test', 1, 580771123, 996881204, '这是一条讨论组消息', 1); + } + startup() { + return 0; + } + exit() { + return 0; + } + enable() { + this.isEnable = true; + return 0; + } + disable() { + this.isEnable = false; + return 0; + } + privateMsg(subType, msgId, fromQQ, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + let res = `这是${this.APP_ID},你发送了:${msg}`; + this.CQ.sendPrivateMsg(fromQQ, res); + // 如果要回复消息,请调用 api 发送,则 return CQMsg.MSG_INTERCEPT - 拦截本条消息,不再由其他应用继续处理 //注意:应用优先级设置为"最高"(10000)时,无法使用本返回值 + // 如果不回复消息,交由之后的应用处理,则 return CQMsg.MSG_IGNORE - 忽略本条消息 + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + groupMsg(subType, msgId, fromGroup, fromQQ, fromAnonymous, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + this.CQ.sendGroupMsg(fromGroup, `这是${this.APP_ID},你发送了:${msg}`); + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + discussMsg(subType, msgId, fromDiscuss, fromQQ, msg, font) { + return __awaiter(this, void 0, void 0, function* () { + this.CQ.send_discuss_msg(fromDiscuss, `这是${this.APP_ID},你发送了:${msg}`); + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + groupUpload(subType, sendTime, fromGroup, fromQQ, file) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + groupAdmin(subType, sendTime, fromGroup, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + groupDecrease(subType, sendTime, fromGroup, fromQQ, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + groupIncrease(subType, sendTime, fromGroup, fromQQ, beingOperateQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + friendAdd(subType, sendTime, fromQQ) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + requestAddFriend(subType, sendTime, fromQQ, msg, responseFlag) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } + requestAddGroup(subType, sendTime, fromGroup, fromQQ, msg, responseFlag) { + return __awaiter(this, void 0, void 0, function* () { + return cq_robot_1.CQMsg.MSG_IGNORE; + }); + } +} +const app = new App(); //类名可以随意 +exports.app = app; +/** + *仅在debug模式下执行,若不需要也可注释掉 + *请注意,因为debug的内容在此处就会执行,因此是最先执行的内容! + */ +if (app.CQ.getDebug()) { + app.debug(); +} diff --git a/package.json b/package.json index 20412d4..0f4f3f1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "dependencies": { "cq-robot": "^0.7.2", "cq-websocket": "^2.0.1", - "got": "^9.6.0", "json5": "^2.1.0", "log4js": "^4.4.0", "moment": "^2.24.0", @@ -44,4 +43,4 @@ "typescript": "^3.5.2", "typescript-eslint-parser": "^22.0.0" } -} +} \ No newline at end of file diff --git a/src/app/ltd.cmyr.demo/index.jsonc b/src/app/com.example.demo/index.jsonc similarity index 94% rename from src/app/ltd.cmyr.demo/index.jsonc rename to src/app/com.example.demo/index.jsonc index b486ce0..892879b 100644 --- a/src/app/ltd.cmyr.demo/index.jsonc +++ b/src/app/com.example.demo/index.jsonc @@ -1,6 +1,5 @@ // 酷Q 的Json文件支持以 // 开头的注释。 -// 打包前,应用的 .dll, .json 的文件名须以appid命名,应用appInfo返回的内容须改为appid -// 如 appid=com.example.demo, 则dll及json文件需分别命名为 com.example.demo.dll、com.example.demo.json +// 如 appid=com.example.demo 具体规则见规则见https://d.cqp.me/Pro/%E5%BC%80%E5%8F%91/%E5%9F%BA%E7%A1%80%E4%BF%A1%E6%81%AF { "ret": 1, // 返回码,固定为1 "apiver": 9, // Api版本本sdk为9 diff --git a/src/app/ltd.cmyr.demo/index.ts b/src/app/com.example.demo/index.ts similarity index 99% rename from src/app/ltd.cmyr.demo/index.ts rename to src/app/com.example.demo/index.ts index c233b3b..4e820a6 100644 --- a/src/app/ltd.cmyr.demo/index.ts +++ b/src/app/com.example.demo/index.ts @@ -4,7 +4,7 @@ import fs = require('fs') import path = require('path') class App extends CQApp { constructor() { - super('ltd.cmyr.demo', __dirname) + super('com.example.demo', __dirname) this.CQ.setDebug(false) this.isEnable = true//注意,只有isEnable为true的插件才会载入,可以将isEnable置为false不载入某插件 } diff --git a/src/app/ltd.cmyr.js.demo/index.js b/src/app/com.example.js.demo/index.js similarity index 98% rename from src/app/ltd.cmyr.js.demo/index.js rename to src/app/com.example.js.demo/index.js index ce05553..66ea89c 100644 --- a/src/app/ltd.cmyr.js.demo/index.js +++ b/src/app/com.example.js.demo/index.js @@ -1,7 +1,7 @@ import { CQApp, CQMsg } from 'cq-robot' class App extends CQApp { constructor() { - super('ltd.cmyr.js.demo', __dirname) + super('com.example.js.demo', __dirname) this.CQ.setDebug(false) } debug() { diff --git a/src/app/ltd.cmyr.js.demo/index.jsonc b/src/app/com.example.js.demo/index.jsonc similarity index 94% rename from src/app/ltd.cmyr.js.demo/index.jsonc rename to src/app/com.example.js.demo/index.jsonc index 5da049c..b4f9e00 100644 --- a/src/app/ltd.cmyr.js.demo/index.jsonc +++ b/src/app/com.example.js.demo/index.jsonc @@ -1,6 +1,5 @@ // 酷Q 的Json文件支持以 // 开头的注释。 -// 打包前,应用的 .dll, .json 的文件名须以appid命名,应用appInfo返回的内容须改为appid -// 如 appid=com.example.demo, 则dll及json文件需分别命名为 com.example.demo.dll、com.example.demo.json +// 如 appid=com.example.demo 具体规则见规则见https://d.cqp.me/Pro/%E5%BC%80%E5%8F%91/%E5%9F%BA%E7%A1%80%E4%BF%A1%E6%81%AF { "ret": 1, // 返回码,固定为1 "apiver": 9, // Api版本本sdk为9 diff --git a/src/conf/setting.jsonc b/src/conf/setting.jsonc index c76af47..66e59cb 100644 --- a/src/conf/setting.jsonc +++ b/src/conf/setting.jsonc @@ -1,9 +1,9 @@ { - "accessToken": "B8BE617E6EC9F72BE6892B922204BC40", // API 访问 token 。见 CQHTTP API 之配置文件说明 + "accessToken": "", // API 访问 token 。见 CQHTTP API 之配置文件说明 "enableAPI": true, // 启用 /api 连线 "enableEvent": true, // 启用 /event 连线 "protocol": "ws:", // 协议名 - "host": "111.231.70.237", // '127.0.0.1' 酷Q服务器 IP + "host": "127.0.0.1", // '127.0.0.1' 酷Q服务器 IP "port": 6700, // 酷Q服务器端口 "baseUrl": "", // 酷Q服务器位址 (SDK在建立连线时会依照此设定加上前缀项 ws:// 及后缀项 `/