@zhin.js/adapter-napcat 3.0.2 → 5.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 (183) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +47 -98
  3. package/adapters/napcat.ts +39 -0
  4. package/agent/tools/ai_tts.ts +2 -2
  5. package/agent/tools/del_group_notice.ts +2 -2
  6. package/agent/tools/delete_essence_msg.ts +2 -2
  7. package/agent/tools/delete_friend.ts +2 -2
  8. package/agent/tools/download_file.ts +2 -2
  9. package/agent/tools/forward_single_msg.ts +2 -2
  10. package/agent/tools/get_ai_characters.ts +2 -2
  11. package/agent/tools/get_essence_list.ts +2 -2
  12. package/agent/tools/get_friend_msg_history.ts +2 -2
  13. package/agent/tools/get_group_file_url.ts +2 -2
  14. package/agent/tools/get_group_info_ex.ts +2 -2
  15. package/agent/tools/get_group_msg_history.ts +2 -2
  16. package/agent/tools/get_group_notice.ts +2 -2
  17. package/agent/tools/get_group_root_files.ts +2 -2
  18. package/agent/tools/get_group_shut_list.ts +2 -2
  19. package/agent/tools/get_mini_app_ark.ts +2 -2
  20. package/agent/tools/get_user_status.ts +2 -2
  21. package/agent/tools/group_sign.ts +2 -2
  22. package/agent/tools/mark_msg_as_read.ts +2 -2
  23. package/agent/tools/ocr_image.ts +2 -2
  24. package/agent/tools/send_forward_msg.ts +2 -2
  25. package/agent/tools/send_group_notice.ts +2 -2
  26. package/agent/tools/send_like.ts +2 -2
  27. package/agent/tools/send_poke.ts +2 -2
  28. package/agent/tools/set_avatar.ts +2 -2
  29. package/agent/tools/set_emoji_reaction.ts +2 -2
  30. package/agent/tools/set_essence_msg.ts +2 -2
  31. package/agent/tools/set_group_portrait.ts +2 -2
  32. package/agent/tools/set_online_status.ts +2 -2
  33. package/agent/tools/set_profile.ts +2 -2
  34. package/agent/tools/set_signature.ts +2 -2
  35. package/agent/tools/set_title.ts +2 -2
  36. package/agent/tools/translate.ts +2 -2
  37. package/agent/tools/upload_group_file.ts +2 -2
  38. package/lib/http-endpoint.d.ts +26 -0
  39. package/lib/http-endpoint.js +147 -0
  40. package/lib/index.d.ts +11 -0
  41. package/lib/index.js +10 -0
  42. package/lib/napcat-agent-deps.d.ts +51 -0
  43. package/lib/napcat-agent-deps.js +33 -0
  44. package/lib/napcat-inbound.d.ts +28 -0
  45. package/lib/{src/napcat-inbound.js → napcat-inbound.js} +28 -13
  46. package/lib/onebot-get-msg.d.ts +14 -0
  47. package/lib/{src/onebot-get-msg.js → onebot-get-msg.js} +0 -1
  48. package/lib/protocol.d.ts +151 -0
  49. package/lib/protocol.js +217 -0
  50. package/lib/webhook.d.ts +2 -0
  51. package/lib/webhook.js +14 -0
  52. package/lib/ws-endpoint.d.ts +61 -0
  53. package/lib/ws-endpoint.js +357 -0
  54. package/lib/ws-transport.d.ts +13 -0
  55. package/lib/ws-transport.js +75 -0
  56. package/lib/ws-types.d.ts +17 -0
  57. package/lib/ws-types.js +1 -0
  58. package/lib/wss-auth.d.ts +2 -0
  59. package/lib/wss-auth.js +16 -0
  60. package/lib/wss-endpoint.d.ts +25 -0
  61. package/lib/wss-endpoint.js +157 -0
  62. package/package.json +44 -33
  63. package/plugin.ts +13 -0
  64. package/schema.json +74 -0
  65. package/src/http-endpoint.ts +197 -0
  66. package/src/index.ts +66 -81
  67. package/src/napcat-agent-deps.ts +81 -15
  68. package/src/napcat-inbound.ts +34 -10
  69. package/src/onebot-get-msg.ts +8 -2
  70. package/src/protocol.ts +373 -0
  71. package/src/webhook.ts +16 -0
  72. package/src/ws-endpoint.ts +452 -0
  73. package/src/ws-transport.ts +105 -0
  74. package/src/ws-types.ts +20 -0
  75. package/src/wss-auth.ts +17 -0
  76. package/src/wss-endpoint.ts +205 -0
  77. package/client/NapCatManagement.tsx +0 -113
  78. package/client/index.tsx +0 -11
  79. package/client/tsconfig.json +0 -7
  80. package/client/utils/api.ts +0 -30
  81. package/dist/index.js +0 -27
  82. package/lib/agent/tools/ai_tts.js +0 -21
  83. package/lib/agent/tools/ai_tts.js.map +0 -1
  84. package/lib/agent/tools/del_group_notice.js +0 -22
  85. package/lib/agent/tools/del_group_notice.js.map +0 -1
  86. package/lib/agent/tools/delete_essence_msg.js +0 -21
  87. package/lib/agent/tools/delete_essence_msg.js.map +0 -1
  88. package/lib/agent/tools/delete_friend.js +0 -20
  89. package/lib/agent/tools/delete_friend.js.map +0 -1
  90. package/lib/agent/tools/download_file.js +0 -19
  91. package/lib/agent/tools/download_file.js.map +0 -1
  92. package/lib/agent/tools/forward_single_msg.js +0 -24
  93. package/lib/agent/tools/forward_single_msg.js.map +0 -1
  94. package/lib/agent/tools/get_ai_characters.js +0 -19
  95. package/lib/agent/tools/get_ai_characters.js.map +0 -1
  96. package/lib/agent/tools/get_essence_list.js +0 -19
  97. package/lib/agent/tools/get_essence_list.js.map +0 -1
  98. package/lib/agent/tools/get_friend_msg_history.js +0 -20
  99. package/lib/agent/tools/get_friend_msg_history.js.map +0 -1
  100. package/lib/agent/tools/get_group_file_url.js +0 -21
  101. package/lib/agent/tools/get_group_file_url.js.map +0 -1
  102. package/lib/agent/tools/get_group_info_ex.js +0 -19
  103. package/lib/agent/tools/get_group_info_ex.js.map +0 -1
  104. package/lib/agent/tools/get_group_msg_history.js +0 -21
  105. package/lib/agent/tools/get_group_msg_history.js.map +0 -1
  106. package/lib/agent/tools/get_group_notice.js +0 -19
  107. package/lib/agent/tools/get_group_notice.js.map +0 -1
  108. package/lib/agent/tools/get_group_root_files.js +0 -19
  109. package/lib/agent/tools/get_group_root_files.js.map +0 -1
  110. package/lib/agent/tools/get_group_shut_list.js +0 -19
  111. package/lib/agent/tools/get_group_shut_list.js.map +0 -1
  112. package/lib/agent/tools/get_mini_app_ark.js +0 -22
  113. package/lib/agent/tools/get_mini_app_ark.js.map +0 -1
  114. package/lib/agent/tools/get_user_status.js +0 -18
  115. package/lib/agent/tools/get_user_status.js.map +0 -1
  116. package/lib/agent/tools/group_sign.js +0 -20
  117. package/lib/agent/tools/group_sign.js.map +0 -1
  118. package/lib/agent/tools/mark_msg_as_read.js +0 -19
  119. package/lib/agent/tools/mark_msg_as_read.js.map +0 -1
  120. package/lib/agent/tools/ocr_image.js +0 -18
  121. package/lib/agent/tools/ocr_image.js.map +0 -1
  122. package/lib/agent/tools/send_forward_msg.js +0 -21
  123. package/lib/agent/tools/send_forward_msg.js.map +0 -1
  124. package/lib/agent/tools/send_group_notice.js +0 -23
  125. package/lib/agent/tools/send_group_notice.js.map +0 -1
  126. package/lib/agent/tools/send_like.js +0 -20
  127. package/lib/agent/tools/send_like.js.map +0 -1
  128. package/lib/agent/tools/send_poke.js +0 -20
  129. package/lib/agent/tools/send_poke.js.map +0 -1
  130. package/lib/agent/tools/set_avatar.js +0 -19
  131. package/lib/agent/tools/set_avatar.js.map +0 -1
  132. package/lib/agent/tools/set_emoji_reaction.js +0 -20
  133. package/lib/agent/tools/set_emoji_reaction.js.map +0 -1
  134. package/lib/agent/tools/set_essence_msg.js +0 -21
  135. package/lib/agent/tools/set_essence_msg.js.map +0 -1
  136. package/lib/agent/tools/set_group_portrait.js +0 -22
  137. package/lib/agent/tools/set_group_portrait.js.map +0 -1
  138. package/lib/agent/tools/set_online_status.js +0 -20
  139. package/lib/agent/tools/set_online_status.js.map +0 -1
  140. package/lib/agent/tools/set_profile.js +0 -23
  141. package/lib/agent/tools/set_profile.js.map +0 -1
  142. package/lib/agent/tools/set_signature.js +0 -19
  143. package/lib/agent/tools/set_signature.js.map +0 -1
  144. package/lib/agent/tools/set_title.js +0 -23
  145. package/lib/agent/tools/set_title.js.map +0 -1
  146. package/lib/agent/tools/translate.js +0 -18
  147. package/lib/agent/tools/translate.js.map +0 -1
  148. package/lib/agent/tools/upload_group_file.js +0 -23
  149. package/lib/agent/tools/upload_group_file.js.map +0 -1
  150. package/lib/src/adapter.js +0 -102
  151. package/lib/src/adapter.js.map +0 -1
  152. package/lib/src/agent-prompt.js +0 -106
  153. package/lib/src/agent-prompt.js.map +0 -1
  154. package/lib/src/endpoint-base.js +0 -315
  155. package/lib/src/endpoint-base.js.map +0 -1
  156. package/lib/src/endpoint-http.js +0 -106
  157. package/lib/src/endpoint-http.js.map +0 -1
  158. package/lib/src/endpoint-ws-client.js +0 -139
  159. package/lib/src/endpoint-ws-client.js.map +0 -1
  160. package/lib/src/endpoint-ws-server.js +0 -137
  161. package/lib/src/endpoint-ws-server.js.map +0 -1
  162. package/lib/src/index.js +0 -58
  163. package/lib/src/index.js.map +0 -1
  164. package/lib/src/napcat-agent-deps.js +0 -19
  165. package/lib/src/napcat-agent-deps.js.map +0 -1
  166. package/lib/src/napcat-inbound.js.map +0 -1
  167. package/lib/src/onebot-get-msg.js.map +0 -1
  168. package/lib/src/routes.js +0 -61
  169. package/lib/src/routes.js.map +0 -1
  170. package/lib/src/segment-mapper.js +0 -2
  171. package/lib/src/segment-mapper.js.map +0 -1
  172. package/lib/src/types.js +0 -6
  173. package/lib/src/types.js.map +0 -1
  174. package/plugin.yml +0 -3
  175. package/src/adapter.ts +0 -109
  176. package/src/agent-prompt.ts +0 -115
  177. package/src/endpoint-base.ts +0 -362
  178. package/src/endpoint-http.ts +0 -95
  179. package/src/endpoint-ws-client.ts +0 -137
  180. package/src/endpoint-ws-server.ts +0 -136
  181. package/src/routes.ts +0 -61
  182. package/src/segment-mapper.ts +0 -1
  183. package/src/types.ts +0 -266
@@ -0,0 +1,197 @@
1
+ /**
2
+ * NapCat HTTP endpoint — POST inbound events + HTTP API outbound.
3
+ */
4
+ import type { IncomingMessage, ServerResponse } from 'node:http';
5
+ import type { EndpointInstance } from '@zhin.js/adapter';
6
+ import type { MessageGateway } from '@zhin.js/core/runtime';
7
+ import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
8
+ import { formatCompact, getLogger } from '@zhin.js/logger';
9
+ import type { CapabilityId } from '@zhin.js/plugin-runtime';
10
+ import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
11
+ import {
12
+ InboundMessageDeduper,
13
+ isNapCatBotMentioned,
14
+ isSelfMessage,
15
+ normalizeMessage,
16
+ } from './napcat-inbound.js';
17
+ import {
18
+ buildSendAction,
19
+ callNapCatHttpAction,
20
+ formatInboundContent,
21
+ formatInboundTarget,
22
+ formatOutboundSegments,
23
+ isMessageEvent,
24
+ senderNickname,
25
+ senderUserId,
26
+ type NapCatEvent,
27
+ type NapCatHttpConfig,
28
+ } from './protocol.js';
29
+ import { readRequestBody } from './webhook.js';
30
+ import { NapCatWsEndpoint } from './ws-endpoint.js';
31
+ import { verifyNapCatAccessToken } from './wss-auth.js';
32
+
33
+ const logger = getLogger('napcat');
34
+
35
+ export interface NapCatHttpEndpointOptions {
36
+ readonly id: CapabilityId;
37
+ readonly gateway: MessageGateway;
38
+ readonly http: HttpHost;
39
+ readonly config: NapCatHttpConfig;
40
+ readonly callHttpAction?: typeof callNapCatHttpAction;
41
+ }
42
+
43
+ export class NapCatHttpEndpoint implements EndpointInstance {
44
+ readonly #options: NapCatHttpEndpointOptions;
45
+ readonly #inboundDeduper = new InboundMessageDeduper();
46
+ readonly #callHttpAction: typeof callNapCatHttpAction;
47
+ #routeReleases: HttpRouteRegistration[] = [];
48
+ #open = false;
49
+ #started = false;
50
+ #unregisterAgent?: () => void;
51
+
52
+ constructor(options: NapCatHttpEndpointOptions) {
53
+ this.#options = options;
54
+ this.#callHttpAction = options.callHttpAction ?? callNapCatHttpAction;
55
+ }
56
+
57
+ async start(): Promise<void> {
58
+ if (this.#started) return;
59
+ this.#started = true;
60
+ this.#unregisterAgent = registerNapcatAgentEndpoint(
61
+ this.#options.config.name,
62
+ this as unknown as NapCatWsEndpoint,
63
+ );
64
+ this.#setupRoutes();
65
+ logger.info(formatCompact({
66
+ op: 'listen',
67
+ endpoint: this.#options.config.name,
68
+ mode: 'http',
69
+ path: this.#options.config.post_path,
70
+ }));
71
+ }
72
+
73
+ open(): void {
74
+ this.#open = true;
75
+ }
76
+
77
+ close(): void {
78
+ this.#open = false;
79
+ }
80
+
81
+ async stop(): Promise<void> {
82
+ this.#open = false;
83
+ for (const release of this.#routeReleases.splice(0)) release();
84
+ this.#unregisterAgent?.();
85
+ this.#unregisterAgent = undefined;
86
+ this.#inboundDeduper.clear();
87
+ this.#started = false;
88
+ logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
89
+ }
90
+
91
+ async send({ target, payload }: { readonly target: string; readonly payload: unknown }): Promise<string> {
92
+ const message = formatOutboundSegments(payload);
93
+ const { action, params } = buildSendAction(target, message);
94
+ const resp = await this.#callHttpAction(
95
+ {
96
+ http_url: this.#options.config.http_url,
97
+ access_token: this.#options.config.access_token,
98
+ },
99
+ action,
100
+ params,
101
+ );
102
+ const data = resp.data as { message_id?: number | string } | undefined;
103
+ const messageId = data?.message_id != null ? String(data.message_id) : '';
104
+ logger.debug(formatCompact({
105
+ op: 'napcat_send',
106
+ endpoint: this.#options.config.name,
107
+ target,
108
+ messageId,
109
+ }));
110
+ return messageId;
111
+ }
112
+
113
+ callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
114
+ return this.#callHttpAction(
115
+ {
116
+ http_url: this.#options.config.http_url,
117
+ access_token: this.#options.config.access_token,
118
+ },
119
+ action,
120
+ params,
121
+ ).then((resp) => resp.data);
122
+ }
123
+
124
+ admit(ev: NapCatEvent): void {
125
+ if (!this.#open || !isMessageEvent(ev)) return;
126
+ if (isSelfMessage(ev)) return;
127
+ const msgId = String(ev.message_id);
128
+ if (!this.#inboundDeduper.shouldProcess(msgId)) return;
129
+ if (Array.isArray(ev.message) || typeof ev.message === 'string') {
130
+ ev = { ...ev, message: normalizeMessage(ev.message) };
131
+ }
132
+ const target = formatInboundTarget(ev);
133
+ const nickname = senderNickname(ev);
134
+ const mentioned = isNapCatBotMentioned(ev);
135
+ void this.#options.gateway.receive({
136
+ adapter: this.#options.id,
137
+ target,
138
+ content: formatInboundContent(ev),
139
+ sender: senderUserId(ev),
140
+ id: msgId,
141
+ metadata: Object.freeze({
142
+ message_type: ev.message_type,
143
+ user_id: ev.user_id != null ? String(ev.user_id) : undefined,
144
+ group_id: ev.group_id != null ? String(ev.group_id) : undefined,
145
+ endpoint: this.#options.config.name,
146
+ time: ev.time,
147
+ self_id: ev.self_id != null ? String(ev.self_id) : undefined,
148
+ role: ev.sender?.role,
149
+ ...(nickname ? { nickname } : {}),
150
+ ...(mentioned ? { mentioned: true } : {}),
151
+ }),
152
+ }).catch((err) => {
153
+ logger.warn(formatCompact({
154
+ op: 'napcat_gateway_receive_failed',
155
+ target,
156
+ error: err instanceof Error ? err.message : String(err),
157
+ }));
158
+ });
159
+ }
160
+
161
+ #setupRoutes(): void {
162
+ const path = this.#options.config.post_path;
163
+ this.#routeReleases.push(
164
+ this.#options.http.route('POST', path, async (request, response) => {
165
+ await this.#handlePost(request, response);
166
+ }, { summary: 'NapCat HTTP event callback', tags: ['napcat'] }),
167
+ );
168
+ }
169
+
170
+ async #handlePost(request: IncomingMessage, response: ServerResponse): Promise<void> {
171
+ try {
172
+ if (!verifyNapCatAccessToken(this.#options.config.access_token, request)) {
173
+ response.writeHead(403, { 'Content-Type': 'application/json' });
174
+ response.end(JSON.stringify({ message: 'Unauthorized' }));
175
+ return;
176
+ }
177
+ const raw = await readRequestBody(request);
178
+ let ev: NapCatEvent;
179
+ try {
180
+ ev = JSON.parse(raw) as NapCatEvent;
181
+ } catch {
182
+ response.writeHead(400, { 'Content-Type': 'application/json' });
183
+ response.end(JSON.stringify({ message: 'Invalid JSON' }));
184
+ return;
185
+ }
186
+ if (this.#open) this.admit(ev);
187
+ response.writeHead(200, { 'Content-Type': 'application/json' });
188
+ response.end(JSON.stringify({ status: 'ok' }));
189
+ } catch (error) {
190
+ logger.error('NapCat HTTP webhook error:', error);
191
+ if (!response.headersSent) {
192
+ response.writeHead(500, { 'Content-Type': 'application/json' });
193
+ response.end(JSON.stringify({ message: 'Internal Server Error' }));
194
+ }
195
+ }
196
+ }
197
+ }
package/src/index.ts CHANGED
@@ -1,91 +1,76 @@
1
- /**
2
- * NapCat 适配器入口
3
- * 支持 OneBot11 标准 + go-cqhttp 扩展 + NapCat 独有 API
4
- * 连接方式:正向 WS / 反向 WS / HTTP
5
- */
6
- import path from 'path';
7
- import {
8
- usePlugin,
9
- type Plugin,
10
- type Context,
11
- type ISceneManagement,
12
- createSceneManagementTools,
13
- registerDefaultScenePlatformPermitChecker,
14
- type ToolFeature,
15
- } from 'zhin.js';
16
- import { registerAgentPromptContributor, unregisterAgentPromptContributor } from 'zhin.js/agent';
17
- import type { Router } from '@zhin.js/host-router';
18
- import { PageManager } from '@zhin.js/host-api';
19
- import { NapCatAdapter } from './adapter.js';
20
- import { createNapCatAgentPromptContributor } from './agent-prompt.js';
21
- import { registerRoutes } from './routes.js';
22
- import { setNapcatAgentDeps } from './napcat-agent-deps.js';
1
+ export {
2
+ buildSendAction,
3
+ buildWsConnectOptions,
4
+ callNapCatHttpAction,
5
+ formatInboundContent,
6
+ formatInboundTarget,
7
+ formatOutboundSegments,
8
+ getChannelId,
9
+ isMessageEvent,
10
+ parseSendTarget,
11
+ resolveNapCatConfig,
12
+ senderNickname,
13
+ senderUserId,
14
+ type MessageSegment,
15
+ type NapCatActionRequest,
16
+ type NapCatActionResponse,
17
+ type NapCatAdapterConfig,
18
+ type NapCatConfigBase,
19
+ type NapCatEndpointConfig,
20
+ type NapCatEvent,
21
+ type NapCatHttpConfig,
22
+ type NapCatMessageEvent,
23
+ type NapCatSender,
24
+ type NapCatWireSegment,
25
+ type NapCatWsConfig,
26
+ type NapCatWssConfig,
27
+ type ParsedSendTarget,
28
+ type ResolvedNapCatConfig,
29
+ } from './protocol.js';
23
30
 
24
- export * from './types.js';
25
- export { NapCatWsClient } from './endpoint-ws-client.js';
26
- export { NapCatWsServer } from './endpoint-ws-server.js';
27
- export { NapCatHttpEndpoint } from './endpoint-http.js';
28
- export { NapCatAdapter, type NapCatEndpoint } from './adapter.js';
29
- export { NapCatEndpointBase } from './endpoint-base.js';
31
+ export {
32
+ InboundMessageDeduper,
33
+ isNapCatBotMentioned,
34
+ isSelfMessage,
35
+ normalizeMessage,
36
+ } from './napcat-inbound.js';
30
37
 
31
- declare module 'zhin.js' {
32
- namespace Plugin {
33
- interface Contexts {
34
- web: PageManager;
35
- router: Router;
36
- }
37
- }
38
- interface Adapters {
39
- napcat: NapCatAdapter;
40
- }
41
- }
38
+ export {
39
+ getEndpoint,
40
+ getNapcatAgentDeps,
41
+ registerNapcatAgentEndpoint,
42
+ setNapcatAgentDeps,
43
+ type NapcatAgentDeps,
44
+ type NapcatAgentEndpoint,
45
+ } from './napcat-agent-deps.js';
42
46
 
43
- const plugin = usePlugin();
44
- const { provide, useContext } = plugin;
47
+ export { parseOneBotGetMsgResponse } from './onebot-get-msg.js';
45
48
 
46
- // ── 适配器注册 ─────────────────────────────────────────────────────
47
- provide({
48
- name: 'napcat',
49
- description: 'NapCatQQ 适配器(OneBot11 + go-cqhttp + NapCat 扩展,正向/反向 WS + HTTP)',
50
- mounted: async (p: Plugin) => {
51
- registerAgentPromptContributor(createNapCatAgentPromptContributor());
52
- const adapter = new NapCatAdapter(p);
53
- await adapter.start();
54
- return adapter;
55
- },
56
- dispose: async (adapter: NapCatAdapter) => {
57
- unregisterAgentPromptContributor('napcat');
58
- await adapter.stop();
59
- },
60
- } as unknown as Context<'napcat'>);
49
+ export {
50
+ NapCatWsEndpoint,
51
+ type NapCatWsEndpointOptions,
52
+ } from './ws-endpoint.js';
61
53
 
62
- // ── AI 工具注册(场景治理;扩展工具见 agent/tools/)────────────────
63
- useContext('tool', 'napcat', (toolService: ToolFeature, napcat: NapCatAdapter) => {
64
- setNapcatAgentDeps({ getAdapter: () => napcat });
54
+ export {
55
+ NapCatWssEndpoint,
56
+ type NapCatWssEndpointOptions,
57
+ } from './wss-endpoint.js';
65
58
 
66
- const disposers: (() => void)[] = [];
67
- disposers.push(registerDefaultScenePlatformPermitChecker('napcat'));
59
+ export {
60
+ NapCatHttpEndpoint,
61
+ type NapCatHttpEndpointOptions,
62
+ } from './http-endpoint.js';
68
63
 
69
- const sceneTools = createSceneManagementTools(
70
- napcat as unknown as ISceneManagement,
71
- 'napcat',
72
- );
73
- disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
64
+ export type { NapCatWsSocket, NapCatWsCreateOptions } from './ws-types.js';
74
65
 
75
- return () => disposers.forEach(d => d());
76
- });
66
+ export {
67
+ callNapCatWsAction,
68
+ decodeWsPayload,
69
+ handleNapCatWsMessage,
70
+ rejectAllPending,
71
+ startNapCatHeartbeat,
72
+ } from './ws-transport.js';
77
73
 
78
- // ── Web 控制台入口 ─────────────────────────────────────────────────
79
- useContext('web', (pageManager) => {
80
- pageManager.addEntry({
81
- id: 'napcat',
82
- development: path.resolve(import.meta.dirname, '../client/index.tsx'),
83
- production: path.resolve(import.meta.dirname, '../dist/index.js'),
84
- meta: { name: 'NapCat' },
85
- });
86
- });
74
+ export { verifyNapCatAccessToken } from './wss-auth.js';
87
75
 
88
- // ── HTTP 路由 ──────────────────────────────────────────────────────
89
- useContext('router', 'napcat', async (router: Router, napcat: NapCatAdapter) => {
90
- registerRoutes(router, napcat);
91
- });
76
+ export { readRequestBody } from './webhook.js';
@@ -1,29 +1,95 @@
1
1
  /**
2
- * Shared runtime deps for napcat agent/ authoring tools.
2
+ * Agent tool deps for napcat.
3
+ * Endpoints register themselves on start; tools look up by config name / endpoint id.
3
4
  */
4
- import type { NapCatAdapter, NapCatEndpoint } from './adapter.js';
5
+
6
+ export interface NapcatAgentEndpoint {
7
+ callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
8
+ setTitle(groupId: number, userId: number, title: string, duration?: number): Promise<boolean>;
9
+ sendLike(userId: number, times?: number): Promise<unknown>;
10
+ deleteFriend(userId: number): Promise<unknown>;
11
+ markMsgAsRead(messageId: number): Promise<unknown>;
12
+ ocrImage(image: string): Promise<unknown>;
13
+ setQQProfile(
14
+ nickname: string,
15
+ company?: string,
16
+ email?: string,
17
+ college?: string,
18
+ personalNote?: string,
19
+ ): Promise<unknown>;
20
+ setGroupPortrait(groupId: number, file: string): Promise<unknown>;
21
+ setEssenceMsg(messageId: number): Promise<unknown>;
22
+ deleteEssenceMsg(messageId: number): Promise<unknown>;
23
+ getEssenceMsgList(groupId: number): Promise<unknown>;
24
+ sendGroupSign(groupId: number): Promise<unknown>;
25
+ sendGroupNotice(groupId: number, content: string, image?: string): Promise<unknown>;
26
+ getGroupNotice(groupId: number): Promise<unknown>;
27
+ deleteGroupNotice(groupId: number, noticeId: string): Promise<unknown>;
28
+ uploadGroupFile(groupId: number, file: string, name: string, folder?: string): Promise<unknown>;
29
+ getGroupRootFiles(groupId: number): Promise<unknown>;
30
+ getGroupFileUrl(groupId: number, fileId: string, busid: number): Promise<unknown>;
31
+ downloadFile(url: string, threadCount?: number, headers?: string[]): Promise<unknown>;
32
+ setOnlineStatus(status: number, extStatus: number): Promise<unknown>;
33
+ setQQAvatar(file: string): Promise<unknown>;
34
+ forwardFriendSingleMsg(userId: number, messageId: number): Promise<unknown>;
35
+ forwardGroupSingleMsg(groupId: number, messageId: number): Promise<unknown>;
36
+ translateEn2Zh(sourceText: string): Promise<unknown>;
37
+ setMsgEmojiLike(messageId: number, emojiId: string): Promise<unknown>;
38
+ sendForwardMsg(messageType: 'private' | 'group', id: number, messages: unknown[]): Promise<unknown>;
39
+ getFriendMsgHistory(userId: number, messageSeq?: number, count?: number): Promise<unknown>;
40
+ getGroupMsgHistory(groupId: number, messageSeq?: number, count?: number): Promise<unknown>;
41
+ setSelfLongnick(longnick: string): Promise<unknown>;
42
+ getGroupInfoEx(groupId: number): Promise<unknown>;
43
+ sendPoke(userId: number, groupId?: number): Promise<unknown>;
44
+ ncGetUserStatus(userId: number): Promise<unknown>;
45
+ getGroupShutList(groupId: number): Promise<unknown>;
46
+ getMiniAppArk(
47
+ type: string,
48
+ title: string,
49
+ desc: string,
50
+ picUrl: string,
51
+ jumpUrl: string,
52
+ ): Promise<unknown>;
53
+ getAiCharacters(groupId: number): Promise<unknown>;
54
+ sendGroupAiRecord(groupId: number, characterId: string, text: string): Promise<unknown>;
55
+ }
5
56
 
6
57
  export interface NapcatAgentDeps {
7
- getAdapter: () => NapCatAdapter;
58
+ getEndpoint: (endpointId: string) => NapcatAgentEndpoint;
8
59
  }
9
60
 
10
- let _deps: NapcatAgentDeps | null = null;
61
+ const endpoints = new Map<string, NapcatAgentEndpoint>();
62
+ let override: NapcatAgentDeps | null = null;
11
63
 
12
- export function setNapcatAgentDeps(deps: NapcatAgentDeps): void {
13
- _deps = deps;
64
+ export function registerNapcatAgentEndpoint(
65
+ endpointId: string,
66
+ endpoint: NapcatAgentEndpoint,
67
+ ): () => void {
68
+ endpoints.set(endpointId, endpoint);
69
+ return () => {
70
+ if (endpoints.get(endpointId) === endpoint) {
71
+ endpoints.delete(endpointId);
72
+ }
73
+ };
14
74
  }
15
75
 
16
- export function getNapcatAgentDeps(): NapcatAgentDeps {
17
- if (!_deps) throw new Error('napcat agent deps not initialized');
18
- return _deps;
76
+ /** Optional override used by tests / transitional callers. Pass `null` to clear. */
77
+ export function setNapcatAgentDeps(deps: NapcatAgentDeps | null): void {
78
+ override = deps;
19
79
  }
20
80
 
21
- export function getAdapter(): NapCatAdapter {
22
- return getNapcatAgentDeps().getAdapter();
81
+ function lookup(endpointId: string): NapcatAgentEndpoint {
82
+ const registered = endpoints.get(endpointId);
83
+ if (!registered) throw new Error(`Endpoint ${endpointId} not found`);
84
+ return registered;
85
+ }
86
+
87
+ export function getNapcatAgentDeps(): NapcatAgentDeps {
88
+ if (override) return override;
89
+ return { getEndpoint: lookup };
23
90
  }
24
91
 
25
- export function getEndpoint(endpointId: string): NapCatEndpoint {
26
- const endpoint = getAdapter().endpoints.get(endpointId);
27
- if (!endpoint) throw new Error(`Endpoint ${endpointId} not found`);
28
- return endpoint;
92
+ /** Convenience for agent/tools (unchanged call sites). */
93
+ export function getEndpoint(endpointId: string): NapcatAgentEndpoint {
94
+ return getNapcatAgentDeps().getEndpoint(endpointId);
29
95
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * NapCat 入站消息治理:去重、自发过滤、消息归一化
3
3
  */
4
- import type { NapCatMessageEvent, MessageSegment } from './types.js';
4
+ import type { NapCatMessageEvent, MessageSegment } from './protocol.js';
5
5
 
6
6
  const DEDUPE_TTL_MS = 120_000;
7
7
 
@@ -24,7 +24,11 @@ export class InboundMessageDeduper {
24
24
  }
25
25
 
26
26
  /** 判断是否为 bot 自身发出的消息 */
27
- export function isSelfMessage(event: NapCatMessageEvent): boolean {
27
+ export function isSelfMessage(event: NapCatMessageEvent | {
28
+ post_type?: string;
29
+ self_id?: number | string;
30
+ user_id?: number | string;
31
+ }): boolean {
28
32
  if (event.post_type === 'message_sent') return true;
29
33
  if (event.self_id != null && event.user_id != null) {
30
34
  return Number(event.self_id) === Number(event.user_id);
@@ -44,14 +48,34 @@ export function normalizeMessage(message: MessageSegment[] | string): MessageSeg
44
48
  return [];
45
49
  }
46
50
 
51
+ const CQ_AT_RE = /\[CQ:at,qq=([^\],\s]+)\]/g;
52
+
53
+ function atTargetMatchesUin(target: unknown, uin: string): boolean {
54
+ const qq = String(target ?? '').trim();
55
+ return Boolean(qq) && qq !== 'all' && Number(qq) === Number(uin);
56
+ }
57
+
47
58
  /**
48
- * 生成用于 notice / request 事件的去重 key
59
+ * OneBot11 mentioned 判定:消息段 {type:'at', data:{qq}} 的 qq 等于本机 uin
60
+ * (事件 self_id)时为 true;`qq === 'all'` 不算。兼容 CQ 字符串形式。
49
61
  */
50
- export function resolveSideEventDedupeKey(event: any, prefix: string): string {
51
- const parts = [prefix, event.time, event.notice_type || event.request_type];
52
- if (event.group_id) parts.push(event.group_id);
53
- if (event.user_id) parts.push(event.user_id);
54
- if (event.message_id) parts.push(event.message_id);
55
- if (event.flag) parts.push(event.flag);
56
- return parts.join(':');
62
+ export function isNapCatBotMentioned(ev: {
63
+ self_id?: number | string;
64
+ message?: MessageSegment[] | string;
65
+ }): boolean {
66
+ if (ev.self_id == null) return false;
67
+ const uin = String(ev.self_id).trim();
68
+ if (!uin) return false;
69
+ const segments = normalizeMessage(ev.message ?? []);
70
+ for (const seg of segments) {
71
+ if (seg.type === 'at' && atTargetMatchesUin(seg.data?.qq, uin)) return true;
72
+ }
73
+ const text = segments
74
+ .map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
75
+ .join('');
76
+ if (!text) return false;
77
+ for (const match of text.matchAll(CQ_AT_RE)) {
78
+ if (atTargetMatchesUin(match[1], uin)) return true;
79
+ }
80
+ return false;
57
81
  }
@@ -1,4 +1,10 @@
1
- import type { MessageSegment, QuotedMessagePayload } from 'zhin.js';
1
+ export interface QuotedMessagePayload {
2
+ messageId: string;
3
+ sender?: { id: string; name: string };
4
+ content: Array<{ type: string; data?: Record<string, unknown> }>;
5
+ raw?: string;
6
+ time?: number;
7
+ }
2
8
 
3
9
  export function parseOneBotGetMsgResponse(
4
10
  messageId: string,
@@ -8,7 +14,7 @@ export function parseOneBotGetMsgResponse(
8
14
  data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
9
15
  let content: QuotedMessagePayload['content'] = [];
10
16
  if (Array.isArray(record.message)) {
11
- content = record.message as MessageSegment[];
17
+ content = record.message as QuotedMessagePayload['content'];
12
18
  } else if (typeof record.raw_message === 'string' && record.raw_message) {
13
19
  content = [{ type: 'text', data: { text: record.raw_message } }];
14
20
  }