@zhin.js/adapter-napcat 6.0.2 → 6.0.5
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.
- package/CHANGELOG.md +95 -0
- package/README.md +3 -3
- package/adapters/napcat.js +2 -2
- package/adapters/napcat.ts +2 -2
- package/lib/http-endpoint.d.ts +2 -5
- package/lib/http-endpoint.js +26 -22
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/napcat-agent-deps.d.ts +3 -3
- package/lib/napcat-agent-deps.js +9 -9
- package/lib/protocol.d.ts +12 -6
- package/lib/protocol.js +33 -26
- package/lib/ws-endpoint.d.ts +2 -5
- package/lib/ws-endpoint.js +31 -28
- package/lib/ws-transport.d.ts +1 -1
- package/lib/ws-transport.js +1 -1
- package/lib/wss-endpoint.d.ts +2 -5
- package/lib/wss-endpoint.js +24 -20
- package/package.json +16 -14
- package/plugin.js +5 -4
- package/schema.json +41 -5
- package/src/http-endpoint.ts +29 -24
- package/src/index.ts +2 -2
- package/src/napcat-agent-deps.ts +10 -10
- package/src/protocol.ts +38 -28
- package/src/ws-endpoint.ts +34 -29
- package/src/ws-transport.ts +2 -2
- package/src/wss-endpoint.ts +27 -22
- /package/commands/endpoint/add/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name:string].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [id].ts} +0 -0
package/lib/ws-endpoint.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
5
|
import { createEndpointLifecycle, } from '@zhin.js/adapter';
|
|
6
|
-
import { formatCompact,
|
|
6
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
7
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
8
8
|
import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
|
|
9
9
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
10
|
-
import { buildSendAction, buildWsConnectOptions, formatInboundContent,
|
|
10
|
+
import { buildSendAction, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
11
11
|
import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, } from './ws-transport.js';
|
|
12
|
-
const logger = getLogger('napcat');
|
|
13
12
|
export class NapCatWsEndpoint {
|
|
13
|
+
#logger;
|
|
14
14
|
#options;
|
|
15
15
|
#inboundDeduper = new InboundMessageDeduper();
|
|
16
16
|
management = createNapCatEndpointManagement(this);
|
|
@@ -21,9 +21,10 @@ export class NapCatWsEndpoint {
|
|
|
21
21
|
#open = false;
|
|
22
22
|
#unregisterAgent;
|
|
23
23
|
constructor(options) {
|
|
24
|
+
this.#logger = getAdapterLogger('napcat', options.config.id);
|
|
24
25
|
this.#options = options;
|
|
25
26
|
this.#lifecycle = createEndpointLifecycle({
|
|
26
|
-
name: options.config.
|
|
27
|
+
name: options.config.id,
|
|
27
28
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
28
29
|
reconnect: {
|
|
29
30
|
initialIntervalMs: options.config.reconnect_interval,
|
|
@@ -36,7 +37,7 @@ export class NapCatWsEndpoint {
|
|
|
36
37
|
async start() {
|
|
37
38
|
if (this.#lifecycle.started)
|
|
38
39
|
return;
|
|
39
|
-
this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.
|
|
40
|
+
this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.id, this);
|
|
40
41
|
try {
|
|
41
42
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
42
43
|
}
|
|
@@ -70,15 +71,15 @@ export class NapCatWsEndpoint {
|
|
|
70
71
|
this.#ws = undefined;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
async send({
|
|
74
|
+
async send({ conversation, payload }) {
|
|
74
75
|
const message = formatOutboundSegments(payload);
|
|
75
|
-
const { action, params } = buildSendAction(
|
|
76
|
+
const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
|
|
76
77
|
const data = await this.callApi(action, params);
|
|
77
78
|
const messageId = data?.message_id != null ? String(data.message_id) : '';
|
|
78
|
-
logger.debug(formatCompact({
|
|
79
|
+
this.#logger.debug(formatCompact({
|
|
79
80
|
op: 'napcat_send',
|
|
80
|
-
endpoint: this.#options.config.
|
|
81
|
-
target
|
|
81
|
+
endpoint: this.#options.config.id,
|
|
82
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
82
83
|
messageId,
|
|
83
84
|
}));
|
|
84
85
|
return messageId;
|
|
@@ -237,31 +238,34 @@ export class NapCatWsEndpoint {
|
|
|
237
238
|
if (Array.isArray(ev.message) || typeof ev.message === 'string') {
|
|
238
239
|
ev = { ...ev, message: normalizeMessage(ev.message) };
|
|
239
240
|
}
|
|
240
|
-
const
|
|
241
|
+
const conversation = napcatInboundConversation(String(this.#options.id), ev);
|
|
241
242
|
const content = formatInboundContent(ev);
|
|
242
243
|
const nickname = senderNickname(ev);
|
|
243
244
|
const mentioned = isNapCatBotMentioned(ev);
|
|
244
245
|
void this.#options.gateway.receive({
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
conversation,
|
|
247
|
+
message: { conversation, id: msgId },
|
|
247
248
|
content,
|
|
248
|
-
sender:
|
|
249
|
-
|
|
249
|
+
sender: {
|
|
250
|
+
id: senderUserId(ev),
|
|
251
|
+
name: nickname,
|
|
252
|
+
...(ev.sender?.role ? { roles: [ev.sender.role] } : {}),
|
|
253
|
+
},
|
|
254
|
+
endpointId: this.#options.config.id,
|
|
255
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
250
256
|
metadata: Object.freeze({
|
|
251
257
|
message_type: ev.message_type,
|
|
252
258
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
253
259
|
group_id: ev.group_id != null ? String(ev.group_id) : undefined,
|
|
254
|
-
endpoint: this.#options.config.name,
|
|
255
260
|
time: ev.time,
|
|
256
261
|
self_id: ev.self_id != null ? String(ev.self_id) : undefined,
|
|
257
262
|
role: ev.sender?.role,
|
|
258
263
|
...(nickname ? { nickname } : {}),
|
|
259
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
260
264
|
}),
|
|
261
265
|
}).catch((err) => {
|
|
262
|
-
logger.warn(formatCompact({
|
|
266
|
+
this.#logger.warn(formatCompact({
|
|
263
267
|
op: 'napcat_gateway_receive_failed',
|
|
264
|
-
target
|
|
268
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
265
269
|
error: err instanceof Error ? err.message : String(err),
|
|
266
270
|
}));
|
|
267
271
|
});
|
|
@@ -287,14 +291,14 @@ export class NapCatWsEndpoint {
|
|
|
287
291
|
return;
|
|
288
292
|
settled = true;
|
|
289
293
|
if (!this.#options.config.access_token) {
|
|
290
|
-
logger.warn(formatCompact({
|
|
291
|
-
endpoint: this.#options.config.
|
|
294
|
+
this.#logger.warn(formatCompact({
|
|
295
|
+
endpoint: this.#options.config.id,
|
|
292
296
|
ok: false,
|
|
293
297
|
error: 'missing access_token',
|
|
294
298
|
}));
|
|
295
299
|
}
|
|
296
|
-
logger.debug(formatCompact({
|
|
297
|
-
endpoint: this.#options.config.
|
|
300
|
+
this.#logger.debug(formatCompact({
|
|
301
|
+
endpoint: this.#options.config.id,
|
|
298
302
|
mode: 'ws',
|
|
299
303
|
url: safeUrl,
|
|
300
304
|
}));
|
|
@@ -313,7 +317,7 @@ export class NapCatWsEndpoint {
|
|
|
313
317
|
});
|
|
314
318
|
ws.on('message', (data) => {
|
|
315
319
|
handleNapCatWsMessage(data, {
|
|
316
|
-
|
|
320
|
+
endpointId: this.#options.config.id,
|
|
317
321
|
pending: this.#pending,
|
|
318
322
|
admit: (event) => this.admit(event),
|
|
319
323
|
});
|
|
@@ -330,9 +334,8 @@ export class NapCatWsEndpoint {
|
|
|
330
334
|
: codeNum === 1006
|
|
331
335
|
? ' [abnormal]'
|
|
332
336
|
: '';
|
|
333
|
-
logger.warn(formatCompact({
|
|
337
|
+
this.#logger.warn(formatCompact({
|
|
334
338
|
op: 'disconnect',
|
|
335
|
-
endpoint: this.#options.config.name,
|
|
336
339
|
code: codeNum,
|
|
337
340
|
error: `${reasonStr || 'closed'}${codeHint}`,
|
|
338
341
|
reconnect_ms: this.#options.config.reconnect_interval,
|
|
@@ -346,9 +349,9 @@ export class NapCatWsEndpoint {
|
|
|
346
349
|
});
|
|
347
350
|
ws.on('error', (err) => {
|
|
348
351
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
349
|
-
logger.warn(formatCompact({
|
|
352
|
+
this.#logger.warn(formatCompact({
|
|
350
353
|
op: 'ws_error',
|
|
351
|
-
endpoint: this.#options.config.
|
|
354
|
+
endpoint: this.#options.config.id,
|
|
352
355
|
ok: false,
|
|
353
356
|
error: error.message,
|
|
354
357
|
}));
|
package/lib/ws-transport.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type NapCatEvent } from './protocol.js';
|
|
|
2
2
|
import { type NapCatPendingAction, type NapCatWsSocket } from './ws-types.js';
|
|
3
3
|
export declare function decodeWsPayload(data: unknown): string;
|
|
4
4
|
export declare function handleNapCatWsMessage(data: unknown, options: {
|
|
5
|
-
readonly
|
|
5
|
+
readonly endpointId: string;
|
|
6
6
|
readonly pending: Map<string, NapCatPendingAction>;
|
|
7
7
|
readonly admit: (ev: NapCatEvent) => void;
|
|
8
8
|
}): void;
|
package/lib/ws-transport.js
CHANGED
|
@@ -34,7 +34,7 @@ export function handleNapCatWsMessage(data, options) {
|
|
|
34
34
|
catch (error) {
|
|
35
35
|
logger.warn(formatCompact({
|
|
36
36
|
op: 'napcat_parse_failed',
|
|
37
|
-
endpoint: options.
|
|
37
|
+
endpoint: options.endpointId,
|
|
38
38
|
error: error instanceof Error ? error.message : String(error),
|
|
39
39
|
}));
|
|
40
40
|
}
|
package/lib/wss-endpoint.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
1
|
+
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
2
2
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
@@ -17,10 +17,7 @@ export declare class NapCatWssEndpoint implements EndpointInstance {
|
|
|
17
17
|
open(): void;
|
|
18
18
|
close(): void;
|
|
19
19
|
stop(): Promise<void>;
|
|
20
|
-
send({
|
|
21
|
-
readonly target: string;
|
|
22
|
-
readonly payload: unknown;
|
|
23
|
-
}): Promise<string>;
|
|
20
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
24
21
|
recallMessage(messageId: string): Promise<void>;
|
|
25
22
|
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
26
23
|
admit(ev: NapCatEvent): void;
|
package/lib/wss-endpoint.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
|
|
3
3
|
*/
|
|
4
4
|
import { clearInterval } from 'node:timers';
|
|
5
|
-
import { formatCompact,
|
|
5
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
7
7
|
import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
|
|
8
8
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
9
|
-
import { buildSendAction, formatInboundContent,
|
|
9
|
+
import { buildSendAction, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
10
10
|
import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, startNapCatHeartbeat, } from './ws-transport.js';
|
|
11
11
|
import { verifyNapCatAccessToken } from './wss-auth.js';
|
|
12
|
-
const logger = getLogger('napcat');
|
|
13
12
|
export class NapCatWssEndpoint {
|
|
13
|
+
#logger;
|
|
14
14
|
#options;
|
|
15
15
|
#inboundDeduper = new InboundMessageDeduper();
|
|
16
16
|
management = createNapCatEndpointManagement(this);
|
|
@@ -23,20 +23,21 @@ export class NapCatWssEndpoint {
|
|
|
23
23
|
#started = false;
|
|
24
24
|
#unregisterAgent;
|
|
25
25
|
constructor(options) {
|
|
26
|
+
this.#logger = getAdapterLogger('napcat', options.config.id);
|
|
26
27
|
this.#options = options;
|
|
27
28
|
}
|
|
28
29
|
async start() {
|
|
29
30
|
if (this.#started)
|
|
30
31
|
return;
|
|
31
32
|
this.#started = true;
|
|
32
|
-
this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.
|
|
33
|
+
this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.id, this);
|
|
33
34
|
const handle = this.#options.http.ws(this.#options.config.path);
|
|
34
35
|
this.#wsRelease = handle.onConnection((connection) => {
|
|
35
36
|
this.#acceptConnection(connection);
|
|
36
37
|
});
|
|
37
|
-
logger.info(formatCompact({
|
|
38
|
+
this.#logger.info(formatCompact({
|
|
38
39
|
op: 'listen',
|
|
39
|
-
endpoint: this.#options.config.
|
|
40
|
+
endpoint: this.#options.config.id,
|
|
40
41
|
mode: 'wss',
|
|
41
42
|
path: this.#options.config.path,
|
|
42
43
|
}));
|
|
@@ -70,9 +71,9 @@ export class NapCatWssEndpoint {
|
|
|
70
71
|
}
|
|
71
72
|
this.#started = false;
|
|
72
73
|
}
|
|
73
|
-
async send({
|
|
74
|
+
async send({ conversation, payload }) {
|
|
74
75
|
const message = formatOutboundSegments(payload);
|
|
75
|
-
const { action, params } = buildSendAction(
|
|
76
|
+
const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
|
|
76
77
|
const data = await this.callApi(action, params);
|
|
77
78
|
return data?.message_id != null ? String(data.message_id) : '';
|
|
78
79
|
}
|
|
@@ -95,30 +96,33 @@ export class NapCatWssEndpoint {
|
|
|
95
96
|
if (Array.isArray(ev.message) || typeof ev.message === 'string') {
|
|
96
97
|
ev = { ...ev, message: normalizeMessage(ev.message) };
|
|
97
98
|
}
|
|
98
|
-
const
|
|
99
|
+
const conversation = napcatInboundConversation(String(this.#options.id), ev);
|
|
99
100
|
const nickname = senderNickname(ev);
|
|
100
101
|
const mentioned = isNapCatBotMentioned(ev);
|
|
101
102
|
void this.#options.gateway.receive({
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
conversation,
|
|
104
|
+
message: { conversation, id: msgId },
|
|
104
105
|
content: formatInboundContent(ev),
|
|
105
|
-
sender:
|
|
106
|
-
|
|
106
|
+
sender: {
|
|
107
|
+
id: senderUserId(ev),
|
|
108
|
+
name: nickname,
|
|
109
|
+
...(ev.sender?.role ? { roles: [ev.sender.role] } : {}),
|
|
110
|
+
},
|
|
111
|
+
endpointId: this.#options.config.id,
|
|
112
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
107
113
|
metadata: Object.freeze({
|
|
108
114
|
message_type: ev.message_type,
|
|
109
115
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
110
116
|
group_id: ev.group_id != null ? String(ev.group_id) : undefined,
|
|
111
|
-
endpoint: this.#options.config.name,
|
|
112
117
|
time: ev.time,
|
|
113
118
|
self_id: ev.self_id != null ? String(ev.self_id) : undefined,
|
|
114
119
|
role: ev.sender?.role,
|
|
115
120
|
...(nickname ? { nickname } : {}),
|
|
116
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
117
121
|
}),
|
|
118
122
|
}).catch((err) => {
|
|
119
|
-
logger.warn(formatCompact({
|
|
123
|
+
this.#logger.warn(formatCompact({
|
|
120
124
|
op: 'napcat_gateway_receive_failed',
|
|
121
|
-
target
|
|
125
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
122
126
|
error: err instanceof Error ? err.message : String(err),
|
|
123
127
|
}));
|
|
124
128
|
});
|
|
@@ -141,7 +145,7 @@ export class NapCatWssEndpoint {
|
|
|
141
145
|
this.#heartbeatTimer = startNapCatHeartbeat(this.#ws, this.#options.config.heartbeat_interval, this.#heartbeatTimer);
|
|
142
146
|
socket.on('message', (data) => {
|
|
143
147
|
handleNapCatWsMessage(data, {
|
|
144
|
-
|
|
148
|
+
endpointId: this.#options.config.id,
|
|
145
149
|
pending: this.#pending,
|
|
146
150
|
admit: (event) => this.admit(event),
|
|
147
151
|
});
|
|
@@ -155,8 +159,8 @@ export class NapCatWssEndpoint {
|
|
|
155
159
|
}
|
|
156
160
|
}
|
|
157
161
|
});
|
|
158
|
-
logger.debug(formatCompact({
|
|
159
|
-
endpoint: this.#options.config.
|
|
162
|
+
this.#logger.debug(formatCompact({
|
|
163
|
+
endpoint: this.#options.config.id,
|
|
160
164
|
mode: 'wss',
|
|
161
165
|
peer: connection.request.socket.remoteAddress,
|
|
162
166
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-napcat",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.5",
|
|
4
4
|
"description": "Zhin.js NapCat adapter for Plugin Runtime (WebSocket client, OneBot11 + NapCat extensions)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -43,12 +43,14 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"ws": "^8.21.1",
|
|
46
|
-
"@zhin.js/adapter": "1.1.
|
|
47
|
-
"@zhin.js/command": "1.0.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/host-http": "1.0.
|
|
50
|
-
"@zhin.js/
|
|
51
|
-
"@zhin.js/
|
|
46
|
+
"@zhin.js/adapter": "1.1.7",
|
|
47
|
+
"@zhin.js/command": "1.0.9",
|
|
48
|
+
"@zhin.js/core": "1.5.4",
|
|
49
|
+
"@zhin.js/host-http": "1.0.8",
|
|
50
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
51
|
+
"@zhin.js/logger": "1.0.76",
|
|
52
|
+
"@zhin.js/permission": "1.0.1",
|
|
53
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
56
|
"@types/node": "^26.1.2",
|
|
@@ -56,16 +58,16 @@
|
|
|
56
58
|
"typescript": "^6.0.3",
|
|
57
59
|
"vitest": "^4.1.10",
|
|
58
60
|
"zod": "^4.4.3",
|
|
59
|
-
"@zhin.js/agent": "1.1.
|
|
60
|
-
"@zhin.js/host-http": "1.0.
|
|
61
|
+
"@zhin.js/agent": "1.1.5",
|
|
62
|
+
"@zhin.js/host-http": "1.0.8"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
65
|
"zod": "^4.0.0",
|
|
64
|
-
"@zhin.js/adapter": "1.1.
|
|
65
|
-
"@zhin.js/agent": "1.1.
|
|
66
|
-
"@zhin.js/core": "1.5.
|
|
67
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
68
|
-
"zhin.js": "6.0.
|
|
66
|
+
"@zhin.js/adapter": "1.1.7",
|
|
67
|
+
"@zhin.js/agent": "1.1.5",
|
|
68
|
+
"@zhin.js/core": "1.5.4",
|
|
69
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
70
|
+
"zhin.js": "6.0.4"
|
|
69
71
|
},
|
|
70
72
|
"peerDependenciesMeta": {
|
|
71
73
|
"zhin.js": {
|
package/plugin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { createEndpointRuntimeState } from '@zhin.js/adapter';
|
|
3
3
|
import { definePlugin } from '@zhin.js/plugin-runtime';
|
|
4
|
-
import {
|
|
4
|
+
import { permissionHostToken, createSceneRolePlatformChecker } from '@zhin.js/permission';
|
|
5
5
|
import { napcatRuntimeStateToken } from "./lib/napcat-runtime-state.js";
|
|
6
6
|
export default definePlugin({
|
|
7
7
|
name: 'napcat',
|
|
@@ -9,9 +9,10 @@ export default definePlugin({
|
|
|
9
9
|
displayName: 'NapCat Adapter',
|
|
10
10
|
},
|
|
11
11
|
setup(context) {
|
|
12
|
-
// 运行中 endpoint 注册表(napcat.endpoint list 的"运行中"数据源)
|
|
13
12
|
context.resources.provide(napcatRuntimeStateToken, createEndpointRuntimeState());
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (context.resources.has(permissionHostToken)) {
|
|
14
|
+
const host = context.resources.use(permissionHostToken);
|
|
15
|
+
return host.registerPlatform('napcat', createSceneRolePlatformChecker());
|
|
16
|
+
}
|
|
16
17
|
},
|
|
17
18
|
});
|
package/schema.json
CHANGED
|
@@ -25,16 +25,48 @@
|
|
|
25
25
|
"type": "number",
|
|
26
26
|
"default": 30000
|
|
27
27
|
},
|
|
28
|
+
"master": {
|
|
29
|
+
"type": [
|
|
30
|
+
"string",
|
|
31
|
+
"number"
|
|
32
|
+
],
|
|
33
|
+
"description": "框架 master(QQ uin;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
34
|
+
},
|
|
35
|
+
"trusted": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": [
|
|
39
|
+
"string",
|
|
40
|
+
"number"
|
|
41
|
+
],
|
|
42
|
+
"description": "Trusted QQ uin"
|
|
43
|
+
},
|
|
44
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
45
|
+
},
|
|
28
46
|
"endpoints": {
|
|
29
47
|
"type": "array",
|
|
30
|
-
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(
|
|
48
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
31
49
|
"items": {
|
|
32
50
|
"type": "object",
|
|
33
51
|
"additionalProperties": true,
|
|
34
52
|
"properties": {
|
|
35
|
-
"
|
|
36
|
-
"type":
|
|
37
|
-
|
|
53
|
+
"master": {
|
|
54
|
+
"type": [
|
|
55
|
+
"string",
|
|
56
|
+
"number"
|
|
57
|
+
],
|
|
58
|
+
"description": "本 endpoint 的框架 master(QQ uin);覆盖顶层 master"
|
|
59
|
+
},
|
|
60
|
+
"trusted": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": {
|
|
63
|
+
"type": [
|
|
64
|
+
"string",
|
|
65
|
+
"number"
|
|
66
|
+
],
|
|
67
|
+
"description": "Trusted QQ uin"
|
|
68
|
+
},
|
|
69
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
38
70
|
},
|
|
39
71
|
"url": {
|
|
40
72
|
"type": "string",
|
|
@@ -55,10 +87,14 @@
|
|
|
55
87
|
"access_token": {
|
|
56
88
|
"type": "string",
|
|
57
89
|
"description": "NapCat access token"
|
|
90
|
+
},
|
|
91
|
+
"id": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"description": "NapCat bot name"
|
|
58
94
|
}
|
|
59
95
|
},
|
|
60
96
|
"required": [
|
|
61
|
-
"
|
|
97
|
+
"id"
|
|
62
98
|
]
|
|
63
99
|
}
|
|
64
100
|
},
|
package/src/http-endpoint.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* NapCat HTTP endpoint — POST inbound events + HTTP API outbound.
|
|
3
3
|
*/
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
-
import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
5
|
+
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
6
6
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
7
7
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
8
|
-
import { formatCompact,
|
|
8
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
9
9
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
10
10
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
11
11
|
import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
|
|
@@ -19,9 +19,10 @@ import {
|
|
|
19
19
|
buildSendAction,
|
|
20
20
|
callNapCatHttpAction,
|
|
21
21
|
formatInboundContent,
|
|
22
|
-
formatInboundTarget,
|
|
23
22
|
formatOutboundSegments,
|
|
24
23
|
isMessageEvent,
|
|
24
|
+
napcatInboundConversation,
|
|
25
|
+
napcatOutboundTarget,
|
|
25
26
|
senderNickname,
|
|
26
27
|
senderUserId,
|
|
27
28
|
type NapCatEvent,
|
|
@@ -31,8 +32,6 @@ import { readRequestBody } from './webhook.js';
|
|
|
31
32
|
import { NapCatWsEndpoint } from './ws-endpoint.js';
|
|
32
33
|
import { verifyNapCatAccessToken } from './wss-auth.js';
|
|
33
34
|
|
|
34
|
-
const logger = getLogger('napcat');
|
|
35
|
-
|
|
36
35
|
export interface NapCatHttpEndpointOptions {
|
|
37
36
|
readonly id: CapabilityId;
|
|
38
37
|
readonly gateway: MessageGateway;
|
|
@@ -42,6 +41,8 @@ export interface NapCatHttpEndpointOptions {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
export class NapCatHttpEndpoint implements EndpointInstance {
|
|
44
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
45
|
+
|
|
45
46
|
readonly #options: NapCatHttpEndpointOptions;
|
|
46
47
|
readonly #inboundDeduper = new InboundMessageDeduper();
|
|
47
48
|
readonly management: EndpointManagement = createNapCatEndpointManagement(this);
|
|
@@ -52,6 +53,7 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
52
53
|
#unregisterAgent?: () => void;
|
|
53
54
|
|
|
54
55
|
constructor(options: NapCatHttpEndpointOptions) {
|
|
56
|
+
this.#logger = getAdapterLogger('napcat', options.config.id);
|
|
55
57
|
this.#options = options;
|
|
56
58
|
this.#callHttpAction = options.callHttpAction ?? callNapCatHttpAction;
|
|
57
59
|
}
|
|
@@ -60,13 +62,13 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
60
62
|
if (this.#started) return;
|
|
61
63
|
this.#started = true;
|
|
62
64
|
this.#unregisterAgent = registerNapcatAgentEndpoint(
|
|
63
|
-
this.#options.config.
|
|
65
|
+
this.#options.config.id,
|
|
64
66
|
this as unknown as NapCatWsEndpoint,
|
|
65
67
|
);
|
|
66
68
|
this.#setupRoutes();
|
|
67
|
-
logger.info(formatCompact({
|
|
69
|
+
this.#logger.info(formatCompact({
|
|
68
70
|
op: 'listen',
|
|
69
|
-
endpoint: this.#options.config.
|
|
71
|
+
endpoint: this.#options.config.id,
|
|
70
72
|
mode: 'http',
|
|
71
73
|
path: this.#options.config.post_path,
|
|
72
74
|
}));
|
|
@@ -87,12 +89,12 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
87
89
|
this.#unregisterAgent = undefined;
|
|
88
90
|
this.#inboundDeduper.clear();
|
|
89
91
|
this.#started = false;
|
|
90
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
92
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
async send({
|
|
95
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
94
96
|
const message = formatOutboundSegments(payload);
|
|
95
|
-
const { action, params } = buildSendAction(
|
|
97
|
+
const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
|
|
96
98
|
const resp = await this.#callHttpAction(
|
|
97
99
|
{
|
|
98
100
|
http_url: this.#options.config.http_url,
|
|
@@ -103,10 +105,10 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
103
105
|
);
|
|
104
106
|
const data = resp.data as { message_id?: number | string } | undefined;
|
|
105
107
|
const messageId = data?.message_id != null ? String(data.message_id) : '';
|
|
106
|
-
logger.debug(formatCompact({
|
|
108
|
+
this.#logger.debug(formatCompact({
|
|
107
109
|
op: 'napcat_send',
|
|
108
|
-
endpoint: this.#options.config.
|
|
109
|
-
target
|
|
110
|
+
endpoint: this.#options.config.id,
|
|
111
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
110
112
|
messageId,
|
|
111
113
|
}));
|
|
112
114
|
return messageId;
|
|
@@ -136,30 +138,33 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
136
138
|
if (Array.isArray(ev.message) || typeof ev.message === 'string') {
|
|
137
139
|
ev = { ...ev, message: normalizeMessage(ev.message) };
|
|
138
140
|
}
|
|
139
|
-
const
|
|
141
|
+
const conversation = napcatInboundConversation(String(this.#options.id), ev);
|
|
140
142
|
const nickname = senderNickname(ev);
|
|
141
143
|
const mentioned = isNapCatBotMentioned(ev);
|
|
142
144
|
void this.#options.gateway.receive({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
conversation,
|
|
146
|
+
message: { conversation, id: msgId },
|
|
145
147
|
content: formatInboundContent(ev),
|
|
146
|
-
sender:
|
|
147
|
-
|
|
148
|
+
sender: {
|
|
149
|
+
id: senderUserId(ev),
|
|
150
|
+
name: nickname,
|
|
151
|
+
...(ev.sender?.role ? { roles: [ev.sender.role] } : {}),
|
|
152
|
+
},
|
|
153
|
+
endpointId: this.#options.config.id,
|
|
154
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
148
155
|
metadata: Object.freeze({
|
|
149
156
|
message_type: ev.message_type,
|
|
150
157
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
151
158
|
group_id: ev.group_id != null ? String(ev.group_id) : undefined,
|
|
152
|
-
endpoint: this.#options.config.name,
|
|
153
159
|
time: ev.time,
|
|
154
160
|
self_id: ev.self_id != null ? String(ev.self_id) : undefined,
|
|
155
161
|
role: ev.sender?.role,
|
|
156
162
|
...(nickname ? { nickname } : {}),
|
|
157
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
158
163
|
}),
|
|
159
164
|
}).catch((err) => {
|
|
160
|
-
logger.warn(formatCompact({
|
|
165
|
+
this.#logger.warn(formatCompact({
|
|
161
166
|
op: 'napcat_gateway_receive_failed',
|
|
162
|
-
target
|
|
167
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
163
168
|
error: err instanceof Error ? err.message : String(err),
|
|
164
169
|
}));
|
|
165
170
|
});
|
|
@@ -194,7 +199,7 @@ export class NapCatHttpEndpoint implements EndpointInstance {
|
|
|
194
199
|
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
195
200
|
response.end(JSON.stringify({ status: 'ok' }));
|
|
196
201
|
} catch (error) {
|
|
197
|
-
logger.error('NapCat HTTP webhook error:', error);
|
|
202
|
+
this.#logger.error('NapCat HTTP webhook error:', error);
|
|
198
203
|
if (!response.headersSent) {
|
|
199
204
|
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
200
205
|
response.end(JSON.stringify({ message: 'Internal Server Error' }));
|
package/src/index.ts
CHANGED
|
@@ -3,12 +3,12 @@ export {
|
|
|
3
3
|
buildWsConnectOptions,
|
|
4
4
|
callNapCatHttpAction,
|
|
5
5
|
formatInboundContent,
|
|
6
|
-
formatInboundTarget,
|
|
7
6
|
formatOutboundSegments,
|
|
8
7
|
getChannelId,
|
|
9
8
|
isMessageEvent,
|
|
10
9
|
mediaRefToOneBotFile,
|
|
11
|
-
|
|
10
|
+
napcatInboundConversation,
|
|
11
|
+
napcatOutboundTarget,
|
|
12
12
|
resolveNapCatConfig,
|
|
13
13
|
senderNickname,
|
|
14
14
|
senderUserId,
|
package/src/napcat-agent-deps.ts
CHANGED
|
@@ -55,20 +55,20 @@ export interface NapcatAgentEndpoint {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export interface NapcatAgentDeps {
|
|
58
|
-
getEndpoint: (
|
|
58
|
+
getEndpoint: (endpointKey: string) => NapcatAgentEndpoint;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const endpoints = new Map<string, NapcatAgentEndpoint>();
|
|
62
62
|
let override: NapcatAgentDeps | null = null;
|
|
63
63
|
|
|
64
64
|
export function registerNapcatAgentEndpoint(
|
|
65
|
-
|
|
65
|
+
endpointKey: string,
|
|
66
66
|
endpoint: NapcatAgentEndpoint,
|
|
67
67
|
): () => void {
|
|
68
|
-
endpoints.set(
|
|
68
|
+
endpoints.set(endpointKey, endpoint);
|
|
69
69
|
return () => {
|
|
70
|
-
if (endpoints.get(
|
|
71
|
-
endpoints.delete(
|
|
70
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
71
|
+
endpoints.delete(endpointKey);
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
74
|
}
|
|
@@ -78,9 +78,9 @@ export function setNapcatAgentDeps(deps: NapcatAgentDeps | null): void {
|
|
|
78
78
|
override = deps;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function lookup(
|
|
82
|
-
const registered = endpoints.get(
|
|
83
|
-
if (!registered) throw new Error(`Endpoint ${
|
|
81
|
+
function lookup(endpointKey: string): NapcatAgentEndpoint {
|
|
82
|
+
const registered = endpoints.get(endpointKey);
|
|
83
|
+
if (!registered) throw new Error(`Endpoint ${endpointKey} not found`);
|
|
84
84
|
return registered;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -90,6 +90,6 @@ export function getNapcatAgentDeps(): NapcatAgentDeps {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/** Convenience for agent/tools (unchanged call sites). */
|
|
93
|
-
export function getEndpoint(
|
|
94
|
-
return getNapcatAgentDeps().getEndpoint(
|
|
93
|
+
export function getEndpoint(endpointKey: string): NapcatAgentEndpoint {
|
|
94
|
+
return getNapcatAgentDeps().getEndpoint(endpointKey);
|
|
95
95
|
}
|