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.
Files changed (50) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +73 -0
  3. package/bin/oicq +2 -0
  4. package/http-api/README.md +187 -0
  5. package/http-api/api.js +217 -0
  6. package/http-api/config.sample.js +44 -0
  7. package/http-api/core.js +480 -0
  8. package/http-api/cq-notice.js +39 -0
  9. package/http-api/filter.js +128 -0
  10. package/http-api/index.js +39 -0
  11. package/index.d.ts +1214 -0
  12. package/index.js +34 -0
  13. package/lib/algo/jce/index.js +63 -0
  14. package/lib/algo/jce/jce.js +341 -0
  15. package/lib/algo/pb.js +159 -0
  16. package/lib/algo/protobuf.min.js +8 -0
  17. package/lib/algo/tea.js +121 -0
  18. package/lib/client-ext.js +312 -0
  19. package/lib/client-net.js +147 -0
  20. package/lib/client.js +594 -0
  21. package/lib/common.js +252 -0
  22. package/lib/core/friendlist.js +415 -0
  23. package/lib/core/nessy.js +148 -0
  24. package/lib/core/ref.d.ts +1 -0
  25. package/lib/core/sysmsg.js +388 -0
  26. package/lib/core/troop.js +586 -0
  27. package/lib/device.js +181 -0
  28. package/lib/exception.js +57 -0
  29. package/lib/message/builder.js +1093 -0
  30. package/lib/message/chat.js +276 -0
  31. package/lib/message/face.js +293 -0
  32. package/lib/message/file.js +425 -0
  33. package/lib/message/history.js +73 -0
  34. package/lib/message/image.js +533 -0
  35. package/lib/message/multi.js +242 -0
  36. package/lib/message/music.js +172 -0
  37. package/lib/message/parser.js +774 -0
  38. package/lib/message/ptt.js +389 -0
  39. package/lib/oicq.js +427 -0
  40. package/lib/onlinepush.js +616 -0
  41. package/lib/ref.d.ts +177 -0
  42. package/lib/service.js +204 -0
  43. package/lib/wtlogin/ecdh.js +15 -0
  44. package/lib/wtlogin/login-password.js +215 -0
  45. package/lib/wtlogin/login-qrcode.js +249 -0
  46. package/lib/wtlogin/tlv.js +331 -0
  47. package/lib/wtlogin/writer.js +88 -0
  48. package/lib/wtlogin/wt.js +422 -0
  49. package/package.json +50 -0
  50. 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 "&amp;";
239
+ if (s === "<") return "&lt;";
240
+ if (s === ">") return "&gt;";
241
+ if (s === "\"") return "&quot;";
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
+ };