oicq-icalingua-plus-plus 1.24.39 → 1.24.41

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.
@@ -172,8 +172,9 @@ class Builder {
172
172
  * @private
173
173
  * @param {string} text
174
174
  * @param {Buffer} attr6
175
+ * @param {Buffer} extra12
175
176
  */
176
- buildTextElem(text, attr6 = null) {
177
+ buildTextElem(text, attr6 = null, extra12 = null) {
177
178
  text = String(text);
178
179
  let n = 0;
179
180
  while (n < text.length) {
@@ -198,7 +199,8 @@ class Builder {
198
199
  this.elems.push({
199
200
  1: {
200
201
  1: chunk,
201
- 3: attr6
202
+ 3: attr6,
203
+ 12: extra12,
202
204
  }
203
205
  });
204
206
  }
@@ -231,7 +233,21 @@ class Builder {
231
233
  const buf = Buffer.allocUnsafe(6);
232
234
  buf.writeUInt8(display.length), buf.writeUInt8(flag, 1), buf.writeUInt32BE(q, 2);
233
235
  const attr6 = Buffer.concat([AT_BUF, buf, BUF2]);
234
- this.buildTextElem(display, attr6);
236
+ let uid = String(q);
237
+ try {
238
+ if (qq !== "all") {
239
+ const member = this.c.gml.get(this.target).get(q);
240
+ uid = member.uid || uid;
241
+ }
242
+ } catch (e) { }
243
+ const extra12 = {
244
+ 3: flag === 1 ? 1 : 2,
245
+ 4: q,
246
+ 5: 0,
247
+ 9: uid,
248
+ 11: 0,
249
+ };
250
+ this.buildTextElem(display, attr6, extra12);
235
251
  }
236
252
 
237
253
  /**
package/lib/oicq.js CHANGED
@@ -14,7 +14,7 @@ const { timestamp, BUF0, BUF16, unzip, log } = require("./common");
14
14
  const push = require("./onlinepush");
15
15
  const sysmsg = require("./core/sysmsg");
16
16
  const { initFL, initGL } = require("./core/friendlist");
17
- const { int32ip2str, getUploadChannel } = require("./service");
17
+ const { int32ip2str, getUploadChannel, checkIPConnectivity } = require("./service");
18
18
 
19
19
  /**
20
20
  * @this {Client}
@@ -102,6 +102,11 @@ function pushReqListener(blob, seq) {
102
102
  if (v[1] === 10) {
103
103
  this.storage.port = v[2][0][3];
104
104
  this.storage.ip = int32ip2str(v[2][0][2]);
105
+ checkIPConnectivity(this.storage.ip, this.storage.port).then((res) => {
106
+ if (res) return;
107
+ this.logger.warn("上传通道IP不可用,将使用通用域名");
108
+ this.storage.ip = "v6.htdata.qq.com";
109
+ });
105
110
  }
106
111
  }
107
112
  this.saveStorageConfig();
@@ -170,7 +175,6 @@ function msfOfflineListener(blob) {
170
175
  * @this {Client}
171
176
  */
172
177
  function kickoffListener(data) {
173
- this._wt.deleteToken();
174
178
  this.status = STATUS_PENDING;
175
179
  this._stopHeartbeat();
176
180
  this.logger.warn(data.info);
@@ -191,6 +195,7 @@ function kickoffListener(data) {
191
195
  this.logger.mark("3秒后重新连接..");
192
196
  setTimeout(this.login.bind(this), 3000);
193
197
  }
198
+ if (sub_type !== "kickoff") this._wt.deleteToken();
194
199
  this.em("system.offline." + sub_type, { message: data.info });
195
200
  }
196
201
 
@@ -209,6 +214,7 @@ Client.prototype._register = async function () {
209
214
  };
210
215
 
211
216
  Client.prototype._var4 = 0;
217
+ Client.prototype._nt_hb_var = 0;
212
218
  Client.prototype._runCircleTask = async function () {
213
219
  if (this._var4++ > 10) {
214
220
  await this._wt.refreshD2();
@@ -235,13 +241,22 @@ Client.prototype._startHeartbeat = function () {
235
241
  }
236
242
  this._wt.syncTimeDiff();
237
243
  this._wt.heartbeat();
238
- if (this.config.useNT) this._wt.ssoHeartbeat();
244
+ if (this.config.useNT) {
245
+ if (this._nt_hb_var++ >= 5) {
246
+ this._nt_hb_var = 0;
247
+ this._wt.ssoHeartbeat();
248
+ }
249
+ }
239
250
  if (timestamp() - this.sig.qsign_token_time >= (60 * 60)) {
240
251
  this.sig.qsign_token_time = timestamp();
241
252
  this.refreshQSignToken();
242
253
  }
243
254
  if (!this.storage.port) {
244
- await getUploadChannel.call(this);
255
+ try {
256
+ await getUploadChannel.call(this);
257
+ } catch {
258
+ this.logger.warn("getUploadChannel failed");
259
+ }
245
260
  }
246
261
  }, 30000);
247
262
  };
@@ -397,6 +412,7 @@ const listeners = {
397
412
  "MessageSvc.PushReaded": pushReadedListener,
398
413
  //"StatSvc.MsfConnProbe": function(){this._wt.ssoHeartbeat()},
399
414
  "trpc.msg.olpush.OlPushService.MsgPush": push.NTMsgListener,
415
+ //"trpc.msg.register_proxy.RegisterProxy.InfoSyncPush": push.infoSyncPushListener,
400
416
  };
401
417
 
402
418
  // MessageSvc.RequestPushStatus 其他PC端login
package/lib/onlinepush.js CHANGED
@@ -839,7 +839,6 @@ async function NTMsgListener(blob, seq) {
839
839
  break;
840
840
  // WIP: NT的系统消息
841
841
  }
842
-
843
842
  }
844
843
 
845
844
  /**
@@ -882,6 +881,133 @@ function c2cNTMsgConverter(msg) {
882
881
  3: body,
883
882
  };
884
883
  }
884
+
885
+ /**
886
+ * @this {import("./ref").Client}
887
+ */
888
+ async function infoSyncPushListener(blob, seq) {
889
+ const o = pb.decode(blob);
890
+ const type = o[3];
891
+ switch (type) {
892
+ case 2:
893
+ try {
894
+ const group_message_sync = o[7]; //群消息
895
+ if (group_message_sync && group_message_sync[3]) {
896
+ const groups = Array.isArray(group_message_sync[3]) ? group_message_sync[3] : [group_message_sync[3]];
897
+ for (let group of groups) {
898
+ const group_id = group[3];
899
+ const from_seq = group[4];
900
+ const to_seq = group[5];
901
+ const msgs = Array.isArray(group[6]) ? group[6] : [group[6]];
902
+ for (let i = msgs.length - 1; i >= 0; i--) {
903
+ const msg = msgs[i];
904
+ await NTMsgSync.call(this, Buffer.from(pb.encode({ 1: msg })), seq);
905
+ }
906
+ }
907
+ }
908
+ const olpush_sync = o[8]; //push732 push528 私聊消息
909
+ if (olpush_sync && olpush_sync[4]) {
910
+ const olpushs_groups = Array.isArray(olpush_sync[4]) ? olpush_sync[4] : [olpush_sync[4]];
911
+ for (let groups of olpushs_groups) {
912
+ const olpushs = Array.isArray(groups[8]) ? groups[8] : [groups[8]];
913
+ for (let i = olpushs.length - 1; i >= 0; i--) {
914
+ const olpush = olpushs[i];
915
+ await NTMsgSync.call(this, Buffer.from(pb.encode({ 1: olpush })), seq);
916
+ }
917
+ }
918
+ }
919
+ const guild_sync = o[9]; //频道,用不上
920
+ } catch (e) {
921
+ this.logger.warn("NT同步消息失败");
922
+ this.logger.debug(e);
923
+ }
924
+ break;
925
+ case 5:
926
+ //群消息列表同步
927
+ try {
928
+ const list = Array.isArray(o[6]) ? o[6] : [o[6]];
929
+ const chats = [];
930
+ for (let chat of list) {
931
+ chats.push({
932
+ group_id: chat[1],
933
+ group_name: String(chat[9]),
934
+ unread_count: chat[2] - chat[3] - 1,
935
+ last_seq: chat[2],
936
+ last_unread_seq: chat[3],
937
+ })
938
+ }
939
+ this.em("new_tech_sync.group_list", chats);
940
+ } catch (e) {
941
+ this.logger.warn("群消息列表同步失败");
942
+ this.logger.debug(e);
943
+ }
944
+ break;
945
+ default:
946
+ break;
947
+ }
948
+ }
949
+
950
+ /**
951
+ * @this {import("./ref").Client}
952
+ */
953
+ async function NTMsgSync(blob, seq) {
954
+ const o = pb.decode(blob);
955
+ const type = o[1][2][1];
956
+ this.logger.debug("NT message type", type);
957
+ switch (type) {
958
+ case 82: //群聊消息
959
+ try {
960
+ const msg = groupNTMsgConverter.call(this, o);
961
+ //生成消息id
962
+ const head = msg[1], content = msg[2], body = msg[3];
963
+ if (head[9][2] === 127) return;
964
+ const user_id = head[1], time = head[6], seq = head[5];
965
+ const group_id = head[9][1], random = body[1][1][3];
966
+ const message_id = genGroupMessageId(group_id, user_id, seq, random, time, content[1]);
967
+ //懒得搞分片消息了
968
+ //解析消息
969
+ const data = await parseGroupMsg.call(this, msg, true);
970
+ if (data && data.raw_message) {
971
+ if (data.user_id === this.uin && this.config.ignore_self)
972
+ return;
973
+ data.reply = (message, auto_escape = false) => this.sendGroupMsg(data.group_id, message, auto_escape);
974
+ data.message_id = message_id;
975
+ const sender = data.sender;
976
+ this.logger.info(`nt sync message: [Group: ${data.group_name}(${data.group_id}), Member: ${sender.card ? sender.card : sender.nickname}(${data.user_id})] ` + data.raw_message);
977
+ this.em("new_tech_sync.group." + data.sub_type, data);
978
+ }
979
+ } catch (e) {
980
+ // this.logger.debug(e);
981
+ }
982
+ break;
983
+ case 141: //陌生人
984
+ case 166: //好友
985
+ case 167: //单向好友
986
+ case 208: //好友语音
987
+ case 529: //离线文件
988
+ try {
989
+ const msg = c2cNTMsgConverter.call(this, o);
990
+ const data = await parseC2CMsg.call(this, msg, true);
991
+ if (data.user_id === this.uin) {
992
+ data.user_id = msg[1][2];
993
+ this.em("new_tech_sync.private." + data.sub_type, data);
994
+ } else {
995
+ if (data && data.raw_message) {
996
+ data.reply = (message, auto_escape = false) => this.sendPrivateMsg(data.user_id, message, auto_escape);
997
+ this.logger.info(`nt sync message: [Private: ${data.user_id}(${data.sub_type})] ` + data.raw_message);
998
+ this.em("new_tech_sync.private." + data.sub_type, data);
999
+ }
1000
+ }
1001
+ } catch (e) {
1002
+ this.logger.debug(e);
1003
+ }
1004
+ break;
1005
+ default:
1006
+ break;
1007
+ }
1008
+ }
1009
+
885
1010
  module.exports = {
886
- onlinePushListener, onlinePushTransListener, c2cMsgSyncListener, groupMsgListener, discussMsgListener, NTMsgListener
1011
+ onlinePushListener, onlinePushTransListener, c2cMsgSyncListener, groupMsgListener, discussMsgListener,
1012
+ NTMsgListener, infoSyncPushListener
887
1013
  };
package/lib/qimei.js CHANGED
@@ -17,6 +17,22 @@ LQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B
17
17
  9NMbHddGSAUmRTCrHQIDAQAB
18
18
  -----END PUBLIC KEY-----`;
19
19
 
20
+ class SeededRandom {
21
+ constructor(seed) {
22
+ this.seed = seed;
23
+ this.state = md5(seed.toString()).toString("hex");
24
+ }
25
+
26
+ next() {
27
+ this.state = md5(this.state + secret).toString("hex");
28
+ return parseInt(this.state.substring(0, 8), 16) / 0xffffffff;
29
+ }
30
+
31
+ nextInt(min, max) {
32
+ return Math.floor(this.next() * (max - min + 1)) + min;
33
+ }
34
+ }
35
+
20
36
  function dateFormat(fmt = "YYYY-mm-dd HH:MM:SS", date = new Date()) {
21
37
  let ret;
22
38
  const opt = {
@@ -43,10 +59,9 @@ function dateFormat(fmt = "YYYY-mm-dd HH:MM:SS", date = new Date()) {
43
59
  * @returns
44
60
  */
45
61
  function genRandomPayloadByDevice(info, apk, platform) {
62
+ const seededRandom = new SeededRandom(info.android_id);
46
63
  const rangeRand = (max = 1, min = 0) => {
47
- if (max < min) [max, min] = [min, max]
48
- const diff = max - min
49
- return Math.floor(Math.random() * diff) + min
64
+ return seededRandom.nextInt(min, max);
50
65
  };
51
66
  const reserved = {
52
67
  "harmony": "0",
@@ -67,10 +82,10 @@ function genRandomPayloadByDevice(info, apk, platform) {
67
82
  "kernel": info.proc_version
68
83
  };
69
84
  let beaconId = "";
70
- const timestamp = Date.now();
71
- const mtime1 = new Date(info.mtime);
85
+ const timestamp = info.mtime || apk.buildtime * 1000 + rangeRand(10 * 24 * 60 * 60 * 1000, 0);
86
+ const mtime1 = new Date(timestamp);
72
87
  const mtimeStr1 = dateFormat("YYYY-mm-ddHHMMSS", mtime1) + "." + info.imei.slice(2, 11);
73
- const mtime2 = new Date(info.mtime - parseInt(info.imei.slice(2, 4)));
88
+ const mtime2 = new Date(timestamp - parseInt(info.imei.slice(2, 4)));
74
89
  const mtimeStr2 = dateFormat("YYYY-mm-ddHHMMSS", mtime2) + "." + info.imei.slice(5, 14);
75
90
  for (let i = 1; i <= 40; i++) {
76
91
  if ([1, 13, 14, 17, 18, 21, 33, 34, 37, 38].includes(i)) {
package/lib/service.js CHANGED
@@ -354,10 +354,47 @@ async function getUploadChannel() {
354
354
  const rsp = pb.decode(blob)[1281];
355
355
  this.storage.sig_session = rsp[1].toBuffer();
356
356
  this.storage.session_key = rsp[2].toBuffer();
357
- this.storage.ip = int32ip2str(rsp[3][2][0][2]);
358
- this.storage.port = rsp[3][2][0][3];
357
+ const server = Array.isArray(rsp[3][2]) ? rsp[3][2][0] : rsp[3][2];
358
+ this.storage.ip = int32ip2str(server[2]);
359
+ this.storage.port = server[3];
360
+ if (!(await checkIPConnectivity(this.storage.ip, this.storage.port))) {
361
+ this.logger.warn("上传通道IP不可用,将使用通用域名");
362
+ this.storage.ip = "v6.htdata.qq.com";
363
+ }
364
+ }
365
+
366
+ /**
367
+ * 检查IP地址是否连通
368
+ * @param {string} ip - 要检查的IP地址
369
+ * @param {number} port - 要检查的端口号
370
+ * @param {number} timeout - 超时时间,单位毫秒
371
+ * @returns {Promise<boolean>}
372
+ */
373
+ function checkIPConnectivity(ip, port, timeout = 5000) {
374
+ return new Promise((resolve) => {
375
+ const socket = new net.Socket();
376
+
377
+ socket.setTimeout(timeout);
378
+
379
+ socket.on('connect', () => {
380
+ socket.destroy();
381
+ resolve(true);
382
+ });
383
+
384
+ socket.on('timeout', () => {
385
+ socket.destroy();
386
+ resolve(false);
387
+ });
388
+
389
+ socket.on('error', () => {
390
+ socket.destroy();
391
+ resolve(false);
392
+ });
393
+
394
+ socket.connect(port, ip);
395
+ });
359
396
  }
360
397
 
361
398
  module.exports = {
362
- downloadFromWeb, highwayUploadStream, int32ip2str, highwayHttpUpload, MAX_UPLOAD_SIZE, getUploadChannel,
399
+ downloadFromWeb, highwayUploadStream, int32ip2str, highwayHttpUpload, MAX_UPLOAD_SIZE, getUploadChannel, checkIPConnectivity
363
400
  };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "oicq-icalingua-plus-plus",
3
3
  "app_name": "oicq-icalingua-plus-plus",
4
- "version": "1.24.39",
5
- "app_version": "1.24.39",
6
- "upday": "2024/08/07",
4
+ "version": "1.24.41",
5
+ "app_version": "1.24.41",
6
+ "upday": "2024/08/09",
7
7
  "protocol_version": "v11",
8
8
  "description": "oicq(https://github.com/takayama-lily/oicq) v1 for Icalingua++",
9
9
  "main": "index.js",