diff --git a/README.md b/README.md index 5afca64e..844725d8 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,12 @@ [![npm version](https://img.shields.io/npm/v/oicq.svg?logo=npm)](https://www.npmjs.com/package/oicq) [![node engine](https://img.shields.io/node/v/oicq.svg)](https://nodejs.org) ← 注意版本 -* QQ(安卓)协议的nodejs实现。参考了一些其他开源仓库如mirai、miraiGo等。 +* QQ(安卓)协议的nodejs实现。也参考了一些其他开源仓库如mirai、miraiGo等。 * 以高效和稳定为第一目的,在此基础上不断完善功能。 * 将会逐步支持手机协议的大部分功能。 * 使用 [CQHTTP](https://cqhttp.cc) 风格的API、事件和参数(少量差异),并且原生支持经典的CQ码。 * 本项目使用AGPL-3.0许可证,旨在学习。不推荐也不提供商业化使用的支持。 +* 有bug请告诉我! ---- diff --git a/client.d.ts b/client.d.ts index 4e3f474f..ce4d7891 100644 --- a/client.d.ts +++ b/client.d.ts @@ -38,6 +38,7 @@ export interface StrangerInfo { sex?: string, age?: number, area?: string, + group_id?: number, } export interface FriendInfo extends StrangerInfo { remark?: string, @@ -111,6 +112,9 @@ export interface RetSendMsg extends RetCommon { ////////// +/** + * @see https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md + */ export interface MessageElem { type: string, data: object, @@ -154,6 +158,7 @@ export interface EventData { source?: string, role?: string, + inviter_id?: number, operator_id?: number, duration?: number, set?: boolean, @@ -182,10 +187,12 @@ export class Client extends events.EventEmitter { private constructor(); login(password_md5?: Buffer | string): void; captchaLogin(captcha: string): void; - terminate(): void; - logout(): Promise; + terminate(): void; //直接关闭连接 + logout(): Promise; //先下线再关闭连接 isOnline(): boolean; + setOnlineStatus(status: number): Promise; //11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰 + getFriendList(): RetFriendList; getStrangerList(): RetStrangerList; getGroupList(): RetGroupList; @@ -220,7 +227,7 @@ export class Client extends events.EventEmitter { inviteFriend(group_id: Uin, user_id: Uin): Promise; sendLike(user_id: Uin, times?: number): Promise; setNickname(nickname: string): Promise; - setGender(gender: 0 | 1 | 2): Promise; + setGender(gender: 0 | 1 | 2): Promise; //0未知 1男 2女 setBirthday(birthday: string | number): Promise; //20110202的形式 setDescription(description?: string): Promise; setSignature(signature?: string): Promise; @@ -228,6 +235,7 @@ export class Client extends events.EventEmitter { getCookies(domain?: string): Promise; getCsrfToken(): Promise; + cleanCache(type?: string): Promise; canSendImage(): RetCommon; canSendRecord(): RetCommon; getVersionInfo(): RetCommon; diff --git a/client.js b/client.js index 357cc70b..2638b51e 100644 --- a/client.js +++ b/client.js @@ -3,6 +3,8 @@ const version = require("./package.json"); const net = require("net"); const fs = require("fs"); const path = require("path"); +const os = require("os"); +const spawn = require("child_process"); const crypto = require("crypto"); const log4js = require("log4js"); const device = require("./device"); @@ -76,6 +78,7 @@ class AndroidClient extends Client { seq_id = 0; handlers = new Map(); seq_cache = new Map(); + notify33cache = new Set(); session_id = crypto.randomBytes(4); random_key = crypto.randomBytes(16); @@ -96,6 +99,7 @@ class AndroidClient extends Client { device_token: BUF0, }; cookies = {}; + msg_times = []; sync_finished = false; sync_cookie; @@ -287,7 +291,8 @@ class AndroidClient extends Client { if (this.heartbeat) return; this.heartbeat = setInterval(async()=>{ - if (Date.now() - this.send_timestamp > 300000) + this._calc_msg_cnt(); + if (Date.now() - this.send_timestamp > 240000) core.getMsg.call(this); try { await wt.heartbeat.call(this); @@ -296,7 +301,7 @@ class AndroidClient extends Client { if (Date.now() - this.recv_timestamp > 10000) this.destroy(); } - }, 30000); + }, 60000); } stopHeartbeat() { clearInterval(this.heartbeat); @@ -378,12 +383,12 @@ class AndroidClient extends Client { */ async useProtocol(fn, params) { if (!this.isOnline() || !this.sync_finished) - return buildApiRet(104); + return buildApiRet(104, null, {code: -1, message: "bot not online"}); try { const rsp = await fn.apply(this, params); if (!rsp) return buildApiRet(1); - if (rsp.result > 0) + if (rsp.result !== 0) return buildApiRet(102, null, { code: rsp.result, @@ -425,12 +430,21 @@ class AndroidClient extends Client { this.emit(post_type, param); } - // 以下是public方法 ---------------------------------------------------------------------------------------------------- - /** - * 密码登陆 - * @param {Buffer|String|undefined} password_md5 这里不传递明文密码 + * 计算每分钟消息数量 */ + _calc_msg_cnt() { + for (let i = 0; i < this.msg_times.length; ++i) { + if (Date.now() - this.msg_times[i] * 1000 <= 60000) { + this.msg_times = this.msg_times.slice(i); + return; + } + } + this.msg_times = []; + } + + // 以下是public方法 ---------------------------------------------------------------------------------------------------- + login(password_md5) { if (this.isOnline()) return; @@ -451,28 +465,17 @@ class AndroidClient extends Client { }); } - /** - * 验证码登陆 - * @param {String} captcha - */ captchaLogin(captcha) { if (!this.captcha_sign) return this.logger.error("未收到图片验证码或已过期,你不能调用captchaLogin函数。"); wt.captchaLogin.call(this, captcha); } - /** - * 直接关闭连接 - * !注意请勿直接调用end和destroy - */ terminate() { this.reconn_flag = false; this.destroy(); } - /** - * 安全下线 - */ async logout() { if (this.isOnline) { try { @@ -486,29 +489,10 @@ class AndroidClient extends Client { return this.status === Client.ONLINE; } - /** - * 返回值的形式为 - * { - * retcode: 0, //0正常 1异步 100参数错误 102调用失败 103超时 - * status: "ok", //ok正常 async异步 failed失败 - * data:null, //数据,类型可能是Object或Map - * error: "" //错误信息,偶尔会有 - * } - * 之后的 @returns 指的都是成功时的data字段 - * - * 设置在线状态 仅支持手机协议 - * @param {Number} status 11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰 - */ async setOnlineStatus(status) { return await this.useProtocol(indi.setStatus, arguments); } - /////////////////////////////////////////////////// - - /** - * 好友列表、陌生人列表、群列表 - * @returns {Map} - */ getFriendList() { return buildApiRet(0, this.fl); } @@ -519,11 +503,6 @@ class AndroidClient extends Client { return buildApiRet(0, this.gl); } - /** - * 群员列表使用懒加载,不会在启动时加载所有的群员列表 - * 只会在系统认为需要用到的时候进行加载和更新 - * @returns {Map} - */ async getGroupMemberList(group_id) { group_id = parseInt(group_id); if (!checkUin(group_id)) @@ -537,42 +516,18 @@ class AndroidClient extends Client { return buildApiRet(0, mlist); return buildApiRet(102); } - - /** - * 获取陌生人资料 - * @returns {JSON} data - */ async getStrangerInfo(user_id, no_cache = false) { return await this.useProtocol(resource.getSI, arguments); } - - /** - * 群资料会自动和服务器同步,一般来说无需使用no_cache获取 - * @returns {JSON} data - */ async getGroupInfo(group_id, no_cache = false) { return await this.useProtocol(resource.getGI, arguments); } - - /** - * 群员资料一般来说也无需使用no_cache获取(性别、年龄等可能更新不及时) - * @returns {JSON} - */ async getGroupMemberInfo(group_id, user_id, no_cache = false) { return await this.useProtocol(resource.getGMI, arguments); } /////////////////////////////////////////////////// - /** - * 发送私聊 - * 发送群聊,被风控会自动转为长消息发送 - * 发送讨论组 - * @param {String|Array} message 数组或字符串格式的消息 - * @param {Boolean} auto_escape 是否不解析CQ码 - * @returns {JSON} - * @field {String} message_id - */ async sendPrivateMsg(user_id, message = "", auto_escape = false) { return await this.useProtocol(chat.sendMsg, [user_id, message, auto_escape, 0]); } @@ -582,11 +537,6 @@ class AndroidClient extends Client { async sendDiscussMsg(discuss_id, message = "", auto_escape = false) { return await this.useProtocol(chat.sendMsg, [discuss_id, message, auto_escape, 2]); } - - /** - * 撤回消息,暂时为立即返回,无法立即知晓是否成功 - * @param {String} message_id - */ async deleteMsg(message_id) { return await this.useProtocol(chat.recallMsg, arguments); } @@ -598,13 +548,13 @@ class AndroidClient extends Client { return await this.useProtocol(troop.setAnonymous, arguments); } async setGroupWholeBan(group_id, enable = true) { - return await this.setGroupSetting(group_id, "shutupTime", enable?-1:0); + return await this.setGroupSetting(group_id, "shutupTime", enable?0xffffffff:0); } async setGroupName(group_id, group_name) { - return await this.setGroupSetting(group_id, "ingGroupName", Buffer.from(String(group_name))); + return await this.setGroupSetting(group_id, "ingGroupName", String(group_name)); } async sendGroupNotice(group_id, content) { - return await this.setGroupSetting(group_id, "ingGroupMemo", Buffer.from(String(content))); + return await this.setGroupSetting(group_id, "ingGroupMemo", String(content)); } async setGroupSetting(group_id, k, v) { return await this.useProtocol(troop.doSetting, arguments); @@ -612,152 +562,63 @@ class AndroidClient extends Client { async setGroupAdmin(group_id, user_id, enable = true) { return await this.useProtocol(troop.setAdmin, arguments); } - - /** - * 设置群头衔,最大长度未测试 - * @param {String} special_title 为空收回 - * @param {Number} duration -1代表无限期 - */ async setGroupSpecialTitle(group_id, user_id, special_title = "", duration = -1) { return await this.useProtocol(troop.setTitle, arguments); } - - /////////////////////////////////////////////////// - - /** - * 设置群名片,超过60字节会被截断 - * @param {String} card 为空还原 - */ async setGroupCard(group_id, user_id, card = "") { return await this.useProtocol(troop.setCard, arguments); } - - /** - * 踢人,即使原来就无此人也会返回成功 - * @param {Boolean} reject_add_request 是否屏蔽 - */ async setGroupKick(group_id, user_id, reject_add_request = false) { return await this.useProtocol(troop.kickMember, arguments); } - - /** - * 禁言,暂时为立即返回,无法立即知晓是否成功 - * @param {Number} duration 秒数 - */ async setGroupBan(group_id, user_id, duration = 1800) { return await this.useProtocol(troop.muteMember, arguments); } - - /** - * 退群,即使你本来就不在此群,也会返回成功 - * @param {Boolean} is_dismiss 不设置is_dismiss只要是群主貌似也可以解散(可能和规模有关?) - */ async setGroupLeave(group_id, is_dismiss = false) { return await this.useProtocol(troop.quitGroup, arguments); } - - /** - * 群戳一戳,暂时为立即返回,无法立即知晓是否成功 - */ async sendGroupPoke(group_id, user_id) { return await this.useProtocol(troop.pokeMember, arguments); } /////////////////////////////////////////////////// - /** - * 处理好友申请 - * @param {String} flag 从事件中得到 - * @param {Boolean} approve - * @param {String} remark 暂未实现remark - * @param {Boolean} block 是否屏蔽 - */ async setFriendAddRequest(flag, approve = true, remark = "", block = false) { return await this.useProtocol(sysmsg.friendAction, arguments); } - - /** - * 处理群申请和邀请 - * @param {String} flag 从事件中得到 - * @param {Boolean} approve - * @param {String} reason 拒绝理由,仅在拒绝他人加群时有效 - * @param {Boolean} block 是否屏蔽 - */ async setGroupAddRequest(flag, approve = true, reason = "", block = false) { return await this.useProtocol(sysmsg.groupAction, arguments); } - /** - * 发送加群申请,即使你已经在群里,也会返回成功 - * ※设置为要正确回答问题的群,暂时回返回失败 - * ※风险接口,每日加群超过一定数量账号必被风控(甚至ip) - * @param {String} comment 附加信息 - */ async addGroup(group_id, comment = "") { return await this.useProtocol(troop.addGroup, arguments); } - - /** - * 加群员为好友,暂不支持非群员(群号可以传0,但是必须有共同群,否则对方无法收到请求) - * ※对方设置要正确回答问题的时候,暂时会返回失败 - * ※风险接口,每日加好友超过一定数量账号必被风控(甚至ip) - * @param {String} comment 附加信息 - */ async addFriend(group_id, user_id, comment = "") { return await this.useProtocol(indi.addFriend, arguments); } - - /** - * 删除好友,即使对方本来就不是你的好友,也会返回成功 - * @param {Boolean} block 是否屏蔽 - */ async deleteFriend(user_id, block = true) { return await this.useProtocol(indi.delFriend, arguments); } - - /** - * 邀请好友入群,暂不支持邀请陌生人 - * ※对方必须是BOT的好友,否则返回失败 - * ※如果BOT不是对方的好友(单向),对方又设置了拒绝陌生人邀请,此时会返回成功但是对方实际收不到邀请 - */ async inviteFriend(group_id, user_id) { return await this.useProtocol(troop.inviteFriend, arguments); } - /** - * 点赞,请勿频繁调用,否则有冻结风险 - */ + async sendLike(user_id, times = 1) { return await this.useProtocol(indi.sendLike, arguments); } - - /** - * @param {String} nickname 昵称最长48字节,允许设为空,别人看到的昵称会变为你的QQ号 - */ async setNickname(nickname) { return await this.useProtocol(indi.setProfile, [0x14E22, String(nickname)]); } - - /** - * @param {String} description 设置个人说明 - */ async setDescription(description = "") { return await this.useProtocol(indi.setProfile, [0x14E33, String(description)]); } - - /** - * @param {Number} gender 性别 0未知 1男 2女 - */ async setGender(gender) { gender = parseInt(gender); if (![0,1,2].includes(gender)) return buildApiRet(100); return await this.useProtocol(indi.setProfile, [0x14E29, Buffer.from([gender])]); } - - /** - * @param {String} birthday 生日必须是20110202这样的形式 - */ async setBirthday(birthday) { try { birthday = String(birthday).replace(/[^\d]/g, ""); @@ -770,18 +631,9 @@ class AndroidClient extends Client { return buildApiRet(100); } } - - /** - * @param {String} signature 个人签名超过254字节会被截断 - */ async setSignature(signature = "") { return await this.useProtocol(indi.setSign, arguments); } - - /** - * 设置个人头像 - * @param {Buffer|String} file Buffer或与图片CQ码中file格式相同的字符串("base64://xxx"或"http://xxx"等) - */ async setPortrait(file) { return await this.useProtocol(indi.setPortrait, arguments); } @@ -807,6 +659,33 @@ class AndroidClient extends Client { return buildApiRet(0, {token}); } + /** + * @param {String} type "image" or "record" or undefined + */ + async cleanCache(type = "") { + switch (type) { + case "image": + case "record": + const file = path.join(this.dir, "..", type, "*"); + const cmd = os.platform().includes("win") ? `del /q ` : `rm -f `; + spawn.exec(cmd + file, (err, stdout, stderr)=>{ + if (err) + return this.logger.error(err); + if (stderr) + return this.logger.error(stderr); + this.logger.info(type + " cache clear"); + }); + break; + case "": + this.cleanCache("image"); + this.cleanCache("record"); + break; + default: + return buildApiRet(100, null, {code:-1, message:"unknown type (image, record, or undefined)"}); + } + return buildApiRet(1); + } + canSendImage() { return buildApiRet(0, {yes: true}); } @@ -817,9 +696,11 @@ class AndroidClient extends Client { return buildApiRet(0, version); } getStatus() { + this._calc_msg_cnt(); return buildApiRet(0, { online: this.isOnline(), status: this.online_status, + msg_cnt_per_min: this.msg_times.length, }) } getLoginInfo() { diff --git a/docs/api.md b/docs/api.md index e56bb534..b9fc58b8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -130,7 +130,8 @@ const client = oicq.createClient(uin, config); ### 发私聊消息、群消息 -message可以使用 `Array` 格式或 `String` 格式,支持CQ码 +message可以使用 `Array` 格式或 `String` 格式,支持CQ码 +参考 [消息段类型](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md) + async `client.sendPrivateMsg(user_id, message[, auto_escape])` + *`message_id`* \ 返回字符串格式的message_id @@ -191,6 +192,7 @@ message可以使用 `Array` 格式或 `String` 格式,支持CQ码 + async `client.getCookies([domain])` 实验性质,更新可能存在问题 + async `client.getCsrfToken()` ++ async `client.cleanCache([type])` + `client.canSendImage()` + `client.canSendRecord()` diff --git a/docs/event.md b/docs/event.md index b8c9a275..dd8dfc1c 100644 --- a/docs/event.md +++ b/docs/event.md @@ -139,6 +139,7 @@ client.on("notice", (data)=>console.log(data)); //监听所有的通知事件 + *`user_id`* + *`nickname`* + *`comment`* + + *`inviter_id`* + *`flag`* + `request.group.invite` 加群邀请 diff --git a/lib/common.js b/lib/common.js index dd18156a..f22c60ac 100644 --- a/lib/common.js +++ b/lib/common.js @@ -11,18 +11,6 @@ function uuid() { const timestamp = ()=>parseInt(Date.now()/1000); const md5 = (data)=>crypto.createHash("md5").update(data).digest(); -function toInt(long) { - if (typeof long === "string") - long = parseInt(long); - if (typeof long === "number") - return isNaN(long) ? 0 : long; - if (typeof long === "bigint") - return parseInt(long); - if (long !== null && long !== undefined && typeof long.high === "number" && typeof long.low === "number") - return parseInt((BigInt(long.high)<<32n)|(BigInt(long.low)&0xffffffffn)); - return 0; -} - function checkUin(uin) { return uin >= 10000 && uin <= 0xffffffff; } @@ -79,6 +67,6 @@ function log(any) { } module.exports = { - uuid, md5, timestamp, toInt, checkUin, uinAutoCheck, + uuid, md5, timestamp, checkUin, uinAutoCheck, log, code2uin, uin2code, }; diff --git a/lib/core.js b/lib/core.js index e82c4235..90b3c933 100644 --- a/lib/core.js +++ b/lib/core.js @@ -9,7 +9,6 @@ const chat = require("./message/chat"); const push = require("./online-push"); const sysmsg = require("./sysmsg"); const BUF0 = Buffer.alloc(0); -const toInt = common.toInt; /** * 无聊的通知 @@ -19,7 +18,6 @@ function onPushReq(blob, seq) { const nested = jce.decodeWrapper(blob); const parent = jce.decode(nested); - this.nextSeq(); const PushResp = jce.encodeStruct([ null, parent[1], parent[3], parent[1] === 3 ? parent[2] : null ]); @@ -42,6 +40,8 @@ function onPushReq(blob, seq) { this.em("internal.change-server", {ip, port}); } +const notify_types = {84:1,87:2,525:22}; + /** * 新消息通知 * @this {import("./ref").Client} @@ -59,7 +59,12 @@ function onPushNotify(blob) { return getMsg.call(this); case 84: case 87: - return sysmsg.getNewGroup.call(this); + case 525: + if (parent[5] === 525) { + this.lock525 = !this.lock525; + if (this.lock525) return; + } + return sysmsg.getNewGroup.call(this, notify_types[parent[5]]); case 187: return sysmsg.getNewFriend.call(this); } @@ -71,86 +76,87 @@ function onPushNotify(blob) { * @param {0|1|2} sync_flag 0:start 1:continue 2:stop */ async function getMsg(sync_flag = 0) { - this.nextSeq(); if (!this.sync_cookie) this.sync_cookie = chat.buildSyncCookie.call(this); - let body = pb.encode("GetMessageRequest", { - syncFlag: sync_flag, - syncCookie: this.sync_cookie, - rambleFlag: 0, - latestRambleNumber: 20, - otherRambleNumber: 3, - onlineSyncFlag: 1, - contextFlag: 1, - msgReqType: 1, + let body = pb.encode({ + 1: sync_flag, + 2: this.sync_cookie, + 3: 0, + 4: 20, + 5: 3, + 6: 1, + 7: 1, + 9: 1, }); try { const blob = await this.sendUNI("MessageSvc.PbGetMsg", body); - const o = pb.decode("GetMessageResponse", blob); - if (o.syncCookie) - this.sync_cookie = o.syncCookie; - if (o.result > 0 || !o.uinPairMsgs) + const rsp = pb.decode(blob); + if (rsp[3]) + this.sync_cookie = rsp[3].raw; + if (rsp[1] > 0 || !rsp[5]) return; - // common.log(o); const items = []; - for (let v of o.uinPairMsgs) { - if (!v.messages) continue; - for (let msg of v.messages) { - const {head, body, content} = msg; - const type = head.msgType; - head.msgType = 187; + if (!Array.isArray(rsp[5])) + rsp[5] = [rsp[5]]; + for (let v of rsp[5]) { + if (!v[4]) continue; + if (!Array.isArray(v[4])) + v[4] = [v[4]]; + for (let msg of v[4]) { + const head = msg[1]; + const type = head[3]; + head[3] = 187; items.push(head); if (!this.sync_finished) continue; - let user_id = toInt(head.fromUin); - if (user_id === this.uin && (this.ignore_self || user_id !== toInt(head.toUin))) + let user_id = head[1]; + if (user_id === this.uin && (this.ignore_self || user_id !== head[2])) continue; - // if (v.lastReadTime === -1 || v.lastReadTime > head.msgTime) - // continue; - let update_flag = false; - if (!this.seq_cache.has(user_id)) { - this.seq_cache.set(user_id, head.msgSeq); - } else { - const seq = this.seq_cache.get(user_id); - if (seq - head.msgSeq >= 0 && seq - head.msgSeq < 1000) - continue; - else { - update_flag = Math.abs(head.msgSeq - seq) > 1 || head.msgSeq % 10 === 0; - this.seq_cache.set(user_id, head.msgSeq); - } - } + + //群员入群 if (type === 33) { (async()=>{ const group_id = common.uin2code(user_id); - user_id = toInt(head.authUin); - try { - const ginfo = (await this.getGroupInfo(group_id)).data; - if (user_id === this.uin) { - this.logger.info(`更新了群列表,新增了群:${group_id}`); - this.getGroupMemberList(group_id); - } else { - ginfo.member_count++; - ginfo.last_join_time = common.timestamp(); - await this.getGroupMemberInfo(group_id, user_id); - } - } catch (e) {} + user_id = head[15]; + const id = BigInt(group_id) * BigInt(user_id) * BigInt(head[5]); + if (this.notify33cache.has(id) || common.timestamp() - head[6] >= 10) + return; + this.notify33cache.add(id); + setTimeout(()=>{ + this.notify33cache.delete(id); + }, 10000); + const ginfo = (await this.getGroupInfo(group_id)).data; + if (!ginfo) return; + if (user_id === this.uin) { + this.logger.info(`更新了群列表,新增了群:${group_id}`); + this.getGroupMemberList(group_id); + } else { + ginfo.member_count++; + ginfo.last_join_time = common.timestamp(); + await this.getGroupMemberInfo(group_id, user_id); + try { + if (this.gml.get(group_id).size) + ginfo.member_count = this.gml.get(group_id).size; + } catch {} + } this.em("notice.group.increase", { group_id, user_id, - nickname: head.authNick + nickname: String(head[16].raw) }); })(); } + + //私聊消息 if ([141, 166, 167, 208].includes(type)) - chat.onPrivateMsg.call(this, type, user_id, head, content, body, update_flag); + chat.onPrivateMsg.call(this, type, head, msg[2], msg[3]); } } if (items.length) { - this.nextSeq(); - this.writeUNI("MessageSvc.PbDeleteMsg", pb.encode("DeleteMessageRequest", {items})); + this.writeUNI("MessageSvc.PbDeleteMsg", pb.encode({1:items})); } - if (o.syncFlag !== 2) - getMsg.call(this, o.syncFlag); + if (rsp[4] !== 2) + getMsg.call(this, rsp[4]); } catch (e) { this.logger.debug("getMsg发生错误。"); this.logger.debug(e); diff --git a/lib/individual.js b/lib/individual.js index d0108ab0..f0cf9c51 100644 --- a/lib/individual.js +++ b/lib/individual.js @@ -5,6 +5,11 @@ const {downloadWebImage, highwayUpload} = require("./service"); const pb = require("./pb"); const jce = require("./jce"); +/** + * 设置头像 + * @this {import("./ref").Client} + * @param {String} file + */ async function setPortrait(file) { let buf; if (file instanceof Buffer) { @@ -26,38 +31,42 @@ async function setPortrait(file) { if (!buf || !buf.length) throw new Error("Fail to get file: " + file); } - this.nextSeq(); - const body = pb.encode("IndividualPortraitReqPkg", { - body: { - uin: this.uin, - reqChannelType: 0, - subcmd: 16, - buType: 1, - netType: 3, - termType: 5, + const body = pb.encode({ + 1281: { + 1: this.uin, + 2: 0, + 3: 16, + 4: 1, + 6: 3, + 7: 5, } }); const blob = await this.sendUNI("HttpConn.0x6ff_501", body); - const resp = pb.decode("IndividualPortraitRspPkg", blob).body; - highwayUpload.call(this, resp.entry.ipport[0].uint32UpIp, resp.entry.ipport[0].uint32UpPort, { - buf, md5: md5(buf), key: resp.upUkey + const rsp = pb.decode(blob)[1281]; + highwayUpload.call(this, rsp[3][2][0][2], rsp[3][2][0][3], { + buf, md5: md5(buf), key: rsp[1].raw }, 5); } +/** + * @this {import("./ref").Client} + * @param {Number} user_id + * @param {Number} times 1~20 + * @returns {import("./ref").ProtocolResponse} + */ async function sendLike(user_id, times = 1) { var [user_id] = uinAutoCheck(user_id); times = parseInt(times); if (!(times > 0 && times <= 20)) times = 1; - this.nextSeq(); const ReqFavorite = jce.encodeStruct([ jce.encodeNested([ - this.uin, 1, this.seq_id, 1, 0, Buffer.from("0C180001060131160131", "hex") + this.uin, 1, this.seq_id + 1, 1, 0, Buffer.from("0C180001060131160131", "hex") ]), user_id, 0, 1, times ]); const extra = { - req_id: this.seq_id, + req_id: this.seq_id + 1, service: "VisitorSvc", method: "ReqFavorite", }; @@ -68,13 +77,19 @@ async function sendLike(user_id, times = 1) { return {result: jce.decode(parent[0])[3]}; } +/** + * 获取对方加好友设置(暂不对外开放) + * @this {import("./ref").Client} + * @param {Number} user_id + * @returns {Number} + */ async function getAddSetting(user_id) { const FS = jce.encodeStruct([ this.uin, user_id, 3004, 0, null, 1 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "GetUserAddFriendSettingReq", }; @@ -86,6 +101,13 @@ async function getAddSetting(user_id) { return parent[2]; } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {String} comment + * @returns {import("./ref").ProtocolResponse} + */ async function addFriend(group_id, user_id, comment = "") { if (group_id == 0) { group_id = 0; @@ -99,10 +121,10 @@ async function addFriend(group_id, user_id, comment = "") { const AF = jce.encodeStruct([ this.uin, user_id, type?1:0, 1, 0, Buffer.byteLength(comment), comment, 0, 1, null, 3004, - 11, null, null, group_id?pb.encode("AddFrdFromGrp", {groupCode: group_id}):null, 0, null, null, 0 + 11, null, null, group_id?pb.encode({1:group_id}):null, 0, null, null, 0 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "AddFriendReq", }; @@ -113,6 +135,12 @@ async function addFriend(group_id, user_id, comment = "") { return {result: parent[6]}; } +/** + * @this {import("./ref").Client} + * @param {Number} user_id + * @param {Boolean} block + * @returns {import("./ref").ProtocolResponse} + */ async function delFriend(user_id, block = true) { var [user_id] = uinAutoCheck(user_id); const DF = jce.encodeStruct([ @@ -120,7 +148,7 @@ async function delFriend(user_id, block = true) { user_id, 2, block?1:0 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "DelFriendReq", }; @@ -131,55 +159,65 @@ async function delFriend(user_id, block = true) { return {result: parent[2]}; } +/** + * 设置个人资料 + * @this {import("./ref").Client} + * @param {Number} k + * @param {Buffer|String} v + * @returns {import("./ref").ProtocolResponse} + */ async function setProfile(k, v) { - this.nextSeq(); v = Buffer.from(v); const buf = Buffer.allocUnsafe(11 + v.length); buf.writeUInt32BE(this.uin), buf.writeUInt8(0, 4); buf.writeInt32BE(k, 5), buf.writeUInt16BE(v.length, 9); buf.fill(v, 11); - const body = pb.encode("OIDBSSOPkg", { - command: 1279, - serviceType: 9, - result: 0, - bodybuffer: buf - }); - const blob = await this.sendUNI("OidbSvc.0x4ff_9", body); - const o = pb.decode("OIDBSSOPkg", blob); - if (o.result === 34) - o.result = 0; - return o; + const blob = await this.sendUNI("OidbSvc.0x4ff_9", buf); + const o = pb.decode(blob); + if (o[3] === 34) + o[3] = 0; + return {result: o[3]}; } +/** + * 设置签名 + * @this {import("./ref").Client} + * @param {String} sign + * @returns {import("./ref").ProtocolResponse} + */ async function setSign(sign = "") { sign = Buffer.from(String(sign)).slice(0, 254); - this.nextSeq(); - const body = pb.encode("SignAuthReqPkg", { - prefix: 2, - timestamp: Date.now(), - head: { - command: 109, - unknown: { - num: 825110830 - }, - ver: this.apkver.substr(0, 5) + const body = pb.encode({ + 1: 2, + 2: Date.now(), + 3: { + 1: 109, + 2: {6: 825110830}, + 3: this.apkver.substr(0, 5) }, - body: { - uin: this.uin, - prefix: 0, - length: 27 + sign.length, - content: Buffer.concat([ + 5: { + 1: this.uin, + 2: 0, + 3: 27 + sign.length, + 4: Buffer.concat([ Buffer.from([0x3, sign.length+1, 0x20]), sign, Buffer.from([0x91,0x04,0x00,0x00,0x00,0x00,0x92,0x04,0x00,0x00,0x00,0x00,0xA2,0x04,0x00,0x00,0x00,0x00,0xA3,0x04,0x00,0x00,0x00,0x00]) ]), - suffix: 0 + 5: 0 }, - suffix: 1 + 6: 1 }); const blob = await this.sendUNI("Signature.auth", body); - return pb.decode("SignAuthRspPkg", blob); + const rsp = pb.decode(blob); + return {result: rsp[1], emsg: rsp[2]}; } +/** + * 设置在线状态 + * @this {import("./ref").Client} + * @param {Number} status + * @returns {import("./ref").ProtocolResponse} + */ async function setStatus(status) { if (this.config.platform !== 1) throw new Error("platform not supported"); @@ -198,7 +236,7 @@ async function setStatus(status) { "", "", null, 1, null, 0, null, sub, 0 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "PushService", method: "SvcReqRegister", }; diff --git a/lib/message/builder.js b/lib/message/builder.js index c8006173..48efcaeb 100644 --- a/lib/message/builder.js +++ b/lib/message/builder.js @@ -5,8 +5,8 @@ const path = require("path"); const crypto = require("crypto"); const spawn = require("child_process"); const {downloadWebImage, downloadWebRecord} = require("../service"); +const {uploadPtt, uploadImages, setPrivateImageNested, setGroupImageNested} = require("./storage"); const common = require("../common"); -const pb = require("../pb"); function unescapeCQ(s) { if (s === "[") return "["; @@ -23,7 +23,6 @@ function unescapeCQInside(s) { const AT_BUF = Buffer.from([0,1,0,0,0]); const BUF1 = Buffer.from([1]); const BUF2 = Buffer.alloc(2); -const BUF4 = Buffer.alloc(4); const FACE_OLD_BUF = Buffer.from([0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0]); /** @@ -33,8 +32,8 @@ const FACE_OLD_BUF = Buffer.from([0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0 */ function buildTextMessage(chain, text) { if (!text) return 0; - text = text.toString() - chain.set({text: {str: text}}, {type: "text"}); + text = String(text); + chain.set({1: {1: text}}, {type: "text"}); return Buffer.byteLength(text); } @@ -58,9 +57,9 @@ function buildAtMessage(chain, cq, group_id) { return 0; const buf = Buffer.allocUnsafe(6); buf.writeUInt8(display.length), buf.writeUInt8(flag, 1), buf.writeUInt32BE(q, 2); - chain.set({text: { - str: display, - attr6Buf: Buffer.concat([AT_BUF, buf, BUF2]) + chain.set({1: { + 1: display, + 3: Buffer.concat([AT_BUF, buf, BUF2]) }}, {type: "at"}); return Buffer.byteLength(display); } @@ -74,10 +73,10 @@ function buildFaceMessage(chain, cq) { if (id < 0 || id > 0xff || isNaN(id)) return false; const old = Buffer.allocUnsafe(2); old.writeUInt16BE(0x1441 + id); - chain.set({face: { - index: id, - old: old, - buf: FACE_OLD_BUF + chain.set({2: { + 1: id, + 2: old, + 11: FACE_OLD_BUF }}, {type: "face"}); return true; } @@ -90,12 +89,14 @@ function buildSFaceMessage(chain, cq) { id = parseInt(id); if (id < 0 || isNaN(id)) return false; if (!text) text = "/" + id; - chain.set({commonElem: { - serviceType: 33, - pbElem: pb.encode("MsgElemInfoServtype33", { - id, text1: text, text2: text - }), - businessType: 1 + chain.set({53: { + 1: 33, + 2: { + 1: id, + 2: text, + 3: text + }, + 3: 1 }}, {type: "sface"}); return true; } @@ -109,22 +110,22 @@ function buildBFaceMessage(chain, cq) { if (!text) text = "原创表情"; text = "["+text.slice(0, 5)+"]"; const o = { - faceName: Buffer.from(text), - itemType: 6, - faceInfo: 1, - faceId: Buffer.from(file.slice(0, 32), "hex"), - tabId: parseInt(file.slice(64)), - subType: 3, - key: Buffer.from(file.slice(32, 64), "hex"), - mediaType: 0, - imageWidth: 200, - imageHeight: 200, + 1: text, + 2: 6, + 3: 1, + 4: Buffer.from(file.slice(0, 32), "hex"), + 5: parseInt(file.slice(64)), + 6: 3, + 7: Buffer.from(file.slice(32, 64), "hex"), + 9: 0, + 10: 200, + 11: 200, }; if (cq.magic && cq.magic instanceof Buffer) - o.mobileparam = cq.magic; - chain.set({marketFace: o}, {type: "bface"}); - chain.set({text: { - str: text, + o[12] = cq.magic; + chain.set({6: o}, {type: "bface"}); + chain.set({1: { + 1: text, }}, {type: "text"}); return true; } catch (e) { @@ -147,36 +148,36 @@ function buildMagicFaceMessage(chain, type, cq) { cq.file = "83c8a293ae65ca140f348120a77448ee3764653339666562636634356536646211415"; return buildBFaceMessage(chain, cq); } + return false; } /** - * @param {Boolean} group_id * @returns {Boolean} */ -async function buildImageMessage(chain, cq, group_id, is_flash = false) { +async function buildImageMessage(chain, cq, is_group, stat) { let {file, url, cache, type, timeout, proxy} = cq; if (!file) return false; - if (type === "flash") is_flash = true; file = file.trim(); - let buf, md5, size; - const o = {type: is_flash?"flash":"image", done: false}; - const nest = { - fileType: 66, - useful: 1, - origin: 1, - original: 1, - filePath: common.uuid(), - md5: crypto.randomBytes(16), - size: crypto.randomBytes(2).readUInt16BE(), - fileId: 0, - resId: "", - downloadPath: "", - pbReserve: Buffer.from([0x78, 0x02]), - flag: BUF4, - oldPicMd5: false, + let fid, buf, md5 = crypto.randomBytes(16), size = 65536; + const nested = {}; //pb nested obj + if (type === "flash") { + var elem = is_group ? {1:nested} : {2:nested}; + elem = { + 53: { + 1: 3, + 2: elem, + 3: 0, + } + }; + } else { + type = "image"; + var elem = is_group ? {8:nested} : {4:nested}; } - const elem = group_id ? {customFace: nest} : {notOnlineImage: nest}; - + const img = { + buf, md5, size, nested + }; + + // 网络图片 if (file.startsWith("http://") || file.startsWith("https://")) { const filename = common.md5(Buffer.from(file, "utf-8")).toString('hex'); const filepath = path.join(process.OICQ.config.cache_root, "image", filename); @@ -193,24 +194,26 @@ async function buildImageMessage(chain, cq, group_id, is_flash = false) { const task = (async()=>{ const buf = await downloadWebImage.call(this, file, proxy=="1", timeout); if (buf) { - nest.buf = buf; - nest.size = buf.length; - nest.md5 = common.md5(buf); - nest.filePath = nest.md5.toString("hex"); - fs.writeFile(filepath, nest.filePath + nest.size, ()=>{}); + img.buf = buf; + img.size = buf.length; + img.md5 = common.md5(buf); + fs.writeFile(filepath, img.md5.toString("hex") + img.size, ()=>{}); } })(); - if (is_flash) - await task; - else - o.task = task; + stat.tasks.push(task); } - } else if (file.startsWith("base64://")) { + } + + // base64图片 + else if (file.startsWith("base64://")) { file = file.replace("base64://", ""); buf = Buffer.from(file, "base64"); md5 = common.md5(buf), size = buf.length; - } else { + } + + else { md5 = Buffer.from(file.slice(0, 32), "hex"); + //本地图片 if (md5.length !== 16) { try { file = file.replace(/^file:\/{2,3}/, ""); @@ -222,37 +225,35 @@ async function buildImageMessage(chain, cq, group_id, is_flash = false) { this.logger.warn(`获取本地图片 ${file} 失败。`); return false; } - } else { + } + //只有md5和size + else { size = parseInt(file.substr(32)); size = size > 0 ? size : 0; } } - if (buf) - nest.buf = buf; - if (size) - nest.size = size; - if (md5) { - nest.md5 = md5; - nest.filePath = md5.toString("hex"); - } - - if (group_id && url && url.includes("gchatpic_new")) { + img.buf = buf; + img.size = size; + img.md5 = md5; + + //有url参数的图片可以直接取得fid + if (is_group && url && url.includes("gchatpic_new")) { const id = url.match(/-[0-9]+-/); - if (id) { - nest.fileId = parseInt(id[0].replace("-", "")) - 0xffffffff; - o.done = true; - } + if (id) + fid = parseInt(id[0].replace("-", "")) - 0xffffffff; } - if (!group_id && url && url.includes("offpic_new")) { + if (!is_group && url && url.includes("offpic_new")) { const id = url.match(/\/\/[0-9]+-[0-9]+-[0-9A-Za-z]+/); - if (id) { - nest.resId = id[0].replace("/", ""); - nest.downloadPath = nest.resId; - o.done = true; - } + if (id) + fid = id[0].replace("/", ""); } - chain.set(elem, o); + + if (fid) + (is_group?setGroupImageNested:setPrivateImageNested).call(this, img, fid); + else + stat.imgs.push(img); + chain.set(elem, {type}); return true; } @@ -267,7 +268,7 @@ async function audioTrans(cache_filepath, file) { filepath = file; file = await fs.promises.readFile(filepath); } else { - tmp = common.uuid(); + tmp = Math.random() + "" + Date.now(); filepath = path.join(path.dirname(cache_filepath), tmp); await fs.promises.writeFile(filepath, file); } @@ -295,10 +296,10 @@ async function audioTrans(cache_filepath, file) { }) } -async function buildRecordMessage(chain, data) { +async function buildRecordMessage(chain, data, group_id) { let {file, cache, timeout, proxy} = data; - if (!file) return false; - let buf, md5, size, ext, url; + if (!file) return; + let buf, md5, size, codec, url; try { file = file.trim(); file = file.replace(/^file:\/{2,3}/, ""); @@ -318,42 +319,46 @@ async function buildRecordMessage(chain, data) { buf = await audioTrans.call(this, cache_filepath, file); } const head = buf.slice(0, 7).toString(); - ext = head.includes("SILK") ? ".slk" : ".amr"; + codec = head.includes("SILK") ? 1 : 0; } catch (e) { this.logger.debug(e); this.logger.warn(`音频文件 ${url} 处理失败。`); - return false; + return; } md5 = common.md5(buf), size = buf.length; + try { + var fid = await uploadPtt.call(this, group_id, buf, md5, codec); + } catch(e) { + this.logger.debug(e); + return this.logger.debug("语音上传失败"); + } const elem = { - fileType: 4, - srcUin: this.uin, - fileMd5: md5, - fileName: md5.toString("hex") + ".amr", - fileSize: size, - boolValid: true, - fileUuid: undefined, - groupFileKey: undefined, - pbReserve: Buffer.from([8, 0, 40, 0, 56, 0]), - buf, md5, size, ext + 1: 4, + 2: this.uin, + 3: fid, + 4: md5, + 5: md5.toString("hex") + ".amr", + 6: size, + 11: 1, + 18: fid, + 30: Buffer.from([8, 0, 40, 0, 56, 0]), }; chain.set(elem, {type: "ptt"}); - return true; } -function buildForwardNode(chain, data) { - let {name, uin, content, time} = data; - uin = parseInt(uin); - if (!common.checkUin(uin) || !content) return false; - content = String(content); - time = time ? parseInt(time) : common.timestamp(); - name = name ? name : "NoName"; - const elem = {text: {str: content}}; - chain.set(elem, { - uin, content, time, name - }); - return true; -} +// function buildForwardNode(chain, data) { +// let {name, uin, content, time} = data; +// uin = parseInt(uin); +// if (!common.checkUin(uin) || !content) return false; +// content = String(content); +// time = time ? parseInt(time) : common.timestamp(); +// name = name ? name : "NoName"; +// const elem = {text: {str: content}}; +// chain.set(elem, { +// uin, content, time, name +// }); +// return true; +// } function buildLocationMessage(chain, data) { let {address, lat, lng, name, id} = data; @@ -379,8 +384,8 @@ function buildLocationMessage(chain, data) { } function buildJsonMessage(chain, obj, text) { - const elem = {lightApp: { - data: Buffer.concat([BUF1, zlib.deflateSync(JSON.stringify(obj))]) + const elem = {51: { + 1: Buffer.concat([BUF1, zlib.deflateSync(JSON.stringify(obj))]) }}; chain.set(elem, { type: "json", text @@ -388,16 +393,16 @@ function buildJsonMessage(chain, obj, text) { return true; } -function buildXmlMessage(chain, xml, svcid, text) { - const elem ={richMsg: { - template1: Buffer.concat([BUF1, zlib.deflateSync(xml)]), - serviceId: svcid, - }}; - chain.set(elem, { - type: "xml", text - }); - return true; -} +// function buildXmlMessage(chain, xml, svcid, text) { +// const elem ={richMsg: { +// template1: Buffer.concat([BUF1, zlib.deflateSync(xml)]), +// serviceId: svcid, +// }}; +// chain.set(elem, { +// type: "xml", text +// }); +// return true; +// } function qs(s, sep = ",", equal = "=") { // fuck nodejs querystring @@ -446,42 +451,30 @@ async function buildMessageFromString(chain, message, escape, group_id, stat) { /** * @param {import("../../client").MessageElem[]|String} message * @param {Boolean} escape - * @param {Number} group_id + * @param {Number} is_group * @returns {Map} */ -async function buildMessage(message, escape, group_id) { +async function buildMessage(target, message, escape, is_group) { const chain = new Map(); const stat = { length: 0, at_cnt: 0, face_cnt: 0, sface_cnt: 0, bface_cnt: 0, img_cnt: 0, - is_forward: false, type: "stat" + is_forward: false, type: "stat", tasks: [], imgs: [] }; chain.set("stat", stat); - if (typeof message === "string") - await buildMessageFromString.call(this, chain, message, escape, group_id, stat); - else { + if (Array.isArray(message)) { for (let v of message) { - if (!v.data) continue; - await buildElement.call(this, chain, v.type, v.data, group_id, stat); + if (!v || !v.data) continue; + await buildElement.call(this, chain, v.type, v.data, is_group?target:0, stat); } + } else { + message = String(message); + await buildMessageFromString.call(this, chain, message, escape, is_group?target:0, stat); } + await Promise.all(stat.tasks); + await uploadImages.call(this, target, stat.imgs, is_group); 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 {Map} chain * @param {String} type @@ -495,9 +488,6 @@ async function buildElement(chain, type, data, group_id, stat) { // if (chain.length > 1 && !stat.is_forward && type === "node") // return; switch (type) { - // case "reply": - // buildReplyMessage(chain, data); - // break; case "text": stat.length += buildTextMessage.call(this, chain, data.text); break; @@ -524,11 +514,12 @@ async function buildElement(chain, type, data, group_id, stat) { ++stat.bface_cnt; break; case "image": - if (await buildImageMessage.call(this, chain, data, group_id)) + if (await buildImageMessage.call(this, chain, data, group_id, stat)) ++stat.img_cnt; break; case "flash": - await buildImageMessage.call(this, chain, data, group_id, true); + data.type = "flash"; + await buildImageMessage.call(this, chain, data, group_id, stat); break; case "record": await buildRecordMessage.call(this, chain, data, group_id); @@ -541,6 +532,7 @@ async function buildElement(chain, type, data, group_id, stat) { buildLocationMessage.call(this, chain, data); break; default: + this.logger.warn("未知的CQ码类型:" + type); break; } } diff --git a/lib/message/chat.js b/lib/message/chat.js index e42ca1dd..b67b4713 100644 --- a/lib/message/chat.js +++ b/lib/message/chat.js @@ -3,10 +3,9 @@ const zlib = require("zlib"); const crypto = require("crypto"); const buildMessage = require("./builder"); const parseMessage = require("./parser"); -const {uploadImages, uploadPtt, uploadMultiMsg} = require("./storage"); +const {uploadMultiMsg} = require("./storage"); const common = require("../common"); const pb = require("../pb"); -const toInt = common.toInt; //send msg---------------------------------------------------------------------------------------------------- @@ -21,83 +20,65 @@ const toInt = common.toInt; async function sendMsg(target, message, escape, type) { var [target] = common.uinAutoCheck(target); - const _sendMsg = async(elems, long = false)=>{ + const _sendMsg = async(rich, long = false)=>{ if (long) - elems.elems = await toLongMessageElems.call(this, type?common.code2uin(target):target, elems.elems); - return await (type?sendGroupMsg:sendPrivateMsg).call(this, target, elems, type); + rich[2] = await toLongMessageElems.call(this, target, rich, type); + return await (type?sendGroupMsg:sendPrivateMsg).call(this, target, rich, type); } - const map = await buildMessage.call(this, message, escape, type===1?target:type); - const elems = [], tasks = [], images = []; + const map = await buildMessage.call(this, target, message, escape, type); + const elems = []; let stat, rsp; for (let [elem, o] of map) { - if (o.task instanceof Promise) - tasks.push(o.task); switch (o.type) { case "stat": stat = o; break; case "ptt": - const ptt = await uploadPtt.call(this, target, elem, type); - elem.fileUuid = elem.groupFileKey = ptt.fileKey; - rsp = await _sendMsg({ptt: elem}); + rsp = await _sendMsg({4: elem}); break; case "flash": - if (!o.done) - await completeImages.call(this, target, [elem[Object.keys(elem)[0]]], type); - const flash = [ - {commonElem: { - serviceType: 3, - pbElem: pb.encode("MsgElemInfoServtype3", elem), - businessType: 0, - }}, - {text: {str: "[闪照]请使用新版手机QQ查看闪照。"}} - ]; - rsp = await _sendMsg({elems: flash}); + rsp = await _sendMsg({2: [elem, {1: {1: "[闪照]请使用新版手机QQ查看闪照。"}}]}); break; case "json": case "xml": const rich = [elem]; if (o.text) - rich.push({text: {str: o.text}}); - rsp = await _sendMsg({elems: rich}); + rich.push({1: {1: o.text}}); + rsp = await _sendMsg({2: rich}); break; default: elems.push(elem); - if (o.type === "image" && !o.done) - images.push(elem[Object.keys(elem)[0]]); } } if (!elems.length) { if (rsp) return rsp; throw new Error("empty message"); } - await Promise.all(tasks); - await completeImages.call(this, target, images, type); stat.length += stat.at_cnt * 22 + stat.face_cnt * 23 + stat.sface_cnt * 42 + stat.bface_cnt * 140 + stat.img_cnt * (type?90:304); stat.length *= 1.05; const is_long = type ? (stat.length>790) : (stat.length>935); - rsp = await _sendMsg({elems}, is_long); + rsp = await _sendMsg({2:elems}, is_long); if (!is_long && rsp.result === 0 && rsp.data && rsp.data.message_id === "") { this.logger.warn(`判定为风控,这条消息将尝试作为长消息再发送一次。`); - return await _sendMsg({elems}, true); + return await _sendMsg({2:elems}, true); } return rsp; } function buildSyncCookie() { const time = common.timestamp(); - return pb.encode("SyncCookie", { - time1: time, - time: time, - ran1: crypto.randomBytes(4).readUInt32BE(), - ran2: crypto.randomBytes(4).readUInt32BE(), - ran3: crypto.randomBytes(4).readUInt32BE(), - const1: this.const1, - const2: this.const2, - const3: this.const3, - lastSyncTime: time, - const4: 0, + return pb.encode({ + 1: time, + 2: time, + 3: this.const1, + 4: this.const2, + 5: crypto.randomBytes(4).readUInt32BE(), + 9: crypto.randomBytes(4).readUInt32BE(), + 11: crypto.randomBytes(4).readUInt32BE(), + 12: this.const3, + 13: time, + 14: 0, }); } @@ -106,46 +87,46 @@ function buildSyncCookie() { * @returns {import("../ref").ProtocolResponse} */ async function sendPrivateMsg(user_id, rich) { - let routing = {c2c: {toUin: user_id}}; + let routing = {1: {1: user_id}}; if (this.sl.has(user_id)) { try { const group_id = this.sl.get(user_id).group_id; if ((await this.getGroupMemberInfo(group_id, user_id)).data) - routing = {grpTmp: { - groupUin: common.code2uin(group_id), - toUin: user_id, + routing = {3: { + 1: common.code2uin(group_id), + 2: user_id, }}; } catch (e) {} } else if (!this.fl.has(user_id)) { for (const [k, v] of this.gml) { if (v.has(user_id)) - routing = {grpTmp: { - groupUin: common.code2uin(k), - toUin: user_id, + routing = {3: { + 1: common.code2uin(k), + 2: user_id, }} } } - this.nextSeq(); - const seq = crypto.randomBytes(2).readUInt16BE(); + const seq = this.seq_id; const random = crypto.randomBytes(2).readUInt16BE(); - const body = pb.encode("SendMessageRequest", { - routingHead:routing, - contentHead:{pkgNum:1,pkgIndex:0,divSeq:0}, - msgBody: {richText: rich}, - msgSeq: seq, - msgRand: random, - SyncCookie: buildSyncCookie.call(this), - msgVia: 1, + const body = pb.encode({ + 1: routing, + 2: {1:1, 2:0, 3:0}, + 3: {1: rich}, + 4: seq, + 5: random, + 6: buildSyncCookie.call(this), + 8: 1, }); const blob = await this.sendUNI("MessageSvc.PbSendMsg", body); - const resp = pb.decode("PbSendMsgResp", blob); - if (resp.result === 0) { - const message_id = genSelfMessageId(user_id, seq, random, resp.sendTime); + const rsp = pb.decode(blob); + if (rsp[1] === 0) { + const message_id = genSelfMessageId(user_id, seq, random, rsp[3]); this.logger.info(`send to: [Private: ${user_id} / message_id: ${message_id}]`); return {result: 0, data: {message_id}}; } - this.logger.error(`send failed: [Private: ${user_id}] ` + resp.errmsg); - return {result: resp.result, emsg: resp.errmsg}; + var emsg = rsp[2] ? String(rsp[2].raw) : undefined; + this.logger.error(`send failed: [Private: ${user_id}] ` + emsg); + return {result: rsp[1], emsg}; } /** @@ -153,16 +134,15 @@ async function sendPrivateMsg(user_id, rich) { * @returns {import("../ref").ProtocolResponse} */ async function sendGroupMsg(target, rich, type) { - this.nextSeq(); - const routing = type === 1 ? {grp: {groupCode: target}} : {dis: {discussUin: target}}; - const random = crypto.randomBytes(4).readInt32BE(); - const body = pb.encode("SendMessageRequest", { - routingHead:routing, - contentHead:{pkgNum:1,pkgIndex:0,divSeq:0}, - msgBody: {richText: rich}, - msgSeq: this.seq_id, - msgRand: random, - msgVia: 0, + const routing = type === 1 ? {2: {1: target}} : {4: {1: target}}; + const random = crypto.randomBytes(4).readUInt32BE(); + const body = pb.encode({ + 1: routing, + 2: {1:1, 2:0, 3:0}, + 3: {1: rich}, + 4: this.seq_id + 1, + 5: random, + 8: 0, }); const event_id = `interval.${target}.${random}`; let message_id = ""; @@ -174,17 +154,19 @@ async function sendGroupMsg(target, rich, type) { this.removeAllListeners(event_id); throw e; } - const resp = pb.decode("PbSendMsgResp", blob); - if (resp.result !== 0) { + const rsp = pb.decode(blob); + if (rsp[1] !== 0) { this.removeAllListeners(event_id); - if (resp.result === 120) - resp.errmsg = "发送失败,在本群被禁言"; - this.logger.error(`send failed: [Group: ${target}] ` + resp.errmsg); - return {result: resp.result, emsg: resp.errmsg}; + if (rsp[1] === 120) + var emsg = "发送失败,在本群被禁言"; + else + var emsg = rsp[2] ? String(rsp[2].raw) : undefined; + this.logger.error(`send failed: [Group: ${target}] ` + emsg); + return {result: rsp[1], emsg}; } if (type === 2) { this.removeAllListeners(event_id); - return resp; + return {result: rsp[1]}; } if (!message_id) { await new Promise((resolve)=>{ @@ -198,55 +180,43 @@ async function sendGroupMsg(target, rich, type) { return {result: 0, data: {message_id}}; } -async function completeImages(target, images, is_group) { - let n = 0; - while (images.length > n) { - try { - const resp = await uploadImages.call(this, target, images.slice(n, n + 20), is_group); - for (let i = 0; i < resp.msgTryUpImgRsp.length; ++i) { - const v = resp.msgTryUpImgRsp[i], nest = images[i]; - nest.fileId = nest.resId = nest.downloadPath = is_group ? v.fid.low : v.upResid; - } - } catch (e) {} - n += 20; - } -} - /** * @this {import("../ref").Client} * @returns {Array} */ -async function toLongMessageElems(uin, elems) { - const seq = crypto.randomBytes(2).readUInt16BE(); - const msg = [{ - head: { - fromUin: this.uin, - msgSeq: seq, - msgTime: common.timestamp(), - msgUid: 0x01000000000000000n | BigInt(seq), - mutiltransHead: { - msgId: 1, +async function toLongMessageElems(uin, rich, is_group) { + const compressed = zlib.gzipSync(pb.encode({ + 1: { + 1: { + 1: this.uin, + 3: is_group?82:9, + 4: 11, + 5: crypto.randomBytes(2).readUInt16BE(), + 6: common.timestamp(), + 9: { + 1: common.uin2code(uin), + 4: this.nickname, + }, + 14: this.nickname, + 20: { + 1:0, + 2:1 + }, }, - msgType: 82, - groupInfo: { - groupCode: common.uin2code(uin), - groupCard: this.nickname, + 3: { + 1: rich, }, }, - body: { - richText: {elems}, - }, - }]; - let resp; + })); try { - resp = await uploadMultiMsg.call(this, uin, msg, 1); + var resid = await uploadMultiMsg.call(this, uin, compressed, is_group); } catch (e) { throw new Error("fail to upload multi msg"); } const templete = ` @@ -258,68 +228,21 @@ async function toLongMessageElems(uin, elems) { `; return [ { - richMsg: { - template1: Buffer.concat([Buffer.from([1]), zlib.deflateSync(templete)]), - serviceId: 35, + 12: { + 1: Buffer.concat([Buffer.from([1]), zlib.deflateSync(templete)]), + 2: 35, } }, { - generalFlags: { - longTextFlag: 1, - longTextResid: resp.msgResid, - pbReserve: Buffer.from([0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]), + 37: { + 6: 1, + 7: resid, + 19: Buffer.from([0x78, 0x00, 0xF8, 0x01, 0x00, 0xC8, 0x02, 0x00]), } }, ]; } -// async function sendForwardMsg(uin, nodes, is_group) { -// const seq = crypto.randomBytes(2).readUInt16BE(), msg = []; -// for (let v of nodes) { -// msg.push({ -// head: { -// fromUin: v.uin, -// msgSeq: seq, -// msgTime: v.time, -// msgUid: 0x01000000000000000n | BigInt(seq), -// mutiltransHead: { -// msgId: 1, -// }, -// msgType: 82, -// groupInfo: { -// groupCode: common.uin2code(uin), -// groupCard: v.name, -// }, -// }, -// body: { -// richText: { -// elems: [{text: {str: v.content}}] -// }, -// }, -// }) -// } -// let resp; -// try { -// resp = await uploadMultiMsg.call(this, uin, msg, 2); -// } catch (e) { -// throw new Error(); -// } -// let preview = ""; -// for (let v of nodes) -// preview += ` ${v.name}:${v.content.substr(0, 30)} ` -// const template = ` -// 群聊的聊天记录 ${preview}
查看转发消息
`; -// const elems = [ -// { -// richMsg: { -// template1: Buffer.concat([Buffer.from([1]), zlib.deflateSync(template)]), -// serviceId: 35, -// } -// }, -// ]; -// return await (is_group?sendGroupMsg:sendPrivateMsg).call(this, is_group?common.uin2code(uin):uin, {elems}, is_group); -// } - function genSelfMessageId(user_id, seq, random, timestamp) { const buf = Buffer.allocUnsafe(12); buf.writeUInt32BE(user_id), buf.writeUInt16BE(seq, 4), buf.writeUInt16BE(random, 6), buf.writeUInt32BE(timestamp, 8); @@ -332,12 +255,12 @@ function parseSelfMessageId(message_id) { } function genGroupMessageId(group_id, seq, random) { const buf = Buffer.allocUnsafe(12); - buf.writeUInt32BE(group_id), buf.writeInt32BE(seq, 4), buf.writeInt32BE(random, 8); + buf.writeUInt32BE(group_id), buf.writeInt32BE(seq&0xffffffff, 4), buf.writeInt32BE(random&0xffffffff, 8); return "1" + buf.toString("base64"); } function parseGroupMessageId(message_id) { const buf = Buffer.from(message_id.substr(1), "base64"); - const group_id = buf.readUInt32BE(), seq = buf.readInt32BE(4), random = buf.readInt32BE(8); + const group_id = buf.readUInt32BE(), seq = buf.readUInt32BE(4), random = buf.readUInt32BE(8); return {group_id, seq, random}; } @@ -349,7 +272,6 @@ async function recallMsg(message_id) { body = recallGroupMsg.call(this, message_id); else body = recallPrivateMsg.call(this, message_id); - this.nextSeq(); await this.sendUNI("PbMessageSvc.PbMsgWithDraw", body); } function recallPrivateMsg(message_id) { @@ -359,34 +281,34 @@ function recallPrivateMsg(message_id) { if (this.sl.get(user_id).group_id) type = 1; } catch (e) {} - return pb.encode("MsgWithDrawReq", { - c2cWithDraw: [{ - subCmd: 1, - msgInfo: [{ - fromUin: this.uin, - toUin: user_id, - msgTime: timestamp, - msgUid: {low:random,high:16777216,unsigned:false}, - msgSeq: seq, - msgRandom: random, + return pb.encode({ + 1: [{ + 1: [{ + 1: this.uin, + 2: user_id, + 3: seq, + 4: 16777216n<<32n|BigInt(random), + 5: timestamp, + 6: random, }], - reserved: Buffer.from([0x8,type]), - longMessageFlag: 0, + 2: 0, + 3: Buffer.from([0x8,type]), + 4: 1, }] }); } function recallGroupMsg(message_id) { const {group_id, seq, random} = parseGroupMessageId(message_id); - return pb.encode("MsgWithDrawReq", { - groupWithDraw: [{ - subCmd: 1, - groupCode: group_id, - msgList: [{ - msgSeq: seq, - msgRandom: random, - msgType: 0, + return pb.encode({ + 2: [{ + 1: 1, + 3: group_id, + 4: [{ + 1: seq, + 2: random, + 3: 0, }], - userDef: Buffer.from([8,0]), + 5: Buffer.from([8,0]), }] }); } @@ -396,14 +318,31 @@ function recallGroupMsg(message_id) { /** * @this {import("../ref").Client} */ -async function onPrivateMsg(type, user_id, head, content, body, update_flag) { - let sub_type, message_id, font = "unknown"; +async function onPrivateMsg(type, head, content, body) { + + const user_id = head[1]; + + let no_cache = false; + if (!this.seq_cache.has(user_id)) { + this.seq_cache.set(user_id, head[5]); + } else { + const seq = this.seq_cache.get(user_id); + if (seq - head[5] >= 0 && seq - head[5] < 1000) + return; + else { + no_cache = Math.abs(head[5] - seq) > 1 || head[5] % 5 === 0; + this.seq_cache.set(user_id, head[5]); + } + } + + let sub_type, message_id, font = "unknown", time = head[6]; + this.msg_times.push(time); const sender = Object.assign({user_id}, this.fl.get(user_id)); if (type === 141) { sub_type = "other"; - if (head.c2cTmpMsgHead && head.c2cTmpMsgHead.groupCode) { + if (head[8] && head[8][4]) { sub_type = "group"; - const group_id = toInt(head.c2cTmpMsgHead.groupCode); + const group_id = head[8][4]; sender.group_id = group_id; } } else if (type === 166 || type === 208) { @@ -414,28 +353,30 @@ async function onPrivateMsg(type, user_id, head, content, body, update_flag) { return; } if (!sender.nickname) { - const stranger = (await this.getStrangerInfo(user_id, update_flag)).data; + const stranger = (await this.getStrangerInfo(user_id, no_cache)).data; if (stranger) { stranger.group_id = sender.group_id; Object.assign(sender, stranger); this.sl.set(user_id, stranger); } } - if (body.richText && body.richText.elems) { + if (body[1] && body[1][2]) { let random = crypto.randomBytes(4).readInt32BE(); - if (body.richText.attr) { - font = body.richText.attr.fontName; - random = body.richText.attr.random; + if (body[1][1]) { + font = String(body[1][1][9].raw); + random = body[1][1][3]; } - message_id = genGroupMessageId(user_id, head.msgSeq, random); + message_id = genGroupMessageId(user_id, head[5], random); try { - var {chain, raw_message} = await parseMessage.call(this, body.richText); + var {chain, raw_message} = await parseMessage.call(this, body[1]); } catch (e) {return} if (raw_message) { this.logger.info(`recv from: [Private: ${user_id}(${sub_type})] ` + raw_message); this.em("message.private." + sub_type, { - message_id, user_id, message: chain, raw_message, font, sender, time: toInt(head.msgTime), - auto_reply: !!(content&&content.autoReply) + message_id, user_id, + message: chain, + raw_message, font, sender, time, + auto_reply: !!(content&&content[4]) }); } } @@ -445,21 +386,32 @@ async function onPrivateMsg(type, user_id, head, content, body, update_flag) { * @this {import("../ref").Client} */ async function onGroupMsg(head, body) { - const user_id = toInt(head.fromUin), time = toInt(head.msgTime); - const group = head.groupInfo, group_id = toInt(group.groupCode), group_name = group.groupName.toString(); - const message_id = genGroupMessageId(group_id, head.msgSeq, body.richText.attr.random); + + const user_id = head[1], + time = head[6]; + + this.msg_times.push(time); + + const group = head[9], + group_id = group[1], + group_name = String(group[8].raw); + + const message_id = genGroupMessageId(group_id, head[5], body[1][1][3]); + if (user_id === this.uin) - this.emit(`interval.${group_id}.${body.richText.attr.random}`, message_id); + this.emit(`interval.${group_id}.${body[1][1][3]}`, message_id); this.getGroupInfo(group_id); try { - var {chain, raw_message, extra} = await parseMessage.call(this, body.richText, group_id); + var {chain, raw_message, extra, anon} = await parseMessage.call(this, body[1], group_id); } catch (e) {return} - let font = body.richText.attr.fontName, card = group.groupCard; - if (extra.groupCard) { - card = String(extra.groupCard); + let font = String(body[1][1][9].raw), + card = String(group[4].raw); + + if (extra[2]) { + card = String(extra[2].raw); if (card.startsWith("\n")) card = card.split("\n").pop().substr(3); } @@ -467,20 +419,20 @@ async function onGroupMsg(head, body) { let anonymous = null, user = null; if (user_id === 80000000) { anonymous = { - id: extra.bubbleId, - name: String(extra.anonNick), - flag: extra.anonId ? extra.anonId.toString("base64") : "" + id: anon[6], + name: String(anon[3].raw), + flag: anon[2] ? anon[2].raw.toString("base64") : "" }; } else { try { user = (await this.getGroupMemberInfo(group_id, user_id)).data; - if (extra.senderTitle) - user.title = String(extra.senderTitle); - if (extra.level) - user.level = String(extra.level); - if (extra.nick && !extra.groupCard) { - user.card = ""; - user.nickname = String(extra.nick); + if (extra[7]) + user.title = String(extra[7].raw); + if (extra[3]) + user.level = extra[3]; + if (extra[1] && !extra[2]) { + user.card = card = ""; + user.nickname = String(extra[1].raw); } else { user.card = card; } @@ -506,9 +458,11 @@ async function onGroupMsg(head, body) { }; const sub_type = anonymous ? "anonymous" : "normal"; - this.logger.info(`recv from: [Group: ${group_name}(${group_id}), Member: ${card}(${user_id})] ` + raw_message); + this.logger.info(`recv from: [Group: ${group_name}(${group_id}), Member: ${card?card:nickname}(${user_id})] ` + raw_message); this.em("message.group." + sub_type, { - message_id, group_id, group_name, user_id, anonymous, message: chain, raw_message, font, sender, time + message_id, group_id, group_name, user_id, anonymous, + message: chain, + raw_message, font, sender, time }); } @@ -516,19 +470,28 @@ async function onGroupMsg(head, body) { * @this {import("../ref").Client} */ async function onDiscussMsg(head, body) { - const user_id = toInt(head.fromUin), time = toInt(head.msgTime); - const discuss = head.discussInfo, discuss_id = toInt(discuss.discussUin), discuss_name = discuss.discussName.toString(); + + const user_id = head[1], + time = head[6]; + + this.msg_times.push(time); + + const discuss = head[13], + discuss_id = discuss[1], + discuss_name = String(discuss[5].raw); if (user_id === this.uin && this.ignore_self) return; - const font = body.richText.attr.fontName, card = discuss.discussRemark, nickname = card; + const font = String(body[1][1][9].raw), + card = nickname = String(discuss[4].raw); + const sender = { user_id, nickname, card }; try { - var {chain, raw_message} = await parseMessage.call(this, body.richText, discuss_id); + var {chain, raw_message} = await parseMessage.call(this, body[1], discuss_id); } catch (e) {return} if (!raw_message) @@ -536,7 +499,9 @@ async function onDiscussMsg(head, body) { this.logger.info(`recv from: [Discuss: ${discuss_name}(${discuss_id}), Member: ${card}(${user_id})] ` + raw_message); this.em("message.discuss", { - discuss_id, discuss_name, user_id, message: chain, raw_message, font, sender, time + discuss_id, discuss_name, user_id, + message: chain, + raw_message, font, sender, time }); } diff --git a/lib/message/parser.js b/lib/message/parser.js index 9cf86d9d..c56c3f8a 100644 --- a/lib/message/parser.js +++ b/lib/message/parser.js @@ -2,7 +2,6 @@ const zlib = require("zlib"); const querystring = require("querystring"); const {downloadMultiMsg, getGroupFileUrl} = require("./storage"); -const common = require("../common"); const pb = require("../pb"); function escapeCQInside(s) { @@ -18,86 +17,92 @@ function escapeCQ(s) { } async function parseMessage(rich, from = 0) { - const elems = rich.elems; - if (rich.ptt) - elems.unshift({ptt: rich.ptt}); - const extra = {}; + const elems = Array.isArray(rich[2]) ? rich[2] : [rich[2]]; + if (rich[4]) + elems.unshift(Object.setPrototypeOf({}, {9999: rich[4]})); + let extra = {}, anon = {}; const chain = []; let raw_message = ""; let bface_tmp = null, ignore_text = false; for (let v of elems) { - const type = Object.keys(v)[0]; + const type = parseInt(Object.keys(Reflect.getPrototypeOf(v))[0]); const msg = {type:"",data:{}}; const o = v[type]; switch (type) { - case "anonGroupMsg": - case "extraInfo": - Object.assign(extra, o); + case 21: //anonGroupMsg + anon = o; break; - case "generalFlags": - if (o.longTextResid) - return await parseMultiMsg.call(this, o.longTextResid); + case 16: //extraInfo + extra = o; break; - case "richMsg": - try { - [msg.type, msg.data] = await parseXmlElem.call(this, o); - ignore_text = true; - } catch (e) {} + case 37: //generalFlags + if (o[7]) + return await parseMultiMsg.call(this, o[7].raw, from); + break; + case 12: //xml break; - case "lightApp": + case 51: try { [msg.type, msg.data] = await parseJsonElem.call(this, o); ignore_text = true; } catch (e) {} break; - case "transElemInfo": + case 5: [msg.type, msg.data] = await parseTransElem.call(this, o, from); ignore_text = true; break; - case "text": + case 1: if (ignore_text) break; - if (bface_tmp && o.str && o.str.startsWith("[")) { + if (bface_tmp && o[1]) { msg.data.file = bface_tmp, msg.type = "bface"; - msg.data.text = o.str.replace("[","").replace("]","").trim(); + msg.data.text = String(o[1].raw).replace("[","").replace("]","").trim(); bface_tmp = null; break; } - if (o.attr6Buf && o.attr6Buf[1] === 1) { + if (o[3] && o[3].raw[1] === 1) { msg.type = "at"; - if (o.attr6Buf[6] === 1) + if (o[3].raw[6] === 1) msg.data.qq = "all" else - msg.data.qq = o.attr6Buf.readUInt32BE(7); + msg.data.qq = o[3].raw.readUInt32BE(7); } else { msg.type = "text"; } - msg.data.text = o.str; + msg.data.text = String(o[1].raw); break; - case "face": - msg.type = "face", msg.data.id = o.index; + case 2: + msg.type = "face", msg.data.id = o[1]; break; - case "marketFace": - bface_tmp = o.faceId.toString("hex") + o.key.toString("hex") + o.tabId; + case 6: + bface_tmp = o[4].raw.toString("hex") + o[7].raw.toString("hex") + o[5]; break; - case "notOnlineImage": - case "customFace": + case 4: //notOnlineImage msg.type = "image"; - msg.data = parseImage(o); + msg.data.file = o[7].raw.toString("hex") + (o[2]?o[2]:""); + if (o[15]) + msg.data.url = "http://c2cpicdw.qpic.cn" + o[15].raw; break; - case "commonElem": - if (o.serviceType === 3) { - const flash = pb.decode("MsgElemInfoServtype3", o.pbElem); + case 8: //customFace + msg.type = "image"; + msg.data.file = o[13].raw.toString("hex") + (o[25]?o[25]:""); + if (o[16]) + msg.data.url = "http://gchat.qpic.cn" + o[16].raw; + break; + case 53: + if (o[1] === 3) { msg.type = "flash"; - msg.data = parseImage(flash[Object.keys(flash)[0]]); + if (o[2][1]) //notOnlineImage + msg.data.file = o[2][1][13].raw.toString("hex") + (o[2][1][25]?o[2][1][25]:""); + else if (o[2][2]) //customFace + msg.data.file = o[2][1][7].raw.toString("hex") + (o[2][1][2]?o[2][1][2]:""); ignore_text = true; - } else if (o.serviceType === 33) { + } else if (o[1] === 33) { msg.type = "sface"; - const sface = pb.decode("MsgElemInfoServtype33", o.pbElem); - msg.data.id = sface.id; - msg.data.text = sface.text1; + msg.data.id = o[2][1]; + msg.data.text = String(o[2][2].raw); } break; - case "ptt": + case 9999: [msg.type, msg.data] = await parsePttElem.call(this, o, from); ignore_text = true; break; @@ -113,50 +118,41 @@ async function parseMessage(rich, from = 0) { raw_message += genCQMsg(msg); } } - return {chain, raw_message, extra}; + return {chain, raw_message, extra, anon}; } function genCQMsg(msg) { return `[CQ:${msg.type},${querystring.stringify(msg.data, ",", "=", {encodeURIComponent: (s)=>s.replace(/&|,|\[|\]/g, escapeCQInside)})}]`; } -function parseImage(o) { - let data = {}; - data.file = o.md5.toString("hex"); - if (o.size) - data.file += o.size; - if (o.origUrl && o.downloadPath) - data.url = "http://c2cpicdw.qpic.cn" + o.origUrl; - else if (o.origUrl) - data.url = "http://gchat.qpic.cn" + o.origUrl; - return data; -} - -async function parseMultiMsg(resid) { - const resp = await downloadMultiMsg.call(this, resid, 1); - return await parseMessage.call(this, resp.msg[0].body.richText); +async function parseMultiMsg(resid, from) { + const buf = await downloadMultiMsg.call(this, resid, 1); + let msg = pb.decode(buf)[1]; + // if (Array.isArray(msg)) msg = msg[0]; + return await parseMessage.call(this, msg[3][1], from); } async function parsePttElem(o) { - const data = {md5: o.fileMd5.toString("hex")}; - if (o.downPara) { - data.file = "https://grouptalk.c2c.qq.com" + String(o.downPara); - } else if (o.fileUuid) { - data.file = o.fileUuid.toString("hex"); + const data = {md5: o[4].raw.toString("hex")}; + if (o[20]) { + const url = String(o[20].raw); + data.file = url.startsWith("http") ? url : "https://grouptalk.c2c.qq.com" + url; + } else if (o[3]) { + data.file = o[3].raw.toString("hex"); } return ["record", data]; } async function parseTransElem(o, from) { - let v = pb.decode("ObjMsg", o.elemValue.slice(3)).msgContentInfo[0].msgFile; - let resp = await getGroupFileUrl.call(this, from, v.busId, v.filePath.toString()); - resp = resp.downloadFileRsp; + let v = pb.decode(o[2].raw.slice(3))[7]; + v = v[2]; + let rsp = await getGroupFileUrl.call(this, from, v[1], v[2].raw); const data = { - name: v.fileName, - url: `http://${resp.downloadIp}/ftn_handler/${resp.downloadUrl.toString("hex")}/?fname=${v.fileName}`, - size: common.toInt(v.fileSize), - md5: resp.md5.toString("hex"), - duration: v.int64DeadTime.low, + name: String(v[4].raw), + url: `http://${rsp[4].raw}/ftn_handler/${rsp[6].raw.toString("hex")}/?fname=${v[4].raw}`, + size: v[3], + md5: rsp[9].raw.toString("hex"), + duration: v[5], }; return ["file", data]; } @@ -170,7 +166,7 @@ async function parseXmlElem() { } async function parseJsonElem(o) { - o = JSON.parse(zlib.unzipSync(o.data.slice(1)).toString()); + o = JSON.parse(zlib.unzipSync(o[1].raw.slice(1))); let type, data = {}; if (o.app === "com.tencent.map") { type = "location"; diff --git a/lib/message/storage.js b/lib/message/storage.js index 79548f37..27dd5ddd 100644 --- a/lib/message/storage.js +++ b/lib/message/storage.js @@ -7,154 +7,177 @@ const querystring = require("querystring"); const tea = require("crypto-tea"); const pb = require("../pb"); const common = require("../common"); -const BUF0 = Buffer.alloc(0); + +function setPrivateImageNested(img, fid) { + Object.assign(img.nested, { + 1: img.md5.toString("hex"), + 2: img.size, + 3: fid, + //5: 1000, + 7: img.md5, + 10: fid, + 13: 1, //? + //16: 3, + 20: 0, //? + }); +} +function setGroupImageNested(img, fid) { + Object.assign(img.nested, { + 2: img.md5.toString("hex"), + 7: fid, + 10: 66, + 12: 1, + 13: img.md5, + //17: 3, + //20: 1000, + //24: 101, + 25: img.size, + 26: 1, //? + }); +} /** - * @param {object[]} images + * @param {object[]} imgs + * @field {object} nested + * @field {Buffer} buf * @field {Buffer} md5 * @field {Number} size */ -async function imageStore(group_id, images) { - this.nextSeq(); +async function imageStore(group_id, imgs) { const req = []; - for (const v of images) { + for (const v of imgs) { req.push({ - groupCode: group_id, - srcUin: this.uin, - fileMd5: v.md5, - fileSize: v.size, - srcTerm: 5, - platformType: 9, - buType: 1, - picType: 1000, - buildVer: this.apkver, - appPicType: 1006, - fileIndex: BUF0, - transferUrl: BUF0, + 1: group_id, + 2: this.uin, + 4: v.md5, + 5: v.size, + 7: 5, + 8: 9, + 9: 1, + 12: 1000, + 13: this.apkver, + 15: 1006, }); } - const body = pb.encode("D388ReqBody", { - netType: 3, - subcmd: 1, - msgTryUpImgReq: req, - extension: BUF0, + const body = pb.encode({ + 1: 3, + 2: 1, + 3: req, }); const blob = await this.sendUNI("ImgStore.GroupPicUp", body); - return pb.decode("D388RespBody", blob); + let rsp = pb.decode(blob)[3]; + rsp = Array.isArray(rsp) ? rsp : [rsp]; + const tasks = []; + for (let i = 0; i < imgs.length; ++i) { + const v = rsp[i]; + setGroupImageNested(imgs[i], v[1]); + if (v[4] || !imgs[i].buf) continue; + if (!Array.isArray(v[6])) { + v[6] = [v[6]]; + v[7] = [v[7]]; + } + const index = i % v[6].length; + imgs[i].key = v[8].raw; + tasks.push(highwayUpload.call(this, v[6][index], v[7][index], imgs[i], 2)); + } + await Promise.all(tasks); } -async function offPicUp(user_id, images) { - this.nextSeq(); +async function offPicUp(user_id, imgs) { const req = []; - for (const v of images) { + for (const v of imgs) { req.push({ - srcUin: this.uin, - dstUin: user_id, - fileMd5: v.md5, - fileSize: v.size, - srcTerm: 5, - platformType: 9, - buType: 1, - imgOriginal: 1, - imgType: 1000, - buildVer: this.apkver, - srvUpload: 1, + 1: this.uin, + 2: user_id, + 4: v.md5, + 5: v.size, + 7: 5, + 8: 9, + 12: 1, + 13: 1, + 16: 1000, + 17: this.apkver, + 22: 1, }); } - const body = pb.encode("OffPicUpReqBody", { - subcmd: 1, - msgTryUpImgReq: req + const body = pb.encode({ + 1: 1, + 2: req }); const blob = await this.sendUNI("LongConn.OffPicUp", body); - return pb.decode("OffPicUpRspBody", blob); + let rsp = pb.decode(blob)[2]; + rsp = Array.isArray(rsp) ? rsp : [rsp]; + const tasks = []; + for (let i = 0; i < imgs.length; ++i) { + const v = rsp[i]; + setPrivateImageNested(imgs[i], v[10].raw); + if (v[5] || !imgs[i].buf) continue; + if (!Array.isArray(v[7])) { + v[7] = [v[7]]; + v[8] = [v[8]]; + } + const index = i % v[7].length; + imgs[i].key = v[9].raw; + tasks.push(highwayUpload.call(this, v[7][index], v[8][index], imgs[i], 1)); + } + await Promise.all(tasks); } -async function uploadImages(target, images, is_group) { - let resp = await (is_group?imageStore:offPicUp).call(this, target, images); - for (let i = 0; i < images.length; ++i) { - const v = resp.msgTryUpImgRsp[i]; - if (v.boolFileExit || !images[i].buf) continue; - const index = i % v.uint32UpIp.length; - v.md5 = images[i].md5, v.buf = images[i].buf, v.key = v.upUkey; - await highwayUpload.call(this, v.uint32UpIp[index], v.uint32UpPort[index], v, is_group?2:1); +async function uploadImages(target, imgs, is_group) { + let n = 0; + while (imgs.length > n) { + try { + await (is_group?imageStore:offPicUp).call(this, target, imgs.slice(n, n + 20)); + } catch {} + n += 20; } - return resp; } -// async function uploadC2CPtt(user_id, ptt) { -// this.nextSeq(); -// const req = []; -// req.push({ -// srcUin: this.uin, -// toUin: user_id, -// type: 2, -// voiceLength: 1, -// fileName: ptt.md5.toString() + ".amr", -// md5: ptt.md5 -// }); -// const body = pb.encode("TryUpC2CPtt", { -// subcmd: 500, -// result: 0, -// msgTryUpPtt: req, -// a806: 17, -// b006: 104, -// sub99999: { -// k1: 3, -// k2: 0, -// k300: 1, -// k500: 3, -// k600: 2, -// k800: 2, -// } -// }); -// const blob = await this.sendUNI("PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500", body); -// const resp = pb.decode("TryUpC2CPtt", blob); -// return resp.msgTryUpPtt[0]; -// } - -async function uploadGroupPtt(group_id, ptt) { - this.nextSeq(); - const req = []; - req.push({ - groupCode: group_id, - srcUin: this.uin, - fileMd5: ptt.md5, - fileSize: ptt.size, - fileName: ptt.md5, - fileId: 0, - srcTerm: 5, - platformType: 9, - buType: 4, - innerIp: 0, - buildVer: this.apkver, - voiceLength: 1, - codec: ptt.ext===".amr"?0:1, - voiceType: 1, - boolNewUpChan: true, - }); - const body = pb.encode("D388ReqBody", { - netType: 3, - subcmd: 3, - msgTryUpPttReq: req, +/** + * @this {import("./ref").Client} + * @param {Number} target + * @param {Buffer} buf + * @param {Buffer} md5 + * @param {0|1} codec + * @returns {Promise} fid + */ +async function uploadPtt(target, buf, md5, codec) { + const body = pb.encode({ + 1: 3, + 2: 3, + 5: [{ + 1: target?target:1, + 2: this.uin, + 3: 0, + 4: md5, + 5: buf.length, + 6: md5, + 7: 5, + 8: 9, + 9: 4, + 11: 0, + 10: this.apkver, + 12: 1, + 13: 1, + 14: codec, + 15: 1, + }], }); const blob = await this.sendUNI("PttStore.GroupPttUp", body); - const resp = pb.decode("D388RespBody", blob); - return resp.msgTryUpPttRsp[0]; -} - -async function uploadPtt(target, ptt, is_group) { - if (!is_group) - target = 1; - const resp = await uploadGroupPtt.call(this, target, ptt); - if (!resp.boolFileExit) { - const ip = int32ip2str(resp.uint32UpIp[0]), port = resp.uint32UpPort[0]; - const ukey = resp.upUkey.toString("hex"), filekey = resp.fileKey.toString("hex"); + const rsp = pb.decode(blob)[5]; + if (!rsp[4]) { + const ip = Array.isArray(rsp[5])?rsp[5][0]:rsp[5], + port = Array.isArray(rsp[6])?rsp[6][0]:rsp[6]; + const ukey = rsp[7].raw.toString("hex"), filekey = rsp[11].raw.toString("hex"); const params = { - ver: 4679, ukey, filekey, - filesize: ptt.size, bmd5: ptt.md5.toString("hex"), - mType: "pttDu", voice_encodec: ptt.ext===".amr"?0:1 + ver: 4679, + ukey, filekey, + filesize: buf.length, + bmd5: md5.toString("hex"), + mType: "pttDu", + voice_encodec: codec } - const url = `http://${ip}:${port}/?` + querystring.stringify(params); + const url = `http://${int32ip2str(ip)}:${port}/?` + querystring.stringify(params); const headers = { "User-Agent": `QQ/${this.apkver} CFNetwork/1126`, "Net-Type": "Wifi" @@ -162,113 +185,88 @@ async function uploadPtt(target, ptt, is_group) { await new Promise((resolve)=>{ http.request(url, {method: 'POST', headers}, resolve) .on("error", resolve) - .end(ptt.buf); + .end(buf); }) } - return resp; + return rsp[11].raw; } -// async function downloadPtt(md5, key) { -// this.nextSeq(); -// const req = []; -// req.push({ -// groupCode: 179763449, -// srcUin: this.uin, -// fileId: 2061878809, -// fileMd5: md5, -// reqTerm: 5, -// reqPlatformType:9, -// innerIp: 0, -// buType: 3, -// buildVer: this.apkver, -// fileKey: key, -// codec: 1, -// isAuto: 1, -// }); -// common.log(req) -// const body = pb.encode("D388ReqBody", { -// netType: 3, -// subcmd: 4, -// msgGetPttReq: req, -// }); -// const blob = await this.sendUNI("PttStore.GroupPttDown", body); -// const resp = pb.decode("D388RespBody", blob); -// common.log(resp) -// return resp.msgGetPttUrlRsp[0]; -// } - -async function uploadMultiMsg(target, msg, bu) { - this.nextSeq(); - const compressed = zlib.gzipSync(pb.encode("PbMultiMsgTransmit", { - msg, pbItemList: [{ - fileName: "MultiMsg", - buffer: pb.encode("PbMultiMsgNew", {msg}), - }] - })); - const body = pb.encode("MultiReqBody", { - subcmd: 1, - termType: 5, - platformType: 9, - netType: 3, - buildVer: this.apkver, - buType: bu, - reqChannelType: 0, - multimsgApplyupReq: [{ - applyId: 0, - dstUin: target, - msgSize: compressed.length, - msgMd5: common.md5(compressed), - msgType: 3, +/** + * @this {import("./ref").Client} + * @param {Number} target + * @param {Buffer} compressed + * @param {Number} is_group + * @returns {Promise} resid + */ +async function uploadMultiMsg(target, compressed, is_group) { + const body = pb.encode({ + 1: 1, + 2: 5, + 3: 9, + 4: 3, + 5: this.apkver, + 6: [{ + 1: target, + 2: compressed.length, + 3: common.md5(compressed), + 4: is_group?3:1, + 5: 0, }], + 8: is_group?1:2, }); const blob = await this.sendUNI("MultiMsg.ApplyUp", body); - let resp = pb.decode("MultiRspBody", blob); - resp = resp.multimsgApplyupRsp[0]; - if (resp.result > 0) + const rsp = pb.decode(blob)[2]; + if (rsp[1] > 0) throw new Error(); - const buf = pb.encode("LongReqBody", { - subcmd: 1, - termType: 5, - platformType: 9, - msgUpReq: [{ - msgType: 3, - dstUin: target, - msgContent: compressed, - storeType: 2, - msgUkey: resp.msgUkey, + const buf = pb.encode({ + 1: 1, + 2: 5, + 3: 9, + 4: [{ + //1: 3, + 2: target, + 4: compressed, + 5: 2, + 6: rsp[3].raw, }], }); const o = { buf: buf, md5: common.md5(buf), - key: resp.msgSig + key: rsp[10].raw } - await highwayUpload.call(this, resp.uint32UpIp[0], resp.uint32UpPort[0], o, 27); - return resp; + const ip = Array.isArray(rsp[4])?rsp[4][0]:rsp[4], + port = Array.isArray(rsp[5])?rsp[5][0]:rsp[5]; + await highwayUpload.call(this, ip, port, o, 27); + return rsp[2].raw; } +/** + * @this {import("./ref").Client} + * @param {Buffer} resid + * @param {Number} bu + * @returns {Promise} + */ async function downloadMultiMsg(resid, bu) { - this.nextSeq(); - const body = pb.encode("MultiReqBody", { - subcmd: 2, - termType: 5, - platformType: 9, - netType: 3, - buildVer: this.apkver, - buType: bu, - reqChannelType: 2, - multimsgApplydownReq: [{ - msgResid: Buffer.from(resid), - msgType: 3, + const body = pb.encode({ + 1: 2, + 2: 5, + 3: 9, + 4: 3, + 5: this.apkver, + 7: [{ + 1: resid, + 2: 3, }], + 8: bu, + 9: 2, }); const blob = await this.sendUNI("MultiMsg.ApplyDown", body); - let resp = pb.decode("MultiRspBody", blob); - - resp = resp.multimsgApplydownRsp[0]; - const ip = int32ip2str(resp.uint32DownIp[0]), port = resp.uint32DownPort[0]; + const rsp = pb.decode(blob)[3]; + const ip = int32ip2str(Array.isArray(rsp[4])?rsp[4][0]:rsp[4]), + port = Array.isArray(rsp[5])?rsp[5][0]:rsp[5]; let url = port == 443 ? "https://ssl.htdata.qq.com" : `http://${ip}:${port}`; - url += String(resp.thumbDownPara); + url += rsp[2].raw; const headers = { "User-Agent": `QQ/${this.apkver} CFNetwork/1126`, "Net-Type": "Wifi" @@ -285,10 +283,11 @@ async function downloadMultiMsg(resid, bu) { buf = zlib.unzipSync(buf); const head_len = buf.readUInt32BE(1); const body_len = buf.readUInt32BE(5); - buf = tea.decrypt(buf.slice(head_len + 9, head_len + 9 + body_len), resp.msgKey); - buf = pb.decode("LongRspBody", buf); - buf = zlib.unzipSync(buf.msgDownRsp[0].msgContent); - resolve(pb.decode("PbMultiMsgTransmit", buf)); + buf = tea.decrypt(buf.slice(head_len + 9, head_len + 9 + body_len), rsp[3].raw); + buf = pb.decode(buf)[3]; + // if (Array.isArray(buf)) buf = buf[0]; + buf = zlib.unzipSync(buf[3].raw); + resolve(buf); } catch (e) { reject(); } @@ -298,23 +297,19 @@ async function downloadMultiMsg(resid, bu) { } async function getGroupFileUrl(group_id, bus_id, file_id) { - this.nextSeq(); - const body = pb.encode("OIDBSSOPkg", { - command: 1750, - serviceType: 2, - bodybuffer: pb.encode("D6D6ReqBody", { - downloadFileReq: { - groupCode: group_id, - appId: 3, - busId: bus_id, - fileId: file_id, - } - }), + const body = pb.encode({ + 3: { + 1: group_id, + 2: 3, + 3: bus_id, + 4: file_id, + } }); const blob = await this.sendUNI("OidbSvc.0x6d6_2", body); - return pb.decode("D6D6RspBody", pb.decode("OIDBSSOPkg", blob).bodybuffer); + return pb.decode(blob)[4][3]; } module.exports = { - uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl + uploadImages, uploadPtt, uploadMultiMsg, downloadMultiMsg, getGroupFileUrl, + setPrivateImageNested, setGroupImageNested } diff --git a/lib/online-push.js b/lib/online-push.js index 6bd903e8..1f50b987 100644 --- a/lib/online-push.js +++ b/lib/online-push.js @@ -1,19 +1,17 @@ "use strict"; -const common = require("./common"); const pb = require("./pb"); const jce = require("./jce"); const chat = require("./message/chat"); -const toInt = common.toInt; /** + * OnlinePush回执 * @param {Number} svrip * @param {Number} seq * @param {Buffer[]} rubbish */ function handleOnlinePush(svrip, seq, rubbish = []) { - this.nextSeq(); const resp = jce.encodeStruct([ - this.uin, rubbish, svrip, null, 0 + this.uin, rubbish, svrip&0xffffffff, null, 0 ]); const extra = { req_id: seq, @@ -26,21 +24,22 @@ function handleOnlinePush(svrip, seq, rubbish = []) { const sub0x27 = { 80: function(data, time) { - const o = data.msgNewGrpName; - const group_id = toInt(o.groupCode); - if (!o.authUin) + const o = data[12]; + const group_id = o[3]; + if (!o[4]) return; + const group_name = String(o[2][2].raw); try { - this.gl.get(group_id).group_name = o.entry.name; + this.gl.get(group_id).group_name = group_name; } catch (e) {} this.em("notice.group.setting", { group_id, time, - user_id: toInt(o.authUin), - group_name: o.entry.name, + user_id: o[4], + group_name, }); }, 5: function(data, time) { - const user_id = toInt(data.msgDelFrdNotify.uin); + const user_id = data[14][1]; let nickname; try { nickname = this.fl.get(user_id).nickname; @@ -53,21 +52,21 @@ const sub0x27 = { }, 20: function(data, time) { // 20002昵称 20009性别 20031生日 23109农历生日 20019说明 20032地区 24002故乡 - const user_id = toInt(data.msgProfile.uin); - const o = data.msgProfile.profile; + const user_id = data[8][1]; + const o = data[8][2]; let key, value; - if (o.type === 20002) { + if (o[1] === 20002) { key = "nickname"; - value = o.value.toString(); - } else if (o.type === 20009) { + value = String(o[2].raw); + } else if (o[1] === 20009) { key = "sex"; - value = ["unknown","male","female"][o.value[0]]; - } else if (o.type === 20031) { + value = ["unknown","male","female"][o[2].raw[0]]; + } else if (o[1] === 20031) { key = "age"; - value = new Date().getFullYear() - o.value.readUInt16BE(); - } else if (o.type === 20019) { + value = new Date().getFullYear() - o[2].raw.readUInt16BE(); + } else if (o[1] === 20019) { key = "description"; - value = o.value.toString(); + value = String(o[2].raw); } else { return; } @@ -83,8 +82,8 @@ const sub0x27 = { } }, 60: function(data, time) { - const user_id = toInt(data.msgNewSign.uin); - const sign = data.msgNewSign.sign; + const user_id = data[10][1]; + const sign = String(data[10][2].raw); try { this.fl.get(user_id).signature = sign; } catch (e) {} @@ -97,9 +96,9 @@ const sub0x27 = { }, 40: function(data, time) { try { - const o = data.msgNewRemark.entry, uin = toInt(o.uin); - if (o.type > 0) return; //0好友备注 1群备注 - this.fl.get(uin).remark = o.remark; + const o = data[9][1], uin = o[2]; + if (o[1] > 0) return; //0好友备注 1群备注 + this.fl.get(uin).remark = String(o[3].raw); } catch (e) {} }, 21: function(data, time) { @@ -109,19 +108,20 @@ const sub0x27 = { const push528 = { 0x8A: function(buf, time) { - let data = pb.decode("Sub8A", buf); - data = data.msgInfo[0]; - const user_id = toInt(data.fromUin); + let data = pb.decode(buf)[1]; + if (Array.isArray(data)) + data = data[0]; + const user_id = data[1]; this.em("notice.friend.recall", { - user_id, message_id: chat.genGroupMessageId(user_id, data.msgSeq, data.msgRandom), time + user_id, message_id: chat.genGroupMessageId(user_id, data[3], data[6]&0xffffffff), time }); }, 0x8B: function(buf, time) { return push528[0x8A].call(this, buf, time); }, 0xB3: function(buf, time) { - const data = pb.decode("SubB3", buf); - const user_id = toInt(data.msgAddFrdNotify.uin), nickname = data.msgAddFrdNotify.nick; + const data = pb.decode(buf)[2]; + const user_id = data[1], nickname = String(data[5].raw); this.fl.set(user_id, { user_id, nickname, sex: "unknown", @@ -138,22 +138,23 @@ const push528 = { }); }, 0xD4: function(buf, time) { - const data = pb.decode("SubD4", buf); - const group_id = toInt(data.groupCode); + const group_id = pb.decode(buf)[1]; this.getGroupInfo(group_id, true); }, 0x3B: function(buf, time) { - const data = pb.decode("Sub3B", buf); - const group_id = toInt(data.groupCode); + const data = pb.decode(buf); + const group_id = data[2]; this.em("notice.group.setting", { group_id, time, - enable_show_title: data.enableShowTitle > 0, + enable_show_title: data[3] > 0, }); }, 0x27: function(buf, time) { - const data = pb.decode("Sub27", buf).sub27[0]; - if (typeof sub0x27[data.type] === "function") - sub0x27[data.type].call(this, data, time); + let data = pb.decode(buf)[1]; + if (Array.isArray(data)) + data = data[0]; + if (typeof sub0x27[data[2]] === "function") + sub0x27[data[2]].call(this, data, time); }, 0x44: function(buf, time) {}, } @@ -178,10 +179,12 @@ const push732 = { 0x0C: function(group_id, buf, time) { const operator_id = buf.readUInt32BE(6); const user_id = buf.readUInt32BE(16); - const duration = buf.readUInt32BE(20); + let duration = buf.readUInt32BE(20); try { - if (user_id === 0) - this.gl.get(group_id).shutup_time_whole = duration?0xffffffff:0; + if (user_id === 0) { + duration = duration ? 0xffffffff : 0; + this.gl.get(group_id).shutup_time_whole = duration; + } else if (user_id === this.uin) this.gl.get(group_id).shutup_time_me = duration ? (time + duration) : 0; this.gml.get(group_id).get(user_id).shutup_time = duration ? (time + duration) : 0; @@ -191,29 +194,29 @@ const push732 = { }); }, 0x11: function(group_id, buf, time) { - const data = pb.decode("NotifyMsgBody", buf.slice(7)); - const operator_id = toInt(data.optMsgRecall.uin); - const msg = data.optMsgRecall.recalledMsgList[0]; - const user_id = toInt(msg.authorUin); - const message_id = chat.genGroupMessageId(group_id, msg.seq, msg.msgRandom); + const data = pb.decode(buf.slice(7))[11]; + const operator_id = data[1]; + const msg = Array.isArray(data[3]) ? data[3][0] : data[3]; + const user_id = msg[6]; + const message_id = chat.genGroupMessageId(group_id, msg[1], msg[3]); this.em("notice.group.recall", { group_id, user_id, operator_id, message_id, time }); }, 0x14: function(group_id, buf, time) { - const data = pb.decode("NotifyMsgBody", buf.slice(7)); - if (data.optGeneralGrayTip) { + const data = pb.decode(buf.slice(7))[26]; + if (data) { let user_id, operator_id, action, suffix; - for (let k in data.optGeneralGrayTip.msgTemplParam) { - const o = data.optGeneralGrayTip.msgTemplParam[k] - if (o.name === "action_str") - action = o.value; - if (o.name === "uin_str1") - operator_id = parseInt(o.value); - if (o.name === "uin_str2") - user_id = parseInt(o.value); - if (o.name === "suffix_str") - suffix = o.value; + for (let o of data[7]) { + const name = String(o[1].raw); + if (name === "action_str") + action = String(o[2].raw); + if (name === "uin_str1") + operator_id = parseInt(String(o[2].raw)); + if (name === "uin_str2") + user_id = parseInt(String(o[2].raw)); + if (name === "suffix_str") + suffix = String(o[2].raw); } if (!operator_id) return; @@ -264,11 +267,11 @@ const push732 = { if (buf[6] === 0x26) { // 改群分类 } - const sub = pb.decode("Sub10", buf.slice(7)); - if (sub.entry && sub.entry.text) { - let str = sub.entry.text; + const sub = pb.decode(buf.slice(7)); + if (sub[5] && sub[5][2]) { + let str = String(sub[5][2].raw); if (str.includes("获得群主授予的")) { - const user_id = toInt(sub.entry.uin); + const user_id = sub[5][5]; str = str.substr(0, str.length - 2); const title = str.substr(str.lastIndexOf("获得群主授予的") + 7); str = str.substr(0, str.length - title.length - 7); @@ -330,13 +333,13 @@ function onOnlinePush(blob, seq) { } function onOnlinePushTrans(blob, seq) { - const o = pb.decode("TransMsgInfo", blob); - handleOnlinePush.call(this, o.svrIp, seq); + const push = pb.decode(blob); + handleOnlinePush.call(this, push[11], seq); if (!this.sync_finished) return; - const time = toInt(o.realMsgTime); - const buf = o.msgData; + const time = push[8]; + const buf = push[10].raw; const group_id = buf.readUInt32BE(); - if (o.msgType === 44) { + if (push[3] === 44) { if (buf[5] === 0 || buf[5] === 1) { const user_id = buf.readUInt32BE(6); const set = buf[10] > 0; @@ -363,7 +366,7 @@ function onOnlinePushTrans(blob, seq) { })(); } } - if (o.msgType === 34) { + if (push[3] === 34) { const user_id = buf.readUInt32BE(5); let operator_id, dismiss = false; if (buf[9] === 0x82 || buf[9] === 0x2) { @@ -395,21 +398,20 @@ function onOnlinePushTrans(blob, seq) { } function onC2CMsgSync(blob, seq) { - const o = pb.decode("PushMessagePacket", blob); - handleOnlinePush.call(this, o.svrip, seq); + handleOnlinePush.call(this, pb.decode(blob)[2], seq); } function onGroupMsg(blob, seq) { - const o = pb.decode("PushMessagePacket", blob); if (!this.sync_finished) return; - chat.onGroupMsg.call(this, o.message.head, o.message.body); + const o = pb.decode(blob); + chat.onGroupMsg.call(this, o[1][1], o[1][3]); } function onDiscussMsg(blob, seq) { - const o = pb.decode("PushMessagePacket", blob); - handleOnlinePush.call(this, o.svrip, seq); + const o = pb.decode(blob); + handleOnlinePush.call(this, o[2], seq); if (!this.sync_finished) return; - chat.onDiscussMsg.call(this, o.message.head, o.message.body); + chat.onDiscussMsg.call(this, o[1][1], o[1][3]); } module.exports = { diff --git a/lib/pb.js b/lib/pb.js index 9c646e6b..9836deb9 100644 --- a/lib/pb.js +++ b/lib/pb.js @@ -1,7027 +1,132 @@ -/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ "use strict"; +const pb = require("protobufjs/light"); -var $protobuf = require("protobufjs/light"); +function isBuffer(buf) { + return buf instanceof Buffer || buf instanceof Uint8Array || buf instanceof ArrayBuffer || buf instanceof SharedArrayBuffer; +} -var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $protobuf.Root())) -.addJSON({ - DelImgReq: { - fields: { - srcUin: { - type: "int64", - id: 1 - }, - dstUin: { - type: "int64", - id: 2 - }, - reqTerm: { - type: "int32", - id: 3 - }, - reqPlatformType: { - type: "int32", - id: 4 - }, - buType: { - type: "int32", - id: 5 - }, - buildVer: { - type: "bytes", - id: 6 - }, - fileResid: { - type: "bytes", - id: 7 - }, - picWidth: { - type: "int32", - id: 8 - }, - picHeight: { - type: "int32", - id: 9 - } - } - }, - DelImgRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - failMsg: { - type: "bytes", - id: 2 - }, - fileResid: { - type: "bytes", - id: 3 - } - } - }, - GetImgUrlReq: { - fields: { - srcUin: { - type: "int64", - id: 1 - }, - dstUin: { - type: "int64", - id: 2 - }, - fileResid: { - type: "bytes", - id: 3 - }, - urlFlag: { - type: "int32", - id: 4 - }, - urlType: { - type: "int32", - id: 6 - }, - reqTerm: { - type: "int32", - id: 7 - }, - reqPlatformType: { - type: "int32", - id: 8 - }, - srcFileType: { - type: "int32", - id: 9 - }, - innerIp: { - type: "int32", - id: 10 - }, - boolAddressBook: { - type: "bool", - id: 11 - }, - buType: { - type: "int32", - id: 12 - }, - buildVer: { - type: "bytes", - id: 13 - }, - picUpTimestamp: { - type: "int32", - id: 14 - }, - reqTransferType: { - type: "int32", - id: 15 - } - } - }, - GetImgUrlRsp: { - fields: { - fileResid: { - type: "bytes", - id: 1 - }, - clientIp: { - type: "int32", - id: 2 - }, - result: { - type: "int32", - id: 3 - }, - failMsg: { - type: "bytes", - id: 4 - }, - bytesThumbDownUrl: { - type: "bytes", - id: 5 - }, - bytesOriginalDownUrl: { - type: "bytes", - id: 6 - }, - msgImgInfo: { - type: "D352ImgInfo", - id: 7 - }, - uint32DownIp: { - rule: "repeated", - type: "int32", - id: 8 - }, - uint32DownPort: { - rule: "repeated", - type: "int32", - id: 9 - }, - thumbDownPara: { - type: "bytes", - id: 10 - }, - originalDownPara: { - type: "bytes", - id: 11 - }, - downDomain: { - type: "bytes", - id: 12 - }, - bytesBigDownUrl: { - type: "bytes", - id: 13 - }, - bigDownPara: { - type: "bytes", - id: 14 - }, - bigThumbDownPara: { - type: "bytes", - id: 15 - }, - httpsUrlFlag: { - type: "int32", - id: 16 - }, - msgDownIp6: { - rule: "repeated", - type: "IPv6Info", - id: 26 - }, - clientIp6: { - type: "bytes", - id: 27 - } - } - }, - D352ImgInfo: { - fields: { - fileMd5: { - type: "bytes", - id: 1 - }, - fileType: { - type: "int32", - id: 2 - }, - fileSize: { - type: "int64", - id: 3 - }, - fileWidth: { - type: "int32", - id: 4 - }, - fileHeight: { - type: "int32", - id: 5 - }, - fileFlag: { - type: "int64", - id: 6 - }, - fileCutPos: { - type: "int32", - id: 7 - } - } - }, - IPv6Info: { - fields: { - ip6: { - type: "bytes", - id: 1 - }, - port: { - type: "int32", - id: 2 - } - } - }, - OffPicUpReqBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - msgTryUpImgReq: { - rule: "repeated", - type: "D352TryUpImgReq", - id: 2 - }, - msgGetImgUrlReq: { - rule: "repeated", - type: "GetImgUrlReq", - id: 3 - }, - msgDelImgReq: { - rule: "repeated", - type: "DelImgReq", - id: 4 - }, - netType: { - type: "int32", - id: 10 - } - } - }, - OffPicUpRspBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - msgTryUpImgRsp: { - rule: "repeated", - type: "TryUpImgRsp", - id: 2 - }, - msgGetimgUrlRsp: { - rule: "repeated", - type: "GetImgUrlRsp", - id: 3 - }, - boolNewBigchan: { - type: "bool", - id: 4 - }, - msgDelImgRsp: { - rule: "repeated", - type: "DelImgRsp", - id: 5 - }, - failMsg: { - type: "string", - id: 10 - } - } - }, - D352TryUpImgReq: { - fields: { - srcUin: { - type: "int32", - id: 1 - }, - dstUin: { - type: "int32", - id: 2 - }, - fileId: { - type: "int32", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - fileSize: { - type: "int32", - id: 5 - }, - filename: { - type: "string", - id: 6 - }, - srcTerm: { - type: "int32", - id: 7 - }, - platformType: { - type: "int32", - id: 8 - }, - innerIP: { - type: "int32", - id: 9 - }, - addressBook: { - type: "int32", - id: 10 - }, - retry: { - type: "int32", - id: 11 - }, - buType: { - type: "int32", - id: 12 - }, - imgOriginal: { - type: "int32", - id: 13 - }, - imgWidth: { - type: "int32", - id: 14 - }, - imgHeight: { - type: "int32", - id: 15 - }, - imgType: { - type: "int32", - id: 16 - }, - buildVer: { - type: "string", - id: 17 - }, - fileIndex: { - type: "bytes", - id: 18 - }, - fileStoreDays: { - type: "int32", - id: 19 - }, - stepFlag: { - type: "int32", - id: 20 - }, - rejectTryFast: { - type: "int32", - id: 21 - }, - srvUpload: { - type: "int32", - id: 22 - }, - transferUrl: { - type: "bytes", - id: 23 - } - } - }, - TryUpImgRsp: { - fields: { - fileId: { - type: "int64", - id: 1 - }, - clientIp: { - type: "int32", - id: 2 - }, - result: { - type: "int32", - id: 3 - }, - failMsg: { - type: "string", - id: 4 - }, - boolFileExit: { - type: "bool", - id: 5 - }, - msgImgInfo: { - type: "D352ImgInfo", - id: 6 - }, - uint32UpIp: { - rule: "repeated", - type: "int32", - id: 7 - }, - uint32UpPort: { - rule: "repeated", - type: "int32", - id: 8 - }, - upUkey: { - type: "bytes", - id: 9 - }, - upResid: { - type: "string", - id: 10 - }, - upUuid: { - type: "string", - id: 11 - }, - upOffset: { - type: "int64", - id: 12 - }, - blockSize: { - type: "int64", - id: 13 - }, - encryptDstip: { - type: "bytes", - id: 14 - }, - roamdays: { - type: "int32", - id: 15 - }, - msgUpIp6: { - rule: "repeated", - type: "IPv6Info", - id: 26 - }, - clientIp6: { - type: "bytes", - id: 27 - }, - thumbDownPara: { - type: "bytes", - id: 60 - }, - originalDownPara: { - type: "bytes", - id: 61 - }, - downDomain: { - type: "bytes", - id: 62 - }, - bigDownPara: { - type: "bytes", - id: 64 - }, - bigThumbDownPara: { - type: "bytes", - id: 65 - }, - httpsUrlFlag: { - type: "int32", - id: 66 - }, - msgInfo4busi: { - type: "TryUpInfo4Busi", - id: 1001 - } - } - }, - TryUpInfo4Busi: { - fields: { - fileResid: { - type: "bytes", - id: 1 - }, - downDomain: { - type: "bytes", - id: 2 - }, - thumbDownUrl: { - type: "bytes", - id: 3 - }, - originalDownUrl: { - type: "bytes", - id: 4 - }, - bigDownUrl: { - type: "bytes", - id: 5 - } - } - }, - DeviceInfo: { - fields: { - bootloader: { - type: "string", - id: 1 - }, - procVersion: { - type: "string", - id: 2 - }, - codename: { - type: "string", - id: 3 - }, - incremental: { - type: "string", - id: 4 - }, - fingerprint: { - type: "string", - id: 5 - }, - bootId: { - type: "string", - id: 6 - }, - androidId: { - type: "string", - id: 7 - }, - baseBand: { - type: "string", - id: 8 - }, - innerVersion: { - type: "string", - id: 9 - } - } - }, - RequestBody: { - fields: { - rptConfigList: { - rule: "repeated", - type: "ConfigSeq", - id: 1 - } - } - }, - ConfigSeq: { - fields: { - type: { - type: "int32", - id: 1 - }, - version: { - type: "int32", - id: 2 - } - } - }, - D50ReqBody: { - fields: { - appid: { - type: "int64", - id: 1 - }, - maxPkgSize: { - type: "int32", - id: 2 - }, - startTime: { - type: "int32", - id: 3 - }, - startIndex: { - type: "int32", - id: 4 - }, - reqNum: { - type: "int32", - id: 5 - }, - uinList: { - rule: "repeated", - type: "int64", - id: 6 - }, - reqMusicSwitch: { - type: "int32", - id: 91001 - }, - reqMutualmarkAlienation: { - type: "int32", - id: 101001 - }, - reqMutualmarkScore: { - type: "int32", - id: 141001 - }, - reqKsingSwitch: { - type: "int32", - id: 151001 - }, - reqMutualmarkLbsshare: { - type: "int32", - id: 181001 - }, - reqC8C77A: { - type: "int32", - id: 251001 - } - } - }, - D388ReqBody: { - fields: { - netType: { - type: "int32", - id: 1 - }, - subcmd: { - type: "int32", - id: 2 - }, - msgTryUpImgReq: { - rule: "repeated", - type: "TryUpImgReq", - id: 3 - }, - msgTryUpPttReq: { - rule: "repeated", - type: "TryUpPttReq", - id: 5 - }, - msgGetPttReq: { - rule: "repeated", - type: "GetPttUrlReq", - id: 6 - }, - commandId: { - type: "int32", - id: 7 - }, - extension: { - type: "bytes", - id: 1001 - } - } - }, - D388RespBody: { - fields: { - clientIp: { - type: "int32", - id: 1 - }, - subCmd: { - type: "int32", - id: 2 - }, - msgTryUpImgRsp: { - rule: "repeated", - type: "TryUpImgResp", - id: 3 - }, - msgTryUpPttRsp: { - rule: "repeated", - type: "TryUpPttResp", - id: 5 - }, - msgGetPttUrlRsp: { - rule: "repeated", - type: "GetPttUrlRsp", - id: 6 - } - } - }, - GetPttUrlReq: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - dstUin: { - type: "int64", - id: 2 - }, - fileId: { - type: "int64", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - reqTerm: { - type: "int32", - id: 5 - }, - reqPlatformType: { - type: "int32", - id: 6 - }, - innerIp: { - type: "int32", - id: 7 - }, - buType: { - type: "int32", - id: 8 - }, - buildVer: { - type: "bytes", - id: 9 - }, - fileKey: { - type: "bytes", - id: 11 - }, - codec: { - type: "int32", - id: 12 - }, - buId: { - type: "int32", - id: 13 - }, - reqTransferType: { - type: "int32", - id: 14 - }, - isAuto: { - type: "int32", - id: 15 - } - } - }, - GetPttUrlRsp: { - fields: { - fileId: { - type: "int64", - id: 1 - }, - fileMd5: { - type: "bytes", - id: 2 - }, - result: { - type: "int32", - id: 3 - }, - failMsg: { - type: "bytes", - id: 4 - }, - bytesDownUrl: { - type: "bytes", - id: 5 - }, - uint32DownIp: { - rule: "repeated", - type: "int32", - id: 6 - }, - uint32DownPort: { - rule: "repeated", - type: "int32", - id: 7 - }, - downDomain: { - type: "bytes", - id: 8 - }, - downPara: { - type: "bytes", - id: 9 - }, - transferType: { - type: "int32", - id: 11 - }, - allowRetry: { - type: "int32", - id: 12 - }, - clientIp6: { - type: "bytes", - id: 27 - }, - strDomain: { - type: "string", - id: 28 - } - } - }, - ReqDataHighwayHead: { - fields: { - msgBasehead: { - type: "DataHighwayHead", - id: 1 - }, - msgSeghead: { - type: "SegHead", - id: 2 - }, - reqExtendinfo: { - type: "bytes", - id: 3 - }, - timestamp: { - type: "int64", - id: 4 - } - } - }, - RspDataHighwayHead: { - fields: { - msgBasehead: { - type: "DataHighwayHead", - id: 1 - }, - msgSeghead: { - type: "SegHead", - id: 2 - }, - errorCode: { - type: "int32", - id: 3 - }, - allowRetry: { - type: "int32", - id: 4 - }, - cachecost: { - type: "int32", - id: 5 - }, - htcost: { - type: "int32", - id: 6 - }, - rspExtendinfo: { - type: "bytes", - id: 7 - }, - timestamp: { - type: "int64", - id: 8 - }, - range: { - type: "int64", - id: 9 - }, - isReset: { - type: "int32", - id: 10 - } - } - }, - DataHighwayHead: { - fields: { - version: { - type: "int32", - id: 1 - }, - uin: { - type: "string", - id: 2 - }, - command: { - type: "string", - id: 3 - }, - seq: { - type: "int32", - id: 4 - }, - retryTimes: { - type: "int32", - id: 5 - }, - appid: { - type: "int32", - id: 6 - }, - dataflag: { - type: "int32", - id: 7 - }, - commandId: { - type: "int32", - id: 8 - }, - buildVer: { - type: "string", - id: 9 - }, - localeId: { - type: "int32", - id: 10 - } - } - }, - SegHead: { - fields: { - serviceid: { - type: "int32", - id: 1 - }, - filesize: { - type: "int64", - id: 2 - }, - dataoffset: { - type: "int64", - id: 3 - }, - datalength: { - type: "int32", - id: 4 - }, - rtcode: { - type: "int32", - id: 5 - }, - serviceticket: { - type: "bytes", - id: 6 - }, - flag: { - type: "int32", - id: 7 - }, - md5: { - type: "bytes", - id: 8 - }, - fileMd5: { - type: "bytes", - id: 9 - }, - cacheAddr: { - type: "int32", - id: 10 - }, - queryTimes: { - type: "int32", - id: 11 - }, - updateCacheip: { - type: "int32", - id: 12 - } - } - }, - TryUpImgReq: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - srcUin: { - type: "int64", - id: 2 - }, - fileId: { - type: "int64", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - fileSize: { - type: "int64", - id: 5 - }, - fileName: { - type: "string", - id: 6 - }, - srcTerm: { - type: "int32", - id: 7 - }, - platformType: { - type: "int32", - id: 8 - }, - buType: { - type: "int32", - id: 9 - }, - picWidth: { - type: "int32", - id: 10 - }, - picHeight: { - type: "int32", - id: 11 - }, - picType: { - type: "int32", - id: 12 - }, - buildVer: { - type: "string", - id: 13 - }, - innerIp: { - type: "int32", - id: 14 - }, - appPicType: { - type: "int32", - id: 15 - }, - originalPic: { - type: "int32", - id: 16 - }, - fileIndex: { - type: "bytes", - id: 17 - }, - dstUin: { - type: "int64", - id: 18 - }, - srvUpload: { - type: "int32", - id: 19 - }, - transferUrl: { - type: "bytes", - id: 20 - } - } - }, - TryUpImgResp: { - fields: { - fileId: { - type: "int64", - id: 1 - }, - result: { - type: "int32", - id: 2 - }, - failMsg: { - type: "string", - id: 3 - }, - boolFileExit: { - type: "bool", - id: 4 - }, - msgImgInfo: { - type: "ImgInfo", - id: 5 - }, - uint32UpIp: { - rule: "repeated", - type: "int32", - id: 6 - }, - uint32UpPort: { - rule: "repeated", - type: "int32", - id: 7 - }, - upUkey: { - type: "bytes", - id: 8 - }, - fid: { - type: "int64", - id: 9 - } - } - }, - TryUpPttReq: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - srcUin: { - type: "int64", - id: 2 - }, - fileId: { - type: "int64", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - fileSize: { - type: "int64", - id: 5 - }, - fileName: { - type: "bytes", - id: 6 - }, - srcTerm: { - type: "int32", - id: 7 - }, - platformType: { - type: "int32", - id: 8 - }, - buType: { - type: "int32", - id: 9 - }, - buildVer: { - type: "string", - id: 10 - }, - innerIp: { - type: "int32", - id: 11 - }, - voiceLength: { - type: "int32", - id: 12 - }, - boolNewUpChan: { - type: "bool", - id: 13 - }, - codec: { - type: "int32", - id: 14 - }, - voiceType: { - type: "int32", - id: 15 - }, - buId: { - type: "int32", - id: 16 - } - } - }, - TryUpPttResp: { - fields: { - fileId: { - type: "int64", - id: 1 - }, - result: { - type: "int32", - id: 2 - }, - failMsg: { - type: "string", - id: 3 - }, - boolFileExit: { - type: "bool", - id: 4 - }, - uint32UpIp: { - rule: "repeated", - type: "int32", - id: 5 - }, - uint32UpPort: { - rule: "repeated", - type: "int32", - id: 6 - }, - upUkey: { - type: "bytes", - id: 7 - }, - upOffset: { - type: "int64", - id: 9 - }, - blockSize: { - type: "int64", - id: 10 - }, - fileKey: { - type: "bytes", - id: 11 - }, - channelType: { - type: "int32", - id: 12 - } - } - }, - ImgInfo: { - fields: { - fileMd5: { - type: "bytes", - id: 1 - }, - fileType: { - type: "int32", - id: 2 - }, - fileSize: { - type: "int64", - id: 3 - }, - fileWidth: { - type: "int32", - id: 4 - }, - fileHeight: { - type: "int32", - id: 5 - } - } - }, - DeleteMessageRequest: { - fields: { - items: { - rule: "repeated", - type: "MessageItem", - id: 1 - } - } - }, - MessageItem: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - msgType: { - type: "int32", - id: 3 - }, - msgSeq: { - type: "int32", - id: 4 - }, - msgUid: { - type: "int64", - id: 5 - }, - sig: { - type: "bytes", - id: 7 - } - } - }, - NotifyMsgBody: { - fields: { - optMsgRecall: { - type: "MessageRecallReminder", - id: 11 - }, - serviceType: { - type: "int32", - id: 13 - }, - optGeneralGrayTip: { - type: "GeneralGrayTipInfo", - id: 26 - }, - } - }, - GeneralGrayTipInfo: { - fields: { - templId: { - type: "int32", - id: 6 - }, - msgTemplParam: { - rule: "repeated", - type: "TemplParam", - id: 7 - }, - } - }, - TemplParam: { - fields: { - name: { - type: "string", - id: 1 - }, - value: { - type: "string", - id: 2 - }, - } - }, - MessageRecallReminder: { - fields: { - uin: { - type: "int64", - id: 1 - }, - nickname: { - type: "bytes", - id: 2 - }, - recalledMsgList: { - rule: "repeated", - type: "RecalledMessageMeta", - id: 3 - }, - reminderContent: { - type: "bytes", - id: 4 - }, - userdef: { - type: "bytes", - id: 5 - }, - groupType: { - type: "int32", - id: 6 - }, - opType: { - type: "int32", - id: 7 - } - } - }, - RecalledMessageMeta: { - fields: { - seq: { - type: "int32", - id: 1 - }, - time: { - type: "int32", - id: 2 - }, - msgRandom: { - type: "int32", - id: 3 - }, - msgType: { - type: "int32", - id: 4 - }, - msgFlag: { - type: "int32", - id: 5 - }, - authorUin: { - type: "int64", - id: 6 - } - } - }, - Sub3B: { - fields: { - groupCode: { - type: "int64", - id: 2 - }, - enableShowTitle: { - type: "int32", - id: 3 - }, - } - }, - SubD4: { - fields: { - groupCode: { - type: "int64", - id: 1 - } - } - }, - Sub8A: { - fields: { - msgInfo: { - rule: "repeated", - type: "C2CMsgInfo", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - instId: { - type: "int32", - id: 3 - }, - longMessageFlag: { - type: "int32", - id: 4 - }, - reserved: { - type: "bytes", - id: 5 - } - } - }, - Sub8AMsgInfo: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - msgSeq: { - type: "int32", - id: 3 - }, - msgUid: { - type: "int64", - id: 4 - }, - msgTime: { - type: "int64", - id: 5 - }, - msgRandom: { - type: "int32", - id: 6 - }, - pkgNum: { - type: "int32", - id: 7 - }, - pkgIndex: { - type: "int32", - id: 8 - }, - devSeq: { - type: "int32", - id: 9 - } - } - }, - SubB3: { - fields: { - type: { - type: "int32", - id: 1 - }, - msgAddFrdNotify: { - type: "SubB3AddFrdNotify", - id: 2 - } - } - }, - SubB3AddFrdNotify: { - fields: { - uin: { - type: "int64", - id: 1 - }, - nick: { - type: "string", - id: 5 - } - } - }, - Sub27: { - fields: { - sub27: { - rule: "repeated", - type: "Sub27Entry", - id: 1 - } - } - }, - Sub27Entry: { - fields: { - type: { - type: "int32", - id: 2 - }, - msgProfile: { - type: "Sub27Profile", - id: 8 - }, - msgNewRemark: { - type: "Sub27NewRemark", - id: 9 - }, - msgNewSign: { - type: "Sub27NewSign", - id: 10 - }, - msgGrpAvatar: { - type: "bytes", - id: 11 - }, - msgNewGrpName: { - type: "Sub27NewGrpName", - id: 12 - }, - msgDelFrdNotify: { - type: "Sub27DelFrdNotify", - id: 14 - }, - } - }, - Sub27NewSign: { - fields: { - uin: { - type: "int64", - id: 1 - }, - sign: { - type: "string", - id: 2 - }, - } - }, - Sub27NewRemark: { - fields: { - entry: { - type: "Sub27RemarkEntry", - id: 1 - }, - } - }, - Sub27RemarkEntry: { - fields: { - type: { - type: "int32", - id: 1 - }, - uin: { - type: "int64", - id: 2 - }, - remark: { - type: "string", - id: 3 - }, - groupCode: { - type: "int64", - id: 4 - }, - } - }, - Sub27Profile: { - fields: { - uin: { - type: "int64", - id: 1 - }, - profile: { - type: "ProfileSetting", - id: 2 - }, - } - }, - ProfileSetting: { - fields: { - type: { - type: "int32", - id: 1 - }, - value: { - type: "bytes", - id: 2 - }, - } - }, - Sub27NewGrpName: { - fields: { - groupUin: { - type: "int64", - id: 1 - }, - entry: { - type: "Sub27NewGrpNameEntry", - id: 2 - }, - groupCode: { - type: "int64", - id: 3 - }, - authUin: { - type: "int64", - id: 4 - }, - } - }, - Sub27NewGrpNameEntry: { - fields: { - name: { - type: "string", - id: 2 - }, - } - }, - Sub27DelFrdNotify: { - fields: { - uin: { - type: "int64", - id: 1 - }, - } - }, - Sub10: { - fields: { - groupCode: { - type: "int64", - id: 4 - }, - entry: { - type: "Sub10Entry", - id: 5 - }, - } - }, - Sub10Entry: { - fields: { - text: { - type: "string", - id: 2 - }, - uin: { - type: "int64", - id: 5 - }, - } - }, - LongMsgDeleteReq: { - fields: { - msgResid: { - type: "bytes", - id: 1 - }, - msgType: { - type: "int32", - id: 2 - } - } - }, - LongMsgDeleteRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - msgResid: { - type: "bytes", - id: 2 - } - } - }, - LongMsgDownReq: { - fields: { - srcUin: { - type: "int32", - id: 1 - }, - msgResid: { - type: "bytes", - id: 2 - }, - msgType: { - type: "int32", - id: 3 - }, - needCache: { - type: "int32", - id: 4 - } - } - }, - LongMsgDownRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - msgResid: { - type: "bytes", - id: 2 - }, - msgContent: { - type: "bytes", - id: 3 - } - } - }, - LongMsgUpReq: { - fields: { - msgType: { - type: "int32", - id: 1 - }, - dstUin: { - type: "int64", - id: 2 - }, - msgId: { - type: "int32", - id: 3 - }, - msgContent: { - type: "bytes", - id: 4 - }, - storeType: { - type: "int32", - id: 5 - }, - msgUkey: { - type: "bytes", - id: 6 - }, - needCache: { - type: "int32", - id: 7 - } - } - }, - LongMsgUpRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - msgId: { - type: "int32", - id: 2 - }, - msgResid: { - type: "bytes", - id: 3 - } - } - }, - LongReqBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - termType: { - type: "int32", - id: 2 - }, - platformType: { - type: "int32", - id: 3 - }, - msgUpReq: { - rule: "repeated", - type: "LongMsgUpReq", - id: 4 - }, - msgDownReq: { - rule: "repeated", - type: "LongMsgDownReq", - id: 5 - }, - msgDelReq: { - rule: "repeated", - type: "LongMsgDeleteReq", - id: 6 - }, - agentType: { - type: "int32", - id: 10 - } - } - }, - LongRspBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - msgUpRsp: { - rule: "repeated", - type: "LongMsgUpRsp", - id: 2 - }, - msgDownRsp: { - rule: "repeated", - type: "LongMsgDownRsp", - id: 3 - }, - msgDelRsp: { - rule: "repeated", - type: "LongMsgDeleteRsp", - id: 4 - } - } - }, - GetMessageRequest: { - fields: { - syncFlag: { - type: "SyncFlag", - id: 1 - }, - syncCookie: { - type: "bytes", - id: 2 - }, - rambleFlag: { - type: "int32", - id: 3 - }, - latestRambleNumber: { - type: "int32", - id: 4 - }, - otherRambleNumber: { - type: "int32", - id: 5 - }, - onlineSyncFlag: { - type: "int32", - id: 6 - }, - contextFlag: { - type: "int32", - id: 7 - }, - whisperSessionId: { - type: "int32", - id: 8 - }, - msgReqType: { - type: "int32", - id: 9 - }, - pubaccountCookie: { - type: "bytes", - id: 10 - }, - msgCtrlBuf: { - type: "bytes", - id: 11 - }, - serverBuf: { - type: "bytes", - id: 12 - } - } - }, - SendMessageRequest: { - fields: { - routingHead: { - type: "RoutingHead", - id: 1 - }, - contentHead: { - type: "ContentHead", - id: 2 - }, - msgBody: { - type: "MessageBody", - id: 3 - }, - msgSeq: { - type: "int32", - id: 4 - }, - msgRand: { - type: "int32", - id: 5 - }, - syncCookie: { - type: "bytes", - id: 6 - }, - msgVia: { - type: "int32", - id: 8 - }, - dataStatist: { - type: "int32", - id: 9 - }, - msgCtrl: { - type: "MsgCtrl", - id: 12 - }, - multiSendSeq: { - type: "int32", - id: 14 - } - } - }, - MsgWithDrawReq: { - fields: { - c2cWithDraw: { - rule: "repeated", - type: "C2CMsgWithDrawReq", - id: 1 - }, - groupWithDraw: { - rule: "repeated", - type: "GroupMsgWithDrawReq", - id: 2 - } - } - }, - C2CMsgWithDrawReq: { - fields: { - msgInfo: { - rule: "repeated", - type: "C2CMsgInfo", - id: 1 - }, - longMessageFlag: { - type: "int32", - id: 2 - }, - reserved: { - type: "bytes", - id: 3 - }, - subCmd: { - type: "int32", - id: 4 - } - } - }, - GroupMsgWithDrawReq: { - fields: { - subCmd: { - type: "int32", - id: 1 - }, - groupType: { - type: "int32", - id: 2 - }, - groupCode: { - type: "int64", - id: 3 - }, - msgList: { - rule: "repeated", - type: "GroupMsgInfo", - id: 4 - }, - userDef: { - type: "bytes", - id: 5 - } - } - }, - GroupMsgInfo: { - fields: { - msgSeq: { - type: "int32", - id: 1 - }, - msgRandom: { - type: "int32", - id: 2 - }, - msgType: { - type: "int32", - id: 3 - } - } - }, - C2CMsgInfo: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - msgSeq: { - type: "int32", - id: 3 - }, - msgUid: { - type: "int64", - id: 4 - }, - msgTime: { - type: "int64", - id: 5 - }, - msgRandom: { - type: "int32", - id: 6 - }, - pkgNum: { - type: "int32", - id: 7 - }, - pkgIndex: { - type: "int32", - id: 8 - }, - divSeq: { - type: "int32", - id: 9 - }, - msgType: { - type: "int32", - id: 10 - }, - routingHead: { - type: "RoutingHead", - id: 20 - } - } - }, - RoutingHead: { - fields: { - c2c: { - type: "C2C", - id: 1 - }, - grp: { - type: "Grp", - id: 2 - }, - grpTmp: { - type: "GrpTmp", - id: 3 - }, - dis: { - type: "Dis", - id: 4 - }, - } - }, - C2C: { - fields: { - toUin: { - type: "int64", - id: 1 - } - } - }, - Grp: { - fields: { - groupCode: { - type: "int64", - id: 1 - } - } - }, - GrpTmp: { - fields: { - groupUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - } - } - }, - Dis: { - fields: { - discussUin: { - type: "int64", - id: 1 - } - } - }, - MsgCtrl: { - fields: { - msgFlag: { - type: "int32", - id: 1 - } - } - }, - GetMessageResponse: { - fields: { - result: { - type: "int32", - id: 1 - }, - errorMessage: { - type: "string", - id: 2 - }, - syncCookie: { - type: "bytes", - id: 3 - }, - syncFlag: { - type: "SyncFlag", - id: 4 - }, - uinPairMsgs: { - rule: "repeated", - type: "UinPairMessage", - id: 5 - }, - bindUin: { - type: "int64", - id: 6 - }, - msgRspType: { - type: "int32", - id: 7 - }, - pubAccountCookie: { - type: "bytes", - id: 8 - }, - isPartialSync: { - type: "bool", - id: 9 - }, - msgCtrlBuf: { - type: "bytes", - id: 10 - } - } - }, - PushMessagePacket: { - fields: { - message: { - type: "Message", - id: 1 - }, - svrip: { - type: "int32", - id: 2 - }, - pushToken: { - type: "bytes", - id: 3 - }, - pingFLag: { - type: "int32", - id: 4 - }, - generalFlag: { - type: "int32", - id: 9 - } - } - }, - UinPairMessage: { - fields: { - lastReadTime: { - type: "int32", - id: 1 - }, - peerUin: { - type: "int64", - id: 2 - }, - msgCompleted: { - type: "int32", - id: 3 - }, - messages: { - rule: "repeated", - type: "Message", - id: 4 - } - } - }, - Message: { - fields: { - head: { - type: "MessageHead", - id: 1 - }, - content: { - type: "ContentHead", - id: 2 - }, - body: { - type: "MessageBody", - id: 3 - } - } - }, - MessageBody: { - fields: { - richText: { - type: "RichText", - id: 1 - }, - msgContent: { - type: "bytes", - id: 2 - }, - msgEncryptContent: { - type: "bytes", - id: 3 - } - } - }, - RichText: { - fields: { - attr: { - type: "Attr", - id: 1 - }, - elems: { - rule: "repeated", - type: "Elem", - id: 2 - }, - notOnlineFile: { - type: "NotOnlineFile", - id: 3 - }, - ptt: { - type: "Ptt", - id: 4 - } - } - }, - Elem: { - fields: { - text: { - type: "Text", - id: 1 - }, - face: { - type: "Face", - id: 2 - }, - onlineImage: { - type: "OnlineImage", - id: 3 - }, - notOnlineImage: { - type: "NotOnlineImage", - id: 4 - }, - transElemInfo: { - type: "TransElem", - id: 5 - }, - marketFace: { - type: "MarketFace", - id: 6 - }, - customFace: { - type: "CustomFace", - id: 8 - }, - funFace: { - type: "FunFace", - id: 10 - }, - richMsg: { - type: "RichMsg", - id: 12 - }, - groupFile: { - type: "GroupFile", - id: 13 - }, - extraInfo: { - type: "ExtraInfo", - id: 16 - }, - videoFile: { - type: "VideoFile", - id: 19 - }, - anonGroupMsg: { - type: "AnonymousGroupMessage", - id: 21 - }, - customElem: { - type: "CustomElem", - id: 31 - }, - generalFlags: { - type: "GeneralFlags", - id: 37 - }, - srcMsg: { - type: "SourceMsg", - id: 45 - }, - lightApp: { - type: "LightAppElem", - id: 51 - }, - eimInfo: { - type: "EIMInfo", - id: 52 - }, - commonElem: { - type: "CommonElem", - id: 53 - } - } - }, - RichMsg: { - fields: { - template1: { - type: "bytes", - id: 1 - }, - serviceId: { - type: "int32", - id: 2 - }, - msgResId: { - type: "bytes", - id: 3 - }, - rand: { - type: "int32", - id: 4 - }, - seq: { - type: "int32", - id: 5 - } - } - }, - CustomElem: { - fields: { - desc: { - type: "bytes", - id: 1 - }, - data: { - type: "bytes", - id: 2 - }, - enumType: { - type: "int32", - id: 3 - }, - ext: { - type: "bytes", - id: 4 - }, - sound: { - type: "bytes", - id: 5 - } - } - }, - Text: { - fields: { - str: { - type: "string", - id: 1 - }, - link: { - type: "string", - id: 2 - }, - attr6Buf: { - type: "bytes", - id: 3 - }, - attr7Buf: { - type: "bytes", - id: 4 - }, - buf: { - type: "bytes", - id: 11 - }, - pbReserve: { - type: "bytes", - id: 12 - } - } - }, - Attr: { - fields: { - codePage: { - type: "int32", - id: 1 - }, - time: { - type: "int32", - id: 2 - }, - random: { - type: "int32", - id: 3 - }, - color: { - type: "int32", - id: 4 - }, - size: { - type: "int32", - id: 5 - }, - effect: { - type: "int32", - id: 6 - }, - charSet: { - type: "int32", - id: 7 - }, - pitchAndFamily: { - type: "int32", - id: 8 - }, - fontName: { - type: "string", - id: 9 - }, - reserveData: { - type: "bytes", - id: 10 - } - } - }, - Ptt: { - fields: { - fileType: { - type: "int32", - id: 1 - }, - srcUin: { - type: "int64", - id: 2 - }, - fileUuid: { - type: "bytes", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - fileName: { - type: "string", - id: 5 - }, - fileSize: { - type: "int32", - id: 6 - }, - reserve: { - type: "bytes", - id: 7 - }, - fileId: { - type: "int32", - id: 8 - }, - serverIp: { - type: "int32", - id: 9 - }, - serverPort: { - type: "int32", - id: 10 - }, - boolValid: { - type: "bool", - id: 11 - }, - signature: { - type: "bytes", - id: 12 - }, - shortcut: { - type: "bytes", - id: 13 - }, - fileKey: { - type: "bytes", - id: 14 - }, - magicPttIndex: { - type: "int32", - id: 15 - }, - voiceSwitch: { - type: "int32", - id: 16 - }, - pttUrl: { - type: "bytes", - id: 17 - }, - groupFileKey: { - type: "bytes", - id: 18 - }, - time: { - type: "int32", - id: 19 - }, - downPara: { - type: "bytes", - id: 20 - }, - format: { - type: "int32", - id: 29 - }, - pbReserve: { - type: "bytes", - id: 30 - }, - bytesPttUrls: { - rule: "repeated", - type: "bytes", - id: 31 - }, - downloadFlag: { - type: "int32", - id: 32 - } - } - }, - OnlineImage: { - fields: { - guid: { - type: "bytes", - id: 1 - }, - filePath: { - type: "bytes", - id: 2 - }, - oldVerSendFile: { - type: "bytes", - id: 3 - } - } - }, - NotOnlineImage: { - fields: { - filePath: { - type: "string", - id: 1 - }, - size: { - type: "int32", - id: 2 - }, - downloadPath: { - type: "string", - id: 3 - }, - oldVerSendFile: { - type: "bytes", - id: 4 - }, - imgType: { - type: "int32", - id: 5 - }, - previewsImage: { - type: "bytes", - id: 6 - }, - md5: { - type: "bytes", - id: 7 - }, - picHeight: { - type: "int32", - id: 8 - }, - picWidth: { - type: "int32", - id: 9 - }, - resId: { - type: "string", - id: 10 - }, - flag: { - type: "bytes", - id: 11 - }, - thumbUrl: { - type: "string", - id: 12 - }, - original: { - type: "int32", - id: 13 - }, - bigUrl: { - type: "string", - id: 14 - }, - origUrl: { - type: "string", - id: 15 - }, - bizType: { - type: "int32", - id: 16 - }, - result: { - type: "int32", - id: 17 - }, - index: { - type: "int32", - id: 18 - }, - opFaceBuf: { - type: "bytes", - id: 19 - }, - oldPicMd5: { - type: "bool", - id: 20 - }, - thumbWidth: { - type: "int32", - id: 21 - }, - thumbHeight: { - type: "int32", - id: 22 - }, - fileId: { - type: "int32", - id: 23 - }, - showLen: { - type: "int32", - id: 24 - }, - downloadLen: { - type: "int32", - id: 25 - }, - pbReserve: { - type: "bytes", - id: 29 - } - } - }, - NotOnlineFile: { - fields: { - fileType: { - type: "int32", - id: 1 - }, - sig: { - type: "bytes", - id: 2 - }, - fileUuid: { - type: "bytes", - id: 3 - }, - fileMd5: { - type: "bytes", - id: 4 - }, - fileName: { - type: "bytes", - id: 5 - }, - fileSize: { - type: "int64", - id: 6 - }, - note: { - type: "bytes", - id: 7 - }, - reserved: { - type: "int32", - id: 8 - }, - subcmd: { - type: "int32", - id: 9 - }, - microCloud: { - type: "int32", - id: 10 - }, - bytesFileUrls: { - rule: "repeated", - type: "bytes", - id: 11 - }, - downloadFlag: { - type: "int32", - id: 12 - }, - dangerEvel: { - type: "int32", - id: 50 - }, - lifeTime: { - type: "int32", - id: 51 - }, - uploadTime: { - type: "int32", - id: 52 - }, - absFileType: { - type: "int32", - id: 53 - }, - clientType: { - type: "int32", - id: 54 - }, - expireTime: { - type: "int32", - id: 55 - }, - pbReserve: { - type: "bytes", - id: 56 - } - } - }, - TransElem: { - fields: { - elemType: { - type: "int32", - id: 1 - }, - elemValue: { - type: "bytes", - id: 2 - } - } - }, - ExtraInfo: { - fields: { - nick: { - type: "bytes", - id: 1 - }, - groupCard: { - type: "bytes", - id: 2 - }, - level: { - type: "int32", - id: 3 - }, - flags: { - type: "int32", - id: 4 - }, - groupMask: { - type: "int32", - id: 5 - }, - msgTailId: { - type: "int32", - id: 6 - }, - senderTitle: { - type: "bytes", - id: 7 - }, - apnsTips: { - type: "bytes", - id: 8 - }, - uin: { - type: "int64", - id: 9 - }, - msgStateFlag: { - type: "int32", - id: 10 - }, - apnsSoundType: { - type: "int32", - id: 11 - }, - newGroupFlag: { - type: "int32", - id: 12 - } - } - }, - GroupFile: { - fields: { - filename: { - type: "bytes", - id: 1 - }, - fileSize: { - type: "int64", - id: 2 - }, - fileId: { - type: "bytes", - id: 3 - }, - batchId: { - type: "bytes", - id: 4 - }, - fileKey: { - type: "bytes", - id: 5 - }, - mark: { - type: "bytes", - id: 6 - }, - sequence: { - type: "int64", - id: 7 - }, - batchItemId: { - type: "bytes", - id: 8 - }, - feedMsgTime: { - type: "int32", - id: 9 - }, - pbReserve: { - type: "bytes", - id: 10 - } - } - }, - AnonymousGroupMessage: { - fields: { - flags: { - type: "int32", - id: 1 - }, - anonId: { - type: "bytes", - id: 2 - }, - anonNick: { - type: "bytes", - id: 3 - }, - headPortrait: { - type: "int32", - id: 4 - }, - expireTime: { - type: "int32", - id: 5 - }, - bubbleId: { - type: "int32", - id: 6 - }, - rankColor: { - type: "bytes", - id: 7 - } - } - }, - VideoFile: { - fields: { - fileUuid: { - type: "bytes", - id: 1 - }, - fileMd5: { - type: "bytes", - id: 2 - }, - fileName: { - type: "bytes", - id: 3 - }, - fileFormat: { - type: "int32", - id: 4 - }, - fileTime: { - type: "int32", - id: 5 - }, - fileSize: { - type: "int32", - id: 6 - }, - thumbWidth: { - type: "int32", - id: 7 - }, - thumbHeight: { - type: "int32", - id: 8 - }, - thumbFileMd5: { - type: "bytes", - id: 9 - }, - source: { - type: "bytes", - id: 10 - }, - thumbFileSize: { - type: "int32", - id: 11 - }, - busiType: { - type: "int32", - id: 12 - }, - fromChatType: { - type: "int32", - id: 13 - }, - toChatType: { - type: "int32", - id: 14 - }, - boolSupportProgressive: { - type: "bool", - id: 15 - }, - fileWidth: { - type: "int32", - id: 16 - }, - fileHeight: { - type: "int32", - id: 17 - }, - subBusiType: { - type: "int32", - id: 18 - }, - videoAttr: { - type: "int32", - id: 19 - }, - bytesThumbFileUrls: { - rule: "repeated", - type: "bytes", - id: 20 - }, - bytesVideoFileUrls: { - rule: "repeated", - type: "bytes", - id: 21 - }, - thumbDownloadFlag: { - type: "int32", - id: 22 - }, - videoDownloadFlag: { - type: "int32", - id: 23 - }, - pbReserve: { - type: "bytes", - id: 24 - } - } - }, - SourceMsg: { - fields: { - origSeqs: { - rule: "repeated", - type: "int32", - id: 1 - }, - senderUin: { - type: "int64", - id: 2 - }, - time: { - type: "int32", - id: 3 - }, - flag: { - type: "int32", - id: 4 - }, - elems: { - rule: "repeated", - type: "Elem", - id: 5 - }, - type: { - type: "int32", - id: 6 - }, - richMsg: { - type: "bytes", - id: 7 - }, - pbReserve: { - type: "bytes", - id: 8 - }, - srcMsg: { - type: "bytes", - id: 9 - }, - toUin: { - type: "int64", - id: 10 - }, - troopName: { - type: "bytes", - id: 11 - } - } - }, - Face: { - fields: { - index: { - type: "int32", - id: 1 - }, - old: { - type: "bytes", - id: 2 - }, - buf: { - type: "bytes", - id: 11 - } - } - }, - LightAppElem: { - fields: { - data: { - type: "bytes", - id: 1 - }, - msgResid: { - type: "bytes", - id: 2 - } - } - }, - CustomFace: { - fields: { - guid: { - type: "bytes", - id: 1 - }, - filePath: { - type: "string", - id: 2 - }, - shortcut: { - type: "string", - id: 3 - }, - buffer: { - type: "bytes", - id: 4 - }, - flag: { - type: "bytes", - id: 5 - }, - oldData: { - type: "bytes", - id: 6 - }, - fileId: { - type: "int32", - id: 7 - }, - serverIp: { - type: "int32", - id: 8 - }, - serverPort: { - type: "int32", - id: 9 - }, - fileType: { - type: "int32", - id: 10 - }, - signature: { - type: "bytes", - id: 11 - }, - useful: { - type: "int32", - id: 12 - }, - md5: { - type: "bytes", - id: 13 - }, - thumbUrl: { - type: "string", - id: 14 - }, - bigUrl: { - type: "string", - id: 15 - }, - origUrl: { - type: "string", - id: 16 - }, - bizType: { - type: "int32", - id: 17 - }, - repeatIndex: { - type: "int32", - id: 18 - }, - repeatImage: { - type: "int32", - id: 19 - }, - imageType: { - type: "int32", - id: 20 - }, - index: { - type: "int32", - id: 21 - }, - width: { - type: "int32", - id: 22 - }, - height: { - type: "int32", - id: 23 - }, - source: { - type: "int32", - id: 24 - }, - size: { - type: "int32", - id: 25 - }, - origin: { - type: "int32", - id: 26 - }, - thumbWidth: { - type: "int32", - id: 27 - }, - thumbHeight: { - type: "int32", - id: 28 - }, - showLen: { - type: "int32", - id: 29 - }, - downloadLen: { - type: "int32", - id: 30 - }, - _400Url: { - type: "string", - id: 31 - }, - _400Width: { - type: "int32", - id: 32 - }, - _400Height: { - type: "int32", - id: 33 - }, - pbReserve: { - type: "bytes", - id: 34 - } - } - }, - ContentHead: { - fields: { - pkgNum: { - type: "int32", - id: 1 - }, - pkgIndex: { - type: "int32", - id: 2 - }, - divSeq: { - type: "int32", - id: 3 - }, - autoReply: { - type: "int32", - id: 4 - } - } - }, - MessageHead: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - msgType: { - type: "int32", - id: 3 - }, - c2cCmd: { - type: "int32", - id: 4 - }, - msgSeq: { - type: "int32", - id: 5 - }, - msgTime: { - type: "int64", - id: 6 - }, - msgUid: { - type: "int64", - id: 7 - }, - c2cTmpMsgHead: { - type: "C2CTempMessageHead", - id: 8 - }, - groupInfo: { - type: "GroupInfo", - id: 9 - }, - fromAppid: { - type: "int32", - id: 10 - }, - fromInstid: { - type: "int32", - id: 11 - }, - userActive: { - type: "int32", - id: 12 - }, - discussInfo: { - type: "DiscussInfo", - id: 13 - }, - fromNick: { - type: "string", - id: 14 - }, - authUin: { - type: "int64", - id: 15 - }, - authNick: { - type: "string", - id: 16 - }, - msgFlag: { - type: "int32", - id: 17 - }, - authRemark: { - type: "string", - id: 18 - }, - groupName: { - type: "string", - id: 19 - }, - mutiltransHead: { - type: "MutilTransHead", - id: 20 - }, - msgInstCtrl: { - type: "InstCtrl", - id: 21 - }, - publicAccountGroupSendFlag: { - type: "int32", - id: 22 - }, - wseqInC2cMsghead: { - type: "int32", - id: 23 - }, - cpid: { - type: "int64", - id: 24 - }, - extGroupKeyInfo: { - type: "ExtGroupKeyInfo", - id: 25 - }, - multiCompatibleText: { - type: "string", - id: 26 - }, - authSex: { - type: "int32", - id: 27 - }, - isSrcMsg: { - type: "bool", - id: 28 - } - } - }, - GroupInfo: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - groupType: { - type: "int32", - id: 2 - }, - groupInfoSeq: { - type: "int64", - id: 3 - }, - groupCard: { - type: "string", - id: 4 - }, - groupRank: { - type: "bytes", - id: 5 - }, - groupLevel: { - type: "int32", - id: 6 - }, - groupCardType: { - type: "int32", - id: 7 - }, - groupName: { - type: "bytes", - id: 8 - } - } - }, - DiscussInfo: { - fields: { - discussUin: { - type: "int64", - id: 1 - }, - discussType: { - type: "int32", - id: 2 - }, - discussInfoSeq: { - type: "int64", - id: 3 - }, - discussRemark: { - type: "bytes", - id: 4 - }, - discussName: { - type: "bytes", - id: 5 - } - } - }, - MutilTransHead: { - fields: { - status: { - type: "int32", - id: 1 - }, - msgId: { - type: "int32", - id: 2 - } - } - }, - C2CTempMessageHead: { - fields: { - c2cType: { - type: "int32", - id: 1 - }, - serviceType: { - type: "int32", - id: 2 - }, - groupUin: { - type: "int64", - id: 3 - }, - groupCode: { - type: "int64", - id: 4 - }, - sig: { - type: "bytes", - id: 5 - }, - sigType: { - type: "int32", - id: 6 - }, - fromPhone: { - type: "string", - id: 7 - }, - toPhone: { - type: "string", - id: 8 - }, - lockDisplay: { - type: "int32", - id: 9 - }, - directionFlag: { - type: "int32", - id: 10 - }, - reserved: { - type: "bytes", - id: 11 - } - } - }, - InstCtrl: { - fields: { - msgSendToInst: { - rule: "repeated", - type: "InstInfo", - id: 1 - }, - msgExcludeInst: { - rule: "repeated", - type: "InstInfo", - id: 2 - }, - msgFromInst: { - type: "InstInfo", - id: 3 - } - } - }, - InstInfo: { - fields: { - apppid: { - type: "int32", - id: 1 - }, - instid: { - type: "int32", - id: 2 - }, - platform: { - type: "int32", - id: 3 - }, - enumDeviceType: { - type: "int32", - id: 10 - } - } - }, - ExtGroupKeyInfo: { - fields: { - curMaxSeq: { - type: "int32", - id: 1 - }, - curTime: { - type: "int64", - id: 2 - } - } - }, - SyncCookie: { - fields: { - time1: { - type: "int64", - id: 1 - }, - time: { - type: "int64", - id: 2 - }, - const1: { - type: "int64", - id: 3 - }, - const2: { - type: "int64", - id: 4 - }, - ran1: { - type: "int64", - id: 5 - }, - ran2: { - type: "int64", - id: 9 - }, - ran3: { - type: "int64", - id: 11 - }, - const3: { - type: "int64", - id: 12 - }, - lastSyncTime: { - type: "int64", - id: 13 - }, - const4: { - type: "int64", - id: 14 - } - } - }, - TransMsgInfo: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - msgType: { - type: "int32", - id: 3 - }, - msgSubtype: { - type: "int32", - id: 4 - }, - msgSeq: { - type: "int32", - id: 5 - }, - msgUid: { - type: "int64", - id: 6 - }, - msgTime: { - type: "int64", - id: 7 - }, - realMsgTime: { - type: "int64", - id: 8 - }, - nickName: { - type: "string", - id: 9 - }, - msgData: { - type: "bytes", - id: 10 - }, - svrIp: { - type: "int32", - id: 11 - }, - extGroupKeyInfo: { - type: "ExtGroupKeyInfo", - id: 12 - }, - generalFlag: { - type: "int32", - id: 17 - } - } - }, - GeneralFlags: { - fields: { - bubbleDiyTextId: { - type: "int32", - id: 1 - }, - groupFlagNew: { - type: "int32", - id: 2 - }, - uin: { - type: "int64", - id: 3 - }, - rpId: { - type: "bytes", - id: 4 - }, - prpFold: { - type: "int32", - id: 5 - }, - longTextFlag: { - type: "int32", - id: 6 - }, - longTextResid: { - type: "string", - id: 7 - }, - groupType: { - type: "int32", - id: 8 - }, - toUinFlag: { - type: "int32", - id: 9 - }, - glamourLevel: { - type: "int32", - id: 10 - }, - memberLevel: { - type: "int32", - id: 11 - }, - groupRankSeq: { - type: "int64", - id: 12 - }, - olympicTorch: { - type: "int32", - id: 13 - }, - babyqGuideMsgCookie: { - type: "bytes", - id: 14 - }, - uin32ExpertFlag: { - type: "int32", - id: 15 - }, - bubbleSubId: { - type: "int32", - id: 16 - }, - pendantId: { - type: "int64", - id: 17 - }, - rpIndex: { - type: "bytes", - id: 18 - }, - pbReserve: { - type: "bytes", - id: 19 - } - } - }, - PbMultiMsgItem: { - fields: { - fileName: { - type: "string", - id: 1 - }, - buffer: { - type: "bytes", - id: 2 - } - } - }, - PbMultiMsgNew: { - fields: { - msg: { - rule: "repeated", - type: "Message", - id: 1 - } - } - }, - PbMultiMsgTransmit: { - fields: { - msg: { - rule: "repeated", - type: "Message", - id: 1 - }, - pbItemList: { - rule: "repeated", - type: "PbMultiMsgItem", - id: 2 - } +class Nested { + constructor(bytes, decoded) { + if (decoded) + Reflect.setPrototypeOf(this, decoded); + this.raw = bytes; } - }, - SyncFlag: { - values: { - START: 0, - CONTINUME: 1, - STOP: 2 - } - }, - ExternMsg: { - fields: { - channelType: { - type: "int32", - id: 1 - } - } - }, - MultiMsgApplyDownReq: { - fields: { - msgResid: { - type: "bytes", - id: 1 - }, - msgType: { - type: "int32", - id: 2 - }, - srcUin: { - type: "int64", - id: 3 - } - } - }, - MultiMsgApplyDownRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - thumbDownPara: { - type: "bytes", - id: 2 - }, - msgKey: { - type: "bytes", - id: 3 - }, - uint32DownIp: { - rule: "repeated", - type: "int32", - id: 4 - }, - uint32DownPort: { - rule: "repeated", - type: "int32", - id: 5 - }, - msgResid: { - type: "bytes", - id: 6 - }, - msgExternInfo: { - type: "ExternMsg", - id: 7 - }, - bytesDownIpV6: { - rule: "repeated", - type: "bytes", - id: 8 - }, - uint32DownV6Port: { - rule: "repeated", - type: "int32", - id: 9 - } - } - }, - MultiMsgApplyUpReq: { - fields: { - dstUin: { - type: "int64", - id: 1 - }, - msgSize: { - type: "int64", - id: 2 - }, - msgMd5: { - type: "bytes", - id: 3 - }, - msgType: { - type: "int32", - id: 4 - }, - applyId: { - type: "int32", - id: 5 - } - } - }, - MultiMsgApplyUpRsp: { - fields: { - result: { - type: "int32", - id: 1 - }, - msgResid: { - type: "string", - id: 2 - }, - msgUkey: { - type: "bytes", - id: 3 - }, - uint32UpIp: { - rule: "repeated", - type: "int32", - id: 4 - }, - uint32UpPort: { - rule: "repeated", - type: "int32", - id: 5 - }, - blockSize: { - type: "int64", - id: 6 - }, - upOffset: { - type: "int64", - id: 7 - }, - applyId: { - type: "int32", - id: 8 - }, - msgKey: { - type: "bytes", - id: 9 - }, - msgSig: { - type: "bytes", - id: 10 - }, - msgExternInfo: { - type: "ExternMsg", - id: 11 - }, - bytesUpIpV6: { - rule: "repeated", - type: "bytes", - id: 12 - }, - uint32UpV6Port: { - rule: "repeated", - type: "int32", - id: 13 - } - } - }, - MultiReqBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - termType: { - type: "int32", - id: 2 - }, - platformType: { - type: "int32", - id: 3 - }, - netType: { - type: "int32", - id: 4 - }, - buildVer: { - type: "string", - id: 5 - }, - multimsgApplyupReq: { - rule: "repeated", - type: "MultiMsgApplyUpReq", - id: 6 - }, - multimsgApplydownReq: { - rule: "repeated", - type: "MultiMsgApplyDownReq", - id: 7 - }, - buType: { - type: "int32", - id: 8 - }, - reqChannelType: { - type: "int32", - id: 9 - } - } - }, - MultiRspBody: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - multimsgApplyupRsp: { - rule: "repeated", - type: "MultiMsgApplyUpRsp", - id: 2 - }, - multimsgApplydownRsp: { - rule: "repeated", - type: "MultiMsgApplyDownRsp", - id: 3 - } - } - }, - MsgPic: { - fields: { - smallPicUrl: { - type: "bytes", - id: 1 - }, - originalPicUrl: { - type: "bytes", - id: 2 - }, - localPicId: { - type: "int32", - id: 3 - } - } - }, - ObjMsg: { - fields: { - msgType: { - type: "int32", - id: 1 - }, - title: { - type: "bytes", - id: 2 - }, - bytesAbstact: { - type: "bytes", - id: 3 - }, - titleExt: { - type: "bytes", - id: 5 - }, - msgPic: { - rule: "repeated", - type: "MsgPic", - id: 6 - }, - msgContentInfo: { - rule: "repeated", - type: "MsgContentInfo", - id: 7 - }, - reportIdShow: { - type: "int32", - id: 8 - } - } - }, - MsgContentInfo: { - fields: { - contentInfoId: { - type: "bytes", - id: 1 - }, - msgFile: { - type: "MsgFile", - id: 2 - } - } - }, - MsgFile: { - fields: { - busId: { - type: "int32", - id: 1 - }, - filePath: { - type: "bytes", - id: 2 - }, - fileSize: { - type: "int64", - id: 3 - }, - fileName: { - type: "string", - id: 4 - }, - int64DeadTime: { - type: "int64", - id: 5 - }, - fileSha1: { - type: "bytes", - id: 6 - }, - ext: { - type: "bytes", - id: 7 - } - } - }, - OIDBSSOPkg: { - fields: { - command: { - type: "int32", - id: 1 - }, - serviceType: { - type: "int32", - id: 2 - }, - result: { - type: "int32", - id: 3 - }, - bodybuffer: { - type: "bytes", - id: 4 - }, - errorMsg: { - type: "string", - id: 5 - }, - clientVersion: { - type: "string", - id: 6 - } - } - }, - D758ReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - toUin: { - type: "D758Uin", - id: 2 - }, - } - }, - D758Uin: { - fields: { - uin: { - type: "int64", - id: 1 - } - } - }, - D88DReqBody: { - fields: { - appid: { - type: "int32", - id: 1 - }, - groupList: { - rule: "repeated", - type: "D88DReqGroupInfo", - id: 2 - }, - pcClientVersion: { - type: "int32", - id: 3 - } - } - }, - D88DRspBody: { - fields: { - groupList: { - rule: "repeated", - type: "D88DRspGroupInfo", - id: 1 - }, - errorInfo: { - type: "bytes", - id: 2 - }, - } - }, - D88DReqGroupInfo: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - groupInfo: { - type: "D88DSTGroupInfo", - id: 2 - }, - lastGetGroupNameTime: { - type: "int32", - id: 3 - } - } - }, - D88DRspGroupInfo: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - result: { - type: "int32", - id: 2 - }, - groupInfo: { - type: "D88DSTGroupInfo", - id: 3 - } - } - }, - D88DSTGroupInfo: { - fields: { - groupOwner: { - type: "int64", - id: 1 - }, - groupCreateTime: { - type: "int32", - id: 2 - }, - groupFlag: { - type: "int32", - id: 3 - }, - groupFlagExt: { - type: "int32", - id: 4 - }, - groupMemberMaxNum: { - type: "int32", - id: 5 - }, - groupMemberNum: { - type: "int32", - id: 6 - }, - groupOption: { - type: "int32", - id: 7 - }, - groupClassExt: { - type: "int32", - id: 8 - }, - groupSpecialClass: { - type: "int32", - id: 9 - }, - groupLevel: { - type: "int32", - id: 10 - }, - groupFace: { - type: "int32", - id: 11 - }, - groupDefaultPage: { - type: "int32", - id: 12 - }, - groupInfoSeq: { - type: "int32", - id: 13 - }, - groupRoamingTime: { - type: "int32", - id: 14 - }, - groupName: { - type: "string", - id: 15 - }, - groupMemo: { - type: "string", - id: 16 - }, - ingGroupFingerMemo: { - type: "string", - id: 17 - }, - ingGroupClassText: { - type: "string", - id: 18 - }, - groupAllianceCode: { - rule: "repeated", - type: "int32", - id: 19 - }, - groupExtraAdmNum: { - type: "int32", - id: 20 - }, - groupUin: { - type: "int64", - id: 21 - }, - groupCurMsgSeq: { - type: "int32", - id: 22 - }, - groupLastMsgTime: { - type: "int32", - id: 23 - }, - ingGroupQuestion: { - type: "string", - id: 24 - }, - ingGroupAnswer: { - type: "string", - id: 25 - }, - groupVisitorMaxNum: { - type: "int32", - id: 26 - }, - groupVisitorCurNum: { - type: "int32", - id: 27 - }, - levelNameSeq: { - type: "int32", - id: 28 - }, - groupAdminMaxNum: { - type: "int32", - id: 29 - }, - groupAioSkint32imestamp: { - type: "int32", - id: 30 - }, - groupBoardSkint32imestamp: { - type: "int32", - id: 31 - }, - ingGroupAioSkinUrl: { - type: "string", - id: 32 - }, - ingGroupBoardSkinUrl: { - type: "string", - id: 33 - }, - groupCoverSkint32imestamp: { - type: "int32", - id: 34 - }, - ingGroupCoverSkinUrl: { - type: "string", - id: 35 - }, - groupGrade: { - type: "int32", - id: 36 - }, - activeMemberNum: { - type: "int32", - id: 37 - }, - certificationType: { - type: "int32", - id: 38 - }, - ingCertificationText: { - type: "string", - id: 39 - }, - ingGroupRichFingerMemo: { - type: "string", - id: 40 - }, - // tagRecord: { - // type: "List", - // id: 41 - // }, - // groupGeoInfo: { - // type: "GroupGeoInfo", - // id: 42 - // }, - headPortraitSeq: { - type: "int32", - id: 43 - }, - // msgHeadPortrait: { - // type: "GroupHeadPortrait", - // id: 44 - // }, - shutupTimestamp: { - type: "int32", - id: 45 - }, - shutupTimestampMe: { - type: "int32", - id: 46 - }, - createSourceFlag: { - type: "int32", - id: 47 - }, - cmduinMsgSeq: { - type: "int32", - id: 48 - }, - cmduinJoint32ime: { - type: "int32", - id: 49 - }, - cmduinUinFlag: { - type: "int32", - id: 50 - }, - cmduinFlagEx: { - type: "int32", - id: 51 - }, - cmduinNewMobileFlag: { - type: "int32", - id: 52 - }, - cmduinReadMsgSeq: { - type: "int32", - id: 53 - }, - cmduinLastMsgTime: { - type: "int32", - id: 54 - }, - groupTypeFlag: { - type: "int32", - id: 55 - }, - appPrivilegeFlag: { - type: "int32", - id: 56 - }, - // stGroupExInfo: { - // type: "GroupExInfoOnly", - // id: 57 - // }, - groupSecLevel: { - type: "int32", - id: 58 - }, - groupSecLevelInfo: { - type: "int32", - id: 59 - }, - cmduinPrivilege: { - type: "int32", - id: 60 - }, - ingPoidInfo: { - type: "bytes", - id: 61 - }, - cmduinFlagEx2: { - type: "int32", - id: 62 - }, - confUin: { - type: "int64", - id: 63 - }, - confMaxMsgSeq: { - type: "int32", - id: 64 - }, - confToGroupTime: { - type: "int32", - id: 65 - }, - passwordRedbagTime: { - type: "int32", - id: 66 - }, - subscriptionUin: { - type: "int64", - id: 67 - }, - memberListChangeSeq: { - type: "int32", - id: 68 - }, - membercardSeq: { - type: "int32", - id: 69 - }, - rootid: { - type: "int64", - id: 70 - }, - parentid: { - type: "int64", - id: 71 - }, - teamSeq: { - type: "int32", - id: 72 - }, - historyMsgBegint32ime: { - type: "int64", - id: 73 - }, - inviteNoAuthNumLimit: { - type: "int64", - id: 74 - }, - cmduinHistoryMsgSeq: { - type: "int32", - id: 75 - }, - cmduinJoinMsgSeq: { - type: "int32", - id: 76 - }, - groupFlagext3: { - type: "int32", - id: 77 - }, - groupOpenAppid: { - type: "int32", - id: 78 - }, - isConfGroup: { - type: "int32", - id: 79 - }, - isModifyConfGroupFace: { - type: "int32", - id: 80 - }, - isModifyConfGroupName: { - type: "int32", - id: 81 - }, - noFingerOpenFlag: { - type: "int32", - id: 82 - }, - noCodeFingerOpenFlag: { - type: "int32", - id: 83 - }, - autoAgreeJoinGroupUserNumForNormalGroup: { - type: "int32", - id: 84 - }, - autoAgreeJoinGroupUserNumForConfGroup: { - type: "int32", - id: 85 - }, - isAllowConfGroupMemberNick: { - type: "int32", - id: 86 - }, - isAllowConfGroupMemberAtAll: { - type: "int32", - id: 87 - }, - isAllowConfGroupMemberModifyGroupName: { - type: "int32", - id: 88 - }, - longGroupName: { - type: "string", - id: 89 - }, - cmduinJoinRealMsgSeq: { - type: "int32", - id: 90 - }, - isGroupFreeze: { - type: "int32", - id: 91 - }, - msgLimitFrequency: { - type: "int32", - id: 92 - }, - joinGroupAuth: { - type: "bytes", - id: 93 - }, - hlGuildAppid: { - type: "int32", - id: 94 - }, - hlGuildSubType: { - type: "int32", - id: 95 - }, - hlGuildOrgid: { - type: "int32", - id: 96 - }, - isAllowHlGuildBinary: { - type: "int32", - id: 97 - }, - cmduinRingtoneid: { - type: "int32", - id: 98 - }, - groupFlagext4: { - type: "int32", - id: 99 - }, - groupFreezeReason: { - type: "int32", - id: 100 - }, - groupCode: { - type: "int64", - id: 101 - }, - } - }, - D8A0RspBody: { - fields: { - optUint64GroupCode: { - type: "int64", - id: 1 - }, - msgKickResult: { - rule: "repeated", - type: "D8A0KickResult", - id: 2 - } - } - }, - D8A0KickResult: { - fields: { - optUint32Result: { - type: "int32", - id: 1 - }, - optUint64MemberUin: { - type: "int64", - id: 2 - } - } - }, - D8A0KickMemberInfo: { - fields: { - optUint32Operate: { - type: "int32", - id: 1 - }, - optUint64MemberUin: { - type: "int64", - id: 2 - }, - optUint32Flag: { - type: "int32", - id: 3 - }, - optBytesMsg: { - type: "bytes", - id: 4 - } - } - }, - D8A0ReqBody: { - fields: { - optUint64GroupCode: { - type: "int64", - id: 1 - }, - msgKickList: { - rule: "repeated", - type: "D8A0KickMemberInfo", - id: 2 - }, - kickList: { - rule: "repeated", - type: "int64", - id: 3 - }, - kickFlag: { - type: "int32", - id: 4 - }, - kickMsg: { - type: "bytes", - id: 5 - } - } - }, - D8FCReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - showFlag: { - type: "int32", - id: 2 - }, - memLevelInfo: { - rule: "repeated", - type: "D8FCMemberInfo", - id: 3 - }, - levelName: { - rule: "repeated", - type: "D8FCLevelName", - id: 4 - }, - updateTime: { - type: "int32", - id: 5 - }, - officeMode: { - type: "int32", - id: 6 - }, - groupOpenAppid: { - type: "int32", - id: 7 - }, - msgClientInfo: { - type: "D8FCClientInfo", - id: 8 - }, - authKey: { - type: "bytes", - id: 9 - } - } - }, - D89AReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - stGroupInfo: { - type: "D89AGroupinfo", - id: 2 - }, - originalOperatorUin: { - type: "int64", - id: 3 - }, - reqGroupOpenAppid: { - type: "int32", - id: 4 - } - } - }, - D89AGroupinfo: { - fields: { - groupExtAdmNum: { - type: "int32", - id: 1 - }, - flag: { - type: "int32", - id: 2 - }, - ingGroupName: { - type: "bytes", - id: 3 - }, - ingGroupMemo: { - type: "bytes", - id: 4 - }, - ingGroupFingerMemo: { - type: "bytes", - id: 5 - }, - ingGroupAioSkinUrl: { - type: "bytes", - id: 6 - }, - ingGroupBoardSkinUrl: { - type: "bytes", - id: 7 - }, - ingGroupCoverSkinUrl: { - type: "bytes", - id: 8 - }, - groupGrade: { - type: "int32", - id: 9 - }, - activeMemberNum: { - type: "int32", - id: 10 - }, - certificationType: { - type: "int32", - id: 11 - }, - ingCertificationText: { - type: "bytes", - id: 12 - }, - ingGroupRichFingerMemo: { - type: "bytes", - id: 13 - }, - stGroupNewguidelines: { - type: "D89AGroupNewGuidelinesInfo", - id: 14 - }, - groupFace: { - type: "int32", - id: 15 - }, - addOption: { - type: "int32", - id: 16 - }, - shutupTime: { - type: "int32", - id: 17 - }, - groupTypeFlag: { - type: "int32", - id: 18 - }, - stringGroupTag: { - type: "bytes", - id: 19 - }, - msgGroupGeoInfo: { - type: "D89AGroupGeoInfo", - id: 20 - }, - groupClassExt: { - type: "int32", - id: 21 - }, - ingGroupClassText: { - type: "bytes", - id: 22 - }, - appPrivilegeFlag: { - type: "int32", - id: 23 - }, - appPrivilegeMask: { - type: "int32", - id: 24 - }, - stGroupExInfo: { - type: "D89AGroupExInfoOnly", - id: 25 - }, - groupSecLevel: { - type: "int32", - id: 26 - }, - groupSecLevelInfo: { - type: "int32", - id: 27 - }, - subscriptionUin: { - type: "int64", - id: 28 - }, - allowMemberInvite: { - type: "int32", - id: 29 - }, - ingGroupQuestion: { - type: "bytes", - id: 30 - }, - ingGroupAnswer: { - type: "bytes", - id: 31 - }, - groupFlagext3: { - type: "int32", - id: 32 - }, - groupFlagext3Mask: { - type: "int32", - id: 33 - }, - groupOpenAppid: { - type: "int32", - id: 34 - }, - noFingerOpenFlag: { - type: "int32", - id: 35 - }, - noCodeFingerOpenFlag: { - type: "int32", - id: 36 - }, - rootId: { - type: "int64", - id: 37 - }, - msgLimitFrequency: { - type: "int32", - id: 38 - } - } - }, - D89AGroupNewGuidelinesInfo: { - fields: { - boolEnabled: { - type: "bool", - id: 1 - }, - ingContent: { - type: "bytes", - id: 2 - } - } - }, - D89AGroupExInfoOnly: { - fields: { - tribeId: { - type: "int32", - id: 1 - }, - moneyForAddGroup: { - type: "int32", - id: 2 - } - } - }, - D89AGroupGeoInfo: { - fields: { - cityId: { - type: "int32", - id: 1 - }, - longtitude: { - type: "int64", - id: 2 - }, - latitude: { - type: "int64", - id: 3 - }, - ingGeoContent: { - type: "bytes", - id: 4 - }, - poiId: { - type: "int64", - id: 5 - } - } - }, - D8FCMemberInfo: { - fields: { - uin: { - type: "int64", - id: 1 - }, - point: { - type: "int32", - id: 2 - }, - activeDay: { - type: "int32", - id: 3 - }, - level: { - type: "int32", - id: 4 - }, - specialTitle: { - type: "bytes", - id: 5 - }, - specialTitleExpireTime: { - type: "int32", - id: 6 - }, - uinName: { - type: "bytes", - id: 7 - }, - memberCardName: { - type: "bytes", - id: 8 - }, - phone: { - type: "bytes", - id: 9 - }, - email: { - type: "bytes", - id: 10 - }, - remark: { - type: "bytes", - id: 11 - }, - gender: { - type: "int32", - id: 12 - }, - job: { - type: "bytes", - id: 13 - }, - tribeLevel: { - type: "int32", - id: 14 - }, - tribePoint: { - type: "int32", - id: 15 - }, - richCardName: { - rule: "repeated", - type: "D8FCCardNameElem", - id: 16 - }, - commRichCardName: { - type: "bytes", - id: 17 - } - } - }, - D8FCCardNameElem: { - fields: { - enumCardType: { - type: "int32", - id: 1 - }, - value: { - type: "bytes", - id: 2 - } - } - }, - D8FCLevelName: { - fields: { - level: { - type: "int32", - id: 1 - }, - name: { - type: "string", - id: 2 - } - } - }, - D8FCClientInfo: { - fields: { - implat: { - type: "int32", - id: 1 - }, - ingClientver: { - type: "string", - id: 2 - } - } - }, - DED3ReqBody: { - fields: { - toUin: { - type: "int64", - id: 1 - }, - groupCode: { - type: "int64", - id: 2 - } - } - }, - DeleteFileReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - parentFolderId: { - type: "string", - id: 4 - }, - fileId: { - type: "string", - id: 5 - } - } - }, - DeleteFileRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - } - } - }, - DownloadFileReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - fileId: { - type: "string", - id: 4 - }, - boolThumbnailReq: { - type: "bool", - id: 5 - }, - urlType: { - type: "int32", - id: 6 - }, - boolPreviewReq: { - type: "bool", - id: 7 - } - } - }, - DownloadFileRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - }, - downloadIp: { - type: "string", - id: 4 - }, - downloadDns: { - type: "bytes", - id: 5 - }, - downloadUrl: { - type: "bytes", - id: 6 - }, - sha: { - type: "bytes", - id: 7 - }, - sha3: { - type: "bytes", - id: 8 - }, - md5: { - type: "bytes", - id: 9 - }, - cookieVal: { - type: "bytes", - id: 10 - }, - saveFileName: { - type: "string", - id: 11 - }, - previewPort: { - type: "int32", - id: 12 - } - } - }, - MoveFileReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - fileId: { - type: "string", - id: 4 - }, - parentFolderId: { - type: "string", - id: 5 - }, - destFolderId: { - type: "string", - id: 6 - } - } - }, - MoveFileRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - }, - parentFolderId: { - type: "string", - id: 4 - } - } - }, - RenameFileReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - fileId: { - type: "string", - id: 4 - }, - parentFolderId: { - type: "string", - id: 5 - }, - newFileName: { - type: "string", - id: 6 - } - } - }, - RenameFileRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - } - } - }, - D6D6ReqBody: { - fields: { - uploadFileReq: { - type: "UploadFileReqBody", - id: 1 - }, - resendFileReq: { - type: "ResendReqBody", - id: 2 - }, - downloadFileReq: { - type: "DownloadFileReqBody", - id: 3 - }, - deleteFileReq: { - type: "DeleteFileReqBody", - id: 4 - }, - renameFileReq: { - type: "RenameFileReqBody", - id: 5 - }, - moveFileReq: { - type: "MoveFileReqBody", - id: 6 - } - } - }, - ResendReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - fileId: { - type: "string", - id: 4 - }, - sha: { - type: "bytes", - id: 5 - } - } - }, - ResendRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - }, - uploadIp: { - type: "string", - id: 4 - }, - fileKey: { - type: "bytes", - id: 5 - }, - checkKey: { - type: "bytes", - id: 6 - } - } - }, - D6D6RspBody: { - fields: { - uploadFileRsp: { - type: "UploadFileRspBody", - id: 1 - }, - resendFileRsp: { - type: "ResendRspBody", - id: 2 - }, - downloadFileRsp: { - type: "DownloadFileRspBody", - id: 3 - }, - deleteFileRsp: { - type: "DeleteFileRspBody", - id: 4 - }, - renameFileRsp: { - type: "RenameFileRspBody", - id: 5 - }, - moveFileRsp: { - type: "MoveFileRspBody", - id: 6 - } - } - }, - UploadFileReqBody: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - appId: { - type: "int32", - id: 2 - }, - busId: { - type: "int32", - id: 3 - }, - entrance: { - type: "int32", - id: 4 - }, - parentFolderId: { - type: "string", - id: 5 - }, - fileName: { - type: "string", - id: 6 - }, - localPath: { - type: "string", - id: 7 - }, - int64FileSize: { - type: "int64", - id: 8 - }, - sha: { - type: "bytes", - id: 9 - }, - sha3: { - type: "bytes", - id: 10 - }, - md5: { - type: "bytes", - id: 11 - } - } - }, - UploadFileRspBody: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - clientWording: { - type: "string", - id: 3 - }, - uploadIp: { - type: "string", - id: 4 - }, - serverDns: { - type: "string", - id: 5 - }, - busId: { - type: "int32", - id: 6 - }, - fileId: { - type: "string", - id: 7 - }, - fileKey: { - type: "bytes", - id: 8 - }, - checkKey: { - type: "bytes", - id: 9 - }, - boolFileExist: { - type: "bool", - id: 10 - } - } - }, - ShortVideoReqBody: { - fields: { - cmd: { - type: "int32", - id: 1 - }, - seq: { - type: "int32", - id: 2 - }, - pttShortVideoDownloadReq: { - type: "ShortVideoDownloadReq", - id: 4 - } - } - }, - ShortVideoRspBody: { - fields: { - cmd: { - type: "int32", - id: 1 - }, - seq: { - type: "int32", - id: 2 - }, - pttShortVideoDownloadRsp: { - type: "ShortVideoDownloadRsp", - id: 4 - } - } - }, - ShortVideoDownloadReq: { - fields: { - fromUin: { - type: "int64", - id: 1 - }, - toUin: { - type: "int64", - id: 2 - }, - chatType: { - type: "int32", - id: 3 - }, - clientType: { - type: "int32", - id: 4 - }, - fileId: { - type: "string", - id: 5 - }, - groupCode: { - type: "int64", - id: 6 - }, - agentType: { - type: "int32", - id: 7 - }, - fileMd5: { - type: "bytes", - id: 8 - }, - businessType: { - type: "int32", - id: 9 - }, - fileType: { - type: "int32", - id: 10 - }, - downType: { - type: "int32", - id: 11 - }, - sceneType: { - type: "int32", - id: 12 - } - } - }, - ShortVideoDownloadRsp: { - fields: { - retCode: { - type: "int32", - id: 1 - }, - retMsg: { - type: "string", - id: 2 - }, - sameAreaOutAddr: { - rule: "repeated", - type: "ShortVideoIpList", - id: 3 - }, - diffAreaOutAddr: { - rule: "repeated", - type: "ShortVideoIpList", - id: 4 - }, - downloadKey: { - type: "bytes", - id: 5 - }, - fileMd5: { - type: "bytes", - id: 6 - }, - sameAreaInnerAddr: { - rule: "repeated", - type: "ShortVideoIpList", - id: 7 - }, - diffAreaInnerAddr: { - rule: "repeated", - type: "ShortVideoIpList", - id: 8 - }, - downloadAddr: { - type: "ShortVideoAddr", - id: 9 - }, - encryptKey: { - type: "bytes", - id: 10 - } - } - }, - ShortVideoIpList: { - fields: { - ip: { - type: "int32", - id: 1 - }, - port: { - type: "int32", - id: 2 - } - } - }, - ShortVideoAddr: { - fields: { - host: { - rule: "repeated", - type: "string", - id: 10 - }, - urlArgs: { - type: "string", - id: 11 - } - } - }, - AddFrdSNInfo: { - fields: { - notSeeDynamic: { - type: "int32", - id: 1 - }, - setSn: { - type: "int32", - id: 2 - } - } - }, - FlagInfo: { - fields: { - grpMsgKickAdmin: { - type: "int32", - id: 1 - }, - grpMsgHiddenGrp: { - type: "int32", - id: 2 - }, - grpMsgWordingDown: { - type: "int32", - id: 3 - }, - frdMsgGetBusiCard: { - type: "int32", - id: 4 - }, - grpMsgGetOfficialAccount: { - type: "int32", - id: 5 - }, - grpMsgGetPayInGroup: { - type: "int32", - id: 6 - }, - frdMsgDiscuss2ManyChat: { - type: "int32", - id: 7 - }, - grpMsgNotAllowJoinGrpInviteNotFrd: { - type: "int32", - id: 8 - }, - frdMsgNeedWaitingMsg: { - type: "int32", - id: 9 - }, - frdMsgUint32NeedAllUnreadMsg: { - type: "int32", - id: 10 - }, - grpMsgNeedAutoAdminWording: { - type: "int32", - id: 11 - }, - grpMsgGetTransferGroupMsgFlag: { - type: "int32", - id: 12 - }, - grpMsgGetQuitPayGroupMsgFlag: { - type: "int32", - id: 13 - }, - grpMsgSupportInviteAutoJoin: { - type: "int32", - id: 14 - }, - grpMsgMaskInviteAutoJoin: { - type: "int32", - id: 15 - }, - grpMsgGetDisbandedByAdmin: { - type: "int32", - id: 16 - }, - grpMsgGetC2cInviteJoinGroup: { - type: "int32", - id: 17 - } - } - }, - FriendInfo: { - fields: { - msgJointFriend: { - type: "string", - id: 1 - }, - msgBlacklist: { - type: "string", - id: 2 - } - } - }, - SGroupInfo: { - fields: { - groupAuthType: { - type: "int32", - id: 1 - }, - displayAction: { - type: "int32", - id: 2 - }, - msgAlert: { - type: "string", - id: 3 - }, - msgDetailAlert: { - type: "string", - id: 4 - }, - msgOtherAdminDone: { - type: "string", - id: 5 - }, - appPrivilegeFlag: { - type: "int32", - id: 6 - } - } - }, - MsgInviteExt: { - fields: { - srcType: { - type: "int32", - id: 1 - }, - srcCode: { - type: "int64", - id: 2 - }, - waitState: { - type: "int32", - id: 3 - } - } - }, - MsgPayGroupExt: { - fields: { - joinGrpTime: { - type: "int64", - id: 1 - }, - quitGrpTime: { - type: "int64", - id: 2 - } - } - }, - ReqNextSystemMsg: { - fields: { - msgNum: { - type: "int32", - id: 1 - }, - followingFriendSeq: { - type: "int64", - id: 2 - }, - followingGroupSeq: { - type: "int64", - id: 3 - }, - checktype: { - type: "int32", - id: 4 - }, - flag: { - type: "FlagInfo", - id: 5 - }, - language: { - type: "int32", - id: 6 - }, - version: { - type: "int32", - id: 7 - }, - friendMsgTypeFlag: { - type: "int64", - id: 8 - } - } - }, - ReqSystemMsg: { - fields: { - msgNum: { - type: "int32", - id: 1 - }, - latestFriendSeq: { - type: "int64", - id: 2 - }, - latestGroupSeq: { - type: "int64", - id: 3 - }, - version: { - type: "int32", - id: 4 - }, - language: { - type: "int32", - id: 5 - } - } - }, - ReqSystemMsgAction: { - fields: { - msgType: { - type: "int32", - id: 1 - }, - msgSeq: { - type: "int64", - id: 2 - }, - reqUin: { - type: "int64", - id: 3 - }, - subType: { - type: "int32", - id: 4 - }, - srcId: { - type: "int32", - id: 5 - }, - subSrcId: { - type: "int32", - id: 6 - }, - groupMsgType: { - type: "int32", - id: 7 - }, - actionInfo: { - type: "SystemMsgActionInfo", - id: 8 - }, - language: { - type: "int32", - id: 9 - } - } - }, - ReqSystemMsgNew: { - fields: { - msgNum: { - type: "int32", - id: 1 - }, - latestFriendSeq: { - type: "int64", - id: 2 - }, - latestGroupSeq: { - type: "int64", - id: 3 - }, - version: { - type: "int32", - id: 4 - }, - checktype: { - type: "int32", - id: 5 - }, - flag: { - type: "FlagInfo", - id: 6 - }, - language: { - type: "int32", - id: 7 - }, - isGetFrdRibbon: { - type: "bool", - id: 8 - }, - isGetGrpRibbon: { - type: "bool", - id: 9 - }, - friendMsgTypeFlag: { - type: "int64", - id: 10 - } - } - }, - ReqSystemMsgRead: { - fields: { - latestFriendSeq: { - type: "int64", - id: 1 - }, - latestGroupSeq: { - type: "int64", - id: 2 - }, - type: { - type: "int32", - id: 3 - }, - checktype: { - type: "int32", - id: 4 - } - } - }, - RspHead: { - fields: { - result: { - type: "int32", - id: 1 - }, - msgFail: { - type: "string", - id: 2 - } - } - }, - RspNextSystemMsg: { - fields: { - head: { - type: "RspHead", - id: 1 - }, - msgs: { - rule: "repeated", - type: "StructMsg", - id: 2 - }, - followingFriendSeq: { - type: "int64", - id: 3 - }, - followingGroupSeq: { - type: "int64", - id: 4 - }, - checktype: { - type: "int32", - id: 5 - }, - gameNick: { - type: "string", - id: 100 - }, - undecidForQim: { - type: "bytes", - id: 101 - }, - unReadCount3: { - type: "int32", - id: 102 - } - } - }, - RspSystemMsg: { - fields: { - head: { - type: "RspHead", - id: 1 - }, - msgs: { - rule: "repeated", - type: "StructMsg", - id: 2 - }, - unreadCount: { - type: "int32", - id: 3 - }, - latestFriendSeq: { - type: "int64", - id: 4 - }, - latestGroupSeq: { - type: "int64", - id: 5 - }, - followingFriendSeq: { - type: "int64", - id: 6 - }, - followingGroupSeq: { - type: "int64", - id: 7 - }, - msgDisplay: { - type: "string", - id: 8 - } - } - }, - RspSystemMsgAction: { - fields: { - head: { - type: "RspHead", - id: 1 - }, - msgDetail: { - type: "string", - id: 2 - }, - type: { - type: "int32", - id: 3 - }, - msgInvalidDecided: { - type: "string", - id: 5 - }, - remarkResult: { - type: "int32", - id: 6 - } - } - }, - RspSystemMsgNew: { - fields: { - head: { - type: "RspHead", - id: 1 - }, - unreadFriendCount: { - type: "int32", - id: 2 - }, - unreadGroupCount: { - type: "int32", - id: 3 - }, - latestFriendSeq: { - type: "int64", - id: 4 - }, - latestGroupSeq: { - type: "int64", - id: 5 - }, - followingFriendSeq: { - type: "int64", - id: 6 - }, - followingGroupSeq: { - type: "int64", - id: 7 - }, - friendmsgs: { - rule: "repeated", - type: "StructMsg", - id: 9 - }, - groupmsgs: { - rule: "repeated", - type: "StructMsg", - id: 10 - }, - msgRibbonFriend: { - type: "StructMsg", - id: 11 - }, - msgRibbonGroup: { - type: "StructMsg", - id: 12 - }, - msgDisplay: { - type: "string", - id: 13 - }, - grpMsgDisplay: { - type: "string", - id: 14 - }, - over: { - type: "int32", - id: 15 - }, - checktype: { - type: "int32", - id: 20 - }, - gameNick: { - type: "string", - id: 100 - }, - undecidForQim: { - type: "bytes", - id: 101 - }, - unReadCount3: { - type: "int32", - id: 102 - } - } - }, - RspSystemMsgRead: { - fields: { - head: { - type: "RspHead", - id: 1 - }, - type: { - type: "int32", - id: 2 - }, - checktype: { - type: "int32", - id: 3 - } - } - }, - StructMsg: { - fields: { - version: { - type: "int32", - id: 1 - }, - msgType: { - type: "int32", - id: 2 - }, - msgSeq: { - type: "int64", - id: 3 - }, - msgTime: { - type: "int64", - id: 4 - }, - reqUin: { - type: "int64", - id: 5 - }, - unreadFlag: { - type: "int32", - id: 6 - }, - msg: { - type: "SystemMsg", - id: 50 - } - } - }, - SystemMsg: { - fields: { - subType: { - type: "int32", - id: 1 - }, - msgTitle: { - type: "string", - id: 2 - }, - msgDescribe: { - type: "string", - id: 3 - }, - msgAdditional: { - type: "string", - id: 4 - }, - msgSource: { - type: "string", - id: 5 - }, - msgDecided: { - type: "string", - id: 6 - }, - srcId: { - type: "int32", - id: 7 - }, - subSrcId: { - type: "int32", - id: 8 - }, - actions: { - rule: "repeated", - type: "SystemMsgAction", - id: 9 - }, - groupCode: { - type: "int64", - id: 10 - }, - actionUin: { - type: "int64", - id: 11 - }, - groupMsgType: { - type: "int32", - id: 12 - }, - groupInviterRole: { - type: "int32", - id: 13 - }, - friendInfo: { - type: "FriendInfo", - id: 14 - }, - groupInfo: { - type: "SGroupInfo", - id: 15 - }, - actorUin: { - type: "int64", - id: 16 - }, - msgActorDescribe: { - type: "string", - id: 17 - }, - msgAdditionalList: { - type: "string", - id: 18 - }, - relation: { - type: "int32", - id: 19 - }, - reqsubtype: { - type: "int32", - id: 20 - }, - cloneUin: { - type: "int64", - id: 21 - }, - discussUin: { - type: "int64", - id: 22 - }, - eimGroupId: { - type: "int64", - id: 23 - }, - msgInviteExtinfo: { - type: "MsgInviteExt", - id: 24 - }, - msgPayGroupExtinfo: { - type: "MsgPayGroupExt", - id: 25 - }, - sourceFlag: { - type: "int32", - id: 26 - }, - gameNick: { - type: "bytes", - id: 27 - }, - gameMsg: { - type: "bytes", - id: 28 - }, - groupFlagext3: { - type: "int32", - id: 29 - }, - groupOwnerUin: { - type: "int64", - id: 30 - }, - doubtFlag: { - type: "int32", - id: 31 - }, - warningTips: { - type: "bytes", - id: 32 - }, - nameMore: { - type: "bytes", - id: 33 - }, - reqUinFaceid: { - type: "int32", - id: 50 - }, - reqUinNick: { - type: "string", - id: 51 - }, - groupName: { - type: "string", - id: 52 - }, - actionUinNick: { - type: "string", - id: 53 - }, - msgQna: { - type: "string", - id: 54 - }, - msgDetail: { - type: "string", - id: 55 - }, - groupExtFlag: { - type: "int32", - id: 57 - }, - actorUinNick: { - type: "string", - id: 58 - }, - picUrl: { - type: "string", - id: 59 - }, - cloneUinNick: { - type: "string", - id: 60 - }, - reqUinBusinessCard: { - type: "string", - id: 61 - }, - eimGroupIdName: { - type: "string", - id: 63 - }, - reqUinPreRemark: { - type: "string", - id: 64 - }, - actionUinQqNick: { - type: "string", - id: 65 - }, - actionUinRemark: { - type: "string", - id: 66 - }, - reqUinGender: { - type: "int32", - id: 67 - }, - reqUinAge: { - type: "int32", - id: 68 - }, - c2cInviteJoinGroupFlag: { - type: "int32", - id: 69 - }, - cardSwitch: { - type: "int32", - id: 101 - } - } - }, - SystemMsgAction: { - fields: { - name: { - type: "string", - id: 1 - }, - result: { - type: "string", - id: 2 - }, - action: { - type: "int32", - id: 3 - }, - actionInfo: { - type: "SystemMsgActionInfo", - id: 4 - }, - detailName: { - type: "string", - id: 5 - } - } - }, - SystemMsgActionInfo: { - fields: { - type: { - type: "int32", - id: 1 - }, - groupCode: { - type: "int64", - id: 2 - }, - sig: { - type: "bytes", - id: 3 - }, - msg: { - type: "string", - id: 50 - }, - groupId: { - type: "int32", - id: 51 - }, - remark: { - type: "string", - id: 52 - }, - blacklist: { - type: "bool", - id: 53 - }, - addFrdSNInfo: { - type: "AddFrdSNInfo", - id: 54 - } - } - }, - PbSendMsgResp: { - fields: { - result: { - type: "int32", - id: 1 - }, - errmsg: { - type: "string", - id: 2 - }, - sendTime: { - type: "int32", - id: 3 - }, - svrbusyWaitTime: { - type: "int32", - id: 4 - }, - msgSendInfo: { - type: "MsgSendInfo", - id: 5 - }, - errtype: { - type: "int32", - id: 6 - }, - transSvrInfo: { - type: "TransSvrInfo", - id: 7 - }, - textAnalysisResult: { - type: "int32", - id: 9 - } - } - }, - MsgSendInfo: { - fields: { - receiver: { - type: "int32", - id: 1 - } - } - }, - TransSvrInfo: { - fields: { - subType: { - type: "int32", - id: 1 - }, - int32RetCode: { - type: "int32", - id: 2 - }, - errMsg: { - type: "bytes", - id: 3 - }, - transInfo: { - type: "bytes", - id: 4 - }, - } - }, - PbDeleteMsgResp: { - fields: { - result: { - type: "int32", - id: 1 - }, - errmsg: { - type: "string", - id: 2 - } - } - }, - MarketFace: { - fields: { - faceName: { - type: "bytes", - id: 1 - }, - itemType: { - type: "int32", - id: 2 - }, - faceInfo: { - type: "int32", - id: 3 - }, - faceId: { - type: "bytes", - id: 4 - }, - tabId: { - type: "int32", - id: 5 - }, - subType: { - type: "int32", - id: 6 - }, - key: { - type: "bytes", - id: 7 - }, - param: { - type: "bytes", - id: 8 - }, - mediaType: { - type: "int32", - id: 9 - }, - imageWidth: { - type: "int32", - id: 10 - }, - imageHeight: { - type: "int32", - id: 11 - }, - mobileparam: { - type: "bytes", - id: 12 - }, - pbReserve: { - type: "bytes", - id: 13 - } - } - }, - FunFace: { - fields: { - msgTurntable: { - type: "bytes", - id: 1 - }, - msgBomb: { - type: "bytes", - id: 2 - } - } - }, - EIMInfo: { - fields: { - rootId: { - type: "int64", - id: 1 - }, - flag: { - type: "string", - id: 2 - } - } - }, - CommonElem: { - fields: { - serviceType: { - type: "int32", - id: 1 - }, - pbElem: { - type: "bytes", - id: 2 - }, - businessType: { - type: "int32", - id: 3 - } - } - }, - MsgElemInfoServtype3: { - fields: { - customFace: { - type: "CustomFace", - id: 1 - }, - notOnlineImage: { - type: "NotOnlineImage", - id: 2 - }, - } - }, - MsgElemInfoServtype33: { - fields: { - id: { - type: "int32", - id: 1 - }, - text1: { - type: "string", - id: 2 - }, - text2: { - type: "string", - id: 3 - }, - } - }, - AddFrdFromGrp: { - fields: { - groupCode: { - type: "int64", - id: 1 - } - } - }, - SignAuthReqPkg: { - fields: { - prefix: { - type: "int32", - id: 1 - }, - timestamp: { - type: "int64", - id: 2 - }, - head: { - type: "SignAuthReqHead", - id: 3 - }, - body: { - type: "SignAuthReqBody", - id: 5 - }, - suffix: { - type: "int32", - id: 6 - } - } - }, - SignAuthReqHead: { - fields: { - command: { - type: "int32", - id: 1 - }, - unknown: { - type: "SignAuthUnknown", - id: 2 - }, - ver: { - type: "string", - id: 3 - } - } - }, - SignAuthUnknown: { - fields: { - num: { - type: "int32", - id: 6 - }, - } - }, - SignAuthReqBody: { - fields: { - uin: { - type: "int64", - id: 1 - }, - prefix: { - type: "int32", - id: 2 - }, - length: { - type: "int32", - id: 3 - }, - content: { - type: "bytes", - id: 4 - }, - suffix: { - type: "int32", - id: 5 - } - } - }, - SignAuthRspPkg: { - fields: { - result: { - type: "int32", - id: 1 - }, - msg: { - type: "string", - id: 2 - }, - } - }, - GetCardReqPkg: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - uin: { - type: "int64", - id: 2 - }, - flag1: { - type: "int32", - id: 3 - }, - flag2: { - type: "int32", - id: 4 - }, - flag3: { - type: "int32", - id: 5 - } - } - }, - GetCardRspPkg: { - fields: { - groupCode: { - type: "int64", - id: 1 - }, - body: { - type: "GetCardRspBody", - id: 3 - } - } - }, - GetCardRspBody: { - fields: { - uin: { - type: "int64", - id: 1 - }, - card: { - type: "string", - id: 8 - }, - sex: { - type: "int32", - id: 9 - }, - area: { - type: "string", - id: 10 - }, - nickname: { - type: "string", - id: 11 - }, - age: { - type: "int32", - id: 12 - }, - rank: { - type: "string", - id: 13 - }, - joinTime: { - type: "int64", - id: 14 - }, - lastSentTime: { - type: "int64", - id: 15 - }, - role: { - type: "int32", - id: 27 - }, - title: { - type: "string", - id: 31 - }, - titleExpireTime: { - type: "int32", - id: 32 - }, - level: { - type: "int32", - id: 39 - }, - } - }, - TryUpC2CPtt: { - fields: { - subcmd: { - type: "int32", - id: 1 - }, - result: { - type: "int32", - id: 2 - }, - msgTryUpPtt: { - rule: "repeated", - type: "RryUpC2CPtt", - id: 7 - }, - a806: { - type: "int32", - id: 101 - }, - b006: { - type: "int32", - id: 102 - }, - sub99999: { - type: "RryUpC2CPtt99999", - id: 99999 - } - } - }, - RryUpC2CPtt: { - fields: { - srcUin: { - type: "int64", - id: 10 - }, - toUin: { - type: "int64", - id: 20 - }, - type: { - type: "int32", - id: 30 - }, - voiceLength: { - type: "int32", - id: 40 - }, - fileName: { - type: "string", - id: 50 - }, - md5: { - type: "bytes", - id: 60 - }, - uint32UpPort: { - rule: "repeated", - type: "int32", - id: 80 - }, - fileKey: { - type: "bytes", - id: 90 - }, - upUkey: { - type: "bytes", - id: 100 - }, - uint32UpIp: { - rule: "repeated", - type: "string", - id: 130 - }, - } - }, - RryUpC2CPtt99999: { - fields: { - k1: { - type: "int32", - id: 1 - }, - k2: { - type: "int32", - id: 2 - }, - k300: { - type: "int32", - id: 90300 - }, - k500: { - type: "int32", - id: 90500 - }, - k600: { - type: "int32", - id: 90600 - }, - k800: { - type: "int32", - id: 90800 - } - } - }, - IndividualPortraitReqPkg: { - fields: { - body: { - type: "IndividualPortraitReqBody", - id: 1281 - }, - } - }, - IndividualPortraitReqBody: { - fields: { - uin: { - type: "int64", - id: 1 - }, - reqChannelType: { - type: "int32", - id: 2 - }, - subcmd: { - type: "int32", - id: 3 - }, - buType: { - type: "int32", - id: 4 - }, - netType: { - type: "int32", - id: 6 - }, - termType: { - type: "int32", - id: 7 - }, - } - }, - IndividualPortraitRspPkg: { - fields: { - body: { - type: "IndividualPortraitRspBody", - id: 1281 - }, - } - }, - IndividualPortraitRspBody: { - fields: { - upUkey: { - type: "bytes", - id: 1 - }, - teaUkey: { - type: "bytes", - id: 2 - }, - entry: { - type: "IndividualPortraitRspEntry", - id: 3 - }, - } - }, - IndividualPortraitRspEntry: { - fields: { - cmd: { - type: "int32", - id: 1 - }, - ipport: { - rule: "repeated", - type: "IndividualPortraitRspIpPort", - id: 2 - }, - } - }, - IndividualPortraitRspIpPort: { - fields: { - uint32UpIp: { - type: "fixed32", - id: 2 - }, - uint32UpPort: { - type: "int32", - id: 3 - }, - } - } -}); +} /** - * @param {String} name - * @param {Object} object - * @returns {Buffer} + * @param {import("protobufjs").Writer} writer */ -function encode(name, object) { - const pb = $root.lookupType(name); - return pb.encode(pb.create(object)).finish(); +function _encode(writer, tag, value) { + if (value === null || value === undefined) + return; + let type = 2; + if (value instanceof Nested) + value = value.raw; + if (typeof value === "object" && !isBuffer(value)) + value = encode(value); + if (typeof value === "bigint") { + const tmp = new pb.util.Long(); + tmp.unsigned = false; + tmp.low = parseInt(value & 0xffffffffn); + tmp.high = parseInt((value & 0xffffffff00000000n) >> 32n); + value = tmp; + type = 0; + } + if (typeof value === "number") + type = Number.isInteger(value) ? 0 : 1; + const head = parseInt(tag) << 3 | type; + writer.uint32(head); + switch (type) { + case 0: + if (value < 0) + writer.sint64(value); + else + writer.int64(value); + break; + case 1: + writer.double(value); + break; + case 2: + writer.bytes(isBuffer(value) ? value : Buffer.from(value)); + break; + } +} + +function encode(o) { + const writer = new pb.Writer(); + for (let tag in o) { + const value = o[tag]; + if (Array.isArray(value)) { + for (let v of value) + _encode(writer, tag, v); + } else { + _encode(writer, tag, value); + } + } + return writer.finish(); +} + +function long2int(long) { + const bigint = (BigInt(long.high)<<32n)|(BigInt(long.low)&0xffffffffn); + const int = parseInt(long); + return Number.isSafeInteger(int) ? int : bigint; +} + +function decode(buf) { + const data = {}; + const reader = new pb.Reader(buf); + while (reader.pos < reader.len) { + const k = reader.uint32(); + const tag = k >> 3, type = k & 0b111; + let value; + switch (type) { + case 0: + value = long2int(reader.int64()); + break; + case 1: + value = long2int(reader.fixed64()); + break; + case 2: + value = reader.bytes(); + let decoded; + try { + decoded = decode(value); + } catch {} + value = new Nested(value, decoded); + break; + case 5: + value = reader.fixed32(); + break; + } + if (Array.isArray(data[tag])) { + data[tag].push(value); + } else if (Reflect.has(data, tag)) { + data[tag] = [data[tag]]; + data[tag].push(value); + } else { + data[tag] = value; + } + } + return data; } /** - * @param {String} name - * @param {Buffer} blob - * @returns {Object} + * @param {String} cmd example: OidbSvc.0x568_22 + * @param {Buffer|object} body */ -function decode(name, blob) { - const pb = $root.lookupType(name); - return pb.toObject(pb.decode(blob)); +function encodeOIDB(cmd, body) { + cmd = cmd.replace("OidbSvc.", "").replace("oidb_", "").split("_"); + const type = parseInt(cmd[1]); + return encode({ + 1: parseInt(cmd[0], 16), + 2: isNaN(type) ? 1 : type, + 3: 0, + 4: body, + 6: "android " + this.apkver.substr(0, 5), + }); } module.exports = { - encode, decode + encode, decode, + encodeOIDB }; diff --git a/lib/ref.d.ts b/lib/ref.d.ts index 8c6ca68f..be5d85b9 100644 --- a/lib/ref.d.ts +++ b/lib/ref.d.ts @@ -106,6 +106,7 @@ export class Client extends oicq.Client { seq_id: number; handlers: Map void>; seq_cache: Map; + notify33cache: Set; session_id: Buffer; random_key: Buffer; @@ -123,6 +124,7 @@ export class Client extends oicq.Client { dir: string; sig: Sig; cookies: object; + msg_times: number[]; nextSeq(): number; send(): Promise; diff --git a/lib/resource.js b/lib/resource.js index a603a1c3..19496865 100644 --- a/lib/resource.js +++ b/lib/resource.js @@ -2,7 +2,6 @@ const common = require("./common"); const pb = require("./pb"); const jce = require("./jce"); -const toInt = common.toInt; const friend_sex_map = { "0":"unknown", "1":"male", "2":"female" @@ -15,19 +14,19 @@ const group_role_map = { }; /** - * 好友列表 + * 加载好友列表(不对外开放) * @this {import("./ref").Client} * @param {Number} start * @returns {Number} 好友总数 */ async function initFL(start) { - const d50 = pb.encode("D50ReqBody", { - appid: 10002, - reqMusicSwitch: 1, - reqMutualmarkAlienation: 1, - reqKsingSwitch: 1, - reqMutualmarkLbsshare: 1, - reqC8C77A: 1, + const d50 = pb.encode({ + 1: 10002, + 91001: 1, + 101001: 1, + 151001: 1, + 181001: 1, + 251001: 1, }); const FL = jce.encodeStruct([ 3, @@ -35,7 +34,7 @@ async function initFL(start) { 31, null, 0, 0, 0, d50, null, [13580, 13581, 13582] ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "GetFriendListReq", }; @@ -63,7 +62,7 @@ async function initFL(start) { } /** - * 群列表 + * 加载群列表(不对外开放) * @this {import("./ref").Client} */ async function initGL() { @@ -71,7 +70,7 @@ async function initGL() { this.uin, 0, null, [], 1, 8, 0, 1, 1 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "GetTroopListReqV2Simplify", }; @@ -105,7 +104,7 @@ async function initGL() { } /** - * 群员列表 + * 加载群员列表(间接对外开放) * @this {import("./ref").Client} */ async function getGML(group_id, next_uin) { @@ -113,7 +112,7 @@ async function getGML(group_id, next_uin) { this.uin, group_id, next_uin, common.code2uin(group_id), 2, 0, 0, 0 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "GetTroopMemberListReq", }; @@ -153,7 +152,7 @@ async function getGML(group_id, next_uin) { } /** - * 群信息 + * 群资料 * @this {import("./ref").Client} * @returns {import("./ref").ProtocolResponse} */ @@ -164,38 +163,32 @@ async function getGI(group_id, no_cache = false) { if (!no_cache && ginfo && common.timestamp() - ginfo.update_time <= 3600) return {result: 0, data: ginfo}; - const list = [{ - groupCode: group_id, - groupInfo: { - groupOwner: 0, - groupCreateTime: 0, - groupMemberMaxNum: 0, - groupMemberNum: 0, - groupName: "", - groupAdminMaxNum: 0, - groupGrade: 0, - activeMemberNum: 0, - shutupTimestamp: 0, - shutupTimestampMe: 0, - cmduinJoint32ime: 0, - cmduinLastMsgTime: 0, - longGroupName: "", - }, - }]; - this.nextSeq(); - const body = pb.encode("OIDBSSOPkg", { - command: 2189, - serviceType: 0, - bodybuffer: pb.encode("D88DReqBody", { - appid: 200000020, - groupList: list, - }) + const body = pb.encode({ + 1: this.sub_appid, + 2: [{ + 1: group_id, + 2: { + 1: 0, + 2: 0, + 5: 0, + 6: 0, + 15: "", + 29: 0, + 36: 0, + 37: 0, + 45: 0, + 46: 0, + 49: 0, + 54: 0, + 89: "", + }, + }], }); try { this.gl.get(group_id).update_time = common.timestamp(); } catch (e) {} const blob = await this.sendUNI("OidbSvc.0x88d_0", body); - const o = pb.decode("D88DRspBody", pb.decode("OIDBSSOPkg", blob).bodybuffer).groupList[0].groupInfo; + const o = pb.decode(blob)[4][1][3]; if (!o) { this.gl.delete(group_id); this.gml.delete(group_id); @@ -203,18 +196,18 @@ async function getGI(group_id, no_cache = false) { } ginfo = { group_id: group_id, - group_name: o.longGroupName ? o.longGroupName : o.groupName, - member_count: o.groupMemberNum, - max_member_count: o.groupMemberMaxNum, - owner_id: toInt(o.groupOwner), - last_join_time: o.cmduinJoint32ime, - last_sent_time: o.cmduinLastMsgTime, - shutup_time_whole: o.shutupTimestamp?0xffffffff:0, - shutup_time_me: o.shutupTimestampMe*1000>Date.now()?o.shutupTimestampMe:0, - create_time: o.groupCreateTime, - grade: o.groupGrade, - max_admin_count: o.groupAdminMaxNum, - active_member_count:o.activeMemberNum, + group_name: o[89] ? String(o[89].raw) : String(o[15].raw), + member_count: o[6], + max_member_count: o[5], + owner_id: o[1], + last_join_time: o[49], + last_sent_time: o[54], + shutup_time_whole: o[45] ? 0xffffffff : 0, + shutup_time_me: o[46]*1000 > Date.now() ? o[46] : 0, + create_time: o[2], + grade: o[36], + max_admin_count: o[29], + active_member_count:o[37], update_time: common.timestamp(), }; this.gl.set(group_id, ginfo); @@ -237,39 +230,39 @@ async function getGMI(group_id, user_id, no_cache = false) { if (!no_cache && minfo && common.timestamp() - minfo.update_time <= 3600) return {result: 0, data: minfo}; - this.nextSeq(); - const body = pb.encode("GetCardReqPkg", { - groupCode: group_id, - uin: user_id, - flag1: 1, - flag2: 1, - flag3: 1, + const body = pb.encode({ + 1: group_id, + 2: user_id, + 3: 1, + 4: 1, + 5: 1, }); try { this.gml.get(group_id).get(user_id).update_time = common.timestamp(); } catch (e) {} const blob = await this.sendUNI("group_member_card.get_group_member_card_info", body); - const o = pb.decode("GetCardRspPkg", blob).body; - if (!o.role) return {result: 1}; - if (o.sex === undefined) o.sex = -1; - if (o.card && o.card.startsWith("\n")) - o.card = o.card.split("\n").pop().substr(3); + const o = pb.decode(blob)[3]; + if (!o[27]) return {result: 1}; + if (o[9] === undefined) o[9] = -1; + const card = String(o[8].raw); + if (card && card.startsWith("\n")) + card = card.split("\n").pop().substr(3); minfo = { group_id: group_id, user_id: user_id, - nickname: o.nickname, - card: o.card, - sex: Reflect.has(o, "sex")?group_sex_map[o.sex]:"unknown", - age: o.age, - area: Reflect.has(o, "area")?o.area:"unknown", - join_time: toInt(o.joinTime), - last_sent_time: toInt(o.lastSentTime), - level: o.level, - rank: o.rank, - role: group_role_map[o.role], + nickname: String(o[11].raw), + card: card, + sex: Reflect.has(o, "9") ? group_sex_map[o[9]&0xffffffff] : "unknown", + age: o[12], + area: Reflect.has(o, "10") ? String(o[10].raw) : "unknown", + join_time: o[14], + last_sent_time: o[15], + level: o[39], + rank: Reflect.has(o, "13") ? String(o[13].raw) : undefined, + role: group_role_map[o[27]], unfriendly: false, - title: Reflect.has(o, "title")?o.title:"", - title_expire_time: Reflect.has(o, "titleExpireTime")?o.titleExpireTime:-1, + title: Reflect.has(o, "31") ? String(o[31].raw) : "", + title_expire_time: Reflect.has(o, "32") ? o[32] : 0xffffffff, card_changeable: true, shutup_time: 0, update_time: common.timestamp(), @@ -301,7 +294,7 @@ async function getSI(user_id, no_cache = false) { arr[101] = 1; const req = jce.encodeStruct(arr); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "KQQ.ProfileService.ProfileServantObj", method: "GetSimpleInfo", }; diff --git a/lib/service.js b/lib/service.js index 24b28cc2..242f6fde 100644 --- a/lib/service.js +++ b/lib/service.js @@ -11,6 +11,7 @@ const MAX_UPLOAD_SIZE = 31457280; function int32ip2str(ip) { if (typeof ip === "string") return ip; + ip = ip & 0xffffffff; return [ ip & 0xff, (ip & 0xff00 ) >> 8, @@ -31,24 +32,24 @@ function buildHighwayUploadRequestPackets(o, cmd, seq = crypto.randomBytes(2).re while (1) { chunk = o.buf.slice(offset, offset + limit); if (!chunk.length) break; - const head = pb.encode("ReqDataHighwayHead", { - msgBasehead: { - version: 1, - uin: String(this.uin), - command: "PicUp.DataUp", - seq: seq++, - appid: this.sub_appid, - dataflag: 4096, - commandId: cmd, - localeId: 2052, + const head = pb.encode({ + 1: { + 1: 1, + 2: String(this.uin), + 3: "PicUp.DataUp", + 4: seq++, + 6: this.sub_appid, + 7: 4096, + 8: cmd, + 10:2052, }, - msgSeghead: { - filesize: size, - dataoffset: offset, - datalength: chunk.length, - serviceticket: o.key, - md5: common.md5(chunk), - fileMd5: o.md5, + 2: { + 2: size, + 3: offset, + 4: chunk.length, + 6: o.key, + 8: common.md5(chunk), + 9: o.md5, } }); offset += limit; @@ -68,6 +69,7 @@ function buildHighwayUploadRequestPackets(o, cmd, seq = crypto.randomBytes(2).re * @param {Number} port * @param {import("./ref").HighwayUploadObject} o * @param {Number} cmd + * @returns {Promise} */ async function highwayUpload(ip, port, o, cmd) { ip = int32ip2str(ip); diff --git a/lib/sysmsg.js b/lib/sysmsg.js index 107067a4..c3da1220 100644 --- a/lib/sysmsg.js +++ b/lib/sysmsg.js @@ -1,28 +1,44 @@ "use strict"; -const {toInt} = require("./common"); const pb = require("./pb"); +/** + * @param {Number} user_id + * @param {BigInt|Number} seq + */ function genFriendRequestFlag(user_id, seq) { - const buf = Buffer.allocUnsafe(12); - buf.writeUInt32BE(user_id), buf.writeInt32BE(seq.low, 4), buf.writeInt32BE(seq.high, 8); - return buf.toString("base64"); + return user_id.toString(16).padStart(8, "0") + seq.toString(16); } + +/** + * @param {String} flag + */ function parseFriendRequestFlag(flag) { - const buf = Buffer.from(flag, "base64"); - const user_id = buf.readUInt32BE(), low = buf.readInt32BE(4), high = buf.readInt32BE(8); - return {user_id, low, high}; + const user_id = parseInt(flag.slice(0, 8), 16); + const seq = BigInt("0x" + flag.slice(8)); + return {user_id, seq}; } + +/** + * @param {Number} user_id + * @param {Number} group_id + * @param {BigInt|Number} seq + * @param {0|1} invite + */ function genGroupRequestFlag(user_id, group_id, seq, invite) { - const buf = Buffer.allocUnsafe(17); + const buf = Buffer.allocUnsafe(8); buf.writeUInt32BE(user_id), buf.writeUInt32BE(group_id, 4); - buf.writeInt32BE(seq.low, 8), buf.writeInt32BE(seq.high, 12), buf.writeInt8(invite, 16); - return buf.toString("base64"); + return buf.toString("hex") + invite + seq.toString(16); } + +/** + * @param {String} flag + */ function parseGroupRequestFlag(flag) { - const buf = Buffer.from(flag, "base64"); - const user_id = buf.readUInt32BE(), group_id = buf.readUInt32BE(4); - const low = buf.readInt32BE(8), high = buf.readInt32BE(12); - return {user_id, group_id, low, high, invite: buf[16]}; + const user_id = parseInt(flag.slice(0, 8), 16); + const group_id = parseInt(flag.slice(8, 16), 16); + const invite = parseInt(flag.slice(16, 17)); + const seq = BigInt("0x" + flag.slice(17)); + return {user_id, group_id, seq, invite}; } /** @@ -30,38 +46,37 @@ function parseGroupRequestFlag(flag) { * @this {import("./ref").Client} */ async function getNewFriend() { - this.nextSeq(); - const body = pb.encode("ReqSystemMsgNew", { - msgNum: 10, - version: 1000, - checktype: 2, - flag: { - frdMsgDiscuss2ManyChat: 1, - frdMsgGetBusiCard: 1, - frdMsgNeedWaitingMsg: 1, - frdMsgUint32NeedAllUnreadMsg: 1, - grpMsgMaskInviteAutoJoin: 1, + const body = pb.encode({ + 1: 10, + 4: 1000, + 5: 2, + 6: { + 4: 1, + 7: 1, + 9: 1, + 10: 1, + 15: 1, }, - language: 0, - isGetFrdRibbon: 0, - isGetGrpRibbon: 0, - friendMsgTypeFlag: 1, + 7: 0, + 8: 0, + 9: 0, + 10: 1, }); try { const blob = await this.sendUNI("ProfileService.Pb.ReqSystemMsgNew.Friend", body); - const o = pb.decode("RspSystemMsgNew", blob); - const v = o.friendmsgs[0]; - const time = toInt(v.msgTime); - const user_id = toInt(v.reqUin); - const flag = genFriendRequestFlag(user_id, v.msgSeq); - this.logger.info(`收到 ${user_id}(${v.msg.reqUinNick}) 的加好友请求 (flag: ${flag})`); + const rsp = pb.decode(blob)[9]; + const v = Array.isArray(rsp) ? rsp[0] : rsp; + const time = v[4]; + const user_id = v[5]; + const nickname = String(v[50][51].raw); + const flag = genFriendRequestFlag(user_id, v[3]); + this.logger.info(`收到 ${user_id}(${nickname}) 的加好友请求 (flag: ${flag})`); this.em("request.friend.add", { - user_id, - nickname: v.msg.reqUinNick, - source: v.msg.msgSource, - comment: v.msg.msgAdditional, - sex: v.msg.reqUinGender===0?"male":(v.msg.reqUinGender===1?"famale":"unknown"), - age: v.msg.reqUinAge, + user_id, nickname, + source: String(v[50][5].raw), + comment: String(v[50][4].raw), + sex: v[50][67]===0?"male":(v[50][67]===1?"famale":"unknown"), + age: v[50][68], flag, time }); } catch (e) { @@ -73,68 +88,75 @@ async function getNewFriend() { /** * 获取群请求 * @this {import("./ref").Client} + * @param {Number} type 1申请 2邀请 22申请(来自群员的邀请) */ -async function getNewGroup() { - this.nextSeq(); - const body = pb.encode("ReqSystemMsgNew", { - msgNum: 10, - version: 1000, - checktype: 3, - flag: { - grpMsgKickAdmin: 1, - grpMsgHiddenGrp: 1, - grpMsgWordingDown: 1, - grpMsgGetOfficialAccount: 1, - grpMsgGetPayInGroup: 1, - frdMsgDiscuss2ManyChat: 1, - grpMsgNotAllowJoinGrpInviteNotFrd: 1, - frdMsgNeedWaitingMsg: 1, - frdMsgUint32NeedAllUnreadMsg: 1, - grpMsgNeedAutoAdminWording: 1, - grpMsgGetTransferGroupMsgFlag: 1, - grpMsgGetQuitPayGroupMsgFlag: 1, - grpMsgSupportInviteAutoJoin: 1, - grpMsgMaskInviteAutoJoin: 1, - grpMsgGetDisbandedByAdmin: 1, - grpMsgGetC2CInviteJoinGroup: 1, +async function getNewGroup(type) { + const body = pb.encode({ + 1: 10, + 4: 1000, + 5: 3, + 6: { + 1: 1, + 2: 1, + 3: 1, + 5: 1, + 6: 1, + 7: 1, + 8: 1, + 9: 1, + 10: 1, + 11: 1, + 12: 1, + 13: 1, + 14: 1, + 15: 1, + 16: 1, + 17: 1, }, - language: 0, - isGetFrdRibbon: 0, - isGetGrpRibbon: 0, - friendMsgTypeFlag: 1, + 7: 0, + 8: 0, + 9: 0, + 10: 1, }); try { const blob = await this.sendUNI("ProfileService.Pb.ReqSystemMsgNew.Group", body); - const o = pb.decode("RspSystemMsgNew", blob); - let v = o.groupmsgs.shift(); - for (let vv of o.groupmsgs) { - if (v.msg.subType !== 1) - v = vv; - else if (vv.msg.subType === 1 && toInt(vv.msgTime) > toInt(v.msgTime)) - v = vv; + const rsp = pb.decode(blob)[10]; + let v; + if (!Array.isArray(rsp)) + v = rsp; + else { + for (let vv of rsp) { + if (vv[50][1] !== 1 || vv[50][12] !== type) + continue; + if (!v || vv[4] > v[4]) + v = vv; + } } - const time = toInt(v.msgTime); - const group_id = toInt(v.msg.groupCode); - if (v.msg.groupMsgType === 1) { - const user_id = toInt(v.reqUin); - const flag = genGroupRequestFlag(user_id, group_id, v.msgSeq); - this.logger.info(`用户 ${user_id}(${v.msg.reqUinNick}) 请求加入群 ${group_id}(${v.msg.groupName}) (flag: ${flag})`); + if (!v) return; + const time = v[4]; + const group_id = v[50][10]; + if (Date.now() - time * 1000 > 5000) return; + if (type === 1 || type === 22) { + const user_id = v[5]; + const nickname = String(v[50][51].raw); + const group_name = String(v[50][52].raw); + const flag = genGroupRequestFlag(user_id, group_id, v[3], 0); + this.logger.info(`用户 ${user_id}(${nickname}) 请求加入群 ${group_id}(${group_name}) (flag: ${flag})`); this.em("request.group.add", { - group_id, user_id, - group_name: v.msg.groupName, - nickname: v.msg.reqUinNick, - comment: v.msg.msgAdditional, + group_id, user_id, group_name, nickname, + comment: String(v[50][4].raw), + inviter_id: type === 22 ? v[50][11] : undefined, flag, time }); - } else if (v.msg.groupMsgType === 2) { - const user_id = toInt(v.msg.actionUin); - const flag = genGroupRequestFlag(user_id, group_id, v.msgSeq, 1); - this.logger.info(`用户 ${user_id}(${v.msg.actionUinNick}) 邀请你加入群 ${group_id}(${v.msg.groupName}) (flag: ${flag})`); + } else if (type === 2) { + const user_id = v[50][11]; + const nickname = String(v[50][53].raw); + const group_name = String(v[50][52].raw); + const flag = genGroupRequestFlag(user_id, group_id, v[3], 1); + this.logger.info(`用户 ${user_id}(${nickname}) 邀请你加入群 ${group_id}(${group_name}) (flag: ${flag})`); this.em("request.group.invite", { - group_id, user_id, - group_name: v.msg.groupName, - nickname: v.msg.actionUinNick, - role: v.msg.groupInviterRole === 1 ? "member" : "admin", + group_id, user_id, group_name, nickname, + role: v[50][13] === 1 ? "member" : "admin", flag, time }); } @@ -150,22 +172,23 @@ async function getNewGroup() { * @returns {import("./ref").ProtocolResponse} */ async function friendAction(flag, approve = true, remark = "", block = false) { - const {user_id, low, high} = parseFriendRequestFlag(flag); - const body = pb.encode("ReqSystemMsgAction", { - msgType: 1, - msgSeq: {low, high, unsigned: false}, - reqUin: user_id, - subType: 1, - srcId: 6, - subSrcId: 7, - actionInfo: { - type: approve?2:3, - blacklist: block?true:false + const {user_id, seq} = parseFriendRequestFlag(flag); + const body = pb.encode({ + 1: 1, + 2: seq, + 3: user_id, + 4: 1, + 5: 6, + 6: 7, + 8: { + 1: approve?2:3, + 52: String(remark), + 53: block?1:0 }, }); const blob = await this.sendUNI("ProfileService.Pb.ReqSystemMsgAction.Friend", body); - const o = pb.decode("RspSystemMsgAction", blob); - return {result: o.head.result, emsg: o.head.msgFail}; + const rsp = pb.decode(blob)[1]; + return {result: rsp[1], emsg: rsp[2]}; } /** @@ -174,25 +197,25 @@ async function friendAction(flag, approve = true, remark = "", block = false) { * @returns {import("./ref").ProtocolResponse} */ async function groupAction(flag, approve = true, reason = "", block = false) { - const {user_id, group_id, low, high, invite} = parseGroupRequestFlag(flag); - const body = pb.encode("ReqSystemMsgAction", { - msgType: 1, - msgSeq: {low, high, unsigned: false}, - reqUin: user_id, - subType: 1, - srcId: 3, - subSrcId: invite?10016:31, - groupMsgType: invite?2:1, - actionInfo: { - type: approve?11:12, - groupCode: group_id, - blacklist: block?true:false, - msg: String(reason), + const {user_id, group_id, seq, invite} = parseGroupRequestFlag(flag); + const body = pb.encode({ + 1: 1, + 2: seq, + 3: user_id, + 4: 1, + 5: 3, + 6: invite?10016:31, + 7: invite?2:1, + 8: { + 1: approve?11:12, + 2: group_id, + 50: String(reason), + 53: block?1:0, }, }); const blob = await this.sendUNI("ProfileService.Pb.ReqSystemMsgAction.Group", body); - const o = pb.decode("RspSystemMsgAction", blob); - return {result: o.head.result, emsg: o.head.msgFail}; + const rsp = pb.decode(blob)[1]; + return {result: rsp[1], emsg: rsp[2]}; } module.exports = { diff --git a/lib/troop.js b/lib/troop.js index 805b0181..2bb24ff4 100644 --- a/lib/troop.js +++ b/lib/troop.js @@ -3,18 +3,19 @@ const {uinAutoCheck} = require("./common"); const pb = require("./pb"); const jce = require("./jce"); +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {Boolean} enable + * @returns {import("./ref").ProtocolResponse} + */ async function setAdmin(group_id, user_id, enable = true) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); - this.nextSeq(); const buf = Buffer.allocUnsafe(9); buf.writeUInt32BE(group_id), buf.writeUInt32BE(user_id, 4), buf.writeUInt8(enable?1:0, 8); - const body = pb.encode("OIDBSSOPkg", { - command: 1372, - serviceType: 1, - bodybuffer: buf, - }); - const blob = await this.sendUNI("OidbSvc.0x55c_1", body); - const result = pb.decode("OIDBSSOPkg", blob).result; + const blob = await this.sendUNI("OidbSvc.0x55c_1", buf); + const result = pb.decode(blob)[3]; if (result === 0) { try { const old_role = this.gml.get(group_id).get(user_id).role; @@ -30,44 +31,66 @@ async function setAdmin(group_id, user_id, enable = true) { return {result}; } +/** + * 设置头衔 + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {String} title + * @param {Number} duration + * @returns {import("./ref").ProtocolResponse} + */ async function setTitle(group_id, user_id, title = "", duration = -1) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); duration = duration&0xffffffff; - this.nextSeq(); - title = Buffer.from(String(title)); + title = String(title); duration = parseInt(duration); - const body = pb.encode("OIDBSSOPkg", { - command: 2300, - serviceType: 2, - bodybuffer: pb.encode("D8FCReqBody", { - groupCode: group_id, - memLevelInfo: [{ - uin: user_id, - uinName: title, - specialTitle: title, - specialTitleExpireTime: duration?duration:-1 - }] - }), + const body = pb.encode({ + 1: group_id, + 3: [{ + 1: user_id, + 7: title, + 5: title, + 6: duration?duration:-1 + }] }); const blob = await this.sendUNI("OidbSvc.0x8fc_2", body); - return pb.decode("OIDBSSOPkg", blob); + return {result: pb.decode(blob)[3]}; } +/** + * 群设置 + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {String} k + * @param {any} v + * @returns {import("./ref").ProtocolResponse} + */ async function doSetting(group_id, k, v) { var [group_id] = uinAutoCheck(group_id); - this.nextSeq(); - const qwerty = { - groupCode: parseInt(group_id), - stGroupInfo: {}, + const settings = { + shutupTime: 17, + ingGroupName: 3, + ingGroupMemo: 4, + } + const tag = settings[k]; + if (!tag) + throw new Error("unknown setting key"); + const body = { + 1: group_id, + 2: {}, }; - qwerty.stGroupInfo[k] = v; - const body = pb.encode("OIDBSSOPkg", { - command: 2202, - bodybuffer: pb.encode("D89AReqBody", qwerty), - }); - await this.sendUNI("OidbSvc.0x89a_0", body); + body[2][tag] = v; + await this.sendUNI("OidbSvc.0x89a_0", pb.encode(body)); } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {String} card + * @returns {import("./ref").ProtocolResponse} + */ async function setCard(group_id, user_id, card = "") { var [group_id, user_id] = uinAutoCheck(group_id, user_id); const MGCREQ = jce.encodeStruct([ @@ -78,7 +101,7 @@ async function setCard(group_id, user_id, card = "") { ] ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "mqq.IMService.FriendListServiceServantObj", method: "ModifyGroupCardReq", }; @@ -90,23 +113,26 @@ async function setCard(group_id, user_id, card = "") { return {result}; } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {Boolean} block + * @returns {import("./ref").ProtocolResponse} + */ async function kickMember(group_id, user_id, block = false) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); - this.nextSeq(); - const body = pb.encode("OIDBSSOPkg", { - command: 2208, - bodybuffer: pb.encode("D8A0ReqBody", { - optUint64GroupCode: group_id, - msgKickList: [{ - optUint32Operate: 5, - optUint64MemberUin: user_id, - optUint32Flag: block?1:0, - }], - }) + const body = pb.encode({ + 1: group_id, + 2: [{ + 1: 5, + 2: user_id, + 3: block?1:0, + }], }); const blob = await this.sendUNI("OidbSvc.0x8a0_0", body); - const o = pb.decode("D8A0RspBody", pb.decode("OIDBSSOPkg", blob).bodybuffer); - const result = o.msgKickResult[0].optUint32Result; + const o = pb.decode(blob)[4]; + const result = o[2][1]; if (result === 0 && this.gml.has(group_id) && this.gml.get(group_id).delete(user_id)) { this.em("notice.group.decrease", { group_id, user_id, @@ -117,23 +143,30 @@ async function kickMember(group_id, user_id, block = false) { return {result}; } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @param {Number} duration + * @returns {import("./ref").ProtocolResponse} + */ async function muteMember(group_id, user_id, duration = 1800) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); duration = parseInt(duration); if (duration > 2592000 || duration < 0) duration = 2592000; - this.nextSeq(); const buf = Buffer.allocUnsafe(15); buf.writeUInt32BE(group_id), buf.writeUInt8(32, 4), buf.writeUInt16BE(1, 5); buf.writeUInt32BE(user_id, 7), buf.writeUInt32BE(duration?duration:0, 11); - const body = pb.encode("OIDBSSOPkg", { - command: 1392, - serviceType: 8, - bodybuffer: buf - }); - await this.sendUNI("OidbSvc.0x570_8", body); + await this.sendUNI("OidbSvc.0x570_8", buf); } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Boolean} dismiss + * @returns {import("./ref").ProtocolResponse} + */ async function quitGroup(group_id, dismiss = false) { var [group_id] = uinAutoCheck(group_id); let command, buf = Buffer.allocUnsafe(8); @@ -148,7 +181,7 @@ async function quitGroup(group_id, dismiss = false) { command, this.uin, buf ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "KQQ.ProfileService.ProfileServantObj", method: "GroupMngReq", }; @@ -159,20 +192,27 @@ async function quitGroup(group_id, dismiss = false) { return {result: parent[1]}; } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @returns {import("./ref").ProtocolResponse} + */ async function pokeMember(group_id, user_id) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); - this.nextSeq(); - const body = pb.encode("OIDBSSOPkg", { - command: 3795, - serviceType: 1, - bodybuffer: pb.encode("DED3ReqBody", { - toUin: user_id, - groupCode: group_id - }) + const body = pb.encode({ + 1: user_id, + 2: group_id }); await this.sendUNI("OidbSvc.0xed3", body); } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {String} comment + * @returns {import("./ref").ProtocolResponse} + */ async function addGroup(group_id, comment = "") { var [group_id] = uinAutoCheck(group_id); comment = Buffer.from(String(comment)).slice(0, 255); @@ -185,7 +225,7 @@ async function addGroup(group_id, comment = "") { null, "", null, "", "", 0 ]); const extra = { - req_id: this.nextSeq(), + req_id: this.seq_id + 1, service: "KQQ.ProfileService.ProfileServantObj", method: "GroupMngReq", }; @@ -196,37 +236,35 @@ async function addGroup(group_id, comment = "") { return {result: parent[1]}; } +/** + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Number} user_id + * @returns {import("./ref").ProtocolResponse} + */ async function inviteFriend(group_id, user_id) { var [group_id, user_id] = uinAutoCheck(group_id, user_id); - this.nextSeq(); - const body = pb.encode("OIDBSSOPkg", { - command: 1880, - serviceType: 1, - result: 0, - bodybuffer: pb.encode("D758ReqBody", { - groupCode: group_id, - toUin: { - uin: user_id - } - }), - clientVersion: "android " + this.apkver.substr(0, 5) + const body = pb.encode({ + 1: group_id, + 2: {1: user_id} }); const blob = await this.sendUNI("OidbSvc.oidb_0x758", body); - const result = pb.decode("OIDBSSOPkg", blob).bodybuffer.length > 6 ? 0 : 1; + const result = pb.decode(blob)[4].raw.length > 6 ? 0 : 1; return {result}; } +/** + * 启用/禁用 匿名 + * @this {import("./ref").Client} + * @param {Number} group_id + * @param {Boolean} enable + * @returns {import("./ref").ProtocolResponse} + */ async function setAnonymous(group_id, enable = true) { var [group_id] = uinAutoCheck(group_id); - this.nextSeq(); const buf = Buffer.allocUnsafe(5); buf.writeUInt32BE(group_id), buf.writeUInt8(enable?1:0, 4); - const body = pb.encode("OIDBSSOPkg", { - command: 1384, - serviceType: 22, - bodybuffer: buf, - }); - await this.sendUNI("OidbSvc.0x568_22", body); + await this.sendUNI("OidbSvc.0x568_22", buf); } module.exports = { diff --git a/lib/wtlogin/tlv.js b/lib/wtlogin/tlv.js index d5970c6a..57f8cf20 100644 --- a/lib/wtlogin/tlv.js +++ b/lib/wtlogin/tlv.js @@ -226,16 +226,16 @@ const tlv_map = { }, 0x52d: function() { const d = this.device; - const buf = pb.encode("DeviceInfo", { - bootloader: d.bootloader, - procVersion: d.proc_version, - codename: d.version.codename, - incremental: d.version.incremental, - fingerprint: d.fingerprint, - bootId: d.boot_id, - androidId: d.android_id, - baseBand: d.baseband, - innerVersion: d.version.incremental, + const buf = pb.encode({ + 1: d.bootloader, + 2: d.proc_version, + 3: d.version.codename, + 4: d.version.incremental, + 5: d.fingerprint, + 6: d.boot_id, + 7: d.android_id, + 8: d.baseband, + 9: d.version.incremental, }); return new Writer().writeBytes(buf); }, diff --git a/lib/wtlogin/wt.js b/lib/wtlogin/wt.js index 81864b60..7c6f9974 100644 --- a/lib/wtlogin/wt.js +++ b/lib/wtlogin/wt.js @@ -6,7 +6,9 @@ const Readable = require("stream").Readable; const ecdh = require("./ecdh"); const Writer = require("./writer"); const tlv = require("./tlv"); +const {timestamp} = require("../common"); const jce = require("../jce"); +const pb = require("../pb"); function encryptOICQBody(body) { return new Writer() @@ -27,6 +29,7 @@ function encryptEMPBody(body) { } /** + * @this {import("../ref").Client} * @param {Buffer} body * @param {Boolean} emp * @returns {Buffer} @@ -52,6 +55,7 @@ function buildOICQPacket(body, emp = false) { } /** + * @this {import("../ref").Client} * @param {String} cmd * @param {Buffer} body * @param {0|1|2} type @@ -89,13 +93,14 @@ function build0x0APacket(cmd, body, type) { } /** + * @this {import("../ref").Client} * @param {String} cmd * @param {Buffer} body * @param {Number} seq * @returns {Buffer} */ function build0x0BPacket(cmd, body, seq = 0) { - seq = seq ? seq : this.seq_id; + seq = seq ? seq : this.nextSeq(); this.logger.trace(`send:${cmd} seq:${seq}`); this.send_timestamp = Date.now(); const type = cmd==="wtlogin.exchange_emp"?2:1; @@ -104,6 +109,8 @@ function build0x0BPacket(cmd, body, seq = 0) { .writeWithLength(this.session_id) .writeU32(4) .read(); + if (cmd.startsWith("OidbSvc.")) + body = pb.encodeOIDB.call(this, cmd, body); sso = new Writer().writeWithLength(sso).writeWithLength(body).read(); body = new Writer() .writeU32(0x0B) @@ -281,7 +288,9 @@ async function exchangeEMP() { */ async function register(logout = false) { this.nextSeq(); - const pb_buf = Buffer.from([0x0A, 0x04, 0x08, 0x2E, 0x10, 0x00, 0x0A, 0x05, 0x08, 0x9B, 0x02, 0x10, 0x00]); + const pb_buf = pb.encode({ + 1: [{1:46, 2:timestamp()}, {1:283, 2:0}] + }); const SvcReqRegister = jce.encodeStruct([ this.uin, logout?0:7, 0, "", logout?21:11, 0, 0, 0, 0, 0, logout?44:0, diff --git a/package-lock.json b/package-lock.json index f6e5ca07..10a9ba16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "oicq", - "version": "1.10.0", + "version": "1.10.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -124,9 +124,9 @@ } }, "jce": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/jce/-/jce-0.1.6.tgz", - "integrity": "sha512-mHzHRfXE5NYcyIm5Nev7JsLV8KtD3VjJSO/UVe2TNQo+EHy1BvdvJUnNH8Y53u3HtxLonQw0PTKkyjj+CYOLww==" + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/jce/-/jce-0.1.7.tgz", + "integrity": "sha512-94lI/5YP9U8pr4AW7fMoicUGNE+aFh5Av0z1chmqicuTJ8WkeWPePrOzMjgb92MVX6gg71t0ixvxaoTO0Tow5g==" }, "jsonfile": { "version": "4.0.0", diff --git a/package.json b/package.json index 823d4ba3..d37031c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oicq", - "version": "1.10.0", + "version": "1.10.1", "description": "QQ protocol!", "main": "client.js", "scripts": { @@ -28,7 +28,7 @@ "dependencies": { "crypto-tea": "^0.2.0", "https-proxy-agent": "^5.0.0", - "jce": "^0.1.6", + "jce": "^0.1.7", "log4js": "^6.3.0", "protobufjs": "^6.10.1" } diff --git a/test.js b/test.js index 716dadfc..bef4cd24 100644 --- a/test.js +++ b/test.js @@ -54,13 +54,13 @@ function account() { }) bot.on("request", (data)=>{ - console.log(data); + console.log("收到request事件", data); }); bot.on("notice", (data)=>{ - console.log(data); + console.log("收到notice事件", data); }); bot.on("message", (data)=>{ - // console.log(data.message_id); + // console.log("收到message事件", data); }); } catch (e) { console.log(e.message); @@ -78,7 +78,8 @@ function password() { }) } function loop() { - const help = `※发言: send target msg + const help = `※友情提示:将log_level设为trace可获得详细的收发包信息。 +※发言: send target msg ※退出: bye ※执行任意代码: eval code`; console.log(help); @@ -99,14 +100,14 @@ function loop() { res = await bot.sendGroupMsg(target, abc[1]); else res = await bot.sendPrivateMsg(target, abc[1]); - console.log(res); + console.log("发送消息结果", res); break; case "eval": try { let res = eval(param); if (res instanceof Promise) res = await res; - console.log(res); + console.log("执行结果", res); } catch (e) { console.log(e.stack); }