oicq-icalingua-plus-plus 1.26.13 → 1.26.16

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.
@@ -158,6 +158,7 @@ notice事件有部分格式默认与cqhttp中的格式不同。
158
158
  |群组文件|表现为CQ码 |notice.group_upload |
159
159
  |群戳一戳|notice.group.poke | |
160
160
  |群设置变|notice.group.setting | |
161
+ |表情回应|notice.group.reaction | |
161
162
  |元事件|meta_event.lifecycle.enable|meta_event.lifecycle.enable|
162
163
  |元事件|meta_event.lifecycle.disable|meta_event.lifecycle.disable|
163
164
  |元事件|meta_event.lifecycle.connect|meta_event.lifecycle.connect|
package/index.d.ts CHANGED
@@ -210,7 +210,7 @@ export interface MemberInfo extends MemberBaseInfo {
210
210
 
211
211
  /**
212
212
  * 使用cqhttp风格的消息元素类型
213
- * @see https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md 在此基础上进行了扩展
213
+ * @see https://github.com/botuniverse/onebot/blob/main/specs/interface/message/segments.md 在此基础上进行了扩展
214
214
  */
215
215
  export type MessageElem = TextElem | AtElem | FaceElem | BfaceElem | MfaceElem |
216
216
  ImgPttElem | LocationElem | MusicElem | ShareElem | JsonElem | XmlElem |
@@ -623,6 +623,15 @@ export interface GroupEssenceEventData extends CommonGroupNoticeEventData {
623
623
  operator_id: number, //操作者
624
624
  operator_name: string, //操作者昵称
625
625
  }
626
+ /** 群表情回应 */
627
+ export interface GroupReactionEvent extends CommonGroupNoticeEventData {
628
+ sub_type: "reaction"; //群表情回应
629
+ user_id: number; //设置回应者
630
+ id: string; //回应的表情id
631
+ type: number; //回应的表情类型
632
+ seq: number; //消息seq
633
+ set: boolean; //设置/取消回应
634
+ }
626
635
  export interface MemberIncreaseEventData extends CommonGroupNoticeEventData {
627
636
  sub_type: "increase", //群增加或群员增加
628
637
  group_id: number,
@@ -936,6 +945,8 @@ export interface EventMap {
936
945
  "notice.group.sign": (this: Client, data: GroupSignEventData) => void;
937
946
  /**群精华事件 */
938
947
  "notice.group.essence": (this: Client, data: GroupEssenceEventData) => void;
948
+ /**群表情回应事件 */
949
+ "notice.group.reaction": (this: Client, data: GroupReactionEventData) => void;
939
950
  /**群设置变更事件 */
940
951
  "notice.group.setting": (this: Client, data: GroupSettingEventData) => void;
941
952
  /**监听以上所有好友notice事件 */
@@ -1127,6 +1138,8 @@ export class Client extends EventEmitter {
1127
1138
  sendGroupSign(group_id: number): Promise<Ret>;
1128
1139
  /** 设置群备注名 */
1129
1140
  setGroupRemark(group_id: number, remark: string): Promise<Ret>;
1141
+ /** 设置群消息表情回应 */
1142
+ setEmojiReaction(group_id: number, msg_seq: number, id: string, type?: number, set?: boolean): Promise<Ret>;
1130
1143
 
1131
1144
  /** 处理好友请求 */
1132
1145
  setFriendAddRequest(flag: string, approve?: boolean, remark?: string, block?: boolean): Promise<Ret>;
package/lib/client.js CHANGED
@@ -550,6 +550,9 @@ class Client extends EventEmitter {
550
550
  setGroupRemark(group_id, remark = "") {
551
551
  return this._useProtocol(troop.setGroupRemark, arguments);
552
552
  }
553
+ setEmojiReaction(group_id, msg_seq, id, type = 1, set = true) {
554
+ return this._useProtocol(troop.setEmojiReaction, arguments);
555
+ }
553
556
 
554
557
  ///////////////////////////////////////////////////
555
558
 
@@ -456,6 +456,7 @@ async function getSI(user_id, no_cache = false) {
456
456
  * @returns {Promise<number>}
457
457
  */
458
458
  async function uid2uin(uid) {
459
+ if (Number.isInteger(Number(uid))) return Number(uid); // 纯数字的uid是误调用的uin,直接返回
459
460
  if (this.uid2uin_map.has(uid))
460
461
  return this.uid2uin_map.get(uid);
461
462
  const body = pb.encode({
package/lib/core/troop.js CHANGED
@@ -660,10 +660,33 @@ async function setFriendRemark(user_id, remark) {
660
660
  const rsp = jce.decode(blob);
661
661
  return { result: rsp[0] };
662
662
  }
663
+ /**
664
+ * 设置表情回应,参考(https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType)
665
+ * @this {import("./ref").Client}
666
+ * @param {number} group_id
667
+ * @param {number} msg_seq
668
+ * @param {string} id
669
+ * @param {number} type
670
+ * @param {boolean} set
671
+ * @returns {import("./ref").ProtocolResponse}
672
+ */
673
+ async function setEmojiReaction(group_id, msg_seq, id, type = 1, set = true) {
674
+ [group_id] = uinAutoCheck(group_id);
675
+ const body = pb.encode({
676
+ 2: group_id,
677
+ 3: msg_seq,
678
+ 4: String(id),
679
+ 5: type,
680
+ });
681
+ const cmd = set ? "OidbSvcTrpcTcp.0x9082_1" : "OidbSvcTrpcTcp.0x9082_2";
682
+ const blob = await this.sendOidb(cmd, body);
683
+ const rsp = pb.decode(blob);
684
+ return { result: rsp[3] };
685
+ }
663
686
 
664
687
  module.exports = {
665
688
  setAdmin, setTitle, setCard, setting, setAnonymous, muteAnonymous, getGroupNotice,
666
689
  kickMember, muteMember, pokeMember, quitGroup, addGroup, inviteFriend,
667
690
  setProfile, setSignature, addFriend, delFriend, setPortrait, setGroupPortrait,
668
- groupSign, getSignature, setGroupRemark, setFriendRemark
691
+ groupSign, getSignature, setGroupRemark, setFriendRemark, setEmojiReaction,
669
692
  };
@@ -1087,16 +1087,18 @@ async function parseForwardMsg(resid, fileName) {
1087
1087
  /**
1088
1088
  * @type {import("../ref").Msg[]}
1089
1089
  */
1090
- let msgs = pb.decode(blob)[2];
1090
+ let msgs = pb.decode(blob)[2], flag = false;
1091
1091
  if (!Array.isArray(msgs))
1092
1092
  msgs = [msgs];
1093
1093
  for (let msg1 of msgs) {
1094
1094
  const m_fileName = msg1[1].toString();
1095
1095
  if (m_fileName === fileName) {
1096
1096
  msgs = msg1;
1097
+ flag = true;
1097
1098
  break;
1098
1099
  }
1099
1100
  }
1101
+ if (!flag) throw new Error("wrong fileName");
1100
1102
  if (Array.isArray(msgs))
1101
1103
  msgs = msgs[0];
1102
1104
  msgs = msgs[2][1];
package/lib/onlinepush.js CHANGED
@@ -6,7 +6,7 @@
6
6
  const pb = require("./algo/pb");
7
7
  const jce = require("./algo/jce");
8
8
  const sysmsg = require("./core/sysmsg");
9
- const { parseC2CMsg ,parseGroupMsg, parseDiscussMsg } = require("./message/parser");
9
+ const { parseC2CMsg, parseGroupMsg, parseDiscussMsg } = require("./message/parser");
10
10
  const { genC2CMessageId, genGroupMessageId, log, uin2code, BUF0, timestamp } = require("./common");
11
11
  const { updateFL, uid2uin } = require("./core/friendlist");
12
12
 
@@ -62,14 +62,8 @@ const sub0x27 = {
62
62
  5: async function (data, time) {
63
63
  let user_id = data[14][1];
64
64
  let nickname = "";
65
- if (typeof user_id !== "number") {
66
- return; //似乎还有老的推送,暂时没必要转换
67
- if (this.config.useNT) {
68
- //nt uid
69
- user_id = await uid2uin.call(this, String(user_id));
70
- } else {
71
- return;
72
- }
65
+ if (this.config.useNT) {
66
+ user_id = await uid2uin.call(this, user_id);
73
67
  }
74
68
  try {
75
69
  nickname = this.fl.get(user_id).nickname;
@@ -80,50 +74,58 @@ const sub0x27 = {
80
74
  user_id, nickname, time
81
75
  });
82
76
  },
83
- 20: function (data, time) {
77
+ 20: async function (data, time) {
84
78
  // 20002昵称 20009性别 20031生日 23109农历生日 20019说明 20032地区 24002故乡 27372在线状态
85
- const user_id = data[8][1];
86
- let o = data[8][2];
87
- if (Array.isArray(o)) {
88
- o = o[0];
79
+ let user_id = data[8][1];
80
+ if (this.config.useNT) {
81
+ user_id = await uid2uin.call(this, String(user_id));
89
82
  }
90
- let key, value;
91
- if (o[1] === 20002) {
92
- key = "nickname";
93
- value = String(o[2]);
94
- } else if (o[1] === 20009) {
95
- key = "sex";
96
- value = ["unknown", "male", "female"][o[2].toBuffer()[0]];
97
- } else if (o[1] === 20031) {
98
- key = "age";
99
- value = new Date().getFullYear() - o[2].toBuffer().readUInt16BE();
100
- } else if (o[1] === 20019) {
101
- key = "description";
102
- value = String(o[2]);
103
- } else if (o[1] === 27372 && user_id === this.uin) {
104
- const status = o[2].toBuffer()[o[2].toBuffer().length - 1];
105
- const old_status = this.online_status, new_status = status_map[status] || 11;
106
- this.online_status = new_status;
107
- if (old_status !== new_status)
108
- this.em("sync.status", { old_status, new_status });
109
- return
110
- } else {
111
- return;
83
+ let list = data[8][2];
84
+ if (!Array.isArray(list)) {
85
+ list = [list];
112
86
  }
113
- try {
114
- this.fl.get(user_id)[key] = value;
115
- } catch (e) { }
116
- if (user_id === this.uin) {
117
- this[key] = value;
118
- this.em("sync.profile", { [key]: value });
119
- } else {
120
- const e = { user_id, time };
121
- e[key] = value;
122
- this.em("notice.friend.profile", e);
87
+ for (let o of list) {
88
+ let key, value;
89
+ if (o[1] === 20002) {
90
+ key = "nickname";
91
+ value = String(o[2]);
92
+ } else if (o[1] === 20009) {
93
+ key = "sex";
94
+ value = ["unknown", "male", "female"][o[2].toBuffer()[0]];
95
+ } else if (o[1] === 20031) {
96
+ key = "age";
97
+ value = new Date().getFullYear() - o[2].toBuffer().readUInt16BE();
98
+ } else if (o[1] === 20019) {
99
+ key = "description";
100
+ value = String(o[2]);
101
+ } else if (o[1] === 27372 && user_id === this.uin) {
102
+ const status = o[2].toBuffer()[o[2].toBuffer().length - 1];
103
+ const old_status = this.online_status, new_status = status_map[status] || 11;
104
+ this.online_status = new_status;
105
+ if (old_status !== new_status)
106
+ this.em("sync.status", { old_status, new_status });
107
+ continue;
108
+ } else {
109
+ continue;
110
+ }
111
+ try {
112
+ this.fl.get(user_id)[key] = value;
113
+ } catch (e) { }
114
+ if (user_id === this.uin) {
115
+ this[key] = value;
116
+ this.em("sync.profile", { [key]: value });
117
+ } else {
118
+ const e = { user_id, time };
119
+ e[key] = value;
120
+ this.em("notice.friend.profile", e);
121
+ }
123
122
  }
124
123
  },
125
- 60: function (data, time) {
126
- const user_id = data[10][1];
124
+ 60: async function (data, time) {
125
+ let user_id = data[10][1];
126
+ if (this.config.useNT) {
127
+ user_id = await uid2uin.call(this, String(user_id));
128
+ }
127
129
  const sign = String(data[10][2]);
128
130
  try {
129
131
  this.fl.get(user_id).signature = sign;
@@ -137,9 +139,13 @@ const sub0x27 = {
137
139
  });
138
140
  }
139
141
  },
140
- 40: function (data, time) {
142
+ 40: async function (data, time) {
141
143
  try {
142
- const o = data[9][1], user_id = o[2];
144
+ const o = data[9][1];
145
+ let user_id = o[2];
146
+ if (this.config.useNT) {
147
+ user_id = await uid2uin.call(this, String(user_id));
148
+ }
143
149
  const remark = String(o[3]);
144
150
  // 0好友备注 1群备注
145
151
  if (o[1] === 1) {
@@ -151,15 +157,16 @@ const sub0x27 = {
151
157
  }
152
158
  } catch (e) { }
153
159
  },
154
- 21: function (data, time) {
155
- if (data[11][1] === 0) {
156
- this.em("sync.profile", { avatar: true });
157
- } else if (data[11][1] === 1) {
160
+ 21: async function (data, time) {
161
+ if (data[11] && data[11][1] === 1) {
162
+ const user_id = data[11][4]; //这里NT上线也是uin,不需要转换
158
163
  this.em("notice.group.setting", {
159
164
  group_id: data[11][3], time,
160
- user_id: data[11][4],
165
+ user_id: user_id,
161
166
  avatar: true,
162
167
  });
168
+ } else {
169
+ this.em("sync.profile", { avatar: true });
163
170
  }
164
171
  }
165
172
  };
@@ -193,14 +200,8 @@ const push528 = {
193
200
  0xB3: async function (buf, time) {
194
201
  const data = pb.decode(buf)[2];
195
202
  let user_id = data[1], nickname = String(data[5]);
196
- if (typeof user_id !== "number") {
197
- return; //似乎还有老的推送,暂时没必要转换
198
- if (this.config.useNT) {
199
- //nt uid
200
- user_id = await uid2uin.call(this, String(user_id));
201
- } else {
202
- return;
203
- }
203
+ if (this.config.useNT) {
204
+ user_id = await uid2uin.call(this, String(user_id));
204
205
  }
205
206
  this.fl.set(user_id, {
206
207
  user_id, nickname,
@@ -248,13 +249,8 @@ const push528 = {
248
249
  0x115: async function (buf, time) {
249
250
  const data = pb.decode(buf);
250
251
  let user_id = data[1];
251
- if (typeof user_id !== "number") {
252
- if (this.config.useNT) {
253
- //nt uid
254
- user_id = await uid2uin.call(this, String(user_id));
255
- } else {
256
- return;
257
- }
252
+ if (this.config.useNT) {
253
+ user_id = await uid2uin.call(this, String(user_id));
258
254
  }
259
255
  const end = data[3][4] === 2;
260
256
  this.em("internal.input", { user_id, end });
@@ -282,19 +278,19 @@ function parsePoke(data) {
282
278
  for (let o of data[7]) {
283
279
  const name = String(o[1]), val = String(o[2]);
284
280
  switch (name) {
285
- case "action_str":
286
- case "alt_str1":
287
- action = action || val;
288
- break;
289
- case "uin_str1":
290
- operator_id = parseInt(val);
291
- break;
292
- case "uin_str2":
293
- user_id = parseInt(val);
294
- break;
295
- case "suffix_str":
296
- suffix = val;
297
- break;
281
+ case "action_str":
282
+ case "alt_str1":
283
+ action = action || val;
284
+ break;
285
+ case "uin_str1":
286
+ operator_id = parseInt(val);
287
+ break;
288
+ case "uin_str2":
289
+ user_id = parseInt(val);
290
+ break;
291
+ case "suffix_str":
292
+ suffix = val;
293
+ break;
298
294
  }
299
295
  }
300
296
  if (!operator_id)
@@ -310,15 +306,15 @@ function parseSign(data) {
310
306
  for (let o of data[7]) {
311
307
  const name = String(o[1]), val = String(o[2]);
312
308
  switch (name) {
313
- case "user_sign":
314
- sign_text = sign_text || val;
315
- break;
316
- case "mqq_uin":
317
- user_id = parseInt(val);
318
- break;
319
- case "mqq_nick":
320
- nickname = val;
321
- break;
309
+ case "user_sign":
310
+ sign_text = sign_text || val;
311
+ break;
312
+ case "mqq_uin":
313
+ user_id = parseInt(val);
314
+ break;
315
+ case "mqq_nick":
316
+ nickname = val;
317
+ break;
322
318
  }
323
319
  }
324
320
  return { user_id, nickname, sign_text };
@@ -330,11 +326,12 @@ function parseSign(data) {
330
326
  * @param {string} field
331
327
  * @param {boolean} enable
332
328
  * @param {number} time
329
+ * @param {number} user_id
333
330
  */
334
- function onGroupSetting(group_id, field, enable, time) {
331
+ function onGroupSetting(group_id, field, enable, time, user_id) {
335
332
  if (!field) return;
336
333
  const e = {
337
- group_id, time
334
+ group_id, time, user_id
338
335
  };
339
336
  e[field] = !!enable;
340
337
  this.em("notice.group.setting", e);
@@ -344,10 +341,18 @@ function onGroupSetting(group_id, field, enable, time) {
344
341
  * @type {({[k: number]: (this: import("./ref").Client, group_id: number, buf: Buffer, time: number) => void})}
345
342
  */
346
343
  const push732 = {
347
- 0x0C: function (group_id, buf, time) {
348
- const operator_id = buf.readUInt32BE(6);
349
- const user_id = buf.readUInt32BE(16);
350
- let duration = buf.readUInt32BE(20);
344
+ 0x0C: async function (group_id, buf, time) {
345
+ let operator_id, user_id, duration;
346
+ if (this.config.useNT) {
347
+ const o = pb.decode(buf);
348
+ operator_id = await uid2uin.call(this, String(o[4]));
349
+ user_id = o[5][3][1] ? await uid2uin.call(this, String(o[5][3][1])) : 0;
350
+ duration = o[5][3][2];
351
+ } else {
352
+ operator_id = buf.readUInt32BE(6);
353
+ user_id = buf.readUInt32BE(16);
354
+ duration = buf.readUInt32BE(20);
355
+ }
351
356
  try {
352
357
  if (user_id === 0) {
353
358
  duration = duration ? 0xffffffff : 0;
@@ -369,10 +374,8 @@ const push732 = {
369
374
  let user_id = msg[6];
370
375
  if (this.config.useNT) {
371
376
  //nt uid
372
- //await this.getGroupMemberList(group_id);
373
- user_id = String(user_id), operator_id = String(operator_id);
374
- user_id = await uid2uin.call(this, user_id);
375
- operator_id = await uid2uin.call(this, operator_id);
377
+ user_id = await uid2uin.call(this, String(user_id));
378
+ operator_id = await uid2uin.call(this, String(operator_id));
376
379
  }
377
380
  const message_id = genGroupMessageId(group_id, user_id, msg[1], msg[3], msg[2], Array.isArray(data[3]) ? data[3].length : 1);
378
381
  this.em("notice.group.recall", {
@@ -437,20 +440,66 @@ const push732 = {
437
440
  var enable = buf[8] === 0x0 || buf[8] === 0x20;
438
441
  onGroupSetting.call(this, group_id, field, enable, time);
439
442
  },
440
- 0x10: function (group_id, buf, time) {
441
- if (buf[6] === 0x22) {
442
- let field;
443
- if (buf[buf.length - 2] === 0x08)
444
- field = "enable_show_honor";
445
- if (buf[buf.length - 2] === 0x10)
446
- field = "enable_show_level";
447
- let enable = buf[buf.length - 1] === 0;
448
- return onGroupSetting.call(this, group_id, field, enable, time);
449
- }
443
+ 0x10: async function (group_id, buf, time) {
450
444
  if (buf[6] === 0x26) {
451
445
  // 改群分类 <Buffer 44 25 6e 9f 10 00 26 08 18 10 96 8a e3 fa 05 18 ff ff ff ff 0f 20 9f dd 95 a1 04 68 17 a8 01 f5 ef e8 b1 01 f2 01 06 18 8c 04 40 9a 4e>
452
446
  }
453
447
  const sub = pb.decode(buf.slice(7));
448
+ const sub_type = sub[13];
449
+ switch (sub_type) {
450
+ case 23: { // 群荣誉相关设置
451
+ const data = sub[30];
452
+ const field_map = {
453
+ 1: "enable_show_honor",
454
+ 2: "enable_show_level",
455
+ };
456
+ for (let field of Object.keys(field_map)) {
457
+ if (data[field] != null) {
458
+ const enable = data[field] === 0;
459
+ let user_id = sub[21];
460
+ if (this.config.useNT) {
461
+ user_id = await uid2uin.call(this, String(user_id));
462
+ }
463
+ onGroupSetting.call(this, group_id, field_map[field], enable, time, user_id);
464
+ }
465
+ }
466
+ return;
467
+ }
468
+ case 12: { // 群名称修改
469
+ let user_id = sub[21];
470
+ if (this.config.useNT) {
471
+ user_id = await uid2uin.call(this, String(user_id));
472
+ }
473
+ const group_name = String(sub[5][2]);
474
+ try {
475
+ this.gl.get(group_id).group_name = group_name;
476
+ } catch (e) { }
477
+ return this.em("notice.group.setting", {
478
+ group_id, time,
479
+ user_id: user_id,
480
+ group_name: group_name,
481
+ });
482
+ return;
483
+ }
484
+ case 35: { // 消息回应
485
+ const data = sub[44];
486
+ let user_id = data[1][1][3][4];
487
+ if (this.config.useNT) {
488
+ user_id = await uid2uin.call(this, String(user_id));
489
+ }
490
+ const set = data[1][1][3][5] === 1;
491
+ return this.em("notice.group.reaction", {
492
+ group_id, time,
493
+ user_id: user_id,
494
+ id: String(data[1][1][3][1]),
495
+ type: data[1][1][3][2],
496
+ seq: data[1][1][2][1],
497
+ set,
498
+ });
499
+ }
500
+ default:
501
+ break;
502
+ }
454
503
  if (sub[5] && sub[5][2]) {
455
504
  let str = String(sub[5][2]);
456
505
  if (str.includes("获得群主授予的")) {
@@ -495,15 +544,7 @@ const push732 = {
495
544
  }
496
545
 
497
546
  let field, enable;
498
- if (sub[13] === 12) {
499
- try {
500
- this.gl.get(group_id).group_name = str;
501
- } catch (e) { }
502
- return this.em("notice.group.setting", {
503
- group_id, time,
504
- user_id: sub[21],
505
- group_name: str,
506
- });
547
+ if (sub[13]) {
507
548
  } else if (str.includes("坦白说")) {
508
549
  field = "enable_confess";
509
550
  enable = str.includes("开启");
@@ -542,6 +583,7 @@ function onlinePushListener(blob, seq) {
542
583
  this.uin, v[1], v[3], v[8], 0, 0, 0, 0, 0, 0, 0
543
584
  ]));
544
585
  if (!this.sync_finished) continue;
586
+ if (this.config.useNT) continue; // NT上线忽略老推送
545
587
  const time = v[5];
546
588
  if (v[2] === 528) {
547
589
  const decoded = jce.decodeNested(v[6]);
@@ -566,6 +608,7 @@ function onlinePushTransListener(blob, seq) {
566
608
  const push = pb.decode(blob);
567
609
  handleOnlinePush.call(this, push[11], seq);
568
610
  if (!this.sync_finished) return;
611
+ if (this.config.useNT) return; // NT上线忽略老推送
569
612
  const time = push[8];
570
613
  const buf = push[10].toBuffer();
571
614
  const group_id = buf.readUInt32BE();
@@ -671,7 +714,7 @@ async function groupMsgListener(blob, seq) {
671
714
  //重组分片消息
672
715
  if (content[2] === 0)
673
716
  this.emit(`interval.${group_id}.${body[1][1][3]}`, message_id);
674
- msg = rebuildFragments(msg);
717
+ msg = rebuildFragments(msg, group_id, this.uin, user_id);
675
718
  if (!msg)
676
719
  return;
677
720
  } else {
@@ -728,14 +771,17 @@ const FRAG = new Map;
728
771
  * 1.是最后一个分片,返回组装好的消息
729
772
  * 2.不是最后一个分片,返回空
730
773
  * @param {import("./ref").Msg} msg
774
+ * @param {number} chat_id
775
+ * @param {number} self_id
776
+ * @param {number} user_id
731
777
  * @returns {import("./ref").Msg}
732
778
  */
733
- function rebuildFragments(msg) {
779
+ function rebuildFragments(msg, chat_id, self_id, user_id) {
734
780
  const head = msg[1], content = msg[2], body = msg[3];
735
- const cnt = content[1], index = content[2], div = content[3];
736
- const id = head[1] + "-" + head[2] + "-" + div;
781
+ const cnt = content[1] || 0, index = content[2] || 0, div = content[3] || 0;
782
+ const id = self_id + "-" + user_id + "-" + chat_id + "-" + div; //self_id-user_id-chat_id-div
737
783
  if (!FRAG.has(id)) {
738
- FRAG.set(id, {3: new Array(cnt)});
784
+ FRAG.set(id, { 3: new Array(cnt) });
739
785
  setTimeout(() => {
740
786
  FRAG.delete(id);
741
787
  }, 5000);
@@ -771,6 +817,20 @@ async function NTMsgListener(blob, seq, sso) {
771
817
  const o = pb.decode(blob);
772
818
  const type = o[1][2] ? o[1][2][1] : 114514;
773
819
  //this.logger.debug("NT message type", type);
820
+ if (o[4]) {
821
+ let trans_info = BUF0;
822
+ try {
823
+ const trans_info = pb.decode(sso.reserved)[23] || BUF0;
824
+ } catch (e) {
825
+ this.logger.debug(e);
826
+ }
827
+ this.writeUni("trpc.msg.olpush.OlPushService.SsoPushAck", pb.encode({
828
+ 1: o[4],
829
+ }), undefined, {
830
+ trans_info: trans_info,
831
+ nt_core: true,
832
+ });
833
+ }
774
834
  switch (type) {
775
835
  case 82: //群聊消息
776
836
  if (!this.sync_finished) return;
@@ -782,8 +842,19 @@ async function NTMsgListener(blob, seq, sso) {
782
842
  const user_id = head[1], time = head[6], seq = head[5];
783
843
  const group_id = head[9][1], random = body[1][1][3];
784
844
  const message_id = genGroupMessageId(group_id, user_id, seq, random, time, content[1]);
785
- //懒得搞分片消息了
786
- this.emit(`interval.${group_id}.${body[1][1][3]}`, message_id);
845
+
846
+ if (content[1] > 1) {
847
+ //重组分片消息
848
+ if (content[2] === 0)
849
+ this.emit(`interval.${group_id}.${body[1][1][3]}`, message_id);
850
+ msg = rebuildFragments(msg, group_id, this.uin, user_id);
851
+ if (!msg)
852
+ return;
853
+ } else {
854
+ //非分片消息
855
+ this.emit(`interval.${group_id}.${body[1][1][3]}`, message_id);
856
+ }
857
+
787
858
  ++this.stat.recv_msg_cnt;
788
859
  //解析消息
789
860
  const data = await parseGroupMsg.call(this, msg, true);
@@ -859,6 +930,17 @@ async function NTMsgListener(blob, seq, sso) {
859
930
  if (!this.sync_finished) return;
860
931
  try {
861
932
  const msg = c2cNTMsgConverter.call(this, o[1]);
933
+ const user_id = msg[1][1];
934
+
935
+ if (msg[2][1] > 1) {
936
+ //重组分片消息
937
+ msg = rebuildFragments(msg, -user_id, this.uin, user_id);
938
+ if (!msg)
939
+ return;
940
+ } else {
941
+ //非分片消息
942
+ }
943
+
862
944
  const data = await parseC2CMsg.call(this, msg, true);
863
945
  if (data.user_id === this.uin) {
864
946
  data.user_id = msg[1][2];
@@ -898,8 +980,6 @@ async function NTMsgListener(blob, seq, sso) {
898
980
  const group_uin = o[1][1][1];
899
981
  const group_id = uin2code(group_uin);
900
982
  const type = o[1][2][3], buf = o[1][3][2].toBuffer(), time = o[1][2][6];
901
- const no_parse = [0x0C, 0x10]; //这些也会推送jce的老包,就不管其只有uid的nt推送了
902
- if (no_parse.includes(type)) break;
903
983
  if (!this.sync_finished) throw new Error("sync not finished");
904
984
  if (typeof push732[type] === "function")
905
985
  push732[type].call(this, group_id, buf, time);
@@ -907,6 +987,75 @@ async function NTMsgListener(blob, seq, sso) {
907
987
  this.logger.debug(e);
908
988
  }
909
989
  break;
990
+ case 44: //群管理权限变动
991
+ try {
992
+ const group_id = uin2code(o[1][1][1]);
993
+ const msg = o[1][3][2], time = o[1][2][6];
994
+ const type = msg[3];
995
+ if (type === 0 || type === 1) {
996
+ const data = msg[4][type === 0 ? 1 : 2];
997
+ const user_id = await uid2uin.call(this, String(data[1]));
998
+ const set = data[2] === 1;
999
+ try {
1000
+ (await this.getGroupMemberInfo(group_id, user_id)).data.role = (set ? "admin" : "member");
1001
+ } catch (e) { }
1002
+ this.em("notice.group.admin", {
1003
+ group_id, user_id, set, time
1004
+ });
1005
+ } else if (type === 0xFF) {
1006
+ const operator_id = await uid2uin.call(this, String(msg[4][3][1])),
1007
+ user_id = await uid2uin.call(this, String(msg[4][3][2]));
1008
+ try {
1009
+ this.gl.get(group_id).owner_id = user_id;
1010
+ (await this.getGroupMemberInfo(group_id, operator_id)).data.role = "member";
1011
+ (await this.getGroupMemberInfo(group_id, user_id)).data.role = "owner";
1012
+ } catch (e) { }
1013
+ this.em("notice.group.transfer", {
1014
+ group_id, operator_id, user_id, time
1015
+ });
1016
+ }
1017
+ } catch (e) {
1018
+ this.logger.debug(e);
1019
+ }
1020
+ break;
1021
+ case 34: //群成员离开
1022
+ try {
1023
+ const group_id = uin2code(o[1][1][1]);
1024
+ const msg = o[1][3][2], time = o[1][2][6];
1025
+ const user_id = await uid2uin.call(this, String(msg[3]));
1026
+ const exit_type = msg[4];
1027
+ let operator_id, dismiss = false, member, group;
1028
+ if (exit_type === 0x82 || exit_type === 0x2) {
1029
+ operator_id = user_id;
1030
+ try {
1031
+ this.gml.get(group_id).delete(user_id);
1032
+ } catch { }
1033
+ } else {
1034
+ operator_id = msg[5] && msg[5][1] ? await uid2uin.call(this, String(msg[5][1][1])) : user_id;
1035
+ if (exit_type === 0x01 || exit_type === 0x81)
1036
+ dismiss = true;
1037
+ if (user_id === this.uin) {
1038
+ group = this.gl.get(group_id);
1039
+ this.gl.delete(group_id);
1040
+ this.gml.delete(group_id);
1041
+ this.logger.info(`更新了群列表,删除了群:${group_id}`);
1042
+ } else {
1043
+ try {
1044
+ this.gml.get(group_id).delete(user_id);
1045
+ } catch { }
1046
+ this.logger.info(`${user_id}离开了群${group_id}`);
1047
+ }
1048
+ }
1049
+ try {
1050
+ this.gl.get(group_id).member_count--;
1051
+ } catch { }
1052
+ this.em("notice.group.decrease", {
1053
+ group_id, user_id, operator_id, dismiss, member, group, time
1054
+ });
1055
+ } catch (e) {
1056
+ this.logger.debug(e);
1057
+ }
1058
+ break;
910
1059
  case 114514: // 1919810
911
1060
  // 部分消息没有content
912
1061
  break;
@@ -914,20 +1063,6 @@ async function NTMsgListener(blob, seq, sso) {
914
1063
  this.logger.warn("Unknown NT message type", type);
915
1064
  break;
916
1065
  }
917
- if (o[4]) {
918
- let trans_info = BUF0;
919
- try {
920
- const trans_info = pb.decode(sso.reserved)[23] || BUF0;
921
- } catch (e) {
922
- this.logger.debug(e);
923
- }
924
- this.writeUni("trpc.msg.olpush.OlPushService.SsoPushAck", pb.encode({
925
- 1: o[4],
926
- }), undefined, {
927
- trans_info: trans_info,
928
- nt_core: true,
929
- });
930
- }
931
1066
  }
932
1067
 
933
1068
  /**
@@ -949,6 +1084,8 @@ function groupNTMsgConverter(msg) {
949
1084
  header[11] = headerNT[4];
950
1085
 
951
1086
  content[1] = contentNT[7];
1087
+ content[2] = contentNT[8];
1088
+ content[3] = contentNT[9];
952
1089
 
953
1090
  return {
954
1091
  1: header,
@@ -981,6 +1118,9 @@ function c2cNTMsgConverter(msg) {
981
1118
  return {
982
1119
  1: header,
983
1120
  2: {
1121
+ 1: contentNT[7],
1122
+ 2: contentNT[8],
1123
+ 3: contentNT[9],
984
1124
  4: contentNT[10], //auto_reply
985
1125
  },
986
1126
  3: body,
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.26.13",
5
- "app_version": "1.26.13",
6
- "upday": "2026/07/31",
4
+ "version": "1.26.16",
5
+ "app_version": "1.26.16",
6
+ "upday": "2026/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",