@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,258 @@
1
+ import type { Sendable } from '@icqqjs/icqq';
2
+ import type { ConversationRef } from '@zhin.js/im-contract';
3
+ import type { MessageSegment } from "zhin.js";
4
+ export type ActionResult = {
5
+ id: string;
6
+ ok: boolean;
7
+ data?: unknown;
8
+ error?: string;
9
+ };
10
+ export type { IcqqMessageEvent } from "./icqq-inbound.js";
11
+ export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
12
+ export interface GuildMessageEventData {
13
+ type: "guild";
14
+ guild_id: string;
15
+ guild_name: string;
16
+ channel_id: string;
17
+ channel_name: string;
18
+ nickname: string;
19
+ tiny_id: string;
20
+ raw_message: string;
21
+ time: number;
22
+ seq: number;
23
+ }
24
+ /**
25
+ * 所有操作常量(@icqqjs/icqq)
26
+ */
27
+ export declare const Actions: {
28
+ readonly PING: "ping";
29
+ readonly LOGOUT: "logout";
30
+ readonly LIST_FRIENDS: "list_friends";
31
+ readonly LIST_GROUPS: "list_groups";
32
+ readonly LIST_GROUP_MEMBERS: "list_group_members";
33
+ readonly LIST_BLACKLIST: "list_blacklist";
34
+ readonly LIST_FRIEND_CLASSES: "list_friend_classes";
35
+ readonly GET_FRIEND_INFO: "get_friend_info";
36
+ readonly GET_GROUP_INFO: "get_group_info";
37
+ readonly GET_GROUP_MEMBER_INFO: "get_group_member_info";
38
+ readonly GET_STRANGER_INFO: "get_stranger_info";
39
+ readonly LIST_STRANGERS: "list_strangers";
40
+ readonly GET_PROFILE: "get_profile";
41
+ readonly GET_STATUS: "get_status";
42
+ readonly GET_ONLINE_STATUS: "get_online_status";
43
+ readonly GET_SELF_PROFILE: "get_self_profile";
44
+ readonly SEND_PRIVATE_MSG: "send_private_msg";
45
+ readonly SEND_GROUP_MSG: "send_group_msg";
46
+ readonly RECALL_MSG: "recall_msg";
47
+ readonly GET_MSG: "get_msg";
48
+ readonly HISTORY_PRIVATE: "history_private";
49
+ readonly HISTORY_GROUP: "history_group";
50
+ readonly HISTORY_BY_MSG_ID: "history_by_msg_id";
51
+ readonly SEND_LONG_MSG: "send_long_msg";
52
+ readonly MARK_READ: "mark_read";
53
+ readonly DELETE_MSG: "delete_msg";
54
+ readonly SET_NICKNAME: "set_nickname";
55
+ readonly SET_GENDER: "set_gender";
56
+ readonly SET_BIRTHDAY: "set_birthday";
57
+ readonly SET_SIGNATURE: "set_signature";
58
+ readonly SET_DESCRIPTION: "set_description";
59
+ readonly SET_AVATAR: "set_avatar";
60
+ readonly SET_ONLINE_STATUS: "set_online_status";
61
+ readonly SET_GROUP_NAME: "set_group_name";
62
+ readonly SET_GROUP_AVATAR: "set_group_avatar";
63
+ readonly SET_GROUP_CARD: "set_group_card";
64
+ readonly SET_GROUP_TITLE: "set_group_title";
65
+ readonly SET_GROUP_ADMIN: "set_group_admin";
66
+ readonly SET_GROUP_REMARK: "set_group_remark";
67
+ readonly GROUP_MUTE: "group_mute";
68
+ readonly GROUP_MUTE_ALL: "group_mute_all";
69
+ readonly GROUP_KICK: "group_kick";
70
+ readonly GROUP_QUIT: "group_quit";
71
+ readonly GROUP_INVITE: "group_invite";
72
+ readonly GROUP_POKE: "group_poke";
73
+ readonly GROUP_ANNOUNCE: "group_announce";
74
+ readonly GROUP_SIGN: "group_sign";
75
+ readonly GROUP_ESSENCE_ADD: "group_essence_add";
76
+ readonly GROUP_ESSENCE_REMOVE: "group_essence_remove";
77
+ readonly GROUP_ALLOW_ANONY: "group_allow_anony";
78
+ readonly GROUP_MUTED_LIST: "group_muted_list";
79
+ readonly GROUP_AT_ALL_REMAIN: "group_at_all_remain";
80
+ readonly FRIEND_POKE: "friend_poke";
81
+ readonly FRIEND_LIKE: "friend_like";
82
+ readonly FRIEND_DELETE: "friend_delete";
83
+ readonly FRIEND_REMARK: "friend_remark";
84
+ readonly FRIEND_CLASS: "friend_class";
85
+ readonly GET_SYSTEM_MSG: "get_system_msg";
86
+ readonly HANDLE_FRIEND_REQUEST: "handle_friend_request";
87
+ readonly HANDLE_GROUP_REQUEST: "handle_group_request";
88
+ readonly ADD_FRIEND_CLASS: "add_friend_class";
89
+ readonly DELETE_FRIEND_CLASS: "delete_friend_class";
90
+ readonly RENAME_FRIEND_CLASS: "rename_friend_class";
91
+ readonly GFS_LIST: "gfs_list";
92
+ readonly GFS_INFO: "gfs_info";
93
+ readonly GFS_MKDIR: "gfs_mkdir";
94
+ readonly GFS_DELETE: "gfs_delete";
95
+ readonly GFS_RENAME: "gfs_rename";
96
+ readonly GFS_STAT: "gfs_stat";
97
+ readonly GFS_MOVE: "gfs_move";
98
+ readonly GFS_DOWNLOAD: "gfs_download";
99
+ readonly IMAGE_OCR: "image_ocr";
100
+ readonly RELOAD_FRIEND_LIST: "reload_friend_list";
101
+ readonly RELOAD_GROUP_LIST: "reload_group_list";
102
+ readonly CLEAN_CACHE: "clean_cache";
103
+ readonly GET_GROUP_SHARE: "get_group_share";
104
+ readonly GROUP_SET_JOIN_TYPE: "group_set_join_type";
105
+ readonly GROUP_SET_RATE_LIMIT: "group_set_rate_limit";
106
+ readonly GROUP_MUTE_ANONY: "group_mute_anony";
107
+ readonly GROUP_ANON_INFO: "group_anon_info";
108
+ readonly ADD_FRIEND: "add_friend";
109
+ readonly SEND_TEMP_MSG: "send_temp_msg";
110
+ readonly GET_ROAMING_STAMP: "get_roaming_stamp";
111
+ readonly DELETE_STAMP: "delete_stamp";
112
+ readonly SEND_PRIVATE_FILE: "send_private_file";
113
+ readonly SEND_GROUP_FILE: "send_group_file";
114
+ readonly FRIEND_RECALL_FILE: "friend_recall_file";
115
+ readonly FRIEND_FORWARD_FILE: "friend_forward_file";
116
+ readonly SEARCH_SAME_GROUP: "search_same_group";
117
+ readonly SEND_CONTACT_SHARE: "send_contact_share";
118
+ readonly GFS_UPLOAD: "gfs_upload";
119
+ readonly GROUP_SET_REACTION: "group_set_reaction";
120
+ readonly GROUP_DEL_REACTION: "group_del_reaction";
121
+ readonly GET_FORWARD_MSG: "get_forward_msg";
122
+ readonly MAKE_FORWARD_MSG: "make_forward_msg";
123
+ readonly GUILD_LIST: "guild_list";
124
+ readonly GUILD_INFO: "guild_info";
125
+ readonly GUILD_CHANNELS: "guild_channels";
126
+ readonly GET_CHANNEL_INFO: "get_channel_info";
127
+ readonly GUILD_MEMBERS: "guild_members";
128
+ readonly GUILD_SEND_MSG: "guild_send_msg";
129
+ readonly GUILD_RECALL_MSG: "guild_recall_msg";
130
+ readonly GET_FILE_INFO: "get_file_info";
131
+ readonly GET_FILE_URL: "get_file_url";
132
+ readonly GET_AVATAR_URL: "get_avatar_url";
133
+ readonly GET_GROUP_AVATAR_URL: "get_group_avatar_url";
134
+ readonly SET_SCREEN_MEMBER_MSG: "set_screen_member_msg";
135
+ readonly GFS_FORWARD: "gfs_forward";
136
+ readonly GFS_FORWARD_OFFLINE: "gfs_forward_offline";
137
+ readonly RELOAD_BLACKLIST: "reload_blacklist";
138
+ readonly RELOAD_STRANGER_LIST: "reload_stranger_list";
139
+ readonly RELOAD_GUILDS: "reload_guilds";
140
+ readonly GET_STATUS_INFO: "get_status_info";
141
+ readonly GET_CLIENT_KEY: "get_client_key";
142
+ readonly GET_PSKEY: "get_pskey";
143
+ readonly UID2UIN: "uid2uin";
144
+ readonly UID2UINS: "uid2uins";
145
+ readonly UIN2UID: "uin2uid";
146
+ readonly UIN2UIDS: "uin2uids";
147
+ readonly GET_COOKIES: "get_cookies";
148
+ readonly GET_CSRF_TOKEN: "get_csrf_token";
149
+ readonly REFRESH_NT_PIC_RKEY: "refresh_nt_pic_rkey";
150
+ readonly SEND_DISCUSS_MSG: "send_discuss_msg";
151
+ readonly GET_VIDEO_URL: "get_video_url";
152
+ readonly GET_ADD_FRIEND_SETTING: "get_add_friend_setting";
153
+ readonly GET_FORUM_URL: "get_forum_url";
154
+ readonly GUILD_CHANNEL_SHARE: "guild_channel_share";
155
+ readonly GET_PIC_URL: "get_pic_url";
156
+ readonly GET_PTT_URL: "get_ptt_url";
157
+ readonly SUBSCRIBE: "subscribe";
158
+ readonly UNSUBSCRIBE: "unsubscribe";
159
+ readonly SET_WEBHOOK: "set_webhook";
160
+ readonly GET_WEBHOOK: "get_webhook";
161
+ readonly SET_NOTIFY: "set_notify";
162
+ readonly GET_NOTIFY: "get_notify";
163
+ };
164
+ /** One expanded ICQQ endpoint configuration produced by AdapterIndex. */
165
+ export interface IcqqEndpointConfig {
166
+ readonly id: string;
167
+ readonly autoReconnect?: boolean;
168
+ readonly outboundMedia?: 'file' | 'base64';
169
+ readonly password?: string;
170
+ readonly platform?: number;
171
+ readonly ver?: string;
172
+ readonly dataDir?: string;
173
+ readonly signApiAddr?: string;
174
+ readonly ignoreSelf?: boolean;
175
+ readonly resend?: boolean;
176
+ readonly cacheGroupMember?: boolean;
177
+ readonly autoServer?: boolean;
178
+ readonly qqnt?: boolean;
179
+ readonly ntLogin?: boolean;
180
+ }
181
+ export interface ResolvedIcqqConfig {
182
+ readonly context: 'icqq';
183
+ readonly id: string;
184
+ readonly autoReconnect: boolean;
185
+ readonly outboundMedia?: 'file' | 'base64';
186
+ readonly password?: string;
187
+ readonly platform?: number;
188
+ readonly ver?: string;
189
+ readonly dataDir?: string;
190
+ readonly signApiAddr?: string;
191
+ readonly ignoreSelf?: boolean;
192
+ readonly resend?: boolean;
193
+ readonly cacheGroupMember?: boolean;
194
+ readonly autoServer?: boolean;
195
+ readonly qqnt?: boolean;
196
+ readonly ntLogin?: boolean;
197
+ }
198
+ export interface IcqqWireSegment {
199
+ readonly type: string;
200
+ readonly data?: Record<string, unknown>;
201
+ }
202
+ export interface IcqqInboundMessage {
203
+ readonly id: string;
204
+ readonly conversation: ConversationRef;
205
+ readonly content: string;
206
+ /**
207
+ * 结构化段视图(canonical Segment,经 toCanonicalSegments 归一)。
208
+ * 与 content(CQ 原文纯文本视图)同源,随 gateway.receive 上送。
209
+ */
210
+ readonly segments?: readonly MessageSegment[];
211
+ readonly sender: {
212
+ readonly id: string;
213
+ readonly name?: string;
214
+ };
215
+ readonly channelType: 'private' | 'group' | 'channel';
216
+ readonly metadata?: Record<string, unknown>;
217
+ readonly replyTo?: {
218
+ readonly id: string;
219
+ };
220
+ }
221
+ export type ParsedIcqqSendTarget = {
222
+ readonly kind: 'private';
223
+ readonly userId: number;
224
+ } | {
225
+ readonly kind: 'group';
226
+ readonly groupId: number;
227
+ } | {
228
+ readonly kind: 'temp';
229
+ readonly groupId: number;
230
+ readonly userId: number;
231
+ } | {
232
+ readonly kind: 'channel';
233
+ readonly guildId: string;
234
+ readonly channelId: string;
235
+ };
236
+ export declare function resolveIcqqConfig(config: IcqqEndpointConfig): ResolvedIcqqConfig;
237
+ /**
238
+ * 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
239
+ * 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
240
+ */
241
+ export declare function icqqInboundConversation(endpointKey: string, input: {
242
+ readonly channelType: 'private' | 'group' | 'channel';
243
+ readonly channelId: string;
244
+ readonly channelParentGroupId?: string;
245
+ readonly guildId?: string;
246
+ }): ConversationRef;
247
+ /**
248
+ * 出站:从 ConversationRef 派生 action 路由。
249
+ * 群容器(parent kind 'group')内的 private 会话即 ICQQ 群临时会话(temp)。
250
+ */
251
+ export declare function icqqOutboundTarget(conversation: ConversationRef): ParsedIcqqSendTarget;
252
+ /**
253
+ * Project canonical Zhin segments directly into the native ICQQ Sendable
254
+ * algebra. ICQQ's direct client does not parse Zhin's historical `[image:x]`
255
+ * notation; passing that notation would send it as literal text.
256
+ */
257
+ export declare function formatOutboundBody(payload: unknown): Sendable;
258
+ export declare function formatInboundContent(rawMessage: string): string;
@@ -0,0 +1,422 @@
1
+ import { resolveIcqqMediaFile } from "./cq-message.js";
2
+ export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
3
+ /**
4
+ * 所有操作常量(@icqqjs/icqq)
5
+ */
6
+ export const Actions = {
7
+ PING: "ping",
8
+ LOGOUT: "logout",
9
+ // ── 列表查询 ──
10
+ LIST_FRIENDS: "list_friends",
11
+ LIST_GROUPS: "list_groups",
12
+ LIST_GROUP_MEMBERS: "list_group_members",
13
+ LIST_BLACKLIST: "list_blacklist",
14
+ LIST_FRIEND_CLASSES: "list_friend_classes",
15
+ // ── 信息查询 ──
16
+ GET_FRIEND_INFO: "get_friend_info",
17
+ GET_GROUP_INFO: "get_group_info",
18
+ GET_GROUP_MEMBER_INFO: "get_group_member_info",
19
+ GET_STRANGER_INFO: "get_stranger_info",
20
+ LIST_STRANGERS: "list_strangers",
21
+ GET_PROFILE: "get_profile",
22
+ GET_STATUS: "get_status",
23
+ GET_ONLINE_STATUS: "get_online_status",
24
+ GET_SELF_PROFILE: "get_self_profile",
25
+ // ── 消息发送 ──
26
+ SEND_PRIVATE_MSG: "send_private_msg",
27
+ SEND_GROUP_MSG: "send_group_msg",
28
+ // ── 消息操作 ──
29
+ RECALL_MSG: "recall_msg",
30
+ GET_MSG: "get_msg",
31
+ HISTORY_PRIVATE: "history_private",
32
+ HISTORY_GROUP: "history_group",
33
+ HISTORY_BY_MSG_ID: "history_by_msg_id",
34
+ SEND_LONG_MSG: "send_long_msg",
35
+ MARK_READ: "mark_read",
36
+ DELETE_MSG: "delete_msg",
37
+ // ── 个人设置 ──
38
+ SET_NICKNAME: "set_nickname",
39
+ SET_GENDER: "set_gender",
40
+ SET_BIRTHDAY: "set_birthday",
41
+ SET_SIGNATURE: "set_signature",
42
+ SET_DESCRIPTION: "set_description",
43
+ SET_AVATAR: "set_avatar",
44
+ SET_ONLINE_STATUS: "set_online_status",
45
+ // ── 群设置 ──
46
+ SET_GROUP_NAME: "set_group_name",
47
+ SET_GROUP_AVATAR: "set_group_avatar",
48
+ SET_GROUP_CARD: "set_group_card",
49
+ SET_GROUP_TITLE: "set_group_title",
50
+ SET_GROUP_ADMIN: "set_group_admin",
51
+ SET_GROUP_REMARK: "set_group_remark",
52
+ // ── 群管理 ──
53
+ GROUP_MUTE: "group_mute",
54
+ GROUP_MUTE_ALL: "group_mute_all",
55
+ GROUP_KICK: "group_kick",
56
+ GROUP_QUIT: "group_quit",
57
+ GROUP_INVITE: "group_invite",
58
+ GROUP_POKE: "group_poke",
59
+ GROUP_ANNOUNCE: "group_announce",
60
+ GROUP_SIGN: "group_sign",
61
+ GROUP_ESSENCE_ADD: "group_essence_add",
62
+ GROUP_ESSENCE_REMOVE: "group_essence_remove",
63
+ GROUP_ALLOW_ANONY: "group_allow_anony",
64
+ GROUP_MUTED_LIST: "group_muted_list",
65
+ GROUP_AT_ALL_REMAIN: "group_at_all_remain",
66
+ // ── 好友操作 ──
67
+ FRIEND_POKE: "friend_poke",
68
+ FRIEND_LIKE: "friend_like",
69
+ FRIEND_DELETE: "friend_delete",
70
+ FRIEND_REMARK: "friend_remark",
71
+ FRIEND_CLASS: "friend_class",
72
+ // ── 系统消息/请求 ──
73
+ GET_SYSTEM_MSG: "get_system_msg",
74
+ HANDLE_FRIEND_REQUEST: "handle_friend_request",
75
+ HANDLE_GROUP_REQUEST: "handle_group_request",
76
+ // ── 好友分组 ──
77
+ ADD_FRIEND_CLASS: "add_friend_class",
78
+ DELETE_FRIEND_CLASS: "delete_friend_class",
79
+ RENAME_FRIEND_CLASS: "rename_friend_class",
80
+ // ── 群文件系统 ──
81
+ GFS_LIST: "gfs_list",
82
+ GFS_INFO: "gfs_info",
83
+ GFS_MKDIR: "gfs_mkdir",
84
+ GFS_DELETE: "gfs_delete",
85
+ GFS_RENAME: "gfs_rename",
86
+ GFS_STAT: "gfs_stat",
87
+ GFS_MOVE: "gfs_move",
88
+ GFS_DOWNLOAD: "gfs_download",
89
+ // ── 其他功能 ──
90
+ IMAGE_OCR: "image_ocr",
91
+ RELOAD_FRIEND_LIST: "reload_friend_list",
92
+ RELOAD_GROUP_LIST: "reload_group_list",
93
+ CLEAN_CACHE: "clean_cache",
94
+ GET_GROUP_SHARE: "get_group_share",
95
+ // ── 群管理扩展 ──
96
+ GROUP_SET_JOIN_TYPE: "group_set_join_type",
97
+ GROUP_SET_RATE_LIMIT: "group_set_rate_limit",
98
+ GROUP_MUTE_ANONY: "group_mute_anony",
99
+ GROUP_ANON_INFO: "group_anon_info",
100
+ // ── 好友操作扩展 ──
101
+ ADD_FRIEND: "add_friend",
102
+ SEND_TEMP_MSG: "send_temp_msg",
103
+ // ── 漫游表情 ──
104
+ GET_ROAMING_STAMP: "get_roaming_stamp",
105
+ DELETE_STAMP: "delete_stamp",
106
+ // ── 文件传输 ──
107
+ SEND_PRIVATE_FILE: "send_private_file",
108
+ SEND_GROUP_FILE: "send_group_file",
109
+ FRIEND_RECALL_FILE: "friend_recall_file",
110
+ FRIEND_FORWARD_FILE: "friend_forward_file",
111
+ SEARCH_SAME_GROUP: "search_same_group",
112
+ SEND_CONTACT_SHARE: "send_contact_share",
113
+ GFS_UPLOAD: "gfs_upload",
114
+ // ── 群消息表态 ──
115
+ GROUP_SET_REACTION: "group_set_reaction",
116
+ GROUP_DEL_REACTION: "group_del_reaction",
117
+ // ── 转发消息 ──
118
+ GET_FORWARD_MSG: "get_forward_msg",
119
+ MAKE_FORWARD_MSG: "make_forward_msg",
120
+ // ── 频道系统 ──
121
+ GUILD_LIST: "guild_list",
122
+ GUILD_INFO: "guild_info",
123
+ GUILD_CHANNELS: "guild_channels",
124
+ GET_CHANNEL_INFO: "get_channel_info",
125
+ GUILD_MEMBERS: "guild_members",
126
+ GUILD_SEND_MSG: "guild_send_msg",
127
+ GUILD_RECALL_MSG: "guild_recall_msg",
128
+ // ── 用户文件操作 ──
129
+ GET_FILE_INFO: "get_file_info",
130
+ GET_FILE_URL: "get_file_url",
131
+ GET_AVATAR_URL: "get_avatar_url",
132
+ GET_GROUP_AVATAR_URL: "get_group_avatar_url",
133
+ // ── 屏蔽群成员消息 ──
134
+ SET_SCREEN_MEMBER_MSG: "set_screen_member_msg",
135
+ // ── 群文件转发 ──
136
+ GFS_FORWARD: "gfs_forward",
137
+ GFS_FORWARD_OFFLINE: "gfs_forward_offline",
138
+ // ── 重载列表 ──
139
+ RELOAD_BLACKLIST: "reload_blacklist",
140
+ RELOAD_STRANGER_LIST: "reload_stranger_list",
141
+ RELOAD_GUILDS: "reload_guilds",
142
+ // ── 在线状态查询 ──
143
+ GET_STATUS_INFO: "get_status_info",
144
+ // ── 密钥/工具 ──
145
+ GET_CLIENT_KEY: "get_client_key",
146
+ GET_PSKEY: "get_pskey",
147
+ UID2UIN: "uid2uin",
148
+ UID2UINS: "uid2uins",
149
+ UIN2UID: "uin2uid",
150
+ UIN2UIDS: "uin2uids",
151
+ GET_COOKIES: "get_cookies",
152
+ GET_CSRF_TOKEN: "get_csrf_token",
153
+ REFRESH_NT_PIC_RKEY: "refresh_nt_pic_rkey",
154
+ SEND_DISCUSS_MSG: "send_discuss_msg",
155
+ // ── 视频/加好友设置 ──
156
+ GET_VIDEO_URL: "get_video_url",
157
+ GET_ADD_FRIEND_SETTING: "get_add_friend_setting",
158
+ // ── 频道扩展 ──
159
+ GET_FORUM_URL: "get_forum_url",
160
+ GUILD_CHANNEL_SHARE: "guild_channel_share",
161
+ // ── 获取图片/语音 URL ──
162
+ GET_PIC_URL: "get_pic_url",
163
+ GET_PTT_URL: "get_ptt_url",
164
+ // ── 消息订阅 ──
165
+ SUBSCRIBE: "subscribe",
166
+ UNSUBSCRIBE: "unsubscribe",
167
+ // ── Webhook ──
168
+ SET_WEBHOOK: "set_webhook",
169
+ GET_WEBHOOK: "get_webhook",
170
+ // ── 系统通知 ──
171
+ SET_NOTIFY: "set_notify",
172
+ GET_NOTIFY: "get_notify",
173
+ };
174
+ export function resolveIcqqConfig(config) {
175
+ const id = typeof config.id === 'string' ? config.id.trim() : '';
176
+ if (!/^\d+$/.test(id)) {
177
+ throw new TypeError('ICQQ endpoint requires a numeric id (QQ uin)');
178
+ }
179
+ const autoReconnect = config.autoReconnect ?? true;
180
+ const outboundMedia = config.outboundMedia;
181
+ const password = config.password;
182
+ const platform = config.platform ?? 2;
183
+ const ver = config.ver ?? '9.1.70';
184
+ const dataDir = config.dataDir ?? `data/icqq/${id}`;
185
+ const signApiAddr = config.signApiAddr;
186
+ const ignoreSelf = config.ignoreSelf ?? true;
187
+ const resend = config.resend;
188
+ const cacheGroupMember = config.cacheGroupMember ?? true;
189
+ const autoServer = config.autoServer ?? true;
190
+ const qqnt = config.qqnt;
191
+ const ntLogin = config.ntLogin;
192
+ return {
193
+ context: 'icqq',
194
+ id,
195
+ autoReconnect,
196
+ ...(outboundMedia ? { outboundMedia } : {}),
197
+ ...(password ? { password } : {}),
198
+ ...(platform != null ? { platform: Number(platform) } : {}),
199
+ ...(ver ? { ver } : {}),
200
+ ...(dataDir ? { dataDir } : {}),
201
+ ...(signApiAddr ? { signApiAddr } : {}),
202
+ ...(ignoreSelf != null ? { ignoreSelf } : {}),
203
+ ...(resend != null ? { resend } : {}),
204
+ ...(cacheGroupMember != null ? { cacheGroupMember } : {}),
205
+ ...(autoServer != null ? { autoServer } : {}),
206
+ ...(qqnt != null ? { qqnt } : {}),
207
+ ...(ntLogin != null ? { ntLogin } : {}),
208
+ };
209
+ }
210
+ /**
211
+ * 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
212
+ * 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
213
+ */
214
+ export function icqqInboundConversation(endpointKey, input) {
215
+ const endpoint = { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey };
216
+ if (input.channelType === 'private' && input.channelParentGroupId) {
217
+ return {
218
+ endpoint,
219
+ kind: 'private',
220
+ id: input.channelId,
221
+ parent: { kind: 'group', id: input.channelParentGroupId },
222
+ };
223
+ }
224
+ if (input.channelType === 'channel' && input.guildId) {
225
+ return {
226
+ endpoint,
227
+ kind: 'channel',
228
+ id: input.channelId,
229
+ parent: { kind: 'channel', id: input.guildId },
230
+ };
231
+ }
232
+ return { endpoint, kind: input.channelType, id: input.channelId };
233
+ }
234
+ /**
235
+ * 出站:从 ConversationRef 派生 action 路由。
236
+ * 群容器(parent kind 'group')内的 private 会话即 ICQQ 群临时会话(temp)。
237
+ */
238
+ export function icqqOutboundTarget(conversation) {
239
+ if (conversation.kind === 'private' && conversation.parent?.kind === 'group') {
240
+ return {
241
+ kind: 'temp',
242
+ groupId: Number(conversation.parent.id),
243
+ userId: Number(conversation.id),
244
+ };
245
+ }
246
+ if (conversation.kind === 'private') {
247
+ return { kind: 'private', userId: Number(conversation.id) };
248
+ }
249
+ if (conversation.kind === 'group') {
250
+ return { kind: 'group', groupId: Number(conversation.id) };
251
+ }
252
+ const guildId = conversation.parent?.kind === 'channel' ? conversation.parent.id : undefined;
253
+ if (!guildId) {
254
+ throw new TypeError(`Invalid icqq channel conversation (missing guild parent): ${conversation.id}`);
255
+ }
256
+ return { kind: 'channel', guildId, channelId: conversation.id };
257
+ }
258
+ const ICQQ_STANDALONE_ELEMENTS = new Set([
259
+ 'record',
260
+ 'video',
261
+ 'file',
262
+ 'json',
263
+ 'share',
264
+ 'long_msg',
265
+ ]);
266
+ /**
267
+ * Project canonical Zhin segments directly into the native ICQQ Sendable
268
+ * algebra. ICQQ's direct client does not parse Zhin's historical `[image:x]`
269
+ * notation; passing that notation would send it as literal text.
270
+ */
271
+ export function formatOutboundBody(payload) {
272
+ if (typeof payload === 'string') {
273
+ const text = payload.trim();
274
+ return text || '\u200b';
275
+ }
276
+ if (payload == null)
277
+ return '\u200b';
278
+ if (!Array.isArray(payload)) {
279
+ // 单个段对象(非数组)按单元素数组处理,杜绝 String(object) → '[object Object]'
280
+ if (isWireSegment(payload)) {
281
+ return formatOutboundBody([payload]);
282
+ }
283
+ if (typeof payload === 'object' && payload !== null && 'text' in payload) {
284
+ const text = String(payload.text ?? '').trim();
285
+ return text || '\u200b';
286
+ }
287
+ const text = String(payload).trim();
288
+ return text || '\u200b';
289
+ }
290
+ const elements = payload.flatMap((seg) => {
291
+ if (typeof seg === 'string')
292
+ return seg;
293
+ const item = seg;
294
+ switch (item.type) {
295
+ case 'text':
296
+ return String(item.data?.text ?? '');
297
+ case 'at':
298
+ case 'mention': {
299
+ const target = item.data?.qq ?? item.data?.id ?? item.data?.target;
300
+ if (target === 'all')
301
+ return { type: 'at', qq: 'all' };
302
+ const qq = Number(target);
303
+ if (!Number.isSafeInteger(qq) || qq <= 0) {
304
+ throw new TypeError(`Invalid ICQQ mention target: ${String(target ?? '')}`);
305
+ }
306
+ return { type: 'at', qq };
307
+ }
308
+ case 'image': {
309
+ const arg = resolveIcqqMediaFile(item.data);
310
+ if (!arg)
311
+ throw new TypeError('ICQQ image segment requires canonical data.media');
312
+ return { type: 'image', file: arg };
313
+ }
314
+ case 'face': {
315
+ const id = Number(item.data?.id);
316
+ if (!Number.isSafeInteger(id) || id < 0) {
317
+ throw new TypeError(`Invalid ICQQ face id: ${String(item.data?.id ?? '')}`);
318
+ }
319
+ return { type: 'face', id };
320
+ }
321
+ case 'reply': {
322
+ const id = String(item.data?.message_id ?? item.data?.id ?? '').trim();
323
+ if (!id)
324
+ throw new TypeError('ICQQ reply segment requires message_id');
325
+ return { type: 'reply', id };
326
+ }
327
+ case 'record':
328
+ case 'audio': {
329
+ const arg = resolveIcqqMediaFile(item.data);
330
+ if (!arg)
331
+ throw new TypeError('ICQQ audio segment requires canonical data.media');
332
+ return { type: 'record', file: arg };
333
+ }
334
+ case 'video': {
335
+ const arg = resolveIcqqMediaFile(item.data);
336
+ if (!arg)
337
+ throw new TypeError('ICQQ video segment requires canonical data.media');
338
+ return { type: 'video', file: arg };
339
+ }
340
+ case 'file': {
341
+ const arg = resolveIcqqMediaFile(item.data);
342
+ if (!arg)
343
+ throw new TypeError('ICQQ file segment requires canonical data.media');
344
+ const name = String(item.data?.name ?? '').trim();
345
+ return { type: 'file', file: arg, ...(name ? { name } : {}) };
346
+ }
347
+ case 'dice':
348
+ return { type: 'dice' };
349
+ case 'rps':
350
+ return { type: 'rps' };
351
+ case 'forward': {
352
+ const resid = String(item.data?.forward_id ?? item.data?.resid ?? item.data?.id ?? '').trim();
353
+ if (!resid)
354
+ throw new TypeError('ICQQ forward segment requires forward_id');
355
+ return { type: 'long_msg', resid };
356
+ }
357
+ case 'share':
358
+ return formatShare(item.data);
359
+ default: {
360
+ const text = item.data?.text;
361
+ if (typeof text === 'string' && text)
362
+ return text;
363
+ throw new TypeError(`Unsupported ICQQ outbound segment: ${item.type}`);
364
+ }
365
+ }
366
+ }).filter((element) => typeof element !== 'string' || element.length > 0);
367
+ if (elements.length === 0)
368
+ return '\u200b';
369
+ const standalone = elements.find((element) => typeof element !== 'string' && ICQQ_STANDALONE_ELEMENTS.has(element.type));
370
+ if (standalone && elements.length !== 1) {
371
+ throw new TypeError(`ICQQ ${standalone.type} element must be sent as a standalone message`);
372
+ }
373
+ return elements.length === 1 ? elements[0] : elements;
374
+ }
375
+ export function formatInboundContent(rawMessage) {
376
+ return rawMessage;
377
+ }
378
+ /** 段对象判定:`{ type: string, data? }`(非数组 payload 的防御入口)。 */
379
+ function isWireSegment(value) {
380
+ return typeof value === 'object'
381
+ && value !== null
382
+ && typeof value.type === 'string';
383
+ }
384
+ function formatShare(data) {
385
+ if (!data)
386
+ throw new TypeError('ICQQ share segment requires data');
387
+ const config = data.config;
388
+ const title = String(data.title ?? '').trim();
389
+ const url = String(data.url ?? '').trim();
390
+ if (!title)
391
+ throw new TypeError('ICQQ share segment requires title');
392
+ if (!url)
393
+ throw new TypeError('ICQQ share segment requires url');
394
+ const optionalString = (value) => {
395
+ const text = String(value ?? '').trim();
396
+ return text || undefined;
397
+ };
398
+ const appid = Number(config?.appid);
399
+ const appname = optionalString(config?.package);
400
+ const appsign = optionalString(config?.sign);
401
+ const shareConfig = Number.isSafeInteger(appid) && appid > 0
402
+ ? {
403
+ appid,
404
+ ...(appname ? { appname } : {}),
405
+ ...(appsign ? { appsign } : {}),
406
+ }
407
+ : undefined;
408
+ const summary = optionalString(data.artist);
409
+ const content = optionalString(data.content);
410
+ const image = optionalString(data.image);
411
+ const audio = optionalString(data.audio);
412
+ return {
413
+ type: 'share',
414
+ title,
415
+ url,
416
+ ...(summary ? { summary } : {}),
417
+ ...(content ? { content } : {}),
418
+ ...(image ? { image } : {}),
419
+ ...(audio ? { audio } : {}),
420
+ ...(shareConfig ? { config: shareConfig } : {}),
421
+ };
422
+ }