@zhin.js/adapter-icqq 1.0.71 → 1.1.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 (111) hide show
  1. package/CHANGELOG.md +1125 -52
  2. package/README.md +116 -431
  3. package/adapters/icqq.js +31 -0
  4. package/adapters/icqq.ts +42 -0
  5. package/agent/PERMITS.md +24 -0
  6. package/agent/prompt-sections/platform.ts +17 -0
  7. package/commands/endpoint/add/[[id]].js +3 -0
  8. package/commands/endpoint/add/[[id]].ts +3 -0
  9. package/commands/endpoint/list.js +3 -0
  10. package/commands/endpoint/list.ts +3 -0
  11. package/commands/endpoint/remove/[id].js +3 -0
  12. package/commands/endpoint/remove/[id].ts +3 -0
  13. package/lib/client.d.ts +19 -0
  14. package/lib/client.js +3 -0
  15. package/lib/cq-message.d.ts +14 -0
  16. package/lib/cq-message.js +106 -0
  17. package/lib/endpoint.d.ts +31 -0
  18. package/lib/endpoint.js +1046 -0
  19. package/lib/forward-msg.d.ts +6 -0
  20. package/lib/forward-msg.js +107 -0
  21. package/lib/icqq-endpoint-commands.d.ts +1 -0
  22. package/lib/icqq-endpoint-commands.js +26 -0
  23. package/lib/icqq-guild.d.ts +57 -0
  24. package/lib/icqq-guild.js +109 -0
  25. package/lib/icqq-inbound.d.ts +165 -0
  26. package/lib/icqq-inbound.js +651 -0
  27. package/lib/icqq-inbox.d.ts +25 -0
  28. package/lib/icqq-inbox.js +129 -0
  29. package/lib/icqq-login-assist.d.ts +27 -0
  30. package/lib/icqq-login-assist.js +109 -0
  31. package/lib/icqq-runtime-state.d.ts +1 -0
  32. package/lib/icqq-runtime-state.js +6 -0
  33. package/lib/index.d.ts +5 -18
  34. package/lib/index.js +5 -124
  35. package/lib/outbound-media.d.ts +14 -0
  36. package/lib/outbound-media.js +76 -0
  37. package/lib/protocol.d.ts +276 -0
  38. package/lib/protocol.js +425 -0
  39. package/lib/types.d.ts +37 -11
  40. package/lib/types.js +0 -1
  41. package/package.json +80 -32
  42. package/plugin.js +19 -0
  43. package/schema.json +162 -0
  44. package/src/client.ts +29 -0
  45. package/src/cq-message.ts +113 -0
  46. package/src/endpoint.ts +1138 -0
  47. package/src/forward-msg.ts +112 -0
  48. package/src/icqq-endpoint-commands.ts +28 -0
  49. package/src/icqq-guild.ts +154 -0
  50. package/src/icqq-inbound.ts +814 -0
  51. package/src/icqq-inbox.ts +147 -0
  52. package/src/icqq-login-assist.ts +132 -0
  53. package/src/icqq-runtime-state.ts +7 -0
  54. package/src/index.ts +30 -149
  55. package/src/outbound-media.ts +103 -0
  56. package/src/protocol.ts +575 -0
  57. package/src/types.ts +40 -11
  58. package/tools/announce.js +16 -0
  59. package/tools/announce.ts +15 -0
  60. package/tools/essence.js +22 -0
  61. package/tools/essence.ts +20 -0
  62. package/tools/friend_list.js +18 -0
  63. package/tools/friend_list.ts +18 -0
  64. package/tools/group_files.js +22 -0
  65. package/tools/group_files.ts +20 -0
  66. package/tools/group_invite.js +23 -0
  67. package/tools/group_invite.ts +20 -0
  68. package/tools/group_kick.js +26 -0
  69. package/tools/group_kick.ts +22 -0
  70. package/tools/group_list.js +19 -0
  71. package/tools/group_list.ts +19 -0
  72. package/tools/group_member_info.js +24 -0
  73. package/tools/group_member_info.ts +26 -0
  74. package/tools/group_sign.js +19 -0
  75. package/tools/group_sign.ts +17 -0
  76. package/tools/list_muted.js +17 -0
  77. package/tools/list_muted.ts +16 -0
  78. package/tools/poke.js +16 -0
  79. package/tools/poke.ts +15 -0
  80. package/tools/send_user_like.js +25 -0
  81. package/tools/send_user_like.ts +22 -0
  82. package/tools/set_anonymous.js +21 -0
  83. package/tools/set_anonymous.ts +19 -0
  84. package/tools/set_mute.js +24 -0
  85. package/tools/set_mute.ts +21 -0
  86. package/tools/set_title.js +25 -0
  87. package/tools/set_title.ts +22 -0
  88. package/tools/user_info.js +14 -0
  89. package/tools/user_info.ts +13 -0
  90. package/LICENSE +0 -21
  91. package/client/ICQQManagement.tsx +0 -139
  92. package/client/LoginAssistPanel.tsx +0 -293
  93. package/client/index.tsx +0 -11
  94. package/client/tsconfig.json +0 -7
  95. package/client/utils/api.ts +0 -20
  96. package/dist/index.js +0 -1
  97. package/lib/adapter.d.ts +0 -34
  98. package/lib/adapter.d.ts.map +0 -1
  99. package/lib/adapter.js +0 -396
  100. package/lib/adapter.js.map +0 -1
  101. package/lib/bot.d.ts +0 -46
  102. package/lib/bot.d.ts.map +0 -1
  103. package/lib/bot.js +0 -457
  104. package/lib/bot.js.map +0 -1
  105. package/lib/index.d.ts.map +0 -1
  106. package/lib/index.js.map +0 -1
  107. package/lib/types.d.ts.map +0 -1
  108. package/lib/types.js.map +0 -1
  109. package/skills/icqq/SKILL.md +0 -22
  110. package/src/adapter.ts +0 -448
  111. package/src/bot.ts +0 -521
@@ -0,0 +1,425 @@
1
+ import { pickCredential } from 'zhin.js/adapter';
2
+ import { resolveIcqqMediaFile } from "./cq-message.js";
3
+ export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
4
+ /**
5
+ * 所有操作常量(@icqqjs/icqq)
6
+ */
7
+ export const Actions = {
8
+ PING: "ping",
9
+ LOGOUT: "logout",
10
+ // ── 列表查询 ──
11
+ LIST_FRIENDS: "list_friends",
12
+ LIST_GROUPS: "list_groups",
13
+ LIST_GROUP_MEMBERS: "list_group_members",
14
+ LIST_BLACKLIST: "list_blacklist",
15
+ LIST_FRIEND_CLASSES: "list_friend_classes",
16
+ // ── 信息查询 ──
17
+ GET_FRIEND_INFO: "get_friend_info",
18
+ GET_GROUP_INFO: "get_group_info",
19
+ GET_GROUP_MEMBER_INFO: "get_group_member_info",
20
+ GET_STRANGER_INFO: "get_stranger_info",
21
+ LIST_STRANGERS: "list_strangers",
22
+ GET_PROFILE: "get_profile",
23
+ GET_STATUS: "get_status",
24
+ GET_ONLINE_STATUS: "get_online_status",
25
+ GET_SELF_PROFILE: "get_self_profile",
26
+ // ── 消息发送 ──
27
+ SEND_PRIVATE_MSG: "send_private_msg",
28
+ SEND_GROUP_MSG: "send_group_msg",
29
+ // ── 消息操作 ──
30
+ RECALL_MSG: "recall_msg",
31
+ GET_MSG: "get_msg",
32
+ HISTORY_PRIVATE: "history_private",
33
+ HISTORY_GROUP: "history_group",
34
+ HISTORY_BY_MSG_ID: "history_by_msg_id",
35
+ SEND_LONG_MSG: "send_long_msg",
36
+ MARK_READ: "mark_read",
37
+ DELETE_MSG: "delete_msg",
38
+ // ── 个人设置 ──
39
+ SET_NICKNAME: "set_nickname",
40
+ SET_GENDER: "set_gender",
41
+ SET_BIRTHDAY: "set_birthday",
42
+ SET_SIGNATURE: "set_signature",
43
+ SET_DESCRIPTION: "set_description",
44
+ SET_AVATAR: "set_avatar",
45
+ SET_ONLINE_STATUS: "set_online_status",
46
+ // ── 群设置 ──
47
+ SET_GROUP_NAME: "set_group_name",
48
+ SET_GROUP_AVATAR: "set_group_avatar",
49
+ SET_GROUP_CARD: "set_group_card",
50
+ SET_GROUP_TITLE: "set_group_title",
51
+ SET_GROUP_ADMIN: "set_group_admin",
52
+ SET_GROUP_REMARK: "set_group_remark",
53
+ // ── 群管理 ──
54
+ GROUP_MUTE: "group_mute",
55
+ GROUP_MUTE_ALL: "group_mute_all",
56
+ GROUP_KICK: "group_kick",
57
+ GROUP_QUIT: "group_quit",
58
+ GROUP_INVITE: "group_invite",
59
+ GROUP_POKE: "group_poke",
60
+ GROUP_ANNOUNCE: "group_announce",
61
+ GROUP_SIGN: "group_sign",
62
+ GROUP_ESSENCE_ADD: "group_essence_add",
63
+ GROUP_ESSENCE_REMOVE: "group_essence_remove",
64
+ GROUP_ALLOW_ANONY: "group_allow_anony",
65
+ GROUP_MUTED_LIST: "group_muted_list",
66
+ GROUP_AT_ALL_REMAIN: "group_at_all_remain",
67
+ // ── 好友操作 ──
68
+ FRIEND_POKE: "friend_poke",
69
+ FRIEND_LIKE: "friend_like",
70
+ FRIEND_DELETE: "friend_delete",
71
+ FRIEND_REMARK: "friend_remark",
72
+ FRIEND_CLASS: "friend_class",
73
+ // ── 系统消息/请求 ──
74
+ GET_SYSTEM_MSG: "get_system_msg",
75
+ HANDLE_FRIEND_REQUEST: "handle_friend_request",
76
+ HANDLE_GROUP_REQUEST: "handle_group_request",
77
+ // ── 好友分组 ──
78
+ ADD_FRIEND_CLASS: "add_friend_class",
79
+ DELETE_FRIEND_CLASS: "delete_friend_class",
80
+ RENAME_FRIEND_CLASS: "rename_friend_class",
81
+ // ── 群文件系统 ──
82
+ GFS_LIST: "gfs_list",
83
+ GFS_INFO: "gfs_info",
84
+ GFS_MKDIR: "gfs_mkdir",
85
+ GFS_DELETE: "gfs_delete",
86
+ GFS_RENAME: "gfs_rename",
87
+ GFS_STAT: "gfs_stat",
88
+ GFS_MOVE: "gfs_move",
89
+ GFS_DOWNLOAD: "gfs_download",
90
+ // ── 其他功能 ──
91
+ IMAGE_OCR: "image_ocr",
92
+ RELOAD_FRIEND_LIST: "reload_friend_list",
93
+ RELOAD_GROUP_LIST: "reload_group_list",
94
+ CLEAN_CACHE: "clean_cache",
95
+ GET_GROUP_SHARE: "get_group_share",
96
+ // ── 群管理扩展 ──
97
+ GROUP_SET_JOIN_TYPE: "group_set_join_type",
98
+ GROUP_SET_RATE_LIMIT: "group_set_rate_limit",
99
+ GROUP_MUTE_ANONY: "group_mute_anony",
100
+ GROUP_ANON_INFO: "group_anon_info",
101
+ // ── 好友操作扩展 ──
102
+ ADD_FRIEND: "add_friend",
103
+ SEND_TEMP_MSG: "send_temp_msg",
104
+ // ── 漫游表情 ──
105
+ GET_ROAMING_STAMP: "get_roaming_stamp",
106
+ DELETE_STAMP: "delete_stamp",
107
+ // ── 文件传输 ──
108
+ SEND_PRIVATE_FILE: "send_private_file",
109
+ SEND_GROUP_FILE: "send_group_file",
110
+ FRIEND_RECALL_FILE: "friend_recall_file",
111
+ FRIEND_FORWARD_FILE: "friend_forward_file",
112
+ SEARCH_SAME_GROUP: "search_same_group",
113
+ SEND_CONTACT_SHARE: "send_contact_share",
114
+ GFS_UPLOAD: "gfs_upload",
115
+ // ── 群消息表态 ──
116
+ GROUP_SET_REACTION: "group_set_reaction",
117
+ GROUP_DEL_REACTION: "group_del_reaction",
118
+ // ── 转发消息 ──
119
+ GET_FORWARD_MSG: "get_forward_msg",
120
+ MAKE_FORWARD_MSG: "make_forward_msg",
121
+ // ── 频道系统 ──
122
+ GUILD_LIST: "guild_list",
123
+ GUILD_INFO: "guild_info",
124
+ GUILD_CHANNELS: "guild_channels",
125
+ GET_CHANNEL_INFO: "get_channel_info",
126
+ GUILD_MEMBERS: "guild_members",
127
+ GUILD_SEND_MSG: "guild_send_msg",
128
+ GUILD_RECALL_MSG: "guild_recall_msg",
129
+ // ── 用户文件操作 ──
130
+ GET_FILE_INFO: "get_file_info",
131
+ GET_FILE_URL: "get_file_url",
132
+ GET_AVATAR_URL: "get_avatar_url",
133
+ GET_GROUP_AVATAR_URL: "get_group_avatar_url",
134
+ // ── 屏蔽群成员消息 ──
135
+ SET_SCREEN_MEMBER_MSG: "set_screen_member_msg",
136
+ // ── 群文件转发 ──
137
+ GFS_FORWARD: "gfs_forward",
138
+ GFS_FORWARD_OFFLINE: "gfs_forward_offline",
139
+ // ── 重载列表 ──
140
+ RELOAD_BLACKLIST: "reload_blacklist",
141
+ RELOAD_STRANGER_LIST: "reload_stranger_list",
142
+ RELOAD_GUILDS: "reload_guilds",
143
+ // ── 在线状态查询 ──
144
+ GET_STATUS_INFO: "get_status_info",
145
+ // ── 密钥/工具 ──
146
+ GET_CLIENT_KEY: "get_client_key",
147
+ GET_PSKEY: "get_pskey",
148
+ UID2UIN: "uid2uin",
149
+ UID2UINS: "uid2uins",
150
+ UIN2UID: "uin2uid",
151
+ UIN2UIDS: "uin2uids",
152
+ GET_COOKIES: "get_cookies",
153
+ GET_CSRF_TOKEN: "get_csrf_token",
154
+ REFRESH_NT_PIC_RKEY: "refresh_nt_pic_rkey",
155
+ SEND_DISCUSS_MSG: "send_discuss_msg",
156
+ // ── 视频/加好友设置 ──
157
+ GET_VIDEO_URL: "get_video_url",
158
+ GET_ADD_FRIEND_SETTING: "get_add_friend_setting",
159
+ // ── 频道扩展 ──
160
+ GET_FORUM_URL: "get_forum_url",
161
+ GUILD_CHANNEL_SHARE: "guild_channel_share",
162
+ // ── 获取图片/语音 URL ──
163
+ GET_PIC_URL: "get_pic_url",
164
+ GET_PTT_URL: "get_ptt_url",
165
+ // ── 消息订阅 ──
166
+ SUBSCRIBE: "subscribe",
167
+ UNSUBSCRIBE: "unsubscribe",
168
+ // ── Webhook ──
169
+ SET_WEBHOOK: "set_webhook",
170
+ GET_WEBHOOK: "get_webhook",
171
+ // ── 系统通知 ──
172
+ SET_NOTIFY: "set_notify",
173
+ GET_NOTIFY: "get_notify",
174
+ };
175
+ export function resolveIcqqConfig(config = {}) {
176
+ const entry = config.endpoints?.find((item) => item.context === 'icqq');
177
+ const id = pickCredential(config.id, entry?.id, process.env.ICQQ_ACCOUNT);
178
+ if (!/^\d+$/.test(id)) {
179
+ throw new TypeError('ICQQ adapter requires numeric id (QQ uin) via plugins.<key>.id or ICQQ_ACCOUNT');
180
+ }
181
+ const autoReconnect = config.autoReconnect ?? entry?.autoReconnect ?? true;
182
+ const outboundMedia = config.outboundMedia ?? entry?.outboundMedia;
183
+ const password = config.password ?? entry?.password;
184
+ const platform = config.platform ?? entry?.platform ?? 2;
185
+ const ver = config.ver ?? entry?.ver ?? '9.1.70';
186
+ const dataDir = config.dataDir ?? entry?.dataDir ?? `data/icqq/${id}`;
187
+ const signApiAddr = config.signApiAddr ?? entry?.signApiAddr;
188
+ const ignoreSelf = config.ignoreSelf ?? entry?.ignoreSelf ?? true;
189
+ const resend = config.resend ?? entry?.resend;
190
+ const cacheGroupMember = config.cacheGroupMember ?? entry?.cacheGroupMember ?? true;
191
+ const autoServer = config.autoServer ?? entry?.autoServer ?? true;
192
+ const qqnt = config.qqnt ?? entry?.qqnt;
193
+ const ntLogin = config.ntLogin ?? entry?.ntLogin;
194
+ return {
195
+ context: 'icqq',
196
+ id,
197
+ autoReconnect,
198
+ ...(outboundMedia ? { outboundMedia } : {}),
199
+ ...(password ? { password } : {}),
200
+ ...(platform != null ? { platform: Number(platform) } : {}),
201
+ ...(ver ? { ver } : {}),
202
+ ...(dataDir ? { dataDir } : {}),
203
+ ...(signApiAddr ? { signApiAddr } : {}),
204
+ ...(ignoreSelf != null ? { ignoreSelf } : {}),
205
+ ...(resend != null ? { resend } : {}),
206
+ ...(cacheGroupMember != null ? { cacheGroupMember } : {}),
207
+ ...(autoServer != null ? { autoServer } : {}),
208
+ ...(qqnt != null ? { qqnt } : {}),
209
+ ...(ntLogin != null ? { ntLogin } : {}),
210
+ };
211
+ }
212
+ /**
213
+ * 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
214
+ * 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
215
+ */
216
+ export function icqqInboundConversation(endpointKey, input) {
217
+ const endpoint = { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey };
218
+ if (input.channelType === 'private' && input.channelParentGroupId) {
219
+ return {
220
+ endpoint,
221
+ kind: 'private',
222
+ id: input.channelId,
223
+ parent: { kind: 'group', id: input.channelParentGroupId },
224
+ };
225
+ }
226
+ if (input.channelType === 'channel' && input.guildId) {
227
+ return {
228
+ endpoint,
229
+ kind: 'channel',
230
+ id: input.channelId,
231
+ parent: { kind: 'channel', id: input.guildId },
232
+ };
233
+ }
234
+ return { endpoint, kind: input.channelType, id: input.channelId };
235
+ }
236
+ /**
237
+ * 出站:从 ConversationRef 派生 action 路由。
238
+ * 群容器(parent kind 'group')内的 private 会话即 ICQQ 群临时会话(temp)。
239
+ */
240
+ export function icqqOutboundTarget(conversation) {
241
+ if (conversation.kind === 'private' && conversation.parent?.kind === 'group') {
242
+ return {
243
+ kind: 'temp',
244
+ groupId: Number(conversation.parent.id),
245
+ userId: Number(conversation.id),
246
+ };
247
+ }
248
+ if (conversation.kind === 'private') {
249
+ return { kind: 'private', userId: Number(conversation.id) };
250
+ }
251
+ if (conversation.kind === 'group') {
252
+ return { kind: 'group', groupId: Number(conversation.id) };
253
+ }
254
+ const guildId = conversation.parent?.kind === 'channel' ? conversation.parent.id : undefined;
255
+ if (!guildId) {
256
+ throw new TypeError(`Invalid icqq channel conversation (missing guild parent): ${conversation.id}`);
257
+ }
258
+ return { kind: 'channel', guildId, channelId: conversation.id };
259
+ }
260
+ const ICQQ_STANDALONE_ELEMENTS = new Set([
261
+ 'record',
262
+ 'video',
263
+ 'file',
264
+ 'json',
265
+ 'long_msg',
266
+ ]);
267
+ /**
268
+ * Project canonical Zhin segments directly into the native ICQQ Sendable
269
+ * algebra. ICQQ's direct client does not parse Zhin's historical `[image:x]`
270
+ * notation; passing that notation would send it as literal text.
271
+ */
272
+ export function formatOutboundBody(payload) {
273
+ if (typeof payload === 'string') {
274
+ const text = payload.trim();
275
+ return text || '\u200b';
276
+ }
277
+ if (payload == null)
278
+ return '\u200b';
279
+ if (!Array.isArray(payload)) {
280
+ // 单个段对象(非数组)按单元素数组处理,杜绝 String(object) → '[object Object]'
281
+ if (isWireSegment(payload)) {
282
+ return formatOutboundBody([payload]);
283
+ }
284
+ if (typeof payload === 'object' && payload !== null && 'text' in payload) {
285
+ const text = String(payload.text ?? '').trim();
286
+ return text || '\u200b';
287
+ }
288
+ const text = String(payload).trim();
289
+ return text || '\u200b';
290
+ }
291
+ const elements = payload.flatMap((seg) => {
292
+ if (typeof seg === 'string')
293
+ return seg;
294
+ const item = seg;
295
+ switch (item.type) {
296
+ case 'text':
297
+ return String(item.data?.text ?? '');
298
+ case 'at':
299
+ case 'mention': {
300
+ const target = item.data?.qq ?? item.data?.id ?? item.data?.target;
301
+ if (target === 'all')
302
+ return { type: 'at', qq: 'all' };
303
+ const qq = Number(target);
304
+ if (!Number.isSafeInteger(qq) || qq <= 0) {
305
+ throw new TypeError(`Invalid ICQQ mention target: ${String(target ?? '')}`);
306
+ }
307
+ return { type: 'at', qq };
308
+ }
309
+ case 'image': {
310
+ const arg = resolveIcqqMediaFile(item.data);
311
+ if (!arg)
312
+ throw new TypeError('ICQQ image segment requires canonical data.media');
313
+ return { type: 'image', file: arg };
314
+ }
315
+ case 'face': {
316
+ const id = Number(item.data?.id);
317
+ if (!Number.isSafeInteger(id) || id < 0) {
318
+ throw new TypeError(`Invalid ICQQ face id: ${String(item.data?.id ?? '')}`);
319
+ }
320
+ return { type: 'face', id };
321
+ }
322
+ case 'reply': {
323
+ const id = String(item.data?.message_id ?? item.data?.id ?? '').trim();
324
+ if (!id)
325
+ throw new TypeError('ICQQ reply segment requires message_id');
326
+ return { type: 'reply', id };
327
+ }
328
+ case 'record':
329
+ case 'audio': {
330
+ const arg = resolveIcqqMediaFile(item.data);
331
+ if (!arg)
332
+ throw new TypeError('ICQQ audio segment requires canonical data.media');
333
+ return { type: 'record', file: arg };
334
+ }
335
+ case 'video': {
336
+ const arg = resolveIcqqMediaFile(item.data);
337
+ if (!arg)
338
+ throw new TypeError('ICQQ video segment requires canonical data.media');
339
+ return { type: 'video', file: arg };
340
+ }
341
+ case 'file': {
342
+ const arg = resolveIcqqMediaFile(item.data);
343
+ if (!arg)
344
+ throw new TypeError('ICQQ file segment requires canonical data.media');
345
+ const name = String(item.data?.name ?? '').trim();
346
+ return { type: 'file', file: arg, ...(name ? { name } : {}) };
347
+ }
348
+ case 'dice':
349
+ return { type: 'dice' };
350
+ case 'rps':
351
+ return { type: 'rps' };
352
+ case 'forward': {
353
+ const resid = String(item.data?.forward_id ?? item.data?.resid ?? item.data?.id ?? '').trim();
354
+ if (!resid)
355
+ throw new TypeError('ICQQ forward segment requires forward_id');
356
+ return { type: 'long_msg', resid };
357
+ }
358
+ case 'share':
359
+ return formatMusicShareJson(item.data);
360
+ default: {
361
+ const text = item.data?.text;
362
+ if (typeof text === 'string' && text)
363
+ return text;
364
+ throw new TypeError(`Unsupported ICQQ outbound segment: ${item.type}`);
365
+ }
366
+ }
367
+ }).filter((element) => typeof element !== 'string' || element.length > 0);
368
+ if (elements.length === 0)
369
+ return '\u200b';
370
+ const standalone = elements.find((element) => typeof element !== 'string' && ICQQ_STANDALONE_ELEMENTS.has(element.type));
371
+ if (standalone && elements.length !== 1) {
372
+ throw new TypeError(`ICQQ ${standalone.type} element must be sent as a standalone message`);
373
+ }
374
+ return elements.length === 1 ? elements[0] : elements;
375
+ }
376
+ export function formatInboundContent(rawMessage) {
377
+ return rawMessage;
378
+ }
379
+ /** 段对象判定:`{ type: string, data? }`(非数组 payload 的防御入口)。 */
380
+ function isWireSegment(value) {
381
+ return typeof value === 'object'
382
+ && value !== null
383
+ && typeof value.type === 'string';
384
+ }
385
+ function formatMusicShareJson(data) {
386
+ if (!data)
387
+ throw new TypeError('ICQQ share segment requires data');
388
+ const config = data.config;
389
+ const title = String(data.title ?? '');
390
+ const desc = String(data.artist ?? '');
391
+ const jumpUrl = String(data.url ?? '');
392
+ const musicUrl = String(data.audio ?? '');
393
+ const preview = String(data.image ?? '');
394
+ const tag = config?.appid === 100497308 ? 'QQ音乐'
395
+ : config?.appid === 100495085 ? '网易云音乐'
396
+ : config?.appid === 100243533 ? '酷我音乐'
397
+ : config?.appid === 205141 ? '酷狗音乐'
398
+ : '音乐';
399
+ const json = {
400
+ app: 'com.tencent.structmsg',
401
+ view: 'music',
402
+ ver: '0.0.0.1',
403
+ desc: 'music',
404
+ prompt: `[分享]${title}`,
405
+ meta: {
406
+ music: {
407
+ action: '',
408
+ android_pkg_name: config?.package ?? '',
409
+ app_type: 1,
410
+ appid: config?.appid ?? 0,
411
+ ctime: 0,
412
+ desc,
413
+ jumpUrl,
414
+ musicUrl,
415
+ preview,
416
+ sourceMsgId: '0',
417
+ source_icon: config?.icon ?? '',
418
+ source_url: '',
419
+ tag,
420
+ title,
421
+ },
422
+ },
423
+ };
424
+ return { type: 'json', data: json };
425
+ }
package/lib/types.d.ts CHANGED
@@ -1,8 +1,4 @@
1
- /**
2
- * ICQQ 适配器类型与配置
3
- */
4
- import type { Config, GroupRole, MemberInfo } from "@icqqjs/icqq";
5
- export type { Config, GroupRole, MemberInfo };
1
+ export type GroupRole = "owner" | "admin" | "member";
6
2
  export interface IcqqSenderInfo {
7
3
  id: string;
8
4
  name: string;
@@ -13,13 +9,43 @@ export interface IcqqSenderInfo {
13
9
  card?: string;
14
10
  title?: string;
15
11
  }
16
- export interface IcqqBotConfig extends Config {
12
+ export interface IcqqEndpointConfig {
17
13
  context: "icqq";
18
14
  name: `${number}`;
19
- password?: string;
20
- scope?: string;
15
+ autoReconnect?: boolean;
16
+ outboundMedia?: "file" | "base64";
21
17
  }
22
- export interface IcqqBot {
23
- $config: IcqqBotConfig;
18
+ export interface FriendInfo {
19
+ user_id: number;
20
+ nickname: string;
21
+ remark?: string;
22
+ class_id?: number;
23
+ }
24
+ export interface GroupInfo {
25
+ group_id: number;
26
+ group_name: string;
27
+ member_count: number;
28
+ max_member_count: number;
29
+ owner_id?: number;
30
+ }
31
+ export interface MemberInfo {
32
+ user_id: number;
33
+ nickname: string;
34
+ card: string;
35
+ role: GroupRole;
36
+ title: string;
37
+ join_time?: number;
38
+ last_sent_time?: number;
39
+ shutup_time?: number;
40
+ }
41
+ export interface SystemMessage {
42
+ type: string;
43
+ user_id?: number;
44
+ nickname?: string;
45
+ group_id?: number;
46
+ group_name?: string;
47
+ comment?: string;
48
+ flag?: string;
49
+ seq?: number;
50
+ time?: number;
24
51
  }
25
- //# sourceMappingURL=types.d.ts.map
package/lib/types.js CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-icqq",
3
- "version": "1.0.71",
4
- "description": "Zhin.js adapter for ICQQ (QQ Bot)",
3
+ "version": "1.1.0",
4
+ "description": "Zhin.js ICQQ adapter — direct connection to @icqqjs/icqq",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -33,52 +33,100 @@
33
33
  "type": "git",
34
34
  "directory": "plugins/adapters/icqq"
35
35
  },
36
- "files": [
37
- "src",
38
- "lib",
39
- "client",
40
- "dist",
41
- "skills",
42
- "README.md",
43
- "CHANGELOG.md"
44
- ],
45
36
  "dependencies": {
46
- "@icqqjs/icqq": "^1.10.18"
47
- },
48
- "devDependencies": {
49
- "@radix-ui/themes": "^3.2.1",
50
- "@types/react": "^19.2.2",
51
- "@types/react-dom": "^19.2.1",
52
- "lucide-react": "^0.469.0",
53
- "radix-ui": "^1.4.3",
54
- "typescript": "^5.3.0",
55
- "@zhin.js/console": "1.0.59",
56
- "zhin.js": "1.0.57"
37
+ "@zhin.js/adapter": "1.1.12",
38
+ "@zhin.js/core": "1.1.35",
39
+ "@zhin.js/feature-kit": "1.1.0",
40
+ "@zhin.js/im-contract": "1.1.0",
41
+ "@zhin.js/logger": "1.1.0",
42
+ "@zhin.js/permission": "1.1.0",
43
+ "@zhin.js/tool": "1.1.0"
57
44
  },
58
45
  "peerDependencies": {
59
- "@zhin.js/client": "1.0.14",
60
- "@zhin.js/console": "1.0.59",
61
- "@zhin.js/core": "1.0.57",
62
- "@zhin.js/http": "1.0.52",
63
- "zhin.js": "1.0.57"
46
+ "@icqqjs/icqq": "^1.12.2",
47
+ "zod": "^4.0.0",
48
+ "@zhin.js/adapter": "^1.1.12",
49
+ "@zhin.js/agent": "^1.1.23",
50
+ "@zhin.js/command": "^1.1.0",
51
+ "@zhin.js/core": "^1.1.35",
52
+ "@zhin.js/prompt-section": "^1.1.0",
53
+ "zhin.js": "^1.1.0"
64
54
  },
65
55
  "peerDependenciesMeta": {
66
- "@zhin.js/client": {
56
+ "@icqqjs/icqq": {
57
+ "optional": true
58
+ },
59
+ "@zhin.js/agent": {
60
+ "optional": true
61
+ },
62
+ "@zhin.js/command": {
67
63
  "optional": true
68
64
  },
69
- "@zhin.js/http": {
65
+ "@zhin.js/prompt-section": {
70
66
  "optional": true
71
67
  },
72
- "@zhin.js/console": {
68
+ "zhin.js": {
69
+ "optional": true
70
+ },
71
+ "zod": {
73
72
  "optional": true
74
73
  }
75
74
  },
75
+ "devDependencies": {
76
+ "@icqqjs/icqq": "^1.12.2",
77
+ "@types/node": "^26.1.2",
78
+ "typescript": "^6.0.3",
79
+ "vitest": "^4.1.10",
80
+ "zod": "^4.4.3",
81
+ "@zhin.js/agent": "1.1.23",
82
+ "@zhin.js/prompt-section": "1.1.0",
83
+ "zhin.js": "1.1.0"
84
+ },
85
+ "files": [
86
+ "adapters",
87
+ "commands",
88
+ "plugin.js",
89
+ "schema.json",
90
+ "src",
91
+ "lib",
92
+ "agent",
93
+ "tools",
94
+ "README.md",
95
+ "CHANGELOG.md"
96
+ ],
76
97
  "publishConfig": {
77
98
  "access": "public",
78
99
  "registry": "https://registry.npmjs.org"
79
100
  },
101
+ "engines": {
102
+ "node": "^20.19.0 || >=22.12.0"
103
+ },
104
+ "zhin": {
105
+ "protocol": 1,
106
+ "type": "plugin",
107
+ "entry": "./plugin.js",
108
+ "engine": "^1.0.0",
109
+ "runtime": "trusted",
110
+ "features": [
111
+ {
112
+ "package": "@zhin.js/adapter",
113
+ "api": "^1.0.0"
114
+ },
115
+ {
116
+ "package": "@zhin.js/command",
117
+ "api": "^1.0.0"
118
+ },
119
+ {
120
+ "package": "@zhin.js/tool",
121
+ "api": "^1.0.0"
122
+ }
123
+ ],
124
+ "plugins": []
125
+ },
80
126
  "scripts": {
81
- "build": "tsc && (test -f ../../services/console/lib/bin.js && node ../../services/console/lib/bin.js build || echo 'Skipping client build: console not built yet')",
82
- "clean": "rimraf lib"
127
+ "build": "tsc",
128
+ "clean": "rimraf lib",
129
+ "test": "pnpm run test:types && NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/icqq/tests",
130
+ "test:types": "tsc -p tsconfig.type-tests.json"
83
131
  }
84
132
  }
package/plugin.js ADDED
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { createEndpointRuntimeState } from 'zhin.js/adapter';
3
+ import { definePlugin } from 'zhin.js';
4
+ import { permissionHostToken, createSceneRolePlatformChecker } from '@zhin.js/permission';
5
+ import { icqqRuntimeStateToken } from "./lib/icqq-runtime-state.js";
6
+ export default definePlugin({
7
+ name: 'icqq',
8
+ metadata: {
9
+ displayName: 'ICQQ Adapter',
10
+ },
11
+ setup(context) {
12
+ // 运行中 endpoint 注册表(icqq.endpoint list 的"运行中"数据源)
13
+ context.resources.provide(icqqRuntimeStateToken, createEndpointRuntimeState());
14
+ const disposePlatform = context.resources.has(permissionHostToken)
15
+ ? context.resources.use(permissionHostToken).registerPlatform('icqq', createSceneRolePlatformChecker())
16
+ : undefined;
17
+ return disposePlatform;
18
+ },
19
+ });