@zhin.js/adapter-icqq 1.1.12 → 1.1.14

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
+ ## 1.1.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 860faa0: Group and private share cards are now confirmed with a real message id: ICQQ's share API returns none, so after an accepted share the adapter reads the conversation history for the bot's own newest card (once more after a short wait). When it cannot be found the delivery stays unconfirmed as before; no id is invented.
8
+ - Updated dependencies [974fa85]
9
+ - Updated dependencies [1e9b3d6]
10
+ - @zhin.js/core@1.1.49
11
+
12
+ ## 1.1.13
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [db06de8]
17
+ - @zhin.js/core@1.1.48
18
+
3
19
  ## 1.1.12
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -184,7 +184,7 @@ MIT
184
184
 
185
185
  发送成功回执只接受平台真实消息 ID。网络超时、断线、缺少消息 ID 的响应会报告 `deliveryUnknown`,框架不会自动重发;手动重发可能重复发送。
186
186
 
187
- ICQQ 原生分享和文件 API 可能已经成功,但只返回空值或文件 ID;这类结果不能当作可引用、可撤回的消息 ID,因此报告投递结果未知。平台操作仍只执行一次。文件撤回应使用平台专用文件 API。
187
+ ICQQ 原生分享和文件 API 可能已经成功,但只返回空值或文件 ID;这类结果不能当作可引用、可撤回的消息 ID。群聊和私聊的分享卡片发出后,适配器会读一次会话记录,找到机器人自己刚发出的卡片消息,用它的真实消息 ID 作为回执(记录有延迟时半秒后再读一次)。找不到时(以及临时会话、频道和文件)仍报告投递结果未知,不编造 ID。平台操作仍只执行一次。文件撤回应使用平台专用文件 API。
188
188
 
189
189
  ### 独立元素按顺序拆分发送
190
190
 
package/lib/endpoint.js CHANGED
@@ -331,15 +331,42 @@ export class IcqqEndpoint extends Endpoint {
331
331
  prepared.dispose();
332
332
  }
333
333
  }
334
+ /**
335
+ * ICQQ's share API returns no message id; a resolved call only means the server accepted it.
336
+ * The card is the bot's own newest card message in the conversation history, so read the id
337
+ * from there (once more after a short wait, history can lag). Undefined keeps the delivery
338
+ * unconfirmed rather than inventing an id.
339
+ */
340
+ async #findOwnCard(history, sentAt) {
341
+ const self = Number(this.client.uin);
342
+ for (const wait of [0, 500]) {
343
+ if (wait)
344
+ await new Promise(resolve => setTimeout(resolve, wait));
345
+ // A missing or throwing history API must not turn an accepted share into a send failure.
346
+ const messages = await Promise.resolve().then(history).catch(() => []);
347
+ const card = [...messages].reverse().find(item => Number(item.sender?.user_id ?? item.user_id) === self
348
+ && (item.time ?? 0) >= sentAt - 2
349
+ && (item.message ?? []).some(element => element.type === 'json' || element.type === 'xml' || element.type === 'share'));
350
+ if (card && card.message_id != null && String(card.message_id).trim())
351
+ return String(card.message_id);
352
+ }
353
+ return undefined;
354
+ }
334
355
  async #sendNative(target, message) {
335
356
  if (isIcqqShareElement(message)) {
336
357
  switch (target.kind) {
337
- case 'private':
338
- await this.client.pickFriend(target.userId).share(message);
339
- return {};
340
- case 'group':
341
- await this.client.pickGroup(target.groupId).share(message);
342
- return {};
358
+ case 'private': {
359
+ const sentAt = Math.floor(Date.now() / 1000);
360
+ const friend = this.client.pickFriend(target.userId);
361
+ await friend.share(message);
362
+ return { message_id: await this.#findOwnCard(() => friend.getChatHistory(), sentAt) };
363
+ }
364
+ case 'group': {
365
+ const sentAt = Math.floor(Date.now() / 1000);
366
+ const group = this.client.pickGroup(target.groupId);
367
+ await group.share(message);
368
+ return { message_id: await this.#findOwnCard(() => group.getChatHistory(), sentAt) };
369
+ }
343
370
  case 'temp':
344
371
  await this.client.pickMember(target.groupId, target.userId).share(message);
345
372
  return {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-icqq",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "Zhin.js ICQQ adapter — direct connection to @icqqjs/icqq",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@zhin.js/adapter": "1.1.21",
37
- "@zhin.js/core": "1.1.47",
37
+ "@zhin.js/agent-feature": "1.1.8",
38
+ "@zhin.js/core": "1.1.49",
38
39
  "@zhin.js/feature-kit": "1.1.7",
39
40
  "@zhin.js/im-contract": "1.1.5",
40
- "@zhin.js/agent-feature": "1.1.8",
41
41
  "@zhin.js/logger": "1.1.2",
42
42
  "@zhin.js/permission": "1.1.7",
43
43
  "@zhin.js/skill": "1.1.8",
@@ -48,9 +48,9 @@
48
48
  "zod": "^4.0.0",
49
49
  "@zhin.js/adapter": "^1.1.21",
50
50
  "@zhin.js/command": "^1.1.9",
51
- "@zhin.js/core": "^1.1.47",
51
+ "@zhin.js/core": "^1.1.49",
52
52
  "@zhin.js/prompt-section": "^1.1.7",
53
- "zhin.js": "^1.1.12"
53
+ "zhin.js": "^1.1.14"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "@icqqjs/icqq": {
@@ -76,7 +76,7 @@
76
76
  "vitest": "^4.1.10",
77
77
  "zod": "^4.4.3",
78
78
  "@zhin.js/prompt-section": "1.1.7",
79
- "zhin.js": "1.1.12"
79
+ "zhin.js": "1.1.14"
80
80
  },
81
81
  "files": [
82
82
  "adapters",
package/src/endpoint.ts CHANGED
@@ -389,18 +389,47 @@ export class IcqqEndpoint extends Endpoint<Client> {
389
389
  }
390
390
  }
391
391
 
392
+ /**
393
+ * ICQQ's share API returns no message id; a resolved call only means the server accepted it.
394
+ * The card is the bot's own newest card message in the conversation history, so read the id
395
+ * from there (once more after a short wait, history can lag). Undefined keeps the delivery
396
+ * unconfirmed rather than inventing an id.
397
+ */
398
+ async #findOwnCard(
399
+ history: () => Promise<readonly { message_id?: unknown; time?: number; message?: readonly { type?: string }[]; sender?: { user_id?: number }; user_id?: number }[]>,
400
+ sentAt: number,
401
+ ): Promise<string | undefined> {
402
+ const self = Number(this.client.uin);
403
+ for (const wait of [0, 500]) {
404
+ if (wait) await new Promise(resolve => setTimeout(resolve, wait));
405
+ // A missing or throwing history API must not turn an accepted share into a send failure.
406
+ const messages = await Promise.resolve().then(history).catch(() => []);
407
+ const card = [...messages].reverse().find(item => Number(item.sender?.user_id ?? item.user_id) === self
408
+ && (item.time ?? 0) >= sentAt - 2
409
+ && (item.message ?? []).some(element => element.type === 'json' || element.type === 'xml' || element.type === 'share'));
410
+ if (card && card.message_id != null && String(card.message_id).trim()) return String(card.message_id);
411
+ }
412
+ return undefined;
413
+ }
414
+
392
415
  async #sendNative(
393
416
  target: ReturnType<typeof icqqOutboundTarget>,
394
417
  message: Sendable,
395
418
  ): Promise<{ message_id?: unknown }> {
396
419
  if (isIcqqShareElement(message)) {
397
420
  switch (target.kind) {
398
- case 'private':
399
- await this.client.pickFriend(target.userId).share(message);
400
- return {};
401
- case 'group':
402
- await this.client.pickGroup(target.groupId).share(message);
403
- return {};
421
+ case 'private': {
422
+ const sentAt = Math.floor(Date.now() / 1000);
423
+ const friend = this.client.pickFriend(target.userId);
424
+ await friend.share(message);
425
+ return { message_id: await this.#findOwnCard(() => friend.getChatHistory(), sentAt) };
426
+ }
427
+ case 'group': {
428
+ const sentAt = Math.floor(Date.now() / 1000);
429
+ const group = this.client.pickGroup(target.groupId);
430
+ await group.share(message);
431
+ return { message_id: await this.#findOwnCard(() => group.getChatHistory(), sentAt) };
432
+ }
404
433
  case 'temp':
405
434
  await this.client.pickMember(target.groupId, target.userId).share(message);
406
435
  return {};