@zhin.js/adapter-icqq 6.0.2 → 7.0.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 (125) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +48 -435
  3. package/adapters/icqq.ts +92 -0
  4. package/agent/tools/announce.ts +2 -2
  5. package/agent/tools/essence.ts +2 -2
  6. package/agent/tools/friend_list.ts +2 -2
  7. package/agent/tools/group_files.ts +2 -2
  8. package/agent/tools/group_invite.ts +2 -2
  9. package/agent/tools/group_list.ts +2 -2
  10. package/agent/tools/group_sign.ts +2 -2
  11. package/agent/tools/list_muted.ts +2 -2
  12. package/agent/tools/poke.ts +2 -2
  13. package/agent/tools/send_user_like.ts +2 -2
  14. package/agent/tools/set_anonymous.ts +2 -2
  15. package/agent/tools/set_title.ts +2 -2
  16. package/lib/agent-prompt.d.ts +2 -0
  17. package/lib/{src/agent-prompt.js → agent-prompt.js} +0 -1
  18. package/lib/cq-message.d.ts +9 -0
  19. package/lib/{src/cq-message.js → cq-message.js} +0 -1
  20. package/lib/endpoint.d.ts +101 -0
  21. package/lib/endpoint.js +715 -0
  22. package/lib/forward-msg.d.ts +26 -0
  23. package/lib/{src/forward-msg.js → forward-msg.js} +0 -1
  24. package/lib/icqq-agent-deps.d.ts +25 -0
  25. package/lib/icqq-agent-deps.js +32 -0
  26. package/lib/icqq-guild.d.ts +53 -0
  27. package/lib/{src/icqq-guild.js → icqq-guild.js} +4 -2
  28. package/lib/icqq-inbound.d.ts +139 -0
  29. package/lib/{src/icqq-inbound.js → icqq-inbound.js} +27 -3
  30. package/lib/icqq-inbox.d.ts +33 -0
  31. package/lib/icqq-inbox.js +129 -0
  32. package/lib/index.d.ts +6 -0
  33. package/lib/index.js +6 -0
  34. package/lib/ipc-client.d.ts +73 -0
  35. package/lib/{src/ipc-client.js → ipc-client.js} +0 -1
  36. package/lib/outbound-media.d.ts +13 -0
  37. package/lib/{src/outbound-media.js → outbound-media.js} +0 -1
  38. package/lib/protocol.d.ts +269 -0
  39. package/lib/{src/protocol.js → protocol.js} +124 -2
  40. package/lib/types.d.ts +87 -0
  41. package/lib/{src/types.js → types.js} +0 -1
  42. package/package.json +49 -45
  43. package/plugin.ts +32 -0
  44. package/schema.json +104 -0
  45. package/src/endpoint.ts +704 -795
  46. package/src/icqq-agent-deps.ts +48 -9
  47. package/src/icqq-guild.ts +10 -4
  48. package/src/icqq-inbound.ts +31 -2
  49. package/src/icqq-inbox.ts +155 -0
  50. package/src/index.ts +36 -69
  51. package/src/protocol.ts +191 -1
  52. package/src/types.ts +16 -0
  53. package/client/ICQQManagement.tsx +0 -147
  54. package/client/LoginAssistPanel.tsx +0 -300
  55. package/client/index.tsx +0 -11
  56. package/client/tsconfig.json +0 -7
  57. package/client/utils/api.ts +0 -30
  58. package/dist/index.js +0 -34
  59. package/lib/agent/tools/announce.js +0 -21
  60. package/lib/agent/tools/announce.js.map +0 -1
  61. package/lib/agent/tools/essence.js +0 -22
  62. package/lib/agent/tools/essence.js.map +0 -1
  63. package/lib/agent/tools/friend_list.js +0 -19
  64. package/lib/agent/tools/friend_list.js.map +0 -1
  65. package/lib/agent/tools/group_files.js +0 -29
  66. package/lib/agent/tools/group_files.js.map +0 -1
  67. package/lib/agent/tools/group_invite.js +0 -20
  68. package/lib/agent/tools/group_invite.js.map +0 -1
  69. package/lib/agent/tools/group_list.js +0 -20
  70. package/lib/agent/tools/group_list.js.map +0 -1
  71. package/lib/agent/tools/group_sign.js +0 -19
  72. package/lib/agent/tools/group_sign.js.map +0 -1
  73. package/lib/agent/tools/list_muted.js +0 -23
  74. package/lib/agent/tools/list_muted.js.map +0 -1
  75. package/lib/agent/tools/poke.js +0 -20
  76. package/lib/agent/tools/poke.js.map +0 -1
  77. package/lib/agent/tools/send_user_like.js +0 -22
  78. package/lib/agent/tools/send_user_like.js.map +0 -1
  79. package/lib/agent/tools/set_anonymous.js +0 -22
  80. package/lib/agent/tools/set_anonymous.js.map +0 -1
  81. package/lib/agent/tools/set_title.js +0 -25
  82. package/lib/agent/tools/set_title.js.map +0 -1
  83. package/lib/src/adapter.js +0 -128
  84. package/lib/src/adapter.js.map +0 -1
  85. package/lib/src/agent-prompt.js.map +0 -1
  86. package/lib/src/cq-message.js.map +0 -1
  87. package/lib/src/endpoint.js +0 -755
  88. package/lib/src/endpoint.js.map +0 -1
  89. package/lib/src/forward-msg.js.map +0 -1
  90. package/lib/src/get-msg.js +0 -46
  91. package/lib/src/get-msg.js.map +0 -1
  92. package/lib/src/icqq-agent-deps.js +0 -10
  93. package/lib/src/icqq-agent-deps.js.map +0 -1
  94. package/lib/src/icqq-guild.js.map +0 -1
  95. package/lib/src/icqq-inbound.js.map +0 -1
  96. package/lib/src/icqq-login-assist-bridge.js +0 -148
  97. package/lib/src/icqq-login-assist-bridge.js.map +0 -1
  98. package/lib/src/icqq-side-events.js +0 -130
  99. package/lib/src/icqq-side-events.js.map +0 -1
  100. package/lib/src/index.js +0 -50
  101. package/lib/src/index.js.map +0 -1
  102. package/lib/src/ipc-client.js.map +0 -1
  103. package/lib/src/login-assist-routes.js +0 -29
  104. package/lib/src/login-assist-routes.js.map +0 -1
  105. package/lib/src/login-ipc-contract.js +0 -35
  106. package/lib/src/login-ipc-contract.js.map +0 -1
  107. package/lib/src/outbound-media.js.map +0 -1
  108. package/lib/src/protocol.js.map +0 -1
  109. package/lib/src/routes.js +0 -67
  110. package/lib/src/routes.js.map +0 -1
  111. package/lib/src/segment-mapper.js +0 -2
  112. package/lib/src/segment-mapper.js.map +0 -1
  113. package/lib/src/tools/index.js +0 -23
  114. package/lib/src/tools/index.js.map +0 -1
  115. package/lib/src/types.js.map +0 -1
  116. package/plugin.yml +0 -3
  117. package/src/adapter.ts +0 -151
  118. package/src/get-msg.ts +0 -56
  119. package/src/icqq-login-assist-bridge.ts +0 -196
  120. package/src/icqq-side-events.ts +0 -143
  121. package/src/login-assist-routes.ts +0 -39
  122. package/src/login-ipc-contract.ts +0 -52
  123. package/src/routes.ts +0 -83
  124. package/src/segment-mapper.ts +0 -1
  125. package/src/tools/index.ts +0 -31
@@ -0,0 +1,269 @@
1
+ /**
2
+ * IPC 协议类型定义与 Action 常量 (ported from @icqqjs/cli)
3
+ *
4
+ * CLI 与守护进程通过 Unix Domain Socket 通信,
5
+ * 使用 JSON + 换行符(\n)分隔的文本协议。
6
+ *
7
+ * 通信流程(@icqqjs/cli `src/daemon/protocol.ts`):
8
+ * 1. CLI 连接 Socket → auth(token)
9
+ * 2. 认证通过后发送 IpcRequest → 等待 IpcResponse
10
+ * 3. 认证通过后自动接收 icqq 事件推送(IpcEvent),断开连接自动停止
11
+ *
12
+ * 登录相关:**推送**走 IpcEvent(`system.login.*`)。
13
+ */
14
+ /** CLI → Daemon 请求 */
15
+ export type IpcRequest = {
16
+ id: string;
17
+ action: string;
18
+ params: Record<string, unknown>;
19
+ };
20
+ /** Daemon → CLI 响应 */
21
+ export type IpcResponse = {
22
+ id: string;
23
+ ok: boolean;
24
+ data?: unknown;
25
+ error?: string;
26
+ };
27
+ /**
28
+ * Daemon → CLI 事件推送(icqq client.em 分发的全部事件)
29
+ *
30
+ * 登录/重连时推送 `system.login.*`(如 `system.login.auth` 含 url + device),
31
+ * 语义在 `event` 字段,不在 OneBot 式 `post_type` 里。
32
+ */
33
+ export type IpcEvent = {
34
+ /** 固定为 `"*"`,客户端应忽略,由本地 handler 按 `event` 过滤 */
35
+ id: string;
36
+ /** icqq 事件名,如 `system.login.auth`、`message.group.normal` */
37
+ event: string;
38
+ /** icqq 事件 toJSON 后的 plain object */
39
+ data: unknown;
40
+ };
41
+ export type IpcMessage = IpcResponse | IpcEvent;
42
+ export type { IcqqIpcMessageEvent } from "./icqq-inbound.js";
43
+ /** 从 IPC 入站 payload 解析可用于引用/撤回的消息 ID */
44
+ export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
45
+ /** Guild 消息事件数据 */
46
+ export interface IpcGuildMessageEventData {
47
+ type: "guild";
48
+ guild_id: string;
49
+ guild_name: string;
50
+ channel_id: string;
51
+ channel_name: string;
52
+ nickname: string;
53
+ tiny_id: string;
54
+ raw_message: string;
55
+ time: number;
56
+ seq: number;
57
+ }
58
+ /**
59
+ * 所有 IPC 操作常量 (from @icqqjs/cli)
60
+ */
61
+ export declare const Actions: {
62
+ readonly PING: "ping";
63
+ readonly LOGOUT: "logout";
64
+ readonly LIST_FRIENDS: "list_friends";
65
+ readonly LIST_GROUPS: "list_groups";
66
+ readonly LIST_GROUP_MEMBERS: "list_group_members";
67
+ readonly LIST_BLACKLIST: "list_blacklist";
68
+ readonly LIST_FRIEND_CLASSES: "list_friend_classes";
69
+ readonly GET_FRIEND_INFO: "get_friend_info";
70
+ readonly GET_GROUP_INFO: "get_group_info";
71
+ readonly GET_GROUP_MEMBER_INFO: "get_group_member_info";
72
+ readonly GET_STRANGER_INFO: "get_stranger_info";
73
+ readonly LIST_STRANGERS: "list_strangers";
74
+ readonly GET_PROFILE: "get_profile";
75
+ readonly GET_STATUS: "get_status";
76
+ readonly GET_ONLINE_STATUS: "get_online_status";
77
+ readonly GET_SELF_PROFILE: "get_self_profile";
78
+ readonly SEND_PRIVATE_MSG: "send_private_msg";
79
+ readonly SEND_GROUP_MSG: "send_group_msg";
80
+ readonly RECALL_MSG: "recall_msg";
81
+ readonly GET_MSG: "get_msg";
82
+ readonly HISTORY_PRIVATE: "history_private";
83
+ readonly HISTORY_GROUP: "history_group";
84
+ readonly HISTORY_BY_MSG_ID: "history_by_msg_id";
85
+ readonly SEND_LONG_MSG: "send_long_msg";
86
+ readonly MARK_READ: "mark_read";
87
+ readonly DELETE_MSG: "delete_msg";
88
+ readonly SET_NICKNAME: "set_nickname";
89
+ readonly SET_GENDER: "set_gender";
90
+ readonly SET_BIRTHDAY: "set_birthday";
91
+ readonly SET_SIGNATURE: "set_signature";
92
+ readonly SET_DESCRIPTION: "set_description";
93
+ readonly SET_AVATAR: "set_avatar";
94
+ readonly SET_ONLINE_STATUS: "set_online_status";
95
+ readonly SET_GROUP_NAME: "set_group_name";
96
+ readonly SET_GROUP_AVATAR: "set_group_avatar";
97
+ readonly SET_GROUP_CARD: "set_group_card";
98
+ readonly SET_GROUP_TITLE: "set_group_title";
99
+ readonly SET_GROUP_ADMIN: "set_group_admin";
100
+ readonly SET_GROUP_REMARK: "set_group_remark";
101
+ readonly GROUP_MUTE: "group_mute";
102
+ readonly GROUP_MUTE_ALL: "group_mute_all";
103
+ readonly GROUP_KICK: "group_kick";
104
+ readonly GROUP_QUIT: "group_quit";
105
+ readonly GROUP_INVITE: "group_invite";
106
+ readonly GROUP_POKE: "group_poke";
107
+ readonly GROUP_ANNOUNCE: "group_announce";
108
+ readonly GROUP_SIGN: "group_sign";
109
+ readonly GROUP_ESSENCE_ADD: "group_essence_add";
110
+ readonly GROUP_ESSENCE_REMOVE: "group_essence_remove";
111
+ readonly GROUP_ALLOW_ANONY: "group_allow_anony";
112
+ readonly GROUP_MUTED_LIST: "group_muted_list";
113
+ readonly GROUP_AT_ALL_REMAIN: "group_at_all_remain";
114
+ readonly FRIEND_POKE: "friend_poke";
115
+ readonly FRIEND_LIKE: "friend_like";
116
+ readonly FRIEND_DELETE: "friend_delete";
117
+ readonly FRIEND_REMARK: "friend_remark";
118
+ readonly FRIEND_CLASS: "friend_class";
119
+ readonly GET_SYSTEM_MSG: "get_system_msg";
120
+ readonly HANDLE_FRIEND_REQUEST: "handle_friend_request";
121
+ readonly HANDLE_GROUP_REQUEST: "handle_group_request";
122
+ readonly ADD_FRIEND_CLASS: "add_friend_class";
123
+ readonly DELETE_FRIEND_CLASS: "delete_friend_class";
124
+ readonly RENAME_FRIEND_CLASS: "rename_friend_class";
125
+ readonly GFS_LIST: "gfs_list";
126
+ readonly GFS_INFO: "gfs_info";
127
+ readonly GFS_MKDIR: "gfs_mkdir";
128
+ readonly GFS_DELETE: "gfs_delete";
129
+ readonly GFS_RENAME: "gfs_rename";
130
+ readonly GFS_STAT: "gfs_stat";
131
+ readonly GFS_MOVE: "gfs_move";
132
+ readonly GFS_DOWNLOAD: "gfs_download";
133
+ readonly IMAGE_OCR: "image_ocr";
134
+ readonly RELOAD_FRIEND_LIST: "reload_friend_list";
135
+ readonly RELOAD_GROUP_LIST: "reload_group_list";
136
+ readonly CLEAN_CACHE: "clean_cache";
137
+ readonly GET_GROUP_SHARE: "get_group_share";
138
+ readonly GROUP_SET_JOIN_TYPE: "group_set_join_type";
139
+ readonly GROUP_SET_RATE_LIMIT: "group_set_rate_limit";
140
+ readonly GROUP_MUTE_ANONY: "group_mute_anony";
141
+ readonly GROUP_ANON_INFO: "group_anon_info";
142
+ readonly ADD_FRIEND: "add_friend";
143
+ readonly SEND_TEMP_MSG: "send_temp_msg";
144
+ readonly GET_ROAMING_STAMP: "get_roaming_stamp";
145
+ readonly DELETE_STAMP: "delete_stamp";
146
+ readonly SEND_PRIVATE_FILE: "send_private_file";
147
+ readonly SEND_GROUP_FILE: "send_group_file";
148
+ readonly FRIEND_RECALL_FILE: "friend_recall_file";
149
+ readonly FRIEND_FORWARD_FILE: "friend_forward_file";
150
+ readonly SEARCH_SAME_GROUP: "search_same_group";
151
+ readonly SEND_CONTACT_SHARE: "send_contact_share";
152
+ readonly GFS_UPLOAD: "gfs_upload";
153
+ readonly GROUP_SET_REACTION: "group_set_reaction";
154
+ readonly GROUP_DEL_REACTION: "group_del_reaction";
155
+ readonly GET_FORWARD_MSG: "get_forward_msg";
156
+ readonly MAKE_FORWARD_MSG: "make_forward_msg";
157
+ readonly GUILD_LIST: "guild_list";
158
+ readonly GUILD_INFO: "guild_info";
159
+ readonly GUILD_CHANNELS: "guild_channels";
160
+ readonly GET_CHANNEL_INFO: "get_channel_info";
161
+ readonly GUILD_MEMBERS: "guild_members";
162
+ readonly GUILD_SEND_MSG: "guild_send_msg";
163
+ readonly GUILD_RECALL_MSG: "guild_recall_msg";
164
+ readonly GET_FILE_INFO: "get_file_info";
165
+ readonly GET_FILE_URL: "get_file_url";
166
+ readonly GET_AVATAR_URL: "get_avatar_url";
167
+ readonly GET_GROUP_AVATAR_URL: "get_group_avatar_url";
168
+ readonly SET_SCREEN_MEMBER_MSG: "set_screen_member_msg";
169
+ readonly GFS_FORWARD: "gfs_forward";
170
+ readonly GFS_FORWARD_OFFLINE: "gfs_forward_offline";
171
+ readonly RELOAD_BLACKLIST: "reload_blacklist";
172
+ readonly RELOAD_STRANGER_LIST: "reload_stranger_list";
173
+ readonly RELOAD_GUILDS: "reload_guilds";
174
+ readonly GET_STATUS_INFO: "get_status_info";
175
+ readonly GET_CLIENT_KEY: "get_client_key";
176
+ readonly GET_PSKEY: "get_pskey";
177
+ readonly UID2UIN: "uid2uin";
178
+ readonly UID2UINS: "uid2uins";
179
+ readonly UIN2UID: "uin2uid";
180
+ readonly UIN2UIDS: "uin2uids";
181
+ readonly GET_COOKIES: "get_cookies";
182
+ readonly GET_CSRF_TOKEN: "get_csrf_token";
183
+ readonly REFRESH_NT_PIC_RKEY: "refresh_nt_pic_rkey";
184
+ readonly SEND_DISCUSS_MSG: "send_discuss_msg";
185
+ readonly GET_VIDEO_URL: "get_video_url";
186
+ readonly GET_ADD_FRIEND_SETTING: "get_add_friend_setting";
187
+ readonly GET_FORUM_URL: "get_forum_url";
188
+ readonly GUILD_CHANNEL_SHARE: "guild_channel_share";
189
+ readonly GET_PIC_URL: "get_pic_url";
190
+ readonly GET_PTT_URL: "get_ptt_url";
191
+ readonly SUBSCRIBE: "subscribe";
192
+ readonly UNSUBSCRIBE: "unsubscribe";
193
+ readonly SET_WEBHOOK: "set_webhook";
194
+ readonly GET_WEBHOOK: "get_webhook";
195
+ readonly SET_NOTIFY: "set_notify";
196
+ readonly GET_NOTIFY: "get_notify";
197
+ };
198
+ /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
199
+ export interface IcqqAdapterConfig {
200
+ readonly name?: string;
201
+ readonly autoReconnect?: boolean;
202
+ readonly outboundMedia?: 'file' | 'base64';
203
+ readonly rpc?: {
204
+ readonly host?: string;
205
+ readonly port?: number;
206
+ readonly token?: string;
207
+ };
208
+ /** Transitional: legacy root `endpoints[]` with `context: icqq`. */
209
+ readonly endpoints?: ReadonlyArray<{
210
+ readonly context?: string;
211
+ readonly name?: string;
212
+ readonly autoReconnect?: boolean;
213
+ readonly outboundMedia?: 'file' | 'base64';
214
+ readonly rpc?: {
215
+ readonly host?: string;
216
+ readonly port?: number;
217
+ readonly token?: string;
218
+ };
219
+ }>;
220
+ }
221
+ export interface ResolvedIcqqConfig {
222
+ readonly context: 'icqq';
223
+ readonly name: string;
224
+ readonly autoReconnect: boolean;
225
+ readonly outboundMedia?: 'file' | 'base64';
226
+ readonly rpc?: {
227
+ readonly host: string;
228
+ readonly port: number;
229
+ readonly token: string;
230
+ };
231
+ }
232
+ export interface IcqqWireSegment {
233
+ readonly type: string;
234
+ readonly data?: Record<string, unknown>;
235
+ }
236
+ export interface IcqqInboundMessage {
237
+ readonly id: string;
238
+ readonly target: string;
239
+ readonly content: string;
240
+ readonly sender: string;
241
+ readonly channelType: 'private' | 'group' | 'channel';
242
+ readonly metadata?: Record<string, unknown>;
243
+ }
244
+ export type ParsedIcqqSendTarget = {
245
+ readonly kind: 'private';
246
+ readonly userId: number;
247
+ } | {
248
+ readonly kind: 'group';
249
+ readonly groupId: number;
250
+ } | {
251
+ readonly kind: 'temp';
252
+ readonly groupId: number;
253
+ readonly userId: number;
254
+ } | {
255
+ readonly kind: 'channel';
256
+ readonly guildId: string;
257
+ readonly channelId: string;
258
+ };
259
+ export declare function resolveIcqqConfig(config?: IcqqAdapterConfig): ResolvedIcqqConfig;
260
+ /** Gateway reply target: `private:uid` / `group:gid` / `temp:gid:uid` / `channel:guild:channel`. */
261
+ export declare function formatInboundTarget(input: {
262
+ readonly channelType: 'private' | 'group' | 'channel';
263
+ readonly channelId: string;
264
+ readonly channelParentGroupId?: string;
265
+ readonly guildId?: string;
266
+ }): string;
267
+ export declare function parseSendTarget(target: string): ParsedIcqqSendTarget;
268
+ export declare function formatOutboundBody(payload: unknown): string;
269
+ export declare function formatInboundContent(rawMessage: string): string;
@@ -9,8 +9,9 @@
9
9
  * 2. 认证通过后发送 IpcRequest → 等待 IpcResponse
10
10
  * 3. 认证通过后自动接收 icqq 事件推送(IpcEvent),断开连接自动停止
11
11
  *
12
- * 登录相关:**推送**走 IpcEvent(`system.login.*`),续传 invoke 见 `login-ipc-contract.ts`。
12
+ * 登录相关:**推送**走 IpcEvent(`system.login.*`)。
13
13
  */
14
+ import { pickCredential } from '@zhin.js/adapter';
14
15
  /** 从 IPC 入站 payload 解析可用于引用/撤回的消息 ID */
15
16
  export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
16
17
  /**
@@ -184,4 +185,125 @@ export const Actions = {
184
185
  SET_NOTIFY: "set_notify",
185
186
  GET_NOTIFY: "get_notify",
186
187
  };
187
- //# sourceMappingURL=protocol.js.map
188
+ export function resolveIcqqConfig(config = {}) {
189
+ const entry = config.endpoints?.find((item) => item.context === 'icqq');
190
+ const name = pickCredential(config.name, entry?.name, process.env.ICQQ_ACCOUNT);
191
+ if (!/^\d+$/.test(name)) {
192
+ throw new TypeError('ICQQ adapter requires numeric name (QQ uin) via plugins.<key>.name or ICQQ_ACCOUNT');
193
+ }
194
+ const autoReconnect = config.autoReconnect ?? entry?.autoReconnect ?? true;
195
+ const outboundMedia = config.outboundMedia ?? entry?.outboundMedia;
196
+ const rpcRaw = config.rpc ?? entry?.rpc;
197
+ const rpc = rpcRaw?.host && rpcRaw.port != null && rpcRaw.token
198
+ ? { host: rpcRaw.host, port: Number(rpcRaw.port), token: rpcRaw.token }
199
+ : undefined;
200
+ return {
201
+ context: 'icqq',
202
+ name,
203
+ autoReconnect,
204
+ ...(outboundMedia ? { outboundMedia } : {}),
205
+ ...(rpc ? { rpc } : {}),
206
+ };
207
+ }
208
+ /** Gateway reply target: `private:uid` / `group:gid` / `temp:gid:uid` / `channel:guild:channel`. */
209
+ export function formatInboundTarget(input) {
210
+ if (input.channelType === 'private' && input.channelParentGroupId) {
211
+ return `temp:${input.channelParentGroupId}:${input.channelId}`;
212
+ }
213
+ if (input.channelType === 'channel' && input.guildId) {
214
+ return `channel:${input.guildId}:${input.channelId}`;
215
+ }
216
+ return `${input.channelType}:${input.channelId}`;
217
+ }
218
+ export function parseSendTarget(target) {
219
+ const trimmed = target.trim();
220
+ if (trimmed.startsWith('temp:')) {
221
+ const rest = trimmed.slice(5);
222
+ const [groupId, userId] = rest.split(':');
223
+ if (!groupId || !userId)
224
+ throw new TypeError(`Invalid icqq temp target: ${target}`);
225
+ return { kind: 'temp', groupId: Number(groupId), userId: Number(userId) };
226
+ }
227
+ if (trimmed.startsWith('channel:')) {
228
+ const rest = trimmed.slice(8);
229
+ const idx = rest.indexOf(':');
230
+ if (idx <= 0)
231
+ throw new TypeError(`Invalid icqq channel target: ${target}`);
232
+ return {
233
+ kind: 'channel',
234
+ guildId: rest.slice(0, idx),
235
+ channelId: rest.slice(idx + 1),
236
+ };
237
+ }
238
+ if (trimmed.startsWith('private:')) {
239
+ return { kind: 'private', userId: Number(trimmed.slice(8)) };
240
+ }
241
+ if (trimmed.startsWith('group:')) {
242
+ return { kind: 'group', groupId: Number(trimmed.slice(6)) };
243
+ }
244
+ // Bare numeric id → private (common reply path)
245
+ if (/^\d+$/.test(trimmed)) {
246
+ return { kind: 'private', userId: Number(trimmed) };
247
+ }
248
+ throw new TypeError(`Invalid icqq send target: ${target}`);
249
+ }
250
+ export function formatOutboundBody(payload) {
251
+ if (typeof payload === 'string') {
252
+ const text = payload.trim();
253
+ return text || '\u200b';
254
+ }
255
+ if (payload == null)
256
+ return '\u200b';
257
+ if (!Array.isArray(payload)) {
258
+ // 单个段对象(非数组)按单元素数组处理,杜绝 String(object) → '[object Object]'
259
+ if (isWireSegment(payload)) {
260
+ return formatOutboundBody([payload]);
261
+ }
262
+ if (typeof payload === 'object' && payload !== null && 'text' in payload) {
263
+ const text = String(payload.text ?? '').trim();
264
+ return text || '\u200b';
265
+ }
266
+ const text = String(payload).trim();
267
+ return text || '\u200b';
268
+ }
269
+ const joined = payload.map((seg) => {
270
+ if (typeof seg === 'string')
271
+ return seg;
272
+ const item = seg;
273
+ switch (item.type) {
274
+ case 'text':
275
+ return String(item.data?.text ?? '');
276
+ case 'at':
277
+ case 'mention':
278
+ return `[at:${item.data?.qq ?? item.data?.id ?? item.data?.target ?? ''}]`;
279
+ case 'image':
280
+ return `[image:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
281
+ case 'face':
282
+ return `[face:${item.data?.id ?? ''}]`;
283
+ case 'reply':
284
+ return `[reply:${item.data?.message_id ?? item.data?.id ?? ''}]`;
285
+ case 'record':
286
+ case 'audio':
287
+ return `[record:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
288
+ case 'video':
289
+ return `[video:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
290
+ default:
291
+ return String(item.data?.text ?? '');
292
+ }
293
+ }).join('').trim();
294
+ return joined || '\u200b';
295
+ }
296
+ export function formatInboundContent(rawMessage) {
297
+ return rawMessage;
298
+ }
299
+ /** base64 媒体回退:CQ `base64://` 由守护进程解码(异机 RPC 场景)。 */
300
+ function base64Media(data) {
301
+ const b64 = data?.base64;
302
+ return typeof b64 === 'string' && b64 ? `base64://${b64}` : undefined;
303
+ }
304
+ /** 段对象判定:`{ type: string, data? }`(非数组 payload 的防御入口)。 */
305
+ function isWireSegment(value) {
306
+ return typeof value === 'object'
307
+ && value !== null
308
+ && typeof value.type === 'string';
309
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * ICQQ 适配器类型与配置
3
+ *
4
+ * 不再直接依赖 @icqqjs/icqq,通过 @icqqjs/cli 守护进程 IPC 通信。
5
+ */
6
+ export type GroupRole = "owner" | "admin" | "member";
7
+ export interface IcqqSenderInfo {
8
+ id: string;
9
+ name: string;
10
+ role?: GroupRole;
11
+ isOwner?: boolean;
12
+ isAdmin?: boolean;
13
+ permissions?: string[];
14
+ card?: string;
15
+ title?: string;
16
+ }
17
+ /**
18
+ * Endpoint 配置:支持本地 IPC 和远程 RPC 两种连接模式。
19
+ *
20
+ * - 本地模式(默认):只需 name(QQ号),自动连接 ~/.icqq/<uin>/daemon.sock
21
+ * - 远程模式:额外配置 rpc.host / rpc.port / rpc.token
22
+ */
23
+ export interface IcqqEndpointConfig {
24
+ context: "icqq";
25
+ /** QQ 号码字符串 */
26
+ name: `${number}`;
27
+ /** RPC 远程连接配置(不配置则使用本地 IPC) */
28
+ rpc?: {
29
+ /** 远程主机地址 */
30
+ host: string;
31
+ /** 远程端口 */
32
+ port: number;
33
+ /** 认证 token(用于 HMAC-SHA256 挑战-响应,不会明文传输) */
34
+ token: string;
35
+ };
36
+ /**
37
+ * IPC/RPC 与守护进程连接意外断开时是否自动重连(指数退避,上限约 30s)。
38
+ * 默认 true;设为 false 则断开后仅将 `$connected` 置为 false,需手动重连。
39
+ */
40
+ autoReconnect?: boolean;
41
+ /**
42
+ * 出站图片/语音/视频:file=本机临时文件路径(需与 icqq 守护进程同机可读);
43
+ * base64=经 CQ `base64://` 交给守护进程解码(Zhin 与 icqq 异进程/异机时用,配置 rpc 时默认 base64)。
44
+ */
45
+ outboundMedia?: "file" | "base64";
46
+ }
47
+ /** IPC 返回的好友信息 */
48
+ export interface IpcFriendInfo {
49
+ user_id: number;
50
+ nickname: string;
51
+ remark?: string;
52
+ class_id?: number;
53
+ }
54
+ /** IPC 返回的群信息 */
55
+ export interface IpcGroupInfo {
56
+ group_id: number;
57
+ group_name: string;
58
+ member_count: number;
59
+ max_member_count: number;
60
+ owner_id?: number;
61
+ }
62
+ /** IPC 返回的群成员信息 */
63
+ export interface IpcMemberInfo {
64
+ user_id: number;
65
+ nickname: string;
66
+ card: string;
67
+ role: GroupRole;
68
+ title: string;
69
+ join_time?: number;
70
+ last_sent_time?: number;
71
+ shutup_time?: number;
72
+ }
73
+ /**
74
+ * GET_SYSTEM_MSG 归一后的待处理请求(@icqqjs/cli NormalizedSystemMessage)。
75
+ * friendRequests 与 groupRequests 均为该形状,按 flag 处理。
76
+ */
77
+ export interface IpcSystemMessage {
78
+ type: string;
79
+ user_id?: number;
80
+ nickname?: string;
81
+ group_id?: number;
82
+ group_name?: string;
83
+ comment?: string;
84
+ flag?: string;
85
+ seq?: number;
86
+ time?: number;
87
+ }
@@ -4,4 +4,3 @@
4
4
  * 不再直接依赖 @icqqjs/icqq,通过 @icqqjs/cli 守护进程 IPC 通信。
5
5
  */
6
6
  export {};
7
- //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-icqq",
3
- "version": "6.0.2",
4
- "description": "Zhin.js adapter for ICQQ (QQ Bot)",
3
+ "version": "7.0.0",
4
+ "description": "Zhin.js ICQQ adapter for Plugin Runtime (IPC daemon client)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -33,60 +33,49 @@
33
33
  "type": "git",
34
34
  "directory": "plugins/adapters/icqq"
35
35
  },
36
- "files": [
37
- "src",
38
- "lib",
39
- "client",
40
- "dist",
41
- "plugin.yml",
42
- "README.md",
43
- "CHANGELOG.md",
44
- "agent"
45
- ],
46
- "devDependencies": {
47
- "@radix-ui/themes": "^3.2.1",
48
- "@types/react": "^19.2.17",
49
- "@types/react-dom": "^19.2.3",
50
- "lucide-react": "^1.22.0",
51
- "radix-ui": "^1.6.2",
52
- "typescript": "^6.0.3",
53
- "zod": "^4.4.3",
54
- "@zhin.js/agent": "1.0.3",
55
- "@zhin.js/cli": "1.0.93",
56
- "@zhin.js/client": "2.0.5",
57
- "@zhin.js/contract": "1.0.3",
58
- "@zhin.js/core": "1.3.4",
59
- "@zhin.js/host-api": "2.0.5",
60
- "@zhin.js/host-router": "2.0.3",
61
- "zhin.js": "4.1.2"
36
+ "dependencies": {
37
+ "@zhin.js/adapter": "1.1.0",
38
+ "@zhin.js/core": "1.4.0",
39
+ "@zhin.js/logger": "1.0.75",
40
+ "@zhin.js/plugin-runtime": "1.1.0"
62
41
  },
63
42
  "peerDependencies": {
64
43
  "zod": "^4.0.0",
65
- "@zhin.js/agent": "1.0.3",
66
- "@zhin.js/client": "2.0.5",
67
- "@zhin.js/host-api": "2.0.5",
68
- "@zhin.js/contract": "1.0.3",
69
- "@zhin.js/core": "1.3.4",
70
- "@zhin.js/host-router": "2.0.3",
71
- "zhin.js": "4.1.2"
44
+ "@zhin.js/adapter": "1.1.0",
45
+ "@zhin.js/agent": "1.0.5",
46
+ "@zhin.js/core": "1.4.0",
47
+ "@zhin.js/plugin-runtime": "1.1.0",
48
+ "zhin.js": "5.0.0"
72
49
  },
73
50
  "peerDependenciesMeta": {
74
- "@zhin.js/agent": {
75
- "optional": true
76
- },
77
- "@zhin.js/client": {
78
- "optional": true
79
- },
80
- "@zhin.js/host-router": {
51
+ "zhin.js": {
81
52
  "optional": true
82
53
  },
83
- "@zhin.js/host-api": {
54
+ "@zhin.js/agent": {
84
55
  "optional": true
85
56
  },
86
57
  "zod": {
87
58
  "optional": true
88
59
  }
89
60
  },
61
+ "devDependencies": {
62
+ "@types/node": "^26.1.0",
63
+ "typescript": "^6.0.3",
64
+ "vitest": "^4.1.10",
65
+ "zod": "^4.4.3",
66
+ "@zhin.js/agent": "1.0.5",
67
+ "zhin.js": "5.0.0"
68
+ },
69
+ "files": [
70
+ "adapters",
71
+ "plugin.ts",
72
+ "schema.json",
73
+ "src",
74
+ "lib",
75
+ "agent",
76
+ "README.md",
77
+ "CHANGELOG.md"
78
+ ],
90
79
  "publishConfig": {
91
80
  "access": "public",
92
81
  "registry": "https://registry.npmjs.org"
@@ -94,8 +83,23 @@
94
83
  "engines": {
95
84
  "node": "^20.19.0 || >=22.12.0"
96
85
  },
86
+ "zhin": {
87
+ "protocol": 1,
88
+ "type": "plugin",
89
+ "entry": "./plugin.ts",
90
+ "engine": "^1.0.0",
91
+ "runtime": "trusted",
92
+ "features": [
93
+ {
94
+ "package": "@zhin.js/adapter",
95
+ "api": "^1.0.0"
96
+ }
97
+ ],
98
+ "plugins": []
99
+ },
97
100
  "scripts": {
98
- "build": "zhin build",
99
- "clean": "rimraf lib"
101
+ "build": "tsc",
102
+ "clean": "rimraf lib",
103
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/icqq/tests"
100
104
  }
101
105
  }
package/plugin.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { definePlugin } from '@zhin.js/plugin-runtime';
2
+
3
+ // Module-level guard: multiple icqq instances (icqq, icqq-2, …) share this
4
+ // module, but the prompt contributor is per-platform — register it once.
5
+ let promptContributorRegistered = false;
6
+
7
+ export default definePlugin({
8
+ name: 'icqq',
9
+ metadata: {
10
+ displayName: 'ICQQ Adapter',
11
+ },
12
+ setup() {
13
+ // Agent prompt contributor (orchestrator/deferred-worker ICQQ guidance).
14
+ // `zhin.js/agent` is an optional peer — skip silently on IM-only installs.
15
+ if (promptContributorRegistered) return;
16
+ let cancelled = false;
17
+ let unregister: (() => void) | undefined;
18
+ void Promise.all([
19
+ import('zhin.js/agent'),
20
+ import('./src/agent-prompt.js'),
21
+ ]).then(([agent, prompt]) => {
22
+ if (cancelled || promptContributorRegistered) return;
23
+ agent.registerAgentPromptContributor(prompt.createIcqqAgentPromptContributor());
24
+ promptContributorRegistered = true;
25
+ unregister = () => agent.unregisterAgentPromptContributor('icqq');
26
+ }).catch(() => { /* optional peer not installed */ });
27
+ return () => {
28
+ cancelled = true;
29
+ unregister?.();
30
+ };
31
+ },
32
+ });