From 3dffac19b4a406c38d2ec2bdacdeb04a799d156b Mon Sep 17 00:00:00 2001 From: takayama Date: Fri, 13 Nov 2020 12:45:55 +0900 Subject: [PATCH 1/6] =?UTF-8?q?add=20=E7=BE=A4=E5=A4=B4=E5=83=8F=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=20=E5=8F=91=E5=9B=BE=E7=89=87=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BC=A0bytes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.d.ts | 3 ++- client.js | 3 +++ docs/api.md | 1 + lib/individual.js | 46 +++++++++++++++++++++++++++++++++++++----- lib/message/builder.js | 16 +++++++++------ 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/client.d.ts b/client.d.ts index 3bbabb98..9fb75e70 100644 --- a/client.d.ts +++ b/client.d.ts @@ -226,7 +226,8 @@ export class Client extends events.EventEmitter { setBirthday(birthday: string | number): Promise; //20110202的形式 setDescription(description?: string): Promise; setSignature(signature?: string): Promise; - setPortrait(file?: Buffer | string): Promise; //图片CQ码中file相同格式 + setPortrait(file: Buffer | string): Promise; //图片CQ码中file相同格式 + setGroupPortrait(group_id: Uin, file: Buffer | string): Promise; getCookies(domain?: string): Promise; getCsrfToken(): Promise; diff --git a/client.js b/client.js index 6d435646..a14e2a1a 100644 --- a/client.js +++ b/client.js @@ -569,6 +569,9 @@ class AndroidClient extends Client { async setPortrait(file) { return await this.useProtocol(indi.setPortrait, arguments); } + async setGroupPortrait(group_id, file) { + return await this.useProtocol(indi.setGroupPortrait, arguments); + } /////////////////////////////////////////////////// diff --git a/docs/api.md b/docs/api.md index b8fa1954..4e50ac3b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -186,6 +186,7 @@ message可以使用 `Array` 格式或 `String` 格式,支持CQ码 + async `client.setDescription([description])` 设置个人说明 + async `client.setSignature([signature])` 设置个性签名 + async `client.setPortrait(file)` 设置个人头像(file为Buffer或图片CQ码中相同格式的字符串) ++ async `client.setGroupPortrait(group_id, file)` 设置群头像 ---- diff --git a/lib/individual.js b/lib/individual.js index b5cdfa95..daac42cb 100644 --- a/lib/individual.js +++ b/lib/individual.js @@ -1,15 +1,15 @@ "use strict"; +const http = require("http"); const {uinAutoCheck, md5} = require("./common"); const {downloadWebImage, highwayUpload, readFile} = require("./service"); const pb = require("./pb"); const jce = require("./jce"); /** - * 设置头像 - * @this {import("./ref").Client} * @param {String|Buffer} file + * @returns {Buffer} */ -async function setPortrait(file) { +async function getImgBuf(file) { let buf; if (file instanceof Buffer) { buf = file; @@ -32,6 +32,16 @@ async function setPortrait(file) { } } } + return buf; +} + +/** + * 设置头像 + * @this {import("./ref").Client} + * @param {String|Buffer} file + */ +async function setPortrait(file) { + const buf = await getImgBuf(file); const body = pb.encode({ 1281: { 1: this.uin, @@ -44,9 +54,35 @@ async function setPortrait(file) { }); const blob = await this.sendUNI("HttpConn.0x6ff_501", body); const rsp = pb.decode(blob)[1281]; - highwayUpload.call(this, rsp[3][2][0][2], rsp[3][2][0][3], { + await highwayUpload.call(this, rsp[3][2][0][2], rsp[3][2][0][3], { buf, md5: md5(buf), key: rsp[1].raw }, 5); + return {result: 0}; +} + +/** + * 群头像 + * @this {import("./ref").Client} + * @param {String|Buffer} file + */ +async function setGroupPortrait(group_id, file) { + var [group_id] = uinAutoCheck(group_id); + const buf = await getImgBuf(file); + await this.getCookies(); + const url = `http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=${this.sig.skey}&range=0&uin=${this.uin}&seq=${this.seq_id}&groupuin=${group_id}&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=${buf.length}`; + try { + await new Promise((resolve, reject)=>{ + http.request(url, {method:"POST"}, (res)=>{ + if (res.statusCode === 200) + resolve(); + else + reject(); + }).on("error", (e)=>reject(e.message)).end(buf); + }); + return {result: 0}; + } catch (e) { + return {result: 102, emsg: e}; + } } /** @@ -254,5 +290,5 @@ async function setStatus(status) { } module.exports = { - setStatus, setProfile, setSign, sendLike, addFriend, delFriend, setPortrait, + setStatus, setProfile, setSign, sendLike, addFriend, delFriend, setPortrait, setGroupPortrait }; \ No newline at end of file diff --git a/lib/message/builder.js b/lib/message/builder.js index 14227548..a0672f19 100644 --- a/lib/message/builder.js +++ b/lib/message/builder.js @@ -2,7 +2,7 @@ const zlib = require("zlib"); const fs = require("fs"); const path = require("path"); -const crypto = require("crypto"); +const {randomBytes} = require("crypto"); const spawn = require("child_process"); const music = require("./music"); const face = require("./face"); @@ -175,8 +175,7 @@ class Builder { async buildImageElem(cq) { let {file, url, cache, type, timeout, proxy} = cq; if (!file) return; - file = file.trim(); - let fid, buf, md5 = crypto.randomBytes(16), size = 65536; + let fid, buf, md5 = randomBytes(16), size = 65536; const nested = {}; //pb nested obj if (type === "flash") { var elem = this.type ? {1:nested} : {2:nested}; @@ -194,9 +193,14 @@ class Builder { const img = { buf, md5, size, nested }; + + // bytes + if (file instanceof Buffer || file instanceof Uint8Array) { + buf = file, md5 = common.md5(file), size = file.length; + } // 网络图片 - if (file.startsWith("http://") || file.startsWith("https://")) { + else if (file.startsWith("http://") || file.startsWith("https://")) { const filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const filepath = path.join(this.c.dir, "..", "image", filename); try { @@ -227,7 +231,7 @@ class Builder { // base64图片 else if (file.startsWith("base64://")) { - file = file.replace("base64://", ""); + file = file.trim().replace("base64://", ""); buf = Buffer.from(file, "base64"); md5 = common.md5(buf), size = buf.length; } @@ -237,7 +241,7 @@ class Builder { //本地图片 if (md5.length !== 16) { try { - file = file.replace(/^file:\/{2,3}/, ""); + file = file.trim().replace(/^file:\/{2,3}/, ""); buf = await readFile(file); md5 = common.md5(buf), size = buf.length; } catch (e) { From dd26492dc272490203f6581eb2270ae1ca1b6809 Mon Sep 17 00:00:00 2001 From: takayama Date: Fri, 13 Nov 2020 13:32:07 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.js | 16 +++++++++++++++- lib/message/chat.js | 5 +++++ lib/ref.d.ts | 10 ++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/client.js b/client.js index a14e2a1a..9d3c4d74 100644 --- a/client.js +++ b/client.js @@ -84,6 +84,15 @@ class AndroidClient extends Client { const2 = crypto.randomBytes(4).readUInt32BE(); const3 = crypto.randomBytes(1)[0]; + stat = { + start_time: timestamp(), + lost_times: 0, + recv_pkt_cnt: 0, + sent_pkt_cnt: 0, + recv_msg_cnt: 0, + sent_msg_cnt: 0, + }; + constructor(uin, config) { super(); this.uin = uin; @@ -117,6 +126,7 @@ class AndroidClient extends Client { }); this.on("close", (e_flag)=>{ this.read(); + ++this.stat.lost_times; if (this.remoteAddress) this.logger.info(`${this.remoteAddress}:${this.remotePort} closed`); this.stopHeartbeat(); @@ -143,6 +153,7 @@ class AndroidClient extends Client { this.reconn_flag = true; this.recv_timestamp = Date.now(); const packet = this.read(len - 4); + ++this.stat.recv_pkt_cnt; try { core.parseIncomingPacket.call(this, packet); } catch (e) { @@ -196,6 +207,7 @@ class AndroidClient extends Client { } async send(packet, timeout = 3000) { + ++this.stat.sent_pkt_cnt; const seq_id = this.seq_id; return new Promise((resolve, reject)=>{ this.write(packet, ()=>{ @@ -213,6 +225,7 @@ class AndroidClient extends Client { }); } writeUNI(cmd, body, seq) { + ++this.stat.sent_pkt_cnt; this.write(wt.build0x0BPacket.apply(this, arguments)); } async sendUNI(cmd, body, seq) { @@ -634,7 +647,8 @@ class AndroidClient extends Client { return buildApiRet(0, { online: this.isOnline(), status: this.online_status, - msg_cnt_per_min: this.calcMsgCnt() + msg_cnt_per_min: this.calcMsgCnt(), + statistics: this.stat, }) } getLoginInfo() { diff --git a/lib/message/chat.js b/lib/message/chat.js index a4dedf0a..0989269e 100644 --- a/lib/message/chat.js +++ b/lib/message/chat.js @@ -30,11 +30,13 @@ async function sendMsg(target, message, escape, type) { rich[2] = []; rich[2].push(builder.anon); } + ++this.stat.sent_msg_cnt; return await (type?sendGroupMsg:sendPrivateMsg).call(this, target, rich, type); } let rsp; for (const buf of builder.b77) { + ++this.stat.sent_msg_cnt; rsp = await sendB77RichMsg.call(this, buf); } for (const elem of builder.ptts) { @@ -331,6 +333,7 @@ function recallGroupMsg(message_id) { */ async function onPrivateMsg(type, head, content, body) { + ++this.stat.recv_msg_cnt; const user_id = head[1], time = head[6], seq = head[5]; @@ -386,6 +389,7 @@ async function onPrivateMsg(type, head, content, body) { */ async function onGroupMsg(head, body) { + ++this.stat.recv_msg_cnt; const user_id = head[1], time = head[6], seq = head[5]; @@ -467,6 +471,7 @@ async function onGroupMsg(head, body) { */ async function onDiscussMsg(head, body) { + ++this.stat.recv_msg_cnt; const user_id = head[1], time = head[6], seq = head[5]; diff --git a/lib/ref.d.ts b/lib/ref.d.ts index fd8fec0c..275972a2 100644 --- a/lib/ref.d.ts +++ b/lib/ref.d.ts @@ -81,6 +81,15 @@ export interface HighwayUploadObject { key: Buffer, } +export interface Statistics { + start_time: number, + lost_times: number, + recv_pkt_cnt: number, + sent_pkt_cnt: number, + recv_msg_cnt: number, + sent_msg_cnt: number, +} + ////////// export class Client extends oicq.Client { @@ -129,6 +138,7 @@ export class Client extends oicq.Client { dir: string; sig: Sig; cookies: object; + stat: Statistics; nextSeq(): number; send(): Promise; From 4ac20b7ceb2bbc84bf7dd7821c9a98f189c47266 Mon Sep 17 00:00:00 2001 From: takayama Date: Fri, 13 Nov 2020 19:12:47 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=A3=8E=E6=8E=A7=E6=98=AF=E5=90=A6=E9=87=8D=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.d.ts | 1 + client.js | 19 +++++++++---------- docs/api.md | 3 ++- lib/core.js | 2 +- lib/message/chat.js | 10 +++++----- lib/ref.d.ts | 2 -- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/client.d.ts b/client.d.ts index 9fb75e70..07dd5357 100644 --- a/client.d.ts +++ b/client.d.ts @@ -11,6 +11,7 @@ export interface ConfBot { platform?: number, //1手机 2平板(默认) 3手表(不支持部分群事件) kickoff?: boolean, //被挤下线是否在3秒后反挤对方,默认false ignore_self?: boolean,//群聊是否无视自己的发言,默认true + resend?: boolean, //被风控时是否尝试用另一种方式强行发送,默认true data_dir?: string, //数据存储文件夹,需要可写权限,默认主目录下的data文件夹 } diff --git a/client.js b/client.js index 9d3c4d74..43bede1c 100644 --- a/client.js +++ b/client.js @@ -98,19 +98,18 @@ class AndroidClient extends Client { this.uin = uin; config = { - platform: 2, //1手机 2平板 3手表(不支持部分群事件) - log_level: "info", //trace,debug,info,warn,error,fatal,off - kickoff: false, //被挤下线是否在3秒后反挤 - ignore_self: true, //是否无视自己的消息(群聊、私聊) - data_dir: path.join(process.mainModule.path, "data"), + platform: 2, + log_level: "info", + kickoff: false, + ignore_self:true, + resend: true, + data_dir: path.join(process.mainModule.path, "data"), ...config }; this.config = config; - this.dir = createCacheDir(config.data_dir, uin); + this.dir = createDataDir(config.data_dir, uin); this.logger = log4js.getLogger(`[BOT:${uin}]`); this.logger.level = config.log_level; - this.ignore_self = !!config.ignore_self; - this.kickoff_reconn = !!config.kickoff; const filepath = path.join(this.dir, `device-${uin}.json`); if (!fs.existsSync(filepath)) @@ -279,7 +278,7 @@ class AndroidClient extends Client { let sub_type; if (data.info.includes("另一")) { sub_type = "kickoff"; - if (this.kickoff_reconn) { + if (this.config.kickoff) { this.logger.info("3秒后重新连接.."); setTimeout(this.login.bind(this), 3000); } else { @@ -671,7 +670,7 @@ process.OICQ = { console.log("OICQ程序启动。当前内核版本:v" + version.version); -function createCacheDir(dir, uin) { +function createDataDir(dir, uin) { if (!fs.existsSync(dir)) fs.mkdirSync(dir, {mode: 0o755, recursive: true}); const img_path = path.join(dir, "image"); diff --git a/docs/api.md b/docs/api.md index 4e50ac3b..597914a7 100644 --- a/docs/api.md +++ b/docs/api.md @@ -37,7 +37,8 @@ const client = oicq.createClient(uin, config); log_level: "info", //日志级别,有trace,debug,info,warn,error,fatal,off kickoff: false, //被挤下线是否在3秒后反挤 ignore_self: true, //是否无视自己的消息(群聊、私聊) - data_dir: undefined, //数据存储文件夹,需要可写权限,默认主目录下的data文件夹 + resend: true, //被风控时是否尝试用另一种方式强行发送 + data_dir: //数据存储文件夹,需要可写权限,默认主目录下的data文件夹 } ``` diff --git a/lib/core.js b/lib/core.js index 68c87cd5..71cee3ec 100644 --- a/lib/core.js +++ b/lib/core.js @@ -108,7 +108,7 @@ async function getMsg(sync_flag = 0) { if (!this.sync_finished) continue; let uin = head[1]; - if (uin === this.uin && (this.ignore_self || uin !== head[2])) + if (uin === this.uin && (this.config.ignore_self || uin !== head[2])) continue; if (![33, 141, 166, 167, 208].includes(type)) continue; diff --git a/lib/message/chat.js b/lib/message/chat.js index 0989269e..ac057578 100644 --- a/lib/message/chat.js +++ b/lib/message/chat.js @@ -51,8 +51,8 @@ async function sendMsg(target, message, escape, type) { throw new Error("empty message"); } rsp = await _sendMsg({2: builder.elems}, builder.isLong()); - if (!builder.isLong() && rsp.result === 0 && rsp.data && rsp.data.message_id === "" && !builder.anon) { - this.logger.warn(`此消息将尝试作为长消息再发送一次。`); + if (this.config.resend && !builder.isLong() && rsp.data && rsp.data.message_id === "" && !builder.anon) { + this.logger.warn(`此消息将尝试以另一种方式再发送一次。`); return await _sendMsg({2: builder.elems}, true); } return rsp; @@ -187,7 +187,7 @@ async function toLongMessageElems(uin, rich, is_group) { 5: crypto.randomBytes(2).readUInt16BE(), 6: common.timestamp(), 9: { - 1: common.uin2code(uin), + 1: uin, 4: this.nickname, }, 14: this.nickname, @@ -443,7 +443,7 @@ async function onGroupMsg(head, body) { } catch (e) {} } - if (user_id === this.uin && this.ignore_self) + if (user_id === this.uin && this.config.ignore_self) return; if (!raw_message) return; @@ -481,7 +481,7 @@ async function onDiscussMsg(head, body) { this.msgExists(discuss_id, 0, seq, time); - if (user_id === this.uin && this.ignore_self) + if (user_id === this.uin && this.config.ignore_self) return; const font = String(body[1][1][9].raw), diff --git a/lib/ref.d.ts b/lib/ref.d.ts index 275972a2..eef1943c 100644 --- a/lib/ref.d.ts +++ b/lib/ref.d.ts @@ -94,11 +94,9 @@ export interface Statistics { export class Client extends oicq.Client { logger: log4js.Logger; - ignore_self: boolean; reconn_flag: boolean; config: oicq.ConfBot; status: Symbol; - kickoff_reconn: boolean; apk: ApkInfo; ksid: string | Buffer; From 2b2dd5f28fdb19567e5b4a198e4f47e7a1a8aada Mon Sep 17 00:00:00 2001 From: takayama Date: Fri, 13 Nov 2020 22:07:08 +0900 Subject: [PATCH 4/6] NotOnlineFile --- lib/core.js | 3 ++- lib/message/builder.js | 4 ++-- lib/message/chat.js | 36 +++++++++++++++++++++++++++++++----- lib/message/parser.js | 2 ++ lib/message/storage.js | 40 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/lib/core.js b/lib/core.js index 71cee3ec..47ac6ea4 100644 --- a/lib/core.js +++ b/lib/core.js @@ -54,6 +54,7 @@ function onPushNotify(blob) { case 166: case 167: case 208: + case 529: return getMsg.call(this); case 84: case 87: @@ -110,7 +111,7 @@ async function getMsg(sync_flag = 0) { let uin = head[1]; if (uin === this.uin && (this.config.ignore_self || uin !== head[2])) continue; - if (![33, 141, 166, 167, 208].includes(type)) + if (![33, 141, 166, 167, 208, 529].includes(type)) continue; if (this.msgExists(uin, type, head[5], head[6])) continue; diff --git a/lib/message/builder.js b/lib/message/builder.js index a0672f19..2186490b 100644 --- a/lib/message/builder.js +++ b/lib/message/builder.js @@ -204,7 +204,7 @@ class Builder { const filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const filepath = path.join(this.c.dir, "..", "image", filename); try { - if (cache === "0") + if (cache == "0") throw new Error("no cache"); const content = await fs.promises.readFile(filepath, "utf8"); md5 = Buffer.from(content.slice(0, 32), "hex"), size = parseInt(content.slice(32)); @@ -301,7 +301,7 @@ class Builder { url = file; const cache_filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const cache_filepath = path.join(this.c.dir, "..", "record", cache_filename); - if (cache !== "0") { + if (cache != "0") { try { buf = await fs.promises.readFile(cache_filepath); } catch (e) {} diff --git a/lib/message/chat.js b/lib/message/chat.js index ac057578..15aa00a5 100644 --- a/lib/message/chat.js +++ b/lib/message/chat.js @@ -3,7 +3,7 @@ const zlib = require("zlib"); const crypto = require("crypto"); const Builder = require("./builder"); const parseMessage = require("./parser"); -const {uploadMultiMsg} = require("./storage"); +const {uploadMultiMsg, getPrivateFileUrl} = require("./storage"); const common = require("../common"); const pb = require("../pb"); @@ -329,10 +329,11 @@ function recallGroupMsg(message_id) { //on message---------------------------------------------------------------------------------------------------- /** + * @param {141|166|167|208|529} type * @this {import("../ref").Client} */ async function onPrivateMsg(type, head, content, body) { - + ++this.stat.recv_msg_cnt; const user_id = head[1], time = head[6], @@ -347,12 +348,10 @@ async function onPrivateMsg(type, head, content, body) { const group_id = head[8][4]; sender.group_id = group_id; } - } else if (type === 166 || type === 208) { - sub_type = this.fl.has(user_id) ? "friend" : "single"; } else if (type === 167) { sub_type = "single"; } else { - return; + sub_type = this.fl.has(user_id) ? "friend" : "single"; } if (sender.nickname === undefined) { const stranger = (await this.getStrangerInfo(user_id, seq%5==0)).data; @@ -362,6 +361,33 @@ async function onPrivateMsg(type, head, content, body) { this.sl.set(user_id, stranger); } } + if (type === 529) { + if (head[4] !== 4 || !body[2]) + return; + try { + const fileid = body[2][1][3].raw, + md5 = body[2][1][4].raw.toString("hex"), + name = String(body[2][1][5].raw), + size = body[2][1][6], + duration = body[2][1][51] ? time + body[2][1][51] : 0; + const url = await getPrivateFileUrl.call(this, fileid); + const raw_message = `[CQ:file,url=${url},size=${size},md5=${md5},duration=${duration},busid=0,fileid=${fileid}]`; + this.logger.info(`recv from: [Private: ${user_id}(${sub_type})] ` + raw_message); + this.em("message.private." + sub_type, { + message_id: "none", user_id, + message: [{ + type: "file", + data: { + url, size, md5, duration, + busid: "0", + fileid: String(fileid) + } + }], + raw_message, font, sender, time + }); + } catch (e) {} + return; + } if (body[1] && body[1][2]) { let random = crypto.randomBytes(4).readInt32BE(); if (body[1][1]) { diff --git a/lib/message/parser.js b/lib/message/parser.js index ae964ff4..cbc19ee2 100644 --- a/lib/message/parser.js +++ b/lib/message/parser.js @@ -163,6 +163,8 @@ async function parseTransElem(o, from) { size: v[3], md5: rsp[9].raw.toString("hex"), duration: v[5], + busid: from.toString(36) + "-" + v[1], + fileid: String(v[2].raw) }; return ["file", data]; } diff --git a/lib/message/storage.js b/lib/message/storage.js index 6eefedad..3ba4fb61 100644 --- a/lib/message/storage.js +++ b/lib/message/storage.js @@ -303,19 +303,51 @@ async function downloadMultiMsg(resid, bu) { }); } -async function getGroupFileUrl(group_id, bus_id, file_id) { +/** + * @this {import("../ref").Client} + * @param {Number} group_id + * @param {Number} busid + * @param {Buffer|String} fileid + */ +async function getGroupFileUrl(group_id, busid, fileid) { const body = pb.encode({ 3: { 1: group_id, 2: 3, - 3: bus_id, - 4: file_id, + 3: busid, + 4: fileid, } }); const blob = await this.sendUNI("OidbSvc.0x6d6_2", body); return pb.decode(blob)[4][3]; } +/** + * @this {import("../ref").Client} + * @param {Buffer|String} fileid + */ +async function getPrivateFileUrl(fileid) { + const body = pb.encode({ + 1: 1200, + 14: { + 10: this.uin, + 20: fileid, + 30: 2 + }, + 101: 3, + 102: 104, + 99999: { + 1: 90200 + } + }); + const blob = await this.sendUNI("OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", body); + const rsp = pb.decode(blob)[14][30]; + let url = String(rsp[50].raw); + if (!url.startsWith("http")) + url = `http://${rsp[30].raw}:${rsp[40]}` + url; + return url; +} + async function getAnonInfo(group_id) { const body = pb.encode({ 1: 1, @@ -335,6 +367,6 @@ async function getAnonInfo(group_id) { } module.exports = { - uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl, + uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl, getPrivateFileUrl, setPrivateImageNested, setGroupImageNested, getAnonInfo } From 2593c26df3896e7357e926a6268e8c62fdfc2a70 Mon Sep 17 00:00:00 2001 From: takayama Date: Fri, 13 Nov 2020 22:07:08 +0900 Subject: [PATCH 5/6] NotOnlineFile --- lib/core.js | 3 ++- lib/message/builder.js | 4 ++-- lib/message/chat.js | 36 +++++++++++++++++++++++++++++----- lib/message/parser.js | 2 ++ lib/message/storage.js | 44 ++++++++++++++++++++++++++++++++++++------ 5 files changed, 75 insertions(+), 14 deletions(-) diff --git a/lib/core.js b/lib/core.js index 71cee3ec..47ac6ea4 100644 --- a/lib/core.js +++ b/lib/core.js @@ -54,6 +54,7 @@ function onPushNotify(blob) { case 166: case 167: case 208: + case 529: return getMsg.call(this); case 84: case 87: @@ -110,7 +111,7 @@ async function getMsg(sync_flag = 0) { let uin = head[1]; if (uin === this.uin && (this.config.ignore_self || uin !== head[2])) continue; - if (![33, 141, 166, 167, 208].includes(type)) + if (![33, 141, 166, 167, 208, 529].includes(type)) continue; if (this.msgExists(uin, type, head[5], head[6])) continue; diff --git a/lib/message/builder.js b/lib/message/builder.js index a0672f19..2186490b 100644 --- a/lib/message/builder.js +++ b/lib/message/builder.js @@ -204,7 +204,7 @@ class Builder { const filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const filepath = path.join(this.c.dir, "..", "image", filename); try { - if (cache === "0") + if (cache == "0") throw new Error("no cache"); const content = await fs.promises.readFile(filepath, "utf8"); md5 = Buffer.from(content.slice(0, 32), "hex"), size = parseInt(content.slice(32)); @@ -301,7 +301,7 @@ class Builder { url = file; const cache_filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const cache_filepath = path.join(this.c.dir, "..", "record", cache_filename); - if (cache !== "0") { + if (cache != "0") { try { buf = await fs.promises.readFile(cache_filepath); } catch (e) {} diff --git a/lib/message/chat.js b/lib/message/chat.js index ac057578..15aa00a5 100644 --- a/lib/message/chat.js +++ b/lib/message/chat.js @@ -3,7 +3,7 @@ const zlib = require("zlib"); const crypto = require("crypto"); const Builder = require("./builder"); const parseMessage = require("./parser"); -const {uploadMultiMsg} = require("./storage"); +const {uploadMultiMsg, getPrivateFileUrl} = require("./storage"); const common = require("../common"); const pb = require("../pb"); @@ -329,10 +329,11 @@ function recallGroupMsg(message_id) { //on message---------------------------------------------------------------------------------------------------- /** + * @param {141|166|167|208|529} type * @this {import("../ref").Client} */ async function onPrivateMsg(type, head, content, body) { - + ++this.stat.recv_msg_cnt; const user_id = head[1], time = head[6], @@ -347,12 +348,10 @@ async function onPrivateMsg(type, head, content, body) { const group_id = head[8][4]; sender.group_id = group_id; } - } else if (type === 166 || type === 208) { - sub_type = this.fl.has(user_id) ? "friend" : "single"; } else if (type === 167) { sub_type = "single"; } else { - return; + sub_type = this.fl.has(user_id) ? "friend" : "single"; } if (sender.nickname === undefined) { const stranger = (await this.getStrangerInfo(user_id, seq%5==0)).data; @@ -362,6 +361,33 @@ async function onPrivateMsg(type, head, content, body) { this.sl.set(user_id, stranger); } } + if (type === 529) { + if (head[4] !== 4 || !body[2]) + return; + try { + const fileid = body[2][1][3].raw, + md5 = body[2][1][4].raw.toString("hex"), + name = String(body[2][1][5].raw), + size = body[2][1][6], + duration = body[2][1][51] ? time + body[2][1][51] : 0; + const url = await getPrivateFileUrl.call(this, fileid); + const raw_message = `[CQ:file,url=${url},size=${size},md5=${md5},duration=${duration},busid=0,fileid=${fileid}]`; + this.logger.info(`recv from: [Private: ${user_id}(${sub_type})] ` + raw_message); + this.em("message.private." + sub_type, { + message_id: "none", user_id, + message: [{ + type: "file", + data: { + url, size, md5, duration, + busid: "0", + fileid: String(fileid) + } + }], + raw_message, font, sender, time + }); + } catch (e) {} + return; + } if (body[1] && body[1][2]) { let random = crypto.randomBytes(4).readInt32BE(); if (body[1][1]) { diff --git a/lib/message/parser.js b/lib/message/parser.js index ae964ff4..cbc19ee2 100644 --- a/lib/message/parser.js +++ b/lib/message/parser.js @@ -163,6 +163,8 @@ async function parseTransElem(o, from) { size: v[3], md5: rsp[9].raw.toString("hex"), duration: v[5], + busid: from.toString(36) + "-" + v[1], + fileid: String(v[2].raw) }; return ["file", data]; } diff --git a/lib/message/storage.js b/lib/message/storage.js index 6eefedad..ce5a80f5 100644 --- a/lib/message/storage.js +++ b/lib/message/storage.js @@ -84,7 +84,7 @@ async function imageStore(group_id, imgs) { v[6] = [v[6]]; v[7] = [v[7]]; } - const index = i % v[6].slice(0, 2).length; + const index = i % v[6].slice(0, 1).length; imgs[i].key = v[8].raw; tasks.push(highwayUpload.call(this, v[6][index], v[7][index], imgs[i], 2)); } @@ -124,7 +124,7 @@ async function offPicUp(user_id, imgs) { v[7] = [v[7]]; v[8] = [v[8]]; } - const index = i % v[7].slice(0, 2).length; + const index = i % v[7].slice(0, 1).length; imgs[i].key = v[9].raw; tasks.push(highwayUpload.call(this, v[7][index], v[8][index], imgs[i], 1)); } @@ -303,19 +303,51 @@ async function downloadMultiMsg(resid, bu) { }); } -async function getGroupFileUrl(group_id, bus_id, file_id) { +/** + * @this {import("../ref").Client} + * @param {Number} group_id + * @param {Number} busid + * @param {Buffer|String} fileid + */ +async function getGroupFileUrl(group_id, busid, fileid) { const body = pb.encode({ 3: { 1: group_id, 2: 3, - 3: bus_id, - 4: file_id, + 3: busid, + 4: fileid, } }); const blob = await this.sendUNI("OidbSvc.0x6d6_2", body); return pb.decode(blob)[4][3]; } +/** + * @this {import("../ref").Client} + * @param {Buffer|String} fileid + */ +async function getPrivateFileUrl(fileid) { + const body = pb.encode({ + 1: 1200, + 14: { + 10: this.uin, + 20: fileid, + 30: 2 + }, + 101: 3, + 102: 104, + 99999: { + 1: 90200 + } + }); + const blob = await this.sendUNI("OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", body); + const rsp = pb.decode(blob)[14][30]; + let url = String(rsp[50].raw); + if (!url.startsWith("http")) + url = `http://${rsp[30].raw}:${rsp[40]}` + url; + return url; +} + async function getAnonInfo(group_id) { const body = pb.encode({ 1: 1, @@ -335,6 +367,6 @@ async function getAnonInfo(group_id) { } module.exports = { - uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl, + uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl, getPrivateFileUrl, setPrivateImageNested, setGroupImageNested, getAnonInfo } From 280a3bf401159141b65c27cf0bbead094715d175 Mon Sep 17 00:00:00 2001 From: takayama Date: Sat, 14 Nov 2020 18:06:07 +0900 Subject: [PATCH 6/6] fix at --- lib/message/builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/message/builder.js b/lib/message/builder.js index 2186490b..115452ad 100644 --- a/lib/message/builder.js +++ b/lib/message/builder.js @@ -84,7 +84,7 @@ class Builder { } catch (e) {} } } - if (dummy || (!common.checkUin(q) && qq !== "all")) { + if (dummy == "1" || (!common.checkUin(q) && qq !== "all")) { if (!display.startsWith("@")) display = "@" + display; return this.buildTextElem(display);