oicq-icalingua-plus-plus 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +73 -0
- package/bin/oicq +2 -0
- package/http-api/README.md +187 -0
- package/http-api/api.js +217 -0
- package/http-api/config.sample.js +44 -0
- package/http-api/core.js +480 -0
- package/http-api/cq-notice.js +39 -0
- package/http-api/filter.js +128 -0
- package/http-api/index.js +39 -0
- package/index.d.ts +1214 -0
- package/index.js +34 -0
- package/lib/algo/jce/index.js +63 -0
- package/lib/algo/jce/jce.js +341 -0
- package/lib/algo/pb.js +159 -0
- package/lib/algo/protobuf.min.js +8 -0
- package/lib/algo/tea.js +121 -0
- package/lib/client-ext.js +312 -0
- package/lib/client-net.js +147 -0
- package/lib/client.js +594 -0
- package/lib/common.js +252 -0
- package/lib/core/friendlist.js +415 -0
- package/lib/core/nessy.js +148 -0
- package/lib/core/ref.d.ts +1 -0
- package/lib/core/sysmsg.js +388 -0
- package/lib/core/troop.js +586 -0
- package/lib/device.js +181 -0
- package/lib/exception.js +57 -0
- package/lib/message/builder.js +1093 -0
- package/lib/message/chat.js +276 -0
- package/lib/message/face.js +293 -0
- package/lib/message/file.js +425 -0
- package/lib/message/history.js +73 -0
- package/lib/message/image.js +533 -0
- package/lib/message/multi.js +242 -0
- package/lib/message/music.js +172 -0
- package/lib/message/parser.js +774 -0
- package/lib/message/ptt.js +389 -0
- package/lib/oicq.js +427 -0
- package/lib/onlinepush.js +616 -0
- package/lib/ref.d.ts +177 -0
- package/lib/service.js +204 -0
- package/lib/wtlogin/ecdh.js +15 -0
- package/lib/wtlogin/login-password.js +215 -0
- package/lib/wtlogin/login-qrcode.js +249 -0
- package/lib/wtlogin/tlv.js +331 -0
- package/lib/wtlogin/writer.js +88 -0
- package/lib/wtlogin/wt.js +422 -0
- package/package.json +50 -0
- package/util.js +115 -0
package/lib/common.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用函数
|
|
3
|
+
*/
|
|
4
|
+
"use strict";
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const crypto = require("crypto");
|
|
7
|
+
const stream = require("stream");
|
|
8
|
+
const zlib = require("zlib");
|
|
9
|
+
const util = require("util");
|
|
10
|
+
const pb = require("./algo/pb");
|
|
11
|
+
|
|
12
|
+
function uuid() {
|
|
13
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
14
|
+
const r = Math.random() * 16 | 0, v = c === "x" ? r : (r & 0x3 | 0x8);
|
|
15
|
+
return v.toString(16);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const timestamp = () => Math.floor(Date.now() / 1000);
|
|
19
|
+
const md5 = (data) => crypto.createHash("md5").update(data).digest();
|
|
20
|
+
const sha1 = (data) => crypto.createHash("sha1").update(data).digest();
|
|
21
|
+
|
|
22
|
+
const pipeline = util.promisify(stream.pipeline);
|
|
23
|
+
const unzip = util.promisify(zlib.unzip);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {stream.Readable} readable
|
|
27
|
+
* @returns {Promise<Buffer>}
|
|
28
|
+
*/
|
|
29
|
+
function md5Stream(readable) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
readable.on("error", reject);
|
|
32
|
+
readable.pipe(
|
|
33
|
+
crypto.createHash("md5")
|
|
34
|
+
.on("error", reject)
|
|
35
|
+
.on("data", resolve)
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} filepath
|
|
42
|
+
* @returns {Promise<[Buffer, Buffer]>}
|
|
43
|
+
*/
|
|
44
|
+
function fileHash(filepath) {
|
|
45
|
+
const readable = fs.createReadStream(filepath);
|
|
46
|
+
const sha1 = new Promise((resolve, reject) => {
|
|
47
|
+
readable.on("error", reject);
|
|
48
|
+
readable.pipe(
|
|
49
|
+
crypto.createHash("sha1")
|
|
50
|
+
.on("error", reject)
|
|
51
|
+
.on("data", resolve)
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
return Promise.all([md5Stream(readable), sha1]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function checkUin(uin) {
|
|
58
|
+
return uin >= 10000 && uin <= 0xffffffff;
|
|
59
|
+
}
|
|
60
|
+
const UIN_ERROR = new Error("bad group_id or user_id");
|
|
61
|
+
/**
|
|
62
|
+
* @param {number} gid
|
|
63
|
+
* @param {number} uid
|
|
64
|
+
*/
|
|
65
|
+
function uinAutoCheck(gid, uid) {
|
|
66
|
+
gid = parseInt(gid);
|
|
67
|
+
if (!checkUin(gid)) {
|
|
68
|
+
throw UIN_ERROR;
|
|
69
|
+
}
|
|
70
|
+
if (arguments.length === 2) {
|
|
71
|
+
uid = parseInt(uid);
|
|
72
|
+
if (!checkUin(uid))
|
|
73
|
+
throw UIN_ERROR;
|
|
74
|
+
}
|
|
75
|
+
return [gid, uid];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {number} groupCode
|
|
80
|
+
*/
|
|
81
|
+
function code2uin(groupCode) {
|
|
82
|
+
let left = Math.floor(groupCode / 1000000);
|
|
83
|
+
if (left >= 0 && left <= 10)
|
|
84
|
+
left += 202;
|
|
85
|
+
else if (left >= 11 && left <= 19)
|
|
86
|
+
left += 469;
|
|
87
|
+
else if (left >= 20 && left <= 66)
|
|
88
|
+
left += 2080;
|
|
89
|
+
else if (left >= 67 && left <= 156)
|
|
90
|
+
left += 1943;
|
|
91
|
+
else if (left >= 157 && left <= 209)
|
|
92
|
+
left += 1990;
|
|
93
|
+
else if (left >= 210 && left <= 309)
|
|
94
|
+
left += 3890;
|
|
95
|
+
else if (left >= 310 && left <= 334)
|
|
96
|
+
left += 3490;
|
|
97
|
+
else if (left >= 335 && left <= 386) //335 336不确定
|
|
98
|
+
left += 2265;
|
|
99
|
+
else if (left >= 387 && left <= 499)
|
|
100
|
+
left += 3490;
|
|
101
|
+
|
|
102
|
+
return left * 1000000 + groupCode % 1000000;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {number} groupUin
|
|
107
|
+
*/
|
|
108
|
+
function uin2code(groupUin) {
|
|
109
|
+
let left = Math.floor(groupUin / 1000000);
|
|
110
|
+
if (left >= 202 && left <= 212)
|
|
111
|
+
left -= 202;
|
|
112
|
+
else if (left >= 480 && left <= 488)
|
|
113
|
+
left -= 469;
|
|
114
|
+
else if (left >= 2100 && left <= 2146)
|
|
115
|
+
left -= 2080;
|
|
116
|
+
else if (left >= 2010 && left <= 2099)
|
|
117
|
+
left -= 1943;
|
|
118
|
+
else if (left >= 2147 && left <= 2199)
|
|
119
|
+
left -= 1990;
|
|
120
|
+
else if (left >= 2600 && left <= 2651)
|
|
121
|
+
left -= 2265;
|
|
122
|
+
else if (left >= 3800 && left <= 3989)
|
|
123
|
+
left -= 3490;
|
|
124
|
+
else if (left >= 4100 && left <= 4199)
|
|
125
|
+
left -= 3890;
|
|
126
|
+
return left * 1000000 + groupUin % 1000000;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function log(any) {
|
|
130
|
+
console.log(util.inspect(any, { depth: 20, showHidden: false, maxArrayLength: 1000, maxStringLength: 20000 }));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 生成私聊消息id
|
|
135
|
+
* @param {number} user_id 对方QQ号
|
|
136
|
+
* @param {number} seq 序号
|
|
137
|
+
* @param {number} random 随机数
|
|
138
|
+
* @param {number} time unix时间戳
|
|
139
|
+
* @param {number} flag 接收为0 发送为1
|
|
140
|
+
*/
|
|
141
|
+
function genC2CMessageId(user_id, seq, random, time, flag = 0) {
|
|
142
|
+
const buf = Buffer.allocUnsafe(17);
|
|
143
|
+
buf.writeUInt32BE(user_id);
|
|
144
|
+
buf.writeInt32BE(seq & 0xffffffff, 4);
|
|
145
|
+
buf.writeInt32BE(random & 0xffffffff, 8);
|
|
146
|
+
buf.writeUInt32BE(time, 12);
|
|
147
|
+
buf.writeUInt8(flag, 16);
|
|
148
|
+
return buf.toString("base64");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 解析私聊消息id
|
|
153
|
+
* @param {string} message_id
|
|
154
|
+
*/
|
|
155
|
+
function parseC2CMessageId(message_id) {
|
|
156
|
+
const buf = Buffer.from(message_id, "base64");
|
|
157
|
+
const user_id = buf.readUInt32BE(),
|
|
158
|
+
seq = buf.readUInt32BE(4),
|
|
159
|
+
random = buf.readUInt32BE(8),
|
|
160
|
+
time = buf.readUInt32BE(12),
|
|
161
|
+
flag = buf.length >= 17 ? buf.readUInt8(16) : 0;
|
|
162
|
+
return { user_id, seq, random, time, flag };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 生成群消息id
|
|
167
|
+
* @param {number} group_id 群号
|
|
168
|
+
* @param {number} user_id 发送者QQ号
|
|
169
|
+
* @param {number} seq 序号
|
|
170
|
+
* @param {number} random 随机数
|
|
171
|
+
* @param {number} time unix时间戳
|
|
172
|
+
* @param {number} pktnum 分片数
|
|
173
|
+
*/
|
|
174
|
+
function genGroupMessageId(group_id, user_id, seq, random, time, pktnum = 1) {
|
|
175
|
+
const buf = Buffer.allocUnsafe(21);
|
|
176
|
+
buf.writeUInt32BE(group_id);
|
|
177
|
+
buf.writeUInt32BE(user_id, 4);
|
|
178
|
+
buf.writeInt32BE(seq & 0xffffffff, 8);
|
|
179
|
+
buf.writeInt32BE(random & 0xffffffff, 12);
|
|
180
|
+
buf.writeUInt32BE(time, 16);
|
|
181
|
+
buf.writeUInt8(pktnum > 1 ? pktnum : 1, 20);
|
|
182
|
+
return buf.toString("base64");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 解析群消息id
|
|
187
|
+
* @param {string} message_id
|
|
188
|
+
*/
|
|
189
|
+
function parseGroupMessageId(message_id) {
|
|
190
|
+
const buf = Buffer.from(message_id, "base64");
|
|
191
|
+
const group_id = buf.readUInt32BE(),
|
|
192
|
+
user_id = buf.readUInt32BE(4),
|
|
193
|
+
seq = buf.readUInt32BE(8),
|
|
194
|
+
random = buf.readUInt32BE(12),
|
|
195
|
+
time = buf.readUInt32BE(16),
|
|
196
|
+
pktnum = buf.length >= 21 ? buf.readUInt8(20) : 1;
|
|
197
|
+
return { group_id, user_id, seq, random, time, pktnum };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {number} random
|
|
202
|
+
*/
|
|
203
|
+
function genMessageUuid(random) {
|
|
204
|
+
return 16777216n << 32n | BigInt(random);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {bigint} msg_uuid
|
|
209
|
+
*/
|
|
210
|
+
function genRandom(msg_uuid) {
|
|
211
|
+
return Number(BigInt(msg_uuid) & 0xffffffffn);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 解析彩色群名片
|
|
216
|
+
* @param {Buffer} buf
|
|
217
|
+
*/
|
|
218
|
+
function parseFunString(buf) {
|
|
219
|
+
if (buf[0] === 0xa) {
|
|
220
|
+
let res = "";
|
|
221
|
+
try {
|
|
222
|
+
let arr = pb.decode(buf)[1];
|
|
223
|
+
if (!Array.isArray(arr))
|
|
224
|
+
arr = [arr];
|
|
225
|
+
for (let v of arr) {
|
|
226
|
+
if (v[2])
|
|
227
|
+
res += String(v[2]);
|
|
228
|
+
}
|
|
229
|
+
} catch { }
|
|
230
|
+
return res;
|
|
231
|
+
} else {
|
|
232
|
+
return String(buf);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function escapeXml(str) {
|
|
237
|
+
return str.replace(/[&"><]/g, function (s) {
|
|
238
|
+
if (s === "&") return "&";
|
|
239
|
+
if (s === "<") return "<";
|
|
240
|
+
if (s === ">") return ">";
|
|
241
|
+
if (s === "\"") return """;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = {
|
|
246
|
+
uuid, md5, sha1, md5Stream, timestamp, checkUin, uinAutoCheck, fileHash, pipeline, unzip,
|
|
247
|
+
log, code2uin, uin2code, parseFunString, escapeXml,
|
|
248
|
+
genC2CMessageId, parseC2CMessageId, genGroupMessageId, parseGroupMessageId, genMessageUuid, genRandom,
|
|
249
|
+
BUF0: Buffer.alloc(0),
|
|
250
|
+
BUF16: Buffer.alloc(16),
|
|
251
|
+
NOOP: () => { },
|
|
252
|
+
};
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 好友列表
|
|
3
|
+
* 群列表,群资料
|
|
4
|
+
* 群员列表,群员资料
|
|
5
|
+
* 相关api
|
|
6
|
+
*/
|
|
7
|
+
"use strict";
|
|
8
|
+
const common = require("../common");
|
|
9
|
+
const pb = require("../algo/pb");
|
|
10
|
+
const jce = require("../algo/jce");
|
|
11
|
+
|
|
12
|
+
const friend_sex_map = {
|
|
13
|
+
"0": "unknown", "1": "male", "2": "female"
|
|
14
|
+
};
|
|
15
|
+
const group_sex_map = {
|
|
16
|
+
"-1": "unknown", "0": "male", "1": "female"
|
|
17
|
+
};
|
|
18
|
+
const group_role_map = {
|
|
19
|
+
"1": "member", "2": "admin", "3": "owner"
|
|
20
|
+
};
|
|
21
|
+
const d50 = pb.encode({
|
|
22
|
+
1: 10002,
|
|
23
|
+
91001: 1,
|
|
24
|
+
101001: 1,
|
|
25
|
+
151001: 1,
|
|
26
|
+
181001: 1,
|
|
27
|
+
251001: 1,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @this {import("./ref").Client}
|
|
32
|
+
* @param {number} start
|
|
33
|
+
* @param {number} limit
|
|
34
|
+
* @param {Map<number, import("./ref").FriendInfo>} tmp
|
|
35
|
+
* @returns {number} 好友总数
|
|
36
|
+
*/
|
|
37
|
+
async function _initFL(start, limit, tmp) {
|
|
38
|
+
const FL = jce.encodeStruct([
|
|
39
|
+
3,
|
|
40
|
+
1, this.uin, start, limit, 0, 0, 0, 0, 0, 1,
|
|
41
|
+
31, null, 0, 0, 0, d50, null, [13580, 13581, 13582]
|
|
42
|
+
]);
|
|
43
|
+
const extra = {
|
|
44
|
+
req_id: this.seq_id + 1,
|
|
45
|
+
service: "mqq.IMService.FriendListServiceServantObj",
|
|
46
|
+
method: "GetFriendListReq",
|
|
47
|
+
};
|
|
48
|
+
const body = jce.encodeWrapper({ FL }, extra);
|
|
49
|
+
const blob = await this.sendUni("friendlist.getFriendGroupList", body, 10);
|
|
50
|
+
const nested = jce.decode(blob);
|
|
51
|
+
// const gmap = { };
|
|
52
|
+
// for (let v of nested[14])
|
|
53
|
+
// gmap[v[0]] = v[1];
|
|
54
|
+
for (let v of nested[7]) {
|
|
55
|
+
let age = 0, area = "unknown";
|
|
56
|
+
if (tmp.has(v[0])) {
|
|
57
|
+
const f = tmp.get(v[0]);
|
|
58
|
+
age = f.age;
|
|
59
|
+
area = f.area;
|
|
60
|
+
}
|
|
61
|
+
this.fl.set(v[0], {
|
|
62
|
+
user_id: v[0],
|
|
63
|
+
nickname: v[14] || "",
|
|
64
|
+
sex: friend_sex_map[v[31]] || "unknown",
|
|
65
|
+
age, area,
|
|
66
|
+
remark: v[3] || "",
|
|
67
|
+
// group: gmap[v[1]] || "unknown",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return nested[5];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 加载好友列表
|
|
75
|
+
* @this {import("./ref").Client}
|
|
76
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
77
|
+
*/
|
|
78
|
+
async function initFL() {
|
|
79
|
+
this.sync_finished = false;
|
|
80
|
+
const tmp = this.fl;
|
|
81
|
+
this.fl = new Map;
|
|
82
|
+
let start = 0, limit = 150;
|
|
83
|
+
while (1) {
|
|
84
|
+
try {
|
|
85
|
+
const total = await _initFL.call(this, start, limit, tmp);
|
|
86
|
+
start += limit;
|
|
87
|
+
if (start > total) break;
|
|
88
|
+
} catch (e) {
|
|
89
|
+
this.logger.debug(e);
|
|
90
|
+
this.logger.warn("加载好友列表出现异常,未加载完成。");
|
|
91
|
+
return { result: -1 };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { result: 0 };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 加载群列表
|
|
99
|
+
* @this {import("./ref").Client}
|
|
100
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
101
|
+
*/
|
|
102
|
+
async function initGL() {
|
|
103
|
+
this.sync_finished = false;
|
|
104
|
+
const GetTroopListReqV2Simplify = jce.encodeStruct([
|
|
105
|
+
this.uin, 0, null, [], 1, 8, 0, 1, 1
|
|
106
|
+
]);
|
|
107
|
+
const extra = {
|
|
108
|
+
req_id: this.seq_id + 1,
|
|
109
|
+
service: "mqq.IMService.FriendListServiceServantObj",
|
|
110
|
+
method: "GetTroopListReqV2Simplify",
|
|
111
|
+
};
|
|
112
|
+
const body = jce.encodeWrapper({ GetTroopListReqV2Simplify }, extra);
|
|
113
|
+
try {
|
|
114
|
+
const blob = await this.sendUni("friendlist.GetTroopListReqV2", body, 10);
|
|
115
|
+
const nested = jce.decode(blob);
|
|
116
|
+
const tmp = this.gl;
|
|
117
|
+
if (this.gl.size < 999)
|
|
118
|
+
this.gl = new Map;
|
|
119
|
+
for (let v of nested[5]) {
|
|
120
|
+
var last_sent_time = 0, create_time = 0, grade = 0, max_admin_count = 0, active_member_count = 0;
|
|
121
|
+
if (tmp.has(v[1])) {
|
|
122
|
+
const g = tmp.get(v[1]);
|
|
123
|
+
var { last_sent_time, create_time, grade, max_admin_count, active_member_count } = g;
|
|
124
|
+
}
|
|
125
|
+
this.gl.set(v[1], {
|
|
126
|
+
group_id: v[1],
|
|
127
|
+
group_name: v[4] || "",
|
|
128
|
+
member_count: v[19],
|
|
129
|
+
max_member_count: v[29],
|
|
130
|
+
owner_id: v[23],
|
|
131
|
+
last_join_time: v[27],
|
|
132
|
+
last_sent_time,
|
|
133
|
+
shutup_time_whole: v[9] ? 0xffffffff : 0,
|
|
134
|
+
shutup_time_me: v[10] * 1000 > Date.now() ? v[10] : 0,
|
|
135
|
+
create_time, grade,
|
|
136
|
+
max_admin_count, active_member_count,
|
|
137
|
+
update_time: 0,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
} catch (e) {
|
|
141
|
+
this.logger.debug(e);
|
|
142
|
+
this.logger.warn("加载群列表出现异常,未加载完成。");
|
|
143
|
+
return { result: -1 };
|
|
144
|
+
}
|
|
145
|
+
return { result: 0 };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @this {import("./ref").Client}
|
|
150
|
+
*/
|
|
151
|
+
async function __getGML(group_id, next_uin) {
|
|
152
|
+
const GTML = jce.encodeStruct([
|
|
153
|
+
this.uin, group_id, next_uin, common.code2uin(group_id), 2, 0, 0, 0
|
|
154
|
+
]);
|
|
155
|
+
const extra = {
|
|
156
|
+
req_id: this.seq_id + 1,
|
|
157
|
+
service: "mqq.IMService.FriendListServiceServantObj",
|
|
158
|
+
method: "GetTroopMemberListReq",
|
|
159
|
+
};
|
|
160
|
+
const body = jce.encodeWrapper({ GTML }, extra);
|
|
161
|
+
const blob = await this.sendUni("friendlist.GetTroopMemberListReq", body, 10);
|
|
162
|
+
const nested = jce.decode(blob);
|
|
163
|
+
const map = new Map(), next = nested[4];
|
|
164
|
+
for (let v of nested[3]) {
|
|
165
|
+
map.set(v[0], {
|
|
166
|
+
group_id: group_id,
|
|
167
|
+
user_id: v[0],
|
|
168
|
+
nickname: v[4] || "",
|
|
169
|
+
card: v[8] || "",
|
|
170
|
+
sex: group_sex_map[v[3]] || "unknown",
|
|
171
|
+
age: v[2] || 0,
|
|
172
|
+
area: "unknown",
|
|
173
|
+
join_time: v[15],
|
|
174
|
+
last_sent_time: v[16],
|
|
175
|
+
level: v[14],
|
|
176
|
+
rank: "",
|
|
177
|
+
role: v[18] % 2 === 1 ? "admin" : "member",
|
|
178
|
+
unfriendly: false,
|
|
179
|
+
title: v[23],
|
|
180
|
+
title_expire_time: v[24] & 0xffffffff,
|
|
181
|
+
card_changeable: true,
|
|
182
|
+
shutup_time: v[30] * 1000 > Date.now() ? v[30] : 0,
|
|
183
|
+
update_time: 0,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const owner = this.gl.get(group_id).owner_id;
|
|
188
|
+
map.get(owner).role = "owner";
|
|
189
|
+
} catch (e) { }
|
|
190
|
+
return { map, next };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* @this {import("./ref").Client}
|
|
194
|
+
*/
|
|
195
|
+
async function _getGML(group_id) {
|
|
196
|
+
let mlist = new Map();
|
|
197
|
+
try {
|
|
198
|
+
var next = 0;
|
|
199
|
+
while (1) {
|
|
200
|
+
var { map, next } = await __getGML.call(this, group_id, next);
|
|
201
|
+
mlist = new Map([...mlist, ...map]);
|
|
202
|
+
if (!next) break;
|
|
203
|
+
}
|
|
204
|
+
} catch (e) { }
|
|
205
|
+
if (!mlist.size) {
|
|
206
|
+
this.gml.delete(group_id);
|
|
207
|
+
return null;
|
|
208
|
+
} else {
|
|
209
|
+
this.gml.set(group_id, mlist);
|
|
210
|
+
return mlist;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 群员列表
|
|
215
|
+
* @this {import("./ref").Client}
|
|
216
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
217
|
+
*/
|
|
218
|
+
async function getGML(group_id, no_cache = false) {
|
|
219
|
+
[group_id] = common.uinAutoCheck(group_id);
|
|
220
|
+
let mlist = this.gml.get(group_id);
|
|
221
|
+
if (!mlist || (no_cache && mlist instanceof Map)) {
|
|
222
|
+
mlist = _getGML.call(this, group_id);
|
|
223
|
+
this.gml.set(group_id, mlist);
|
|
224
|
+
}
|
|
225
|
+
if (mlist instanceof Promise)
|
|
226
|
+
mlist = await mlist;
|
|
227
|
+
if (mlist)
|
|
228
|
+
return { result: 0, data: mlist };
|
|
229
|
+
return { result: -1, emsg: "未加入的群" };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const gi_buf = pb.encode({
|
|
233
|
+
1: 0,
|
|
234
|
+
2: 0,
|
|
235
|
+
5: 0,
|
|
236
|
+
6: 0,
|
|
237
|
+
15: "",
|
|
238
|
+
29: 0,
|
|
239
|
+
36: 0,
|
|
240
|
+
37: 0,
|
|
241
|
+
45: 0,
|
|
242
|
+
46: 0,
|
|
243
|
+
49: 0,
|
|
244
|
+
54: 0,
|
|
245
|
+
89: "",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 群资料
|
|
250
|
+
* @this {import("./ref").Client}
|
|
251
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
252
|
+
*/
|
|
253
|
+
async function getGI(group_id, no_cache = false) {
|
|
254
|
+
[group_id] = common.uinAutoCheck(group_id);
|
|
255
|
+
|
|
256
|
+
let ginfo = this.gl.get(group_id);
|
|
257
|
+
if (!no_cache && ginfo && common.timestamp() - ginfo.update_time <= this.config.internal_cache_life)
|
|
258
|
+
return { result: 0, data: ginfo };
|
|
259
|
+
|
|
260
|
+
const body = pb.encode({
|
|
261
|
+
1: this.apk.subid,
|
|
262
|
+
2: {
|
|
263
|
+
1: group_id,
|
|
264
|
+
2: gi_buf,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
try {
|
|
268
|
+
this.gl.get(group_id).update_time = common.timestamp();
|
|
269
|
+
} catch (e) { }
|
|
270
|
+
const blob = await this.sendOidb("OidbSvc.0x88d_0", body);
|
|
271
|
+
const o = pb.decode(blob)[4][1][3];
|
|
272
|
+
if (!o) {
|
|
273
|
+
this.gl.delete(group_id);
|
|
274
|
+
this.gml.delete(group_id);
|
|
275
|
+
return { result: -1, emsg: "未加入的群" };
|
|
276
|
+
}
|
|
277
|
+
ginfo = {
|
|
278
|
+
group_id: group_id,
|
|
279
|
+
group_name: o[89] ? String(o[89]) : String(o[15]),
|
|
280
|
+
member_count: o[6],
|
|
281
|
+
max_member_count: o[5],
|
|
282
|
+
owner_id: o[1],
|
|
283
|
+
last_join_time: o[49],
|
|
284
|
+
last_sent_time: o[54],
|
|
285
|
+
shutup_time_whole: o[45] ? 0xffffffff : 0,
|
|
286
|
+
shutup_time_me: o[46] * 1000 > Date.now() ? o[46] : 0,
|
|
287
|
+
create_time: o[2],
|
|
288
|
+
grade: o[36],
|
|
289
|
+
max_admin_count: o[29],
|
|
290
|
+
active_member_count: o[37],
|
|
291
|
+
update_time: common.timestamp(),
|
|
292
|
+
};
|
|
293
|
+
this.gl.set(group_id, ginfo);
|
|
294
|
+
return { result: 0, data: ginfo };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 群员资料
|
|
299
|
+
* @this {import("./ref").Client}
|
|
300
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
301
|
+
*/
|
|
302
|
+
async function getGMI(group_id, user_id, no_cache = false) {
|
|
303
|
+
[group_id, user_id] = common.uinAutoCheck(group_id, user_id);
|
|
304
|
+
if (!this.gml.has(group_id))
|
|
305
|
+
this.getGroupMemberList(group_id);
|
|
306
|
+
let minfo;
|
|
307
|
+
try {
|
|
308
|
+
minfo = this.gml.get(group_id).get(user_id);
|
|
309
|
+
} catch (e) { }
|
|
310
|
+
if (!no_cache && minfo && common.timestamp() - minfo.update_time <= this.config.internal_cache_life)
|
|
311
|
+
return { result: 0, data: minfo };
|
|
312
|
+
|
|
313
|
+
const body = pb.encode({
|
|
314
|
+
1: group_id,
|
|
315
|
+
2: user_id,
|
|
316
|
+
3: 1,
|
|
317
|
+
4: 1,
|
|
318
|
+
5: 1,
|
|
319
|
+
});
|
|
320
|
+
try {
|
|
321
|
+
this.gml.get(group_id).get(user_id).update_time = common.timestamp();
|
|
322
|
+
} catch (e) { }
|
|
323
|
+
const blob = await this.sendUni("group_member_card.get_group_member_card_info", body);
|
|
324
|
+
const o = pb.decode(blob)[3];
|
|
325
|
+
if (!o[27]) {
|
|
326
|
+
try {
|
|
327
|
+
this.gml.get(group_id).delete(user_id);
|
|
328
|
+
} catch (e) { }
|
|
329
|
+
return { result: -1, emsg: "幽灵群员" };
|
|
330
|
+
}
|
|
331
|
+
if (o[9] === undefined) o[9] = -1;
|
|
332
|
+
const card = o[8] ? common.parseFunString(o[8].toBuffer()) : "";
|
|
333
|
+
minfo = {
|
|
334
|
+
group_id: group_id,
|
|
335
|
+
user_id: user_id,
|
|
336
|
+
nickname: Reflect.has(o, "11") ? String(o[11]) : "",
|
|
337
|
+
card: card,
|
|
338
|
+
sex: Reflect.has(o, "9") ? group_sex_map[o[9] & 0xffffffff] : "unknown",
|
|
339
|
+
age: o[12] || 0,
|
|
340
|
+
area: Reflect.has(o, "10") ? String(o[10]) : "unknown",
|
|
341
|
+
join_time: o[14],
|
|
342
|
+
last_sent_time: o[15],
|
|
343
|
+
level: o[39],
|
|
344
|
+
rank: Reflect.has(o, "13") ? String(o[13]) : undefined,
|
|
345
|
+
role: group_role_map[o[27]],
|
|
346
|
+
unfriendly: false,
|
|
347
|
+
title: Reflect.has(o, "31") ? String(o[31]) : "",
|
|
348
|
+
title_expire_time: Reflect.has(o, "32") ? o[32] : 0xffffffff,
|
|
349
|
+
card_changeable: true,
|
|
350
|
+
shutup_time: 0,
|
|
351
|
+
update_time: common.timestamp(),
|
|
352
|
+
};
|
|
353
|
+
try {
|
|
354
|
+
try {
|
|
355
|
+
minfo.shutup_time = this.gml.get(group_id).get(user_id).shutup_time;
|
|
356
|
+
} catch (e) { }
|
|
357
|
+
this.gml.get(group_id).set(user_id, minfo);
|
|
358
|
+
} catch (e) { }
|
|
359
|
+
return { result: 0, data: minfo };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 陌生人资料(也用于更新好友信息中的一些字段)
|
|
364
|
+
* @this {import("./ref").Client}
|
|
365
|
+
* @returns {import("./ref").ProtocolResponse}
|
|
366
|
+
*/
|
|
367
|
+
async function getSI(user_id, no_cache = false) {
|
|
368
|
+
[user_id] = common.uinAutoCheck(user_id);
|
|
369
|
+
let user = this.sl.get(user_id);
|
|
370
|
+
if (!no_cache && user)
|
|
371
|
+
return { result: 0, data: user };
|
|
372
|
+
|
|
373
|
+
const arr = [
|
|
374
|
+
null,
|
|
375
|
+
0, "", [user_id], 1, 1, 0, 0, 0, 1, 0, 1
|
|
376
|
+
];
|
|
377
|
+
arr[101] = 1;
|
|
378
|
+
const req = jce.encodeStruct(arr);
|
|
379
|
+
const extra = {
|
|
380
|
+
req_id: this.seq_id + 1,
|
|
381
|
+
service: "KQQ.ProfileService.ProfileServantObj",
|
|
382
|
+
method: "GetSimpleInfo",
|
|
383
|
+
};
|
|
384
|
+
const body = jce.encodeWrapper({ req }, extra);
|
|
385
|
+
const blob = await this.sendUni("ProfileService.GetSimpleInfo", body);
|
|
386
|
+
const nested = jce.decode(blob);
|
|
387
|
+
for (let v of nested) {
|
|
388
|
+
const area = (v[13] + " " + v[14] + " " + v[15]).trim();
|
|
389
|
+
const user = {
|
|
390
|
+
user_id: v[1],
|
|
391
|
+
nickname: v[5] || "",
|
|
392
|
+
sex: group_sex_map[v[3]] || "unknown",
|
|
393
|
+
age: v[4] || 0,
|
|
394
|
+
area: area ? area : "unknown",
|
|
395
|
+
};
|
|
396
|
+
let o = this.fl.get(v[1]);
|
|
397
|
+
if (!o)
|
|
398
|
+
o = this.sl.get(v[1]);
|
|
399
|
+
if (o) {
|
|
400
|
+
o.nickname = user.nickname;
|
|
401
|
+
o.area = user.area;
|
|
402
|
+
if (user.sex !== "unknown")
|
|
403
|
+
o.sex = user.sex;
|
|
404
|
+
if (user.age)
|
|
405
|
+
o.age = user.age;
|
|
406
|
+
}
|
|
407
|
+
return { result: 0, data: user };
|
|
408
|
+
}
|
|
409
|
+
return { result: -1, emsg: "没有这个人" };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
module.exports = {
|
|
413
|
+
initFL, initGL,
|
|
414
|
+
getGML, getGI, getGMI, getSI,
|
|
415
|
+
};
|