@zhin.js/adapter-icqq 6.0.0 → 6.0.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 (161) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +3 -3
  3. package/{skills/icqq → agent}/PERMITS.md +6 -6
  4. package/agent/tools/announce.ts +21 -0
  5. package/agent/tools/essence.ts +22 -0
  6. package/agent/tools/friend_list.ts +19 -0
  7. package/agent/tools/group_files.ts +27 -0
  8. package/agent/tools/group_invite.ts +20 -0
  9. package/agent/tools/group_list.ts +20 -0
  10. package/agent/tools/group_sign.ts +19 -0
  11. package/agent/tools/list_muted.ts +22 -0
  12. package/agent/tools/poke.ts +20 -0
  13. package/agent/tools/send_user_like.ts +22 -0
  14. package/agent/tools/set_anonymous.ts +22 -0
  15. package/agent/tools/set_title.ts +25 -0
  16. package/dist/index.js +2 -2
  17. package/lib/agent/tools/announce.js +21 -0
  18. package/lib/agent/tools/announce.js.map +1 -0
  19. package/lib/agent/tools/essence.js +22 -0
  20. package/lib/agent/tools/essence.js.map +1 -0
  21. package/lib/agent/tools/friend_list.js +19 -0
  22. package/lib/agent/tools/friend_list.js.map +1 -0
  23. package/lib/agent/tools/group_files.js +29 -0
  24. package/lib/agent/tools/group_files.js.map +1 -0
  25. package/lib/agent/tools/group_invite.js +20 -0
  26. package/lib/agent/tools/group_invite.js.map +1 -0
  27. package/lib/agent/tools/group_list.js +20 -0
  28. package/lib/agent/tools/group_list.js.map +1 -0
  29. package/lib/agent/tools/group_sign.js +19 -0
  30. package/lib/agent/tools/group_sign.js.map +1 -0
  31. package/lib/agent/tools/list_muted.js +23 -0
  32. package/lib/agent/tools/list_muted.js.map +1 -0
  33. package/lib/agent/tools/poke.js +20 -0
  34. package/lib/agent/tools/poke.js.map +1 -0
  35. package/lib/agent/tools/send_user_like.js +22 -0
  36. package/lib/agent/tools/send_user_like.js.map +1 -0
  37. package/lib/agent/tools/set_anonymous.js +22 -0
  38. package/lib/agent/tools/set_anonymous.js.map +1 -0
  39. package/lib/agent/tools/set_title.js +25 -0
  40. package/lib/agent/tools/set_title.js.map +1 -0
  41. package/lib/{adapter.js → src/adapter.js} +13 -6
  42. package/lib/src/adapter.js.map +1 -0
  43. package/lib/src/agent-prompt.js.map +1 -0
  44. package/lib/{cq-message.js → src/cq-message.js} +12 -3
  45. package/lib/src/cq-message.js.map +1 -0
  46. package/lib/{endpoint.js → src/endpoint.js} +130 -63
  47. package/lib/src/endpoint.js.map +1 -0
  48. package/lib/{forward-msg.js → src/forward-msg.js} +4 -1
  49. package/lib/src/forward-msg.js.map +1 -0
  50. package/lib/src/get-msg.js.map +1 -0
  51. package/lib/src/icqq-agent-deps.js +10 -0
  52. package/lib/src/icqq-agent-deps.js.map +1 -0
  53. package/lib/src/icqq-guild.js +115 -0
  54. package/lib/src/icqq-guild.js.map +1 -0
  55. package/lib/{icqq-inbound.js → src/icqq-inbound.js} +15 -5
  56. package/lib/src/icqq-inbound.js.map +1 -0
  57. package/lib/src/icqq-login-assist-bridge.js.map +1 -0
  58. package/lib/{icqq-side-events.js → src/icqq-side-events.js} +28 -92
  59. package/lib/src/icqq-side-events.js.map +1 -0
  60. package/lib/{index.js → src/index.js} +0 -1
  61. package/lib/src/index.js.map +1 -0
  62. package/lib/src/ipc-client.js.map +1 -0
  63. package/lib/src/login-assist-routes.js.map +1 -0
  64. package/lib/src/login-ipc-contract.js.map +1 -0
  65. package/lib/src/outbound-media.js.map +1 -0
  66. package/lib/src/protocol.js.map +1 -0
  67. package/lib/src/routes.js.map +1 -0
  68. package/lib/src/segment-mapper.js +2 -0
  69. package/lib/src/segment-mapper.js.map +1 -0
  70. package/lib/src/tools/index.js +23 -0
  71. package/lib/src/tools/index.js.map +1 -0
  72. package/lib/src/types.js.map +1 -0
  73. package/package.json +25 -21
  74. package/src/adapter.ts +13 -6
  75. package/src/agent-prompt.ts +1 -2
  76. package/src/cq-message.ts +11 -3
  77. package/src/endpoint.ts +158 -82
  78. package/src/forward-msg.ts +2 -2
  79. package/src/icqq-agent-deps.ts +18 -0
  80. package/src/icqq-guild.ts +157 -0
  81. package/src/icqq-inbound.ts +16 -5
  82. package/src/icqq-side-events.ts +33 -118
  83. package/src/index.ts +0 -8
  84. package/src/segment-mapper.ts +1 -0
  85. package/src/tools/index.ts +16 -397
  86. package/src/types.ts +0 -45
  87. package/lib/adapter.d.ts +0 -39
  88. package/lib/adapter.d.ts.map +0 -1
  89. package/lib/adapter.js.map +0 -1
  90. package/lib/agent-prompt.d.ts +0 -3
  91. package/lib/agent-prompt.d.ts.map +0 -1
  92. package/lib/agent-prompt.js.map +0 -1
  93. package/lib/cq-message.d.ts +0 -10
  94. package/lib/cq-message.d.ts.map +0 -1
  95. package/lib/cq-message.js.map +0 -1
  96. package/lib/endpoint.d.ts +0 -99
  97. package/lib/endpoint.d.ts.map +0 -1
  98. package/lib/endpoint.js.map +0 -1
  99. package/lib/forward-msg.d.ts +0 -27
  100. package/lib/forward-msg.d.ts.map +0 -1
  101. package/lib/forward-msg.js.map +0 -1
  102. package/lib/get-msg.d.ts +0 -3
  103. package/lib/get-msg.d.ts.map +0 -1
  104. package/lib/get-msg.js.map +0 -1
  105. package/lib/icqq-inbound.d.ts +0 -130
  106. package/lib/icqq-inbound.d.ts.map +0 -1
  107. package/lib/icqq-inbound.js.map +0 -1
  108. package/lib/icqq-login-assist-bridge.d.ts +0 -5
  109. package/lib/icqq-login-assist-bridge.d.ts.map +0 -1
  110. package/lib/icqq-login-assist-bridge.js.map +0 -1
  111. package/lib/icqq-side-events.d.ts +0 -34
  112. package/lib/icqq-side-events.d.ts.map +0 -1
  113. package/lib/icqq-side-events.js.map +0 -1
  114. package/lib/index.d.ts +0 -20
  115. package/lib/index.d.ts.map +0 -1
  116. package/lib/index.js.map +0 -1
  117. package/lib/ipc-client.d.ts +0 -74
  118. package/lib/ipc-client.d.ts.map +0 -1
  119. package/lib/ipc-client.js.map +0 -1
  120. package/lib/login-assist-routes.d.ts +0 -7
  121. package/lib/login-assist-routes.d.ts.map +0 -1
  122. package/lib/login-assist-routes.js.map +0 -1
  123. package/lib/login-ipc-contract.d.ts +0 -44
  124. package/lib/login-ipc-contract.d.ts.map +0 -1
  125. package/lib/login-ipc-contract.js.map +0 -1
  126. package/lib/outbound-media.d.ts +0 -14
  127. package/lib/outbound-media.d.ts.map +0 -1
  128. package/lib/outbound-media.js.map +0 -1
  129. package/lib/protocol.d.ts +0 -198
  130. package/lib/protocol.d.ts.map +0 -1
  131. package/lib/protocol.js.map +0 -1
  132. package/lib/routes.d.ts +0 -8
  133. package/lib/routes.d.ts.map +0 -1
  134. package/lib/routes.js.map +0 -1
  135. package/lib/tools/index.d.ts +0 -10
  136. package/lib/tools/index.d.ts.map +0 -1
  137. package/lib/tools/index.js +0 -341
  138. package/lib/tools/index.js.map +0 -1
  139. package/lib/types.d.ts +0 -117
  140. package/lib/types.d.ts.map +0 -1
  141. package/lib/types.js.map +0 -1
  142. package/lib/typing-indicator-example.d.ts +0 -108
  143. package/lib/typing-indicator-example.d.ts.map +0 -1
  144. package/lib/typing-indicator-example.js +0 -220
  145. package/lib/typing-indicator-example.js.map +0 -1
  146. package/lib/typing-indicator.d.ts +0 -87
  147. package/lib/typing-indicator.d.ts.map +0 -1
  148. package/lib/typing-indicator.js +0 -229
  149. package/lib/typing-indicator.js.map +0 -1
  150. package/src/typing-indicator-example.ts +0 -269
  151. package/src/typing-indicator.ts +0 -317
  152. /package/lib/{agent-prompt.js → src/agent-prompt.js} +0 -0
  153. /package/lib/{get-msg.js → src/get-msg.js} +0 -0
  154. /package/lib/{icqq-login-assist-bridge.js → src/icqq-login-assist-bridge.js} +0 -0
  155. /package/lib/{ipc-client.js → src/ipc-client.js} +0 -0
  156. /package/lib/{login-assist-routes.js → src/login-assist-routes.js} +0 -0
  157. /package/lib/{login-ipc-contract.js → src/login-ipc-contract.js} +0 -0
  158. /package/lib/{outbound-media.js → src/outbound-media.js} +0 -0
  159. /package/lib/{protocol.js → src/protocol.js} +0 -0
  160. /package/lib/{routes.js → src/routes.js} +0 -0
  161. /package/lib/{types.js → src/types.js} +0 -0
@@ -1,29 +1,12 @@
1
1
  /**
2
2
  * icqq IPC 非 message 入站:notice / request / meta(对齐 OneBot 与 @icqqjs/icqq EventMap)
3
- * @see https://icqq.pages.dev/interfaces/EventMap.html
4
3
  */
5
- import { Notice, Request, formatCompact } from "zhin.js";
6
- import type { NoticeType } from "zhin.js";
7
- import type { RequestType } from "zhin.js";
4
+ import { mapNoticeParts, mapRequestParts, resolveSideEventDedupeKey, senderFromId, buildNotice, buildRequest, formatCompact, type OneBotLikeRawEvent } from 'zhin.js';
5
+
8
6
  import { Actions } from "./protocol.js";
9
7
  import type { IpcClient } from "./ipc-client.js";
10
8
 
11
- /** OneBot / icqq 守护进程推送的通用事件字段 */
12
- export interface IcqqIpcRawEvent {
13
- post_type?: string;
14
- time?: number;
15
- self_id?: number;
16
- notice_type?: string;
17
- request_type?: string;
18
- sub_type?: string;
19
- meta_event_type?: string;
20
- group_id?: number;
21
- user_id?: number;
22
- operator_id?: number;
23
- flag?: string;
24
- comment?: string;
25
- [key: string]: unknown;
26
- }
9
+ export type IcqqIpcRawEvent = OneBotLikeRawEvent;
27
10
 
28
11
  export function resolveIcqqEventPostType(
29
12
  data: Record<string, unknown>,
@@ -54,110 +37,39 @@ export function isIcqqMetaPayload(data: unknown): data is IcqqIpcRawEvent {
54
37
  return pt === "meta_event" || (typeof pt === "string" && pt.startsWith("system."));
55
38
  }
56
39
 
57
- /** 去重用:notice / request 无稳定 id 时组合键 */
58
- export function resolveSideEventDedupeKey(
59
- data: IcqqIpcRawEvent,
60
- kind: "notice" | "request" | "meta",
61
- ): string {
62
- if (data.flag != null && String(data.flag) !== "") {
63
- return `${kind}:${data.flag}`;
64
- }
65
- const time = data.time ?? 0;
66
- const type =
67
- data.notice_type ?? data.request_type ?? data.meta_event_type ?? kind;
68
- const scope = data.group_id ?? data.user_id ?? "";
69
- const sub = data.sub_type ?? "";
70
- return `${kind}:${time}_${type}_${scope}_${sub}`;
71
- }
72
-
73
- const NOTICE_TYPE_MAP: Record<string, NoticeType> = {
74
- group_increase: "group_member_increase",
75
- group_decrease: "group_member_decrease",
76
- group_admin: "group_admin_change",
77
- group_ban: "group_ban",
78
- group_recall: "group_recall",
79
- friend_recall: "friend_recall",
80
- friend_add: "friend_add",
81
- group_upload: "group_upload",
82
- group_sign: "group_sign",
83
- group_transfer: "group_transfer",
84
- // icqq EventMap 命名 → Zhin NoticeType
85
- "group.increase": "group_member_increase",
86
- "group.decrease": "group_member_decrease",
87
- "group.admin": "group_admin_change",
88
- "group.ban": "group_ban",
89
- "group.recall": "group_recall",
90
- "friend.increase": "friend_add",
91
- "friend.decrease": "friend_recall",
92
- "friend.recall": "friend_recall",
93
- };
94
-
95
- function resolveNoticeZhinType(event: IcqqIpcRawEvent): NoticeType {
96
- const raw = event.notice_type ?? "";
97
- if (NOTICE_TYPE_MAP[raw]) return NOTICE_TYPE_MAP[raw];
98
- if (raw === "notify") {
99
- if (event.sub_type === "poke") {
100
- return event.group_id != null ? "group_poke" : "friend_poke";
101
- }
102
- return `notify_${event.sub_type ?? "unknown"}` as NoticeType;
103
- }
104
- if (raw.startsWith("notice.")) {
105
- const suffix = raw.slice("notice.".length).replace(/\./g, "_");
106
- return (NOTICE_TYPE_MAP[suffix] ?? suffix) as NoticeType;
107
- }
108
- return raw as NoticeType;
109
- }
110
-
111
- function senderFromId(id: unknown, name?: string) {
112
- if (id == null) return undefined;
113
- const s = String(id);
114
- return { id: s, name: name ?? s };
115
- }
116
-
117
40
  export function formatIcqqNotice(
118
41
  event: IcqqIpcRawEvent,
119
42
  endpointName: string,
120
- ): ReturnType<typeof Notice.from<IcqqIpcRawEvent>> {
121
- const $type = resolveNoticeZhinType(event);
43
+ ) {
44
+ const raw = event.notice_type ?? "";
122
45
  const isGroup = event.group_id != null;
123
- const channelId = String(
46
+ const { scene_type, sub_type } = mapNoticeParts('icqq', raw, { sub_type: event.sub_type, is_group: isGroup });
47
+ const sceneId = String(
124
48
  event.group_id ?? event.user_id ?? event.operator_id ?? "",
125
49
  );
126
50
  const tsSec = event.time ?? Math.floor(Date.now() / 1000);
127
51
 
128
- return Notice.from(event, {
52
+ return buildNotice(event, {
129
53
  $id: resolveSideEventDedupeKey(event, "notice"),
130
54
  $adapter: "icqq",
131
55
  $endpoint: endpointName,
132
- $type,
133
- $subType: event.sub_type,
134
- $channel: {
135
- id: channelId,
136
- type: isGroup ? "group" : "private",
137
- },
138
- $operator: senderFromId(event.operator_id),
56
+ $type: "notice",
57
+ $scene_id: sceneId,
58
+ $scene_type: scene_type,
59
+ $sub_type: sub_type,
60
+ $actor: senderFromId(event.operator_id),
139
61
  $target: senderFromId(event.user_id),
140
62
  $timestamp: tsSec * 1000,
141
63
  });
142
64
  }
143
65
 
144
- function resolveRequestZhinType(event: IcqqIpcRawEvent): RequestType {
145
- const rt = event.request_type ?? "";
146
- if (rt === "friend" || rt.startsWith("friend")) return "friend_add";
147
- if (rt === "group" || rt.startsWith("group")) {
148
- return event.sub_type === "invite" ? "group_invite" : "group_add";
149
- }
150
- return rt as RequestType;
151
- }
152
-
153
66
  export function formatIcqqRequest(
154
67
  event: IcqqIpcRawEvent,
155
68
  endpointName: string,
156
69
  ipc: IpcClient,
157
- ): ReturnType<typeof Request.from<IcqqIpcRawEvent>> {
158
- const $type = resolveRequestZhinType(event);
159
- const isGroup = event.group_id != null;
160
- const channelId = String(event.group_id ?? event.user_id ?? "");
70
+ ) {
71
+ const { scene_type, sub_type } = mapRequestParts('icqq', event.request_type ?? '', event.sub_type);
72
+ const sceneId = String(event.group_id ?? event.user_id ?? "");
161
73
  const tsSec = event.time ?? Math.floor(Date.now() / 1000);
162
74
  const flag = event.flag ?? resolveSideEventDedupeKey(event, "request");
163
75
 
@@ -166,18 +78,16 @@ export function formatIcqqRequest(
166
78
  (typeof event.request_type === "string" &&
167
79
  event.request_type.startsWith("friend"));
168
80
 
169
- return Request.from(event, {
81
+ return buildRequest(event, {
170
82
  $id: String(flag),
171
83
  $adapter: "icqq",
172
84
  $endpoint: endpointName,
173
- $type,
174
- $subType: event.sub_type,
175
- $channel: {
176
- id: channelId,
177
- type: isGroup ? "group" : "private",
178
- },
179
- $sender: senderFromId(event.user_id) ?? { id: "", name: "" },
180
- $comment: event.comment,
85
+ $type: "request",
86
+ $scene_id: sceneId,
87
+ $scene_type: scene_type,
88
+ $sub_type: sub_type,
89
+ $actor: senderFromId(event.user_id) ?? { id: "", name: "" },
90
+ $comment: typeof event.comment === "string" ? event.comment : undefined,
181
91
  $timestamp: tsSec * 1000,
182
92
  $approve: async (remark?: string) => {
183
93
  const action = isFriend
@@ -208,21 +118,26 @@ export function formatIcqqRequest(
208
118
  });
209
119
  }
210
120
 
211
- /** meta_event / system.*:返回是否建议在上线后刷新好友/群列表 */
212
121
  export function shouldRefreshListsOnMeta(event: IcqqIpcRawEvent): boolean {
213
122
  if (event.meta_event_type === "lifecycle") {
214
123
  const sub = event.sub_type;
215
124
  return sub === "connect" || sub === "enable";
216
125
  }
217
- const pt = event.post_type ?? "";
126
+ const pt = String(event.post_type ?? "");
218
127
  if (pt === "system.online" || pt.endsWith(".online")) return true;
219
128
  return false;
220
129
  }
221
130
 
222
131
  export function formatIcqqMetaLog(event: IcqqIpcRawEvent): string {
132
+ const meta =
133
+ (typeof event.meta_event_type === "string" && event.meta_event_type) ||
134
+ (typeof event.post_type === "string" && event.post_type) ||
135
+ "meta";
136
+ const subType = typeof event.sub_type === "string" ? event.sub_type : undefined;
137
+ const time = typeof event.time === "number" ? event.time : undefined;
223
138
  return formatCompact({
224
- meta: event.meta_event_type ?? event.post_type ?? "meta",
225
- sub_type: event.sub_type,
226
- time: event.time,
139
+ meta,
140
+ sub_type: subType,
141
+ time,
227
142
  });
228
143
  }
package/src/index.ts CHANGED
@@ -31,14 +31,6 @@ declare module "zhin.js" {
31
31
  export * from "./types.js";
32
32
  export { IcqqEndpoint } from "./endpoint.js";
33
33
  export { IcqqAdapter } from "./adapter.js";
34
- export {
35
- ICQQTypingIndicatorManager,
36
- createICQQTypingIndicatorManager,
37
- startICQQTypingIndicator,
38
- enableTypingIndicator,
39
- type ICQQTypingIndicatorConfig,
40
- } from "./typing-indicator.js";
41
- export type { TypingIndicatorConfig } from "./types.js";
42
34
 
43
35
  const plugin = usePlugin();
44
36
  const { provide, useContext, addCommand, root } = plugin;
@@ -0,0 +1 @@
1
+ export { toCanonicalSegments, fromCanonicalSegments } from 'zhin.js';
@@ -1,13 +1,12 @@
1
1
  /**
2
- * ICQQ 平台特有工具注册 — 通过 IPC 调用守护进程 Actions
2
+ * ICQQ 场景管理工具注册
3
3
  *
4
- * 通用群管理工具由 createGroupManagementTools() 自动生成;
5
- * 本模块注册 ICQQ 独有的扩展工具。
4
+ * 平台特有工具已迁移至 agent/tools/;本模块仅注册通用群管工具。
6
5
  */
7
- import type { ToolFeature } from "zhin.js";
8
- import { createGroupManagementTools } from "zhin.js";
6
+ import { createSceneManagementTools, type ToolFeature } from 'zhin.js';
7
+
9
8
  import type { IcqqAdapter } from "../adapter.js";
10
- import { Actions } from "../protocol.js";
9
+ import { setIcqqAgentDeps } from "../icqq-agent-deps.js";
11
10
 
12
11
  export function registerTools(
13
12
  toolService: ToolFeature,
@@ -16,397 +15,17 @@ export function registerTools(
16
15
  ): () => void {
17
16
  const disposers: (() => void)[] = [];
18
17
 
19
- // ── 通用群管工具 ───────────────────────────────────────────────────
20
- const groupTools = createGroupManagementTools(icqq, "icqq");
21
- disposers.push(...groupTools.map((t) => toolService.addTool(t, pluginName)));
22
-
23
- // ── 设置头衔 ───────────────────────────────────────────────────────
24
- disposers.push(
25
- toolService.addTool(
26
- {
27
- name: "icqq_set_title",
28
- description: "设置 QQ 群成员的专属头衔",
29
- parameters: {
30
- type: "object",
31
- properties: {
32
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
33
- group_id: { type: "number", description: "目标群号" },
34
- user_id: { type: "number", description: "目标成员 QQ号" },
35
- title: { type: "string", description: "头衔文字" },
36
- duration: { type: "number", description: "持续时间(秒),-1永久" },
37
- },
38
- required: ["endpoint_id", "group_id", "user_id", "title"],
39
- },
40
- platforms: ["icqq"],
41
- tags: ["icqq"],
42
- permissions: ["platform(icqq,group_owner)"],
43
- execute: async (args: Record<string, any>) => {
44
- const endpoint = icqq.endpoints.get(args.endpoint_id);
45
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
46
- const resp = await endpoint.ipc.request(Actions.SET_GROUP_TITLE, {
47
- group_id: args.group_id,
48
- user_id: args.user_id,
49
- title: args.title,
50
- duration: args.duration ?? -1,
51
- });
52
- return { success: resp.ok, message: resp.ok ? `已将 ${args.user_id} 的头衔设为 "${args.title}"` : (resp.error ?? "设置失败") };
53
- },
54
- },
55
- pluginName,
56
- ),
57
- );
58
-
59
- // ── 发送群公告 ─────────────────────────────────────────────────────
60
- disposers.push(
61
- toolService.addTool(
62
- {
63
- name: "icqq_announce",
64
- description: "发送 QQ 群公告(需要管理员权限)",
65
- parameters: {
66
- type: "object",
67
- properties: {
68
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
69
- group_id: { type: "number", description: "目标群号" },
70
- content: { type: "string", description: "公告内容" },
71
- },
72
- required: ["endpoint_id", "group_id", "content"],
73
- },
74
- platforms: ["icqq"],
75
- tags: ["icqq"],
76
- permissions: ["platform(icqq,group_admin)"],
77
- execute: async (args: Record<string, any>) => {
78
- const endpoint = icqq.endpoints.get(args.endpoint_id);
79
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
80
- const resp = await endpoint.ipc.request(Actions.GROUP_ANNOUNCE, {
81
- group_id: args.group_id,
82
- content: args.content,
83
- });
84
- return { success: resp.ok, message: resp.ok ? "群公告已发送" : (resp.error ?? "发送失败") };
85
- },
86
- },
87
- pluginName,
88
- ),
89
- );
90
-
91
- // ── 戳一戳 ─────────────────────────────────────────────────────────
92
- disposers.push(
93
- toolService.addTool(
94
- {
95
- name: "icqq_poke",
96
- description: "在 QQ 群中对某个成员执行戳一戳互动操作",
97
- parameters: {
98
- type: "object",
99
- properties: {
100
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
101
- group_id: { type: "number", description: "目标群号" },
102
- user_id: { type: "number", description: "要戳的目标成员 QQ号" },
103
- },
104
- required: ["endpoint_id", "group_id", "user_id"],
105
- },
106
- platforms: ["icqq"],
107
- tags: ["icqq"],
108
- execute: async (args: Record<string, any>) => {
109
- const endpoint = icqq.endpoints.get(args.endpoint_id);
110
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
111
- const resp = await endpoint.ipc.request(Actions.GROUP_POKE, {
112
- group_id: args.group_id,
113
- user_id: args.user_id,
114
- });
115
- return { success: resp.ok, message: resp.ok ? `已戳了戳 ${args.user_id}` : (resp.error ?? "戳一戳失败") };
116
- },
117
- },
118
- pluginName,
119
- ),
120
- );
121
-
122
- // ── 获取被禁言列表 ─────────────────────────────────────────────────
123
- disposers.push(
124
- toolService.addTool(
125
- {
126
- name: "icqq_list_muted",
127
- description: "查询 QQ 群中当前被禁言的成员列表",
128
- parameters: {
129
- type: "object",
130
- properties: {
131
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
132
- group_id: { type: "number", description: "目标群号" },
133
- },
134
- required: ["endpoint_id", "group_id"],
135
- },
136
- platforms: ["icqq"],
137
- tags: ["icqq"],
138
- permissions: ["platform(icqq,group_admin)"],
139
- execute: async (args: Record<string, any>) => {
140
- const endpoint = icqq.endpoints.get(args.endpoint_id);
141
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
142
- const resp = await endpoint.ipc.request(Actions.GROUP_MUTED_LIST, {
143
- group_id: args.group_id,
144
- });
145
- if (!resp.ok) throw new Error(resp.error ?? "获取禁言列表失败");
146
- const list = Array.isArray(resp.data) ? resp.data : [];
147
- return { muted_members: list, count: list.length };
148
- },
149
- },
150
- pluginName,
151
- ),
152
- );
153
-
154
- // ── 给用户点赞 ─────────────────────────────────────────────────────
155
- disposers.push(
156
- toolService.addTool(
157
- {
158
- name: "icqq_send_user_like",
159
- description: "给用户点赞(竖大拇指),每人每天最多 20 次",
160
- parameters: {
161
- type: "object",
162
- properties: {
163
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
164
- user_id: { type: "number", description: "要点赞的目标用户 QQ号" },
165
- times: { type: "number", description: "点赞次数(1-20),默认 1" },
166
- },
167
- required: ["endpoint_id", "user_id"],
168
- },
169
- platforms: ["icqq"],
170
- tags: ["icqq"],
171
- execute: async (args: Record<string, any>) => {
172
- const endpoint = icqq.endpoints.get(args.endpoint_id);
173
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
174
- const resp = await endpoint.ipc.request(Actions.FRIEND_LIKE, {
175
- user_id: args.user_id,
176
- times: Math.min(args.times ?? 1, 20),
177
- });
178
- return { success: resp.ok, message: resp.ok ? `已给 ${args.user_id} 点赞` : (resp.error ?? "点赞失败") };
179
- },
180
- },
181
- pluginName,
182
- ),
183
- );
184
-
185
- // ── 设置匿名聊天 ───────────────────────────────────────────────────
186
- disposers.push(
187
- toolService.addTool(
188
- {
189
- name: "icqq_set_anonymous",
190
- description: "开启或关闭 QQ 群的匿名聊天功能",
191
- parameters: {
192
- type: "object",
193
- properties: {
194
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
195
- group_id: { type: "number", description: "目标群号" },
196
- enable: { type: "boolean", description: "true=开启,false=关闭,默认 true" },
197
- },
198
- required: ["endpoint_id", "group_id"],
199
- },
200
- platforms: ["icqq"],
201
- tags: ["icqq"],
202
- permissions: ["platform(icqq,group_owner)"],
203
- execute: async (args: Record<string, any>) => {
204
- const endpoint = icqq.endpoints.get(args.endpoint_id);
205
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
206
- const enable = args.enable ?? true;
207
- const resp = await endpoint.ipc.request(Actions.GROUP_ALLOW_ANONY, {
208
- group_id: args.group_id,
209
- enable,
210
- });
211
- return { success: resp.ok, message: resp.ok ? (enable ? "已开启匿名聊天" : "已关闭匿名聊天") : (resp.error ?? "操作失败") };
212
- },
213
- },
214
- pluginName,
215
- ),
216
- );
217
-
218
- // ── 群文件列表 ─────────────────────────────────────────────────────
219
- disposers.push(
220
- toolService.addTool(
221
- {
222
- name: "icqq_group_files",
223
- description: "获取 QQ 群的群文件列表",
224
- parameters: {
225
- type: "object",
226
- properties: {
227
- endpoint_id: { type: "string", description: "Endpoint 名称", contextKey: 'endpointId' },
228
- group_id: { type: "number", description: "群号" },
229
- },
230
- required: ["endpoint_id", "group_id"],
231
- },
232
- platforms: ["icqq"],
233
- tags: ["icqq"],
234
- execute: async (args: Record<string, any>) => {
235
- const endpoint = icqq.endpoints.get(args.endpoint_id);
236
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
237
- const resp = await endpoint.ipc.request(Actions.GFS_LIST, {
238
- group_id: args.group_id,
239
- });
240
- if (!resp.ok) throw new Error(resp.error ?? "获取群文件失败");
241
- const files = Array.isArray(resp.data) ? resp.data : [];
242
- if (!files.length) return { files: [], message: "群文件为空" };
243
- return {
244
- files: files.slice(0, 30).map((f: any) => ({
245
- name: f.name,
246
- size: f.size,
247
- uploader: f.uploader_uin,
248
- upload_time: f.upload_time,
249
- })),
250
- count: files.length,
251
- };
252
- },
253
- },
254
- pluginName,
255
- ),
256
- );
257
-
258
- // ── 好友列表 ───────────────────────────────────────────────────────
259
- disposers.push(
260
- toolService.addTool(
261
- {
262
- name: "icqq_friend_list",
263
- description: "获取 QQ 好友列表",
264
- parameters: {
265
- type: "object",
266
- properties: {
267
- endpoint_id: { type: "string", description: "Endpoint 名称", contextKey: 'endpointId' },
268
- },
269
- required: ["endpoint_id"],
270
- },
271
- platforms: ["icqq"],
272
- tags: ["icqq"],
273
- execute: async (args: Record<string, any>) => {
274
- const endpoint = icqq.endpoints.get(args.endpoint_id);
275
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
276
- const friends = Array.from(endpoint.friends.values()).map((f) => ({
277
- user_id: f.user_id,
278
- nickname: f.nickname,
279
- remark: f.remark,
280
- }));
281
- return { friends: friends.slice(0, 50), count: endpoint.friends.size };
282
- },
283
- },
284
- pluginName,
285
- ),
286
- );
287
-
288
- // ── 群列表 ─────────────────────────────────────────────────────────
289
- disposers.push(
290
- toolService.addTool(
291
- {
292
- name: "icqq_group_list",
293
- description: "获取 Endpoint 的 QQ 群列表",
294
- parameters: {
295
- type: "object",
296
- properties: {
297
- endpoint_id: { type: "string", description: "Endpoint 名称", contextKey: 'endpointId' },
298
- },
299
- required: ["endpoint_id"],
300
- },
301
- platforms: ["icqq"],
302
- tags: ["icqq"],
303
- execute: async (args: Record<string, any>) => {
304
- const endpoint = icqq.endpoints.get(args.endpoint_id);
305
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
306
- const groups = Array.from(endpoint.groups.values()).map((g) => ({
307
- group_id: g.group_id,
308
- group_name: g.group_name,
309
- member_count: g.member_count,
310
- max_member_count: g.max_member_count,
311
- }));
312
- return { groups: groups.slice(0, 50), count: endpoint.groups.size };
313
- },
314
- },
315
- pluginName,
316
- ),
317
- );
318
-
319
- // ── 群签到 ─────────────────────────────────────────────────────────
320
- disposers.push(
321
- toolService.addTool(
322
- {
323
- name: "icqq_group_sign",
324
- description: "QQ 群签到打卡",
325
- parameters: {
326
- type: "object",
327
- properties: {
328
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
329
- group_id: { type: "number", description: "目标群号" },
330
- },
331
- required: ["endpoint_id", "group_id"],
332
- },
333
- platforms: ["icqq"],
334
- tags: ["icqq"],
335
- execute: async (args: Record<string, any>) => {
336
- const endpoint = icqq.endpoints.get(args.endpoint_id);
337
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
338
- const resp = await endpoint.ipc.request(Actions.GROUP_SIGN, {
339
- group_id: args.group_id,
340
- });
341
- return { success: resp.ok, message: resp.ok ? "群签到成功" : (resp.error ?? "签到失败") };
342
- },
343
- },
344
- pluginName,
345
- ),
346
- );
347
-
348
- // ── 邀请入群 ───────────────────────────────────────────────────────
349
- disposers.push(
350
- toolService.addTool(
351
- {
352
- name: "icqq_group_invite",
353
- description: "邀请好友加入 QQ 群",
354
- parameters: {
355
- type: "object",
356
- properties: {
357
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
358
- group_id: { type: "number", description: "目标群号" },
359
- user_id: { type: "number", description: "要邀请的 QQ号" },
360
- },
361
- required: ["endpoint_id", "group_id", "user_id"],
362
- },
363
- platforms: ["icqq"],
364
- tags: ["icqq"],
365
- execute: async (args: Record<string, any>) => {
366
- const endpoint = icqq.endpoints.get(args.endpoint_id);
367
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
368
- const resp = await endpoint.ipc.request(Actions.GROUP_INVITE, {
369
- group_id: args.group_id,
370
- user_id: args.user_id,
371
- });
372
- return { success: resp.ok, message: resp.ok ? `已邀请 ${args.user_id} 加入群` : (resp.error ?? "邀请失败") };
373
- },
374
- },
375
- pluginName,
376
- ),
377
- );
378
-
379
- // ── 设为/移除精华消息 ──────────────────────────────────────────────
380
- disposers.push(
381
- toolService.addTool(
382
- {
383
- name: "icqq_essence",
384
- description: "设置或移除 QQ 群精华消息",
385
- parameters: {
386
- type: "object",
387
- properties: {
388
- endpoint_id: { type: "string", description: "Endpoint QQ号", contextKey: 'endpointId' },
389
- message_id: { type: "string", description: "消息 ID" },
390
- action: { type: "string", description: "add=设为精华, remove=移除精华" },
391
- },
392
- required: ["endpoint_id", "message_id", "action"],
393
- },
394
- platforms: ["icqq"],
395
- tags: ["icqq"],
396
- permissions: ["platform(icqq,group_admin)"],
397
- execute: async (args: Record<string, any>) => {
398
- const endpoint = icqq.endpoints.get(args.endpoint_id);
399
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
400
- const action = args.action === "add" ? Actions.GROUP_ESSENCE_ADD : Actions.GROUP_ESSENCE_REMOVE;
401
- const resp = await endpoint.ipc.request(action, {
402
- message_id: args.message_id,
403
- });
404
- return { success: resp.ok, message: resp.ok ? (args.action === "add" ? "已设为精华" : "已移除精华") : (resp.error ?? "操作失败") };
405
- },
406
- },
407
- pluginName,
408
- ),
409
- );
18
+ setIcqqAgentDeps({
19
+ getEndpoint: (endpointId) => {
20
+ const endpoint = icqq.endpoints.get(endpointId);
21
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
22
+ return endpoint;
23
+ },
24
+ getAdapter: () => icqq,
25
+ });
26
+
27
+ const sceneTools = createSceneManagementTools(icqq, "icqq");
28
+ disposers.push(...sceneTools.map((t) => toolService.addTool(t, pluginName)));
410
29
 
411
30
  return () => disposers.forEach((d) => d());
412
31
  }