Skip to content
This repository has been archived by the owner on Nov 2, 2023. It is now read-only.

Commit

Permalink
Merge pull request #17 from takayama-lily/dev
Browse files Browse the repository at this point in the history
-
  • Loading branch information
takayama-lily authored Sep 2, 2020
2 parents 86f9952 + 49a64c8 commit 22fe07a
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 81 deletions.
21 changes: 15 additions & 6 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,8 @@ class AndroidClient extends Client {
* 使用此函数关闭连接,不要使用end和destroy
*/
terminate() {
if (this.status === Client.OFFLINE)
return;
this.reconn_flag = false;
this.end();
this.destroy();
}

isOnline() {
Expand Down Expand Up @@ -682,18 +680,19 @@ class AndroidClient extends Client {
* @param {Number} group_id
* @param {String|Array} message
* @param {Boolean} auto_escape Default: false
* @param {Boolean} as_long 作为长消息发送(可以避免被风控)
* @returns {Ojbect} data
* @field {Number} message_id
*/
async sendGroupMsg(group_id, message = "", auto_escape = false) {
async sendGroupMsg(group_id, message = "", auto_escape = false, as_long = false) {
group_id = parseInt(group_id);
if (!checkUin(group_id))
return buildApiRet(100);
if (!await this.hasGroup(group_id))
return buildApiRet(102);
try {
try {
var packet = await outgoing.buildSendGroupMessageRequestPacket(group_id, message, auto_escape, this);
var packet = await outgoing.buildSendGroupMessageRequestPacket(group_id, message, auto_escape, as_long, this);
} catch (e) {
return buildApiRet(100);
}
Expand All @@ -714,8 +713,18 @@ class AndroidClient extends Client {
if (this.listenerCount(event_id) > 0) {
this.removeAllListeners(event_id);
message_id = await new Promise((resolve)=>{
this.once(event_id, resolve);
const id = setTimeout(()=>{
this.logger.info(`可能被风控了,这条消息将尝试作为长消息发送。`);
this.removeAllListeners(event_id);
resolve(false);
}, 1000);
this.once(event_id, (a)=>{
clearTimeout(id);
resolve(a);
});
});
if (!message_id)
return await this.sendGroupMsg(group_id, message, auto_escape, true)
};

this.logger.info(`send to: [Group: ${group_id}] ` + message);
Expand Down
2 changes: 1 addition & 1 deletion docs/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
|-|-|-|
|at||[CQ:at,qq=123456,text=@ABC] text用来定义@不到时的输出|
|face|◯|[CQ:face,id=104]
|bface||
|bface|仅解析|
|image|◯|[CQ:image,file=202cb962ac59075b964b07152d234b70123456] 收到的图片<br>[CQ:image,file=C:/123.jpg] 本地图片<br>[CQ:image,cache=0,file=http://abc.com] 网络图片
|record||
|music||
Expand Down
151 changes: 90 additions & 61 deletions lib/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ function toCQAt(user_id, text) {
function toCQFace(id) {
return `[CQ:face,id=${id}]`;
}
function toBFace(text) {
return `[CQ:bface,text=${text}]`;
}
function toCQImage(file, url) {
return `[CQ:image,file=${file},url=${url}]`;
}
Expand Down Expand Up @@ -66,6 +69,13 @@ function parseMessage(elems) {
case "transElemInfo":
break;
case "text":
if (o.attr7Buf) {
msg.type = "bface";
msg.data.text = o.str;
chain.push(msg);
raw_message += toBFace(o.str);
break;
}
if (o.attr6Buf && o.attr6Buf[1] === 1) {
msg.type = "at";
if (o.attr6Buf[6] === 1)
Expand Down Expand Up @@ -122,8 +132,9 @@ const FACE_OLD_BUF = Buffer.from([0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0
* @returns {Number} text byte length
*/
function buildTextMessage(chain, text) {
if (text)
chain.push({text: {str: text.toString()}});
if (!text) return 0;
text = text.toString()
chain.push({text: {str: text}});
return Buffer.byteLength(text);
}

Expand Down Expand Up @@ -299,55 +310,36 @@ async function buildImageMessage(chain, cq, is_group) {
* @param {String} message
* @param {Boolean} escape
* @param {Boolean} is_group
* @returns {Object}
* @param {Object} stat
* @field length
* @field at_cnt
* @field face_cnt
* @field img_cnt
* @param {Promise[]} tasks
*/
async function buildMessageFromString(chain, message, escape = false, is_group = true) {
if (escape)
return buildTextMessage(chain, message);
async function buildMessageFromString(chain, message, escape, is_group, stat, tasks) {
if (escape) {
stat.length += buildTextMessage(chain, message);
return;
}
const res = message.matchAll(/\[CQ:[^\]]+\]/g);
let prev_index = 0
let length = 0, at_cnt = 0, face_cnt = 0, img_cnt = 0, tasks = [];
for (let v of res) {
const text = message.slice(prev_index, v.index).replace(/&amp;|&#91;|&#93;/g, _ss_);
if (text)
length += buildTextMessage(chain, text);
stat.length += buildTextMessage(chain, text);

const elem = v[0];
let cq = elem.replace("[CQ:", "cqtype=");
cq = cq.substr(0, cq.length - 1).replace(/&amp;|&#91;|&#93;/g, _ss_);
cq = querystring.parse(cq, ",");
switch (cq.cqtype.trim()) {
case "at":
const l = buildAtMessage(chain, cq);
if (l > 0)
length += l, ++at_cnt;
break;
case "face":
if (buildFaceMessage(chain, cq))
++face_cnt;
break;
case "image":
const task = await buildImageMessage(chain, cq, is_group);
if (task !== false)
++img_cnt;
if (task instanceof Promise)
tasks.push(task);
break;
default:
break;
}

await buildElement(chain, cq.cqtype.trim(), cq, is_group, stat, tasks)

prev_index = v.index + elem.length;
}
if (prev_index < message.length)
length += buildTextMessage(chain, message.slice(prev_index).replace(/&amp;|&#91;|&#93;/g, _ss_));
if (tasks.length)
await Promise.all(tasks);
return {length, at_cnt, face_cnt, img_cnt};
stat.length += buildTextMessage(chain, message.slice(prev_index).replace(/&amp;|&#91;|&#93;/g, _ss_));
}

/**
Expand All @@ -358,48 +350,85 @@ async function buildMessageFromString(chain, message, escape = false, is_group =
* @returns {Array} chain 头元素记录了图片信息,尾元素记录了是否是长消息
*/
async function buildMessage(message, escape, is_group) {
const chain = [[]];
var length = 0, at_cnt = 0, face_cnt = 0, img_cnt = 0, tasks = [];
const chain = [[]], tasks = [];
const stat = {
length: 0, at_cnt: 0, face_cnt: 0, img_cnt: 0
};
if (typeof message === "string")
var {length, at_cnt, face_cnt, img_cnt} = await buildMessageFromString(chain, message, escape, is_group);
await buildMessageFromString(chain, message, escape, is_group, stat, tasks);
else {
for (let v of message) {
if (!v.data) continue;
switch (v.type) {
case "text":
length += buildTextMessage(chain, v.data);
break;
case "at":
const l = buildAtMessage(chain, v.data);
if (l > 0)
length += l, ++at_cnt;
break;
case "face":
if (buildFaceMessage(chain, v.data))
++face_cnt;
break;
case "image":
const task = await buildImageMessage(chain, v.data, is_group);
if (task !== false)
++img_cnt;
if (task instanceof Promise)
tasks.push(task);
break;
default:
break;
}
await buildElement(chain, v.type, v.data, is_group, stat, tasks);
}
}
if (tasks.length)
await Promise.all(tasks);

length += at_cnt * 22 + face_cnt * 23 + img_cnt * (is_group?90:304);
length *= 1.1;
const is_long = is_group ? (length>790) : (length>935);
stat.length += stat.at_cnt * 22 + stat.face_cnt * 23 + stat.img_cnt * (is_group?90:304);
stat.length *= 1.05;
const is_long = is_group ? (stat.length>790) : (stat.length>935);
chain.push(is_long);
return chain;
}

// function buildReplyMessage(chain, cq) {
// const {seq} = common.parseGroupMessageId(cq.id);
// chain.push({
// srcMsg: {
// origSeqs: [ seq ],
// // senderUin: ,
// // time: ,
// flag: 1,
// type: 1,
// pbReserve: Buffer.from([0x18, 0xaf, 0xf4, 0xcd, 0xcc, 0x84, 0x80, 0x80, 0x80, 0x01]),
// // toUin:
// }
// });
// }

/**
* @param {Object[]} chain 消息链
* @param {String} type 元素类型
* @param {Object} data 元素数据
* @param {Boolean} is_group
* @param {Object} stat 各元素长度统计
* @field length
* @field at_cnt
* @field face_cnt
* @field img_cnt
* @param {Promise[]} tasks 异步任务列表
*/
async function buildElement(chain, type, data, is_group, stat, tasks) {
switch (type) {
// case "reply":
// buildReplyMessage(chain, data);
// break;
case "text":
case "bface":
stat.length += buildTextMessage(chain, data.text);
break;
case "at":
const l = buildAtMessage(chain, data);
if (l > 0)
stat.length += l, ++stat.at_cnt;
break;
case "face":
if (buildFaceMessage(chain, data))
++stat.face_cnt;
break;
case "image":
const task = await buildImageMessage(chain, data, is_group);
if (task !== false)
++stat.img_cnt;
if (task instanceof Promise)
tasks.push(task);
break;
default:
break;
}
}

//----------------------------------------------------------------------------------------------------

module.exports = {
Expand Down
18 changes: 9 additions & 9 deletions lib/outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ async function buildPrivateMessageRequestPacket(user_id, message, escape, c) {
});
return commonUNI(c, CMD.SEND_MSG, body);
}
async function buildSendGroupMessageRequestPacket(group_id, message, escape, c) {
async function buildSendGroupMessageRequestPacket(group_id, message, escape, as_long, c) {
let elems = await buildMessage(message, escape, true);
const images = elems.shift().slice(0, 20), is_long = elems.pop();

Expand All @@ -500,7 +500,7 @@ async function buildSendGroupMessageRequestPacket(group_id, message, escape, c)
if (!elems.length)
throw new Error("消息内容为空");

if (is_long)
if (is_long || as_long)
elems = await toLongMessageElems(common.code2uin(group_id), elems, c);

c.nextSeq();
Expand All @@ -526,15 +526,15 @@ async function toLongMessageElems(uin, elems, c) {
msgTime: common.timestamp(),
msgUid: 0x01000000000000000n | BigInt(common.rand()),
mutiltransHead: {
msgId: 1,
msgId: common.rand(),
},
msgType: 82,
// groupInfo: {
// groupCode: common.uin2code(uin),
// groupRank: 2,
// groupName: Buffer.from("abc"),
// groupCard: c.nickname,
// },
groupInfo: {
groupCode: common.uin2code(uin),
groupRank: BUF0,
groupName: BUF0,
groupCard: c.nickname,
},
},
body: {
richText: {elems},
Expand Down
4 changes: 2 additions & 2 deletions lib/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const tea = require("crypto-tea");
const pb = require("./pb");
const common = require("./common");
const BUF0 = Buffer.alloc(0);
const MAX_IMG_SIZE = 31457280;

function int32ip2str(ip) {
return [
Expand All @@ -28,7 +29,7 @@ function int32ip2str(ip) {
*/
function buildHighwayUploadRequestPackets(uin, o, cmd, seq = common.rand()) {
uin = uin.toString();
const packets = [], limit = 65536, size = o.buf.length;
const packets = [], limit = MAX_IMG_SIZE, size = o.buf.length;
let chunk, offset = 0;
while (1) {
chunk = o.buf.slice(offset, offset + limit);
Expand Down Expand Up @@ -168,7 +169,6 @@ async function downloadRichMsg(app_down_resp) {
});
}

const MAX_IMG_SIZE = 31457280;
/**
* @async
* @param {*} url
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "oicq",
"version": "1.0.11",
"version": "1.0.12",
"description": "QQ protocol!",
"main": "client.js",
"scripts": {
Expand Down

0 comments on commit 22fe07a

Please sign in to comment.