@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.
@@ -3,14 +3,14 @@
3
3
  */
4
4
  import WebSocket from 'ws';
5
5
  import { createEndpointLifecycle, } from '@zhin.js/adapter';
6
- import { formatCompact, getLogger } from '@zhin.js/logger';
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, formatInboundTarget, formatOutboundSegments, isMessageEvent, senderNickname, senderUserId, } from './protocol.js';
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.name,
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.name, this);
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({ target, payload }) {
74
+ async send({ conversation, payload }) {
74
75
  const message = formatOutboundSegments(payload);
75
- const { action, params } = buildSendAction(target, message);
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.name,
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 target = formatInboundTarget(ev);
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
- adapter: this.#options.id,
246
- target,
246
+ conversation,
247
+ message: { conversation, id: msgId },
247
248
  content,
248
- sender: senderUserId(ev),
249
- id: msgId,
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.name,
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.name,
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
- endpointName: this.#options.config.name,
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.name,
354
+ endpoint: this.#options.config.id,
352
355
  ok: false,
353
356
  error: error.message,
354
357
  }));
@@ -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 endpointName: string;
5
+ readonly endpointId: string;
6
6
  readonly pending: Map<string, NapCatPendingAction>;
7
7
  readonly admit: (ev: NapCatEvent) => void;
8
8
  }): void;
@@ -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.endpointName,
37
+ endpoint: options.endpointId,
38
38
  error: error instanceof Error ? error.message : String(error),
39
39
  }));
40
40
  }
@@ -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({ target, payload }: {
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;
@@ -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, getLogger } from '@zhin.js/logger';
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, formatInboundTarget, formatOutboundSegments, isMessageEvent, senderNickname, senderUserId, } from './protocol.js';
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.name, this);
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.name,
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({ target, payload }) {
74
+ async send({ conversation, payload }) {
74
75
  const message = formatOutboundSegments(payload);
75
- const { action, params } = buildSendAction(target, message);
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 target = formatInboundTarget(ev);
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
- adapter: this.#options.id,
103
- target,
103
+ conversation,
104
+ message: { conversation, id: msgId },
104
105
  content: formatInboundContent(ev),
105
- sender: senderUserId(ev),
106
- id: msgId,
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
- endpointName: this.#options.config.name,
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.name,
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.2",
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.4",
47
- "@zhin.js/command": "1.0.6",
48
- "@zhin.js/core": "1.5.1",
49
- "@zhin.js/host-http": "1.0.5",
50
- "@zhin.js/logger": "1.0.75",
51
- "@zhin.js/plugin-runtime": "1.1.2"
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.2",
60
- "@zhin.js/host-http": "1.0.5"
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.4",
65
- "@zhin.js/agent": "1.1.2",
66
- "@zhin.js/core": "1.5.1",
67
- "@zhin.js/plugin-runtime": "1.1.2",
68
- "zhin.js": "6.0.1"
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 { registerDefaultScenePlatformPermitChecker } from '@zhin.js/core';
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
- // 平台权限门禁:scene_admin / scene_owner 由 sender role 判定(见各 endpoint admit metadata)
15
- return registerDefaultScenePlatformPermitChecker('napcat');
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。每项与顶层字段同构(name 必填,其余覆盖顶层)",
48
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
31
49
  "items": {
32
50
  "type": "object",
33
51
  "additionalProperties": true,
34
52
  "properties": {
35
- "name": {
36
- "type": "string",
37
- "description": "NapCat bot name"
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
- "name"
97
+ "id"
62
98
  ]
63
99
  }
64
100
  },
@@ -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, getLogger } from '@zhin.js/logger';
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.name,
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.name,
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', endpoint: this.#options.config.name }));
92
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
91
93
  }
92
94
 
93
- async send({ target, payload }: { readonly target: string; readonly payload: unknown }): Promise<string> {
95
+ async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
94
96
  const message = formatOutboundSegments(payload);
95
- const { action, params } = buildSendAction(target, message);
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.name,
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 target = formatInboundTarget(ev);
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
- adapter: this.#options.id,
144
- target,
145
+ conversation,
146
+ message: { conversation, id: msgId },
145
147
  content: formatInboundContent(ev),
146
- sender: senderUserId(ev),
147
- id: msgId,
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
- parseSendTarget,
10
+ napcatInboundConversation,
11
+ napcatOutboundTarget,
12
12
  resolveNapCatConfig,
13
13
  senderNickname,
14
14
  senderUserId,
@@ -55,20 +55,20 @@ export interface NapcatAgentEndpoint {
55
55
  }
56
56
 
57
57
  export interface NapcatAgentDeps {
58
- getEndpoint: (endpointId: string) => NapcatAgentEndpoint;
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
- endpointId: string,
65
+ endpointKey: string,
66
66
  endpoint: NapcatAgentEndpoint,
67
67
  ): () => void {
68
- endpoints.set(endpointId, endpoint);
68
+ endpoints.set(endpointKey, endpoint);
69
69
  return () => {
70
- if (endpoints.get(endpointId) === endpoint) {
71
- endpoints.delete(endpointId);
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(endpointId: string): NapcatAgentEndpoint {
82
- const registered = endpoints.get(endpointId);
83
- if (!registered) throw new Error(`Endpoint ${endpointId} not found`);
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(endpointId: string): NapcatAgentEndpoint {
94
- return getNapcatAgentDeps().getEndpoint(endpointId);
93
+ export function getEndpoint(endpointKey: string): NapcatAgentEndpoint {
94
+ return getNapcatAgentDeps().getEndpoint(endpointKey);
95
95
  }