@zhin.js/adapter-icqq 1.0.71 → 1.1.2

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 (122) hide show
  1. package/CHANGELOG.md +1216 -52
  2. package/PERMITS.md +24 -0
  3. package/README.md +123 -429
  4. package/adapters/icqq/index.js +31 -0
  5. package/adapters/icqq/index.ts +42 -0
  6. package/agents/icqq/agent.json +20 -0
  7. package/agents/icqq/boundaries.md +3 -0
  8. package/agents/icqq/conventions.md +3 -0
  9. package/agents/icqq/skills/icqq-directory/SKILL.md +20 -0
  10. package/agents/icqq/skills/icqq-directory/tools/friend_list/index.js +19 -0
  11. package/agents/icqq/skills/icqq-directory/tools/friend_list/index.ts +19 -0
  12. package/agents/icqq/skills/icqq-directory/tools/group_list/index.js +20 -0
  13. package/agents/icqq/skills/icqq-directory/tools/group_list/index.ts +20 -0
  14. package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.js +26 -0
  15. package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.ts +28 -0
  16. package/agents/icqq/skills/icqq-directory/tools/user_info/index.js +15 -0
  17. package/agents/icqq/skills/icqq-directory/tools/user_info/index.ts +14 -0
  18. package/agents/icqq/skills/icqq-group-admin/SKILL.md +25 -0
  19. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.js +18 -0
  20. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.ts +17 -0
  21. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +24 -0
  22. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +22 -0
  23. package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js +23 -0
  24. package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts +20 -0
  25. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js +27 -0
  26. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts +23 -0
  27. package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js +23 -0
  28. package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts +21 -0
  29. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +25 -0
  30. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +22 -0
  31. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +26 -0
  32. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +23 -0
  33. package/agents/icqq/skills/icqq-group-state/SKILL.md +17 -0
  34. package/agents/icqq/skills/icqq-group-state/tools/group_files/index.js +24 -0
  35. package/agents/icqq/skills/icqq-group-state/tools/group_files/index.ts +22 -0
  36. package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.js +18 -0
  37. package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.ts +17 -0
  38. package/agents/icqq/skills/icqq-interaction/SKILL.md +19 -0
  39. package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.js +20 -0
  40. package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.ts +18 -0
  41. package/agents/icqq/skills/icqq-interaction/tools/poke/index.js +17 -0
  42. package/agents/icqq/skills/icqq-interaction/tools/poke/index.ts +16 -0
  43. package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.js +26 -0
  44. package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.ts +22 -0
  45. package/agents/icqq/system.md +3 -0
  46. package/commands/icqq/endpoint/add/[[id]]/index.js +3 -0
  47. package/commands/icqq/endpoint/add/[[id]]/index.ts +3 -0
  48. package/commands/icqq/endpoint/definition.js +31 -0
  49. package/commands/icqq/endpoint/definition.ts +32 -0
  50. package/commands/icqq/endpoint/list/index.js +3 -0
  51. package/commands/icqq/endpoint/list/index.ts +3 -0
  52. package/commands/icqq/endpoint/remove/[id]/index.js +3 -0
  53. package/commands/icqq/endpoint/remove/[id]/index.ts +3 -0
  54. package/lib/client.d.ts +19 -0
  55. package/lib/client.js +3 -0
  56. package/lib/content-resolver.d.ts +10 -0
  57. package/lib/content-resolver.js +98 -0
  58. package/lib/cq-message.d.ts +14 -0
  59. package/lib/cq-message.js +106 -0
  60. package/lib/endpoint.d.ts +30 -0
  61. package/lib/endpoint.js +1003 -0
  62. package/lib/forward-msg.d.ts +6 -0
  63. package/lib/forward-msg.js +107 -0
  64. package/lib/icqq-guild.d.ts +57 -0
  65. package/lib/icqq-guild.js +109 -0
  66. package/lib/icqq-inbound.d.ts +165 -0
  67. package/lib/icqq-inbound.js +651 -0
  68. package/lib/icqq-inbox.d.ts +25 -0
  69. package/lib/icqq-inbox.js +129 -0
  70. package/lib/icqq-login-assist.d.ts +27 -0
  71. package/lib/icqq-login-assist.js +109 -0
  72. package/lib/icqq-runtime-state.d.ts +1 -0
  73. package/lib/icqq-runtime-state.js +6 -0
  74. package/lib/index.d.ts +5 -18
  75. package/lib/index.js +5 -124
  76. package/lib/outbound-media.d.ts +14 -0
  77. package/lib/outbound-media.js +76 -0
  78. package/lib/protocol.d.ts +258 -0
  79. package/lib/protocol.js +422 -0
  80. package/lib/types.d.ts +33 -13
  81. package/lib/types.js +0 -1
  82. package/package.json +86 -32
  83. package/plugin.js +19 -0
  84. package/prompt-sections/platform/index.js +16 -0
  85. package/prompt-sections/platform/index.ts +16 -0
  86. package/schema.json +168 -0
  87. package/src/client.ts +29 -0
  88. package/src/content-resolver.ts +131 -0
  89. package/src/cq-message.ts +113 -0
  90. package/src/endpoint.ts +1084 -0
  91. package/src/forward-msg.ts +112 -0
  92. package/src/icqq-guild.ts +154 -0
  93. package/src/icqq-inbound.ts +814 -0
  94. package/src/icqq-inbox.ts +147 -0
  95. package/src/icqq-login-assist.ts +132 -0
  96. package/src/icqq-runtime-state.ts +7 -0
  97. package/src/index.ts +30 -149
  98. package/src/outbound-media.ts +103 -0
  99. package/src/protocol.ts +551 -0
  100. package/src/types.ts +35 -13
  101. package/LICENSE +0 -21
  102. package/client/ICQQManagement.tsx +0 -139
  103. package/client/LoginAssistPanel.tsx +0 -293
  104. package/client/index.tsx +0 -11
  105. package/client/tsconfig.json +0 -7
  106. package/client/utils/api.ts +0 -20
  107. package/dist/index.js +0 -1
  108. package/lib/adapter.d.ts +0 -34
  109. package/lib/adapter.d.ts.map +0 -1
  110. package/lib/adapter.js +0 -396
  111. package/lib/adapter.js.map +0 -1
  112. package/lib/bot.d.ts +0 -46
  113. package/lib/bot.d.ts.map +0 -1
  114. package/lib/bot.js +0 -457
  115. package/lib/bot.js.map +0 -1
  116. package/lib/index.d.ts.map +0 -1
  117. package/lib/index.js.map +0 -1
  118. package/lib/types.d.ts.map +0 -1
  119. package/lib/types.js.map +0 -1
  120. package/skills/icqq/SKILL.md +0 -22
  121. package/src/adapter.ts +0 -448
  122. package/src/bot.ts +0 -521
@@ -0,0 +1,551 @@
1
+ import type { MessageElem, Sendable, ShareElem } from '@icqqjs/icqq';
2
+ import type { ConversationRef } from '@zhin.js/im-contract';
3
+ import type { MessageSegment } from "zhin.js";
4
+ import { resolveIcqqMediaFile } from "./cq-message.js";
5
+
6
+ export type ActionResult = {
7
+ id: string;
8
+ ok: boolean;
9
+ data?: unknown;
10
+ error?: string;
11
+ };
12
+
13
+ export type { IcqqMessageEvent } from "./icqq-inbound.js";
14
+
15
+ export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
16
+
17
+ export interface GuildMessageEventData {
18
+ type: "guild";
19
+ guild_id: string;
20
+ guild_name: string;
21
+ channel_id: string;
22
+ channel_name: string;
23
+ nickname: string;
24
+ tiny_id: string;
25
+ raw_message: string;
26
+ time: number;
27
+ seq: number;
28
+ }
29
+
30
+ /**
31
+ * 所有操作常量(@icqqjs/icqq)
32
+ */
33
+ export const Actions = {
34
+ PING: "ping",
35
+ LOGOUT: "logout",
36
+
37
+ // ── 列表查询 ──
38
+ LIST_FRIENDS: "list_friends",
39
+ LIST_GROUPS: "list_groups",
40
+ LIST_GROUP_MEMBERS: "list_group_members",
41
+ LIST_BLACKLIST: "list_blacklist",
42
+ LIST_FRIEND_CLASSES: "list_friend_classes",
43
+
44
+ // ── 信息查询 ──
45
+ GET_FRIEND_INFO: "get_friend_info",
46
+ GET_GROUP_INFO: "get_group_info",
47
+ GET_GROUP_MEMBER_INFO: "get_group_member_info",
48
+ GET_STRANGER_INFO: "get_stranger_info",
49
+ LIST_STRANGERS: "list_strangers",
50
+ GET_PROFILE: "get_profile",
51
+ GET_STATUS: "get_status",
52
+ GET_ONLINE_STATUS: "get_online_status",
53
+ GET_SELF_PROFILE: "get_self_profile",
54
+
55
+ // ── 消息发送 ──
56
+ SEND_PRIVATE_MSG: "send_private_msg",
57
+ SEND_GROUP_MSG: "send_group_msg",
58
+
59
+ // ── 消息操作 ──
60
+ RECALL_MSG: "recall_msg",
61
+ GET_MSG: "get_msg",
62
+ HISTORY_PRIVATE: "history_private",
63
+ HISTORY_GROUP: "history_group",
64
+ HISTORY_BY_MSG_ID: "history_by_msg_id",
65
+ SEND_LONG_MSG: "send_long_msg",
66
+ MARK_READ: "mark_read",
67
+ DELETE_MSG: "delete_msg",
68
+
69
+ // ── 个人设置 ──
70
+ SET_NICKNAME: "set_nickname",
71
+ SET_GENDER: "set_gender",
72
+ SET_BIRTHDAY: "set_birthday",
73
+ SET_SIGNATURE: "set_signature",
74
+ SET_DESCRIPTION: "set_description",
75
+ SET_AVATAR: "set_avatar",
76
+ SET_ONLINE_STATUS: "set_online_status",
77
+
78
+ // ── 群设置 ──
79
+ SET_GROUP_NAME: "set_group_name",
80
+ SET_GROUP_AVATAR: "set_group_avatar",
81
+ SET_GROUP_CARD: "set_group_card",
82
+ SET_GROUP_TITLE: "set_group_title",
83
+ SET_GROUP_ADMIN: "set_group_admin",
84
+ SET_GROUP_REMARK: "set_group_remark",
85
+
86
+ // ── 群管理 ──
87
+ GROUP_MUTE: "group_mute",
88
+ GROUP_MUTE_ALL: "group_mute_all",
89
+ GROUP_KICK: "group_kick",
90
+ GROUP_QUIT: "group_quit",
91
+ GROUP_INVITE: "group_invite",
92
+ GROUP_POKE: "group_poke",
93
+ GROUP_ANNOUNCE: "group_announce",
94
+ GROUP_SIGN: "group_sign",
95
+ GROUP_ESSENCE_ADD: "group_essence_add",
96
+ GROUP_ESSENCE_REMOVE: "group_essence_remove",
97
+ GROUP_ALLOW_ANONY: "group_allow_anony",
98
+ GROUP_MUTED_LIST: "group_muted_list",
99
+ GROUP_AT_ALL_REMAIN: "group_at_all_remain",
100
+
101
+ // ── 好友操作 ──
102
+ FRIEND_POKE: "friend_poke",
103
+ FRIEND_LIKE: "friend_like",
104
+ FRIEND_DELETE: "friend_delete",
105
+ FRIEND_REMARK: "friend_remark",
106
+ FRIEND_CLASS: "friend_class",
107
+
108
+ // ── 系统消息/请求 ──
109
+ GET_SYSTEM_MSG: "get_system_msg",
110
+ HANDLE_FRIEND_REQUEST: "handle_friend_request",
111
+ HANDLE_GROUP_REQUEST: "handle_group_request",
112
+
113
+ // ── 好友分组 ──
114
+ ADD_FRIEND_CLASS: "add_friend_class",
115
+ DELETE_FRIEND_CLASS: "delete_friend_class",
116
+ RENAME_FRIEND_CLASS: "rename_friend_class",
117
+
118
+ // ── 群文件系统 ──
119
+ GFS_LIST: "gfs_list",
120
+ GFS_INFO: "gfs_info",
121
+ GFS_MKDIR: "gfs_mkdir",
122
+ GFS_DELETE: "gfs_delete",
123
+ GFS_RENAME: "gfs_rename",
124
+ GFS_STAT: "gfs_stat",
125
+ GFS_MOVE: "gfs_move",
126
+ GFS_DOWNLOAD: "gfs_download",
127
+
128
+ // ── 其他功能 ──
129
+ IMAGE_OCR: "image_ocr",
130
+ RELOAD_FRIEND_LIST: "reload_friend_list",
131
+ RELOAD_GROUP_LIST: "reload_group_list",
132
+ CLEAN_CACHE: "clean_cache",
133
+ GET_GROUP_SHARE: "get_group_share",
134
+
135
+ // ── 群管理扩展 ──
136
+ GROUP_SET_JOIN_TYPE: "group_set_join_type",
137
+ GROUP_SET_RATE_LIMIT: "group_set_rate_limit",
138
+ GROUP_MUTE_ANONY: "group_mute_anony",
139
+ GROUP_ANON_INFO: "group_anon_info",
140
+
141
+ // ── 好友操作扩展 ──
142
+ ADD_FRIEND: "add_friend",
143
+ SEND_TEMP_MSG: "send_temp_msg",
144
+
145
+ // ── 漫游表情 ──
146
+ GET_ROAMING_STAMP: "get_roaming_stamp",
147
+ DELETE_STAMP: "delete_stamp",
148
+
149
+ // ── 文件传输 ──
150
+ SEND_PRIVATE_FILE: "send_private_file",
151
+ SEND_GROUP_FILE: "send_group_file",
152
+ FRIEND_RECALL_FILE: "friend_recall_file",
153
+ FRIEND_FORWARD_FILE: "friend_forward_file",
154
+ SEARCH_SAME_GROUP: "search_same_group",
155
+ SEND_CONTACT_SHARE: "send_contact_share",
156
+ GFS_UPLOAD: "gfs_upload",
157
+
158
+ // ── 群消息表态 ──
159
+ GROUP_SET_REACTION: "group_set_reaction",
160
+ GROUP_DEL_REACTION: "group_del_reaction",
161
+
162
+ // ── 转发消息 ──
163
+ GET_FORWARD_MSG: "get_forward_msg",
164
+ MAKE_FORWARD_MSG: "make_forward_msg",
165
+
166
+ // ── 频道系统 ──
167
+ GUILD_LIST: "guild_list",
168
+ GUILD_INFO: "guild_info",
169
+ GUILD_CHANNELS: "guild_channels",
170
+ GET_CHANNEL_INFO: "get_channel_info",
171
+ GUILD_MEMBERS: "guild_members",
172
+ GUILD_SEND_MSG: "guild_send_msg",
173
+ GUILD_RECALL_MSG: "guild_recall_msg",
174
+
175
+ // ── 用户文件操作 ──
176
+ GET_FILE_INFO: "get_file_info",
177
+ GET_FILE_URL: "get_file_url",
178
+ GET_AVATAR_URL: "get_avatar_url",
179
+ GET_GROUP_AVATAR_URL: "get_group_avatar_url",
180
+
181
+ // ── 屏蔽群成员消息 ──
182
+ SET_SCREEN_MEMBER_MSG: "set_screen_member_msg",
183
+
184
+ // ── 群文件转发 ──
185
+ GFS_FORWARD: "gfs_forward",
186
+ GFS_FORWARD_OFFLINE: "gfs_forward_offline",
187
+
188
+ // ── 重载列表 ──
189
+ RELOAD_BLACKLIST: "reload_blacklist",
190
+ RELOAD_STRANGER_LIST: "reload_stranger_list",
191
+ RELOAD_GUILDS: "reload_guilds",
192
+
193
+ // ── 在线状态查询 ──
194
+ GET_STATUS_INFO: "get_status_info",
195
+
196
+ // ── 密钥/工具 ──
197
+ GET_CLIENT_KEY: "get_client_key",
198
+ GET_PSKEY: "get_pskey",
199
+ UID2UIN: "uid2uin",
200
+ UID2UINS: "uid2uins",
201
+ UIN2UID: "uin2uid",
202
+ UIN2UIDS: "uin2uids",
203
+ GET_COOKIES: "get_cookies",
204
+ GET_CSRF_TOKEN: "get_csrf_token",
205
+ REFRESH_NT_PIC_RKEY: "refresh_nt_pic_rkey",
206
+ SEND_DISCUSS_MSG: "send_discuss_msg",
207
+
208
+ // ── 视频/加好友设置 ──
209
+ GET_VIDEO_URL: "get_video_url",
210
+ GET_ADD_FRIEND_SETTING: "get_add_friend_setting",
211
+
212
+ // ── 频道扩展 ──
213
+ GET_FORUM_URL: "get_forum_url",
214
+ GUILD_CHANNEL_SHARE: "guild_channel_share",
215
+
216
+ // ── 获取图片/语音 URL ──
217
+ GET_PIC_URL: "get_pic_url",
218
+ GET_PTT_URL: "get_ptt_url",
219
+
220
+ // ── 消息订阅 ──
221
+ SUBSCRIBE: "subscribe",
222
+ UNSUBSCRIBE: "unsubscribe",
223
+
224
+ // ── Webhook ──
225
+ SET_WEBHOOK: "set_webhook",
226
+ GET_WEBHOOK: "get_webhook",
227
+
228
+ // ── 系统通知 ──
229
+ SET_NOTIFY: "set_notify",
230
+ GET_NOTIFY: "get_notify",
231
+ } as const;
232
+
233
+ /** One expanded ICQQ endpoint configuration produced by AdapterIndex. */
234
+ export interface IcqqEndpointConfig {
235
+ readonly id: string;
236
+ readonly autoReconnect?: boolean;
237
+ readonly outboundMedia?: 'file' | 'base64';
238
+ readonly password?: string;
239
+ readonly platform?: number;
240
+ readonly ver?: string;
241
+ readonly dataDir?: string;
242
+ readonly signApiAddr?: string;
243
+ readonly ignoreSelf?: boolean;
244
+ readonly resend?: boolean;
245
+ readonly cacheGroupMember?: boolean;
246
+ readonly autoServer?: boolean;
247
+ readonly qqnt?: boolean;
248
+ readonly ntLogin?: boolean;
249
+ }
250
+
251
+ export interface ResolvedIcqqConfig {
252
+ readonly context: 'icqq';
253
+ readonly id: string;
254
+ readonly autoReconnect: boolean;
255
+ readonly outboundMedia?: 'file' | 'base64';
256
+ readonly password?: string;
257
+ readonly platform?: number;
258
+ readonly ver?: string;
259
+ readonly dataDir?: string;
260
+ readonly signApiAddr?: string;
261
+ readonly ignoreSelf?: boolean;
262
+ readonly resend?: boolean;
263
+ readonly cacheGroupMember?: boolean;
264
+ readonly autoServer?: boolean;
265
+ readonly qqnt?: boolean;
266
+ readonly ntLogin?: boolean;
267
+ }
268
+
269
+ export interface IcqqWireSegment {
270
+ readonly type: string;
271
+ readonly data?: Record<string, unknown>;
272
+ }
273
+
274
+ export interface IcqqInboundMessage {
275
+ readonly id: string;
276
+ readonly conversation: ConversationRef;
277
+ readonly content: string;
278
+ /**
279
+ * 结构化段视图(canonical Segment,经 toCanonicalSegments 归一)。
280
+ * 与 content(CQ 原文纯文本视图)同源,随 gateway.receive 上送。
281
+ */
282
+ readonly segments?: readonly MessageSegment[];
283
+ readonly sender: { readonly id: string; readonly name?: string };
284
+ readonly channelType: 'private' | 'group' | 'channel';
285
+ readonly metadata?: Record<string, unknown>;
286
+ readonly replyTo?: { readonly id: string };
287
+ }
288
+
289
+ export type ParsedIcqqSendTarget =
290
+ | { readonly kind: 'private'; readonly userId: number }
291
+ | { readonly kind: 'group'; readonly groupId: number }
292
+ | { readonly kind: 'temp'; readonly groupId: number; readonly userId: number }
293
+ | { readonly kind: 'channel'; readonly guildId: string; readonly channelId: string };
294
+
295
+ export function resolveIcqqConfig(config: IcqqEndpointConfig): ResolvedIcqqConfig {
296
+ const id = typeof config.id === 'string' ? config.id.trim() : '';
297
+ if (!/^\d+$/.test(id)) {
298
+ throw new TypeError('ICQQ endpoint requires a numeric id (QQ uin)');
299
+ }
300
+ const autoReconnect = config.autoReconnect ?? true;
301
+ const outboundMedia = config.outboundMedia;
302
+ const password = config.password;
303
+ const platform = config.platform ?? 2;
304
+ const ver = config.ver ?? '9.1.70';
305
+ const dataDir = config.dataDir ?? `data/icqq/${id}`;
306
+ const signApiAddr = config.signApiAddr;
307
+ const ignoreSelf = config.ignoreSelf ?? true;
308
+ const resend = config.resend;
309
+ const cacheGroupMember = config.cacheGroupMember ?? true;
310
+ const autoServer = config.autoServer ?? true;
311
+ const qqnt = config.qqnt;
312
+ const ntLogin = config.ntLogin;
313
+ return {
314
+ context: 'icqq',
315
+ id,
316
+ autoReconnect,
317
+ ...(outboundMedia ? { outboundMedia } : {}),
318
+ ...(password ? { password } : {}),
319
+ ...(platform != null ? { platform: Number(platform) } : {}),
320
+ ...(ver ? { ver } : {}),
321
+ ...(dataDir ? { dataDir } : {}),
322
+ ...(signApiAddr ? { signApiAddr } : {}),
323
+ ...(ignoreSelf != null ? { ignoreSelf } : {}),
324
+ ...(resend != null ? { resend } : {}),
325
+ ...(cacheGroupMember != null ? { cacheGroupMember } : {}),
326
+ ...(autoServer != null ? { autoServer } : {}),
327
+ ...(qqnt != null ? { qqnt } : {}),
328
+ ...(ntLogin != null ? { ntLogin } : {}),
329
+ };
330
+ }
331
+
332
+ /**
333
+ * 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
334
+ * 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
335
+ */
336
+ export function icqqInboundConversation(endpointKey: string, input: {
337
+ readonly channelType: 'private' | 'group' | 'channel';
338
+ readonly channelId: string;
339
+ readonly channelParentGroupId?: string;
340
+ readonly guildId?: string;
341
+ }): ConversationRef {
342
+ const endpoint = { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey };
343
+ if (input.channelType === 'private' && input.channelParentGroupId) {
344
+ return {
345
+ endpoint,
346
+ kind: 'private',
347
+ id: input.channelId,
348
+ parent: { kind: 'group', id: input.channelParentGroupId },
349
+ };
350
+ }
351
+ if (input.channelType === 'channel' && input.guildId) {
352
+ return {
353
+ endpoint,
354
+ kind: 'channel',
355
+ id: input.channelId,
356
+ parent: { kind: 'channel', id: input.guildId },
357
+ };
358
+ }
359
+ return { endpoint, kind: input.channelType, id: input.channelId };
360
+ }
361
+
362
+ /**
363
+ * 出站:从 ConversationRef 派生 action 路由。
364
+ * 群容器(parent kind 'group')内的 private 会话即 ICQQ 群临时会话(temp)。
365
+ */
366
+ export function icqqOutboundTarget(conversation: ConversationRef): ParsedIcqqSendTarget {
367
+ if (conversation.kind === 'private' && conversation.parent?.kind === 'group') {
368
+ return {
369
+ kind: 'temp',
370
+ groupId: Number(conversation.parent.id),
371
+ userId: Number(conversation.id),
372
+ };
373
+ }
374
+ if (conversation.kind === 'private') {
375
+ return { kind: 'private', userId: Number(conversation.id) };
376
+ }
377
+ if (conversation.kind === 'group') {
378
+ return { kind: 'group', groupId: Number(conversation.id) };
379
+ }
380
+ const guildId = conversation.parent?.kind === 'channel' ? conversation.parent.id : undefined;
381
+ if (!guildId) {
382
+ throw new TypeError(`Invalid icqq channel conversation (missing guild parent): ${conversation.id}`);
383
+ }
384
+ return { kind: 'channel', guildId, channelId: conversation.id };
385
+ }
386
+
387
+ const ICQQ_STANDALONE_ELEMENTS = new Set([
388
+ 'record',
389
+ 'video',
390
+ 'file',
391
+ 'json',
392
+ 'share',
393
+ 'long_msg',
394
+ ] as const);
395
+
396
+ /**
397
+ * Project canonical Zhin segments directly into the native ICQQ Sendable
398
+ * algebra. ICQQ's direct client does not parse Zhin's historical `[image:x]`
399
+ * notation; passing that notation would send it as literal text.
400
+ */
401
+ export function formatOutboundBody(payload: unknown): Sendable {
402
+ if (typeof payload === 'string') {
403
+ const text = payload.trim();
404
+ return text || '\u200b';
405
+ }
406
+ if (payload == null) return '\u200b';
407
+ if (!Array.isArray(payload)) {
408
+ // 单个段对象(非数组)按单元素数组处理,杜绝 String(object) → '[object Object]'
409
+ if (isWireSegment(payload)) {
410
+ return formatOutboundBody([payload]);
411
+ }
412
+ if (typeof payload === 'object' && payload !== null && 'text' in payload) {
413
+ const text = String((payload as { text?: unknown }).text ?? '').trim();
414
+ return text || '\u200b';
415
+ }
416
+ const text = String(payload).trim();
417
+ return text || '\u200b';
418
+ }
419
+ const elements = payload.flatMap<string | MessageElem>((seg) => {
420
+ if (typeof seg === 'string') return seg;
421
+ const item = seg as IcqqWireSegment;
422
+ switch (item.type) {
423
+ case 'text':
424
+ return String(item.data?.text ?? '');
425
+ case 'at':
426
+ case 'mention': {
427
+ const target = item.data?.qq ?? item.data?.id ?? item.data?.target;
428
+ if (target === 'all') return { type: 'at', qq: 'all' };
429
+ const qq = Number(target);
430
+ if (!Number.isSafeInteger(qq) || qq <= 0) {
431
+ throw new TypeError(`Invalid ICQQ mention target: ${String(target ?? '')}`);
432
+ }
433
+ return { type: 'at', qq };
434
+ }
435
+ case 'image': {
436
+ const arg = resolveIcqqMediaFile(item.data);
437
+ if (!arg) throw new TypeError('ICQQ image segment requires canonical data.media');
438
+ return { type: 'image', file: arg };
439
+ }
440
+ case 'face': {
441
+ const id = Number(item.data?.id);
442
+ if (!Number.isSafeInteger(id) || id < 0) {
443
+ throw new TypeError(`Invalid ICQQ face id: ${String(item.data?.id ?? '')}`);
444
+ }
445
+ return { type: 'face', id };
446
+ }
447
+ case 'reply': {
448
+ const id = String(item.data?.message_id ?? item.data?.id ?? '').trim();
449
+ if (!id) throw new TypeError('ICQQ reply segment requires message_id');
450
+ return { type: 'reply', id };
451
+ }
452
+ case 'record':
453
+ case 'audio': {
454
+ const arg = resolveIcqqMediaFile(item.data);
455
+ if (!arg) throw new TypeError('ICQQ audio segment requires canonical data.media');
456
+ return { type: 'record', file: arg };
457
+ }
458
+ case 'video': {
459
+ const arg = resolveIcqqMediaFile(item.data);
460
+ if (!arg) throw new TypeError('ICQQ video segment requires canonical data.media');
461
+ return { type: 'video', file: arg };
462
+ }
463
+ case 'file': {
464
+ const arg = resolveIcqqMediaFile(item.data);
465
+ if (!arg) throw new TypeError('ICQQ file segment requires canonical data.media');
466
+ const name = String(item.data?.name ?? '').trim();
467
+ return { type: 'file', file: arg, ...(name ? { name } : {}) };
468
+ }
469
+ case 'dice':
470
+ return { type: 'dice' };
471
+ case 'rps':
472
+ return { type: 'rps' };
473
+ case 'forward': {
474
+ const resid = String(item.data?.forward_id ?? item.data?.resid ?? item.data?.id ?? '').trim();
475
+ if (!resid) throw new TypeError('ICQQ forward segment requires forward_id');
476
+ return { type: 'long_msg', resid };
477
+ }
478
+ case 'share':
479
+ return formatShare(item.data);
480
+ default: {
481
+ const text = item.data?.text;
482
+ if (typeof text === 'string' && text) return text;
483
+ throw new TypeError(`Unsupported ICQQ outbound segment: ${item.type}`);
484
+ }
485
+ }
486
+ }).filter((element) => typeof element !== 'string' || element.length > 0);
487
+
488
+ if (elements.length === 0) return '\u200b';
489
+ const standalone = elements.find(
490
+ (element): element is MessageElem =>
491
+ typeof element !== 'string' && ICQQ_STANDALONE_ELEMENTS.has(element.type as never),
492
+ );
493
+ if (standalone && elements.length !== 1) {
494
+ throw new TypeError(`ICQQ ${standalone.type} element must be sent as a standalone message`);
495
+ }
496
+ return elements.length === 1 ? elements[0]! : elements;
497
+ }
498
+
499
+ export function formatInboundContent(rawMessage: string): string {
500
+ return rawMessage;
501
+ }
502
+
503
+ /** 段对象判定:`{ type: string, data? }`(非数组 payload 的防御入口)。 */
504
+ function isWireSegment(value: unknown): value is IcqqWireSegment {
505
+ return typeof value === 'object'
506
+ && value !== null
507
+ && typeof (value as { type?: unknown }).type === 'string';
508
+ }
509
+
510
+ function formatShare(data: Record<string, unknown> | undefined): ShareElem {
511
+ if (!data) throw new TypeError('ICQQ share segment requires data');
512
+ const config = data.config as {
513
+ appid?: number;
514
+ package?: string;
515
+ sign?: string;
516
+ } | undefined;
517
+ const title = String(data.title ?? '').trim();
518
+ const url = String(data.url ?? '').trim();
519
+ if (!title) throw new TypeError('ICQQ share segment requires title');
520
+ if (!url) throw new TypeError('ICQQ share segment requires url');
521
+
522
+ const optionalString = (value: unknown): string | undefined => {
523
+ const text = String(value ?? '').trim();
524
+ return text || undefined;
525
+ };
526
+ const appid = Number(config?.appid);
527
+ const appname = optionalString(config?.package);
528
+ const appsign = optionalString(config?.sign);
529
+ const shareConfig = Number.isSafeInteger(appid) && appid > 0
530
+ ? {
531
+ appid,
532
+ ...(appname ? { appname } : {}),
533
+ ...(appsign ? { appsign } : {}),
534
+ }
535
+ : undefined;
536
+ const summary = optionalString(data.artist);
537
+ const content = optionalString(data.content);
538
+ const image = optionalString(data.image);
539
+ const audio = optionalString(data.audio);
540
+
541
+ return {
542
+ type: 'share',
543
+ title,
544
+ url,
545
+ ...(summary ? { summary } : {}),
546
+ ...(content ? { content } : {}),
547
+ ...(image ? { image } : {}),
548
+ ...(audio ? { audio } : {}),
549
+ ...(shareConfig ? { config: shareConfig } : {}),
550
+ };
551
+ }
package/src/types.ts CHANGED
@@ -1,9 +1,4 @@
1
- /**
2
- * ICQQ 适配器类型与配置
3
- */
4
- import type { Config, GroupRole, MemberInfo } from "@icqqjs/icqq";
5
-
6
- export type { Config, GroupRole, MemberInfo };
1
+ export type GroupRole = "owner" | "admin" | "member";
7
2
 
8
3
  export interface IcqqSenderInfo {
9
4
  id: string;
@@ -16,13 +11,40 @@ export interface IcqqSenderInfo {
16
11
  title?: string;
17
12
  }
18
13
 
19
- export interface IcqqBotConfig extends Config {
20
- context: "icqq";
21
- name: `${number}`;
22
- password?: string;
23
- scope?: string;
14
+ export interface FriendInfo {
15
+ user_id: number;
16
+ nickname: string;
17
+ remark?: string;
18
+ class_id?: number;
19
+ }
20
+
21
+ export interface GroupInfo {
22
+ group_id: number;
23
+ group_name: string;
24
+ member_count: number;
25
+ max_member_count: number;
26
+ owner_id?: number;
27
+ }
28
+
29
+ export interface MemberInfo {
30
+ user_id: number;
31
+ nickname: string;
32
+ card: string;
33
+ role: GroupRole;
34
+ title: string;
35
+ join_time?: number;
36
+ last_sent_time?: number;
37
+ shutup_time?: number;
24
38
  }
25
39
 
26
- export interface IcqqBot {
27
- $config: IcqqBotConfig;
40
+ export interface SystemMessage {
41
+ type: string;
42
+ user_id?: number;
43
+ nickname?: string;
44
+ group_id?: number;
45
+ group_name?: string;
46
+ comment?: string;
47
+ flag?: string;
48
+ seq?: number;
49
+ time?: number;
28
50
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 凉菜
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.