@zhin.js/adapter-napcat 6.0.14 → 7.0.1

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 (60) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +18 -3
  3. package/adapters/napcat.js +2 -8
  4. package/adapters/napcat.ts +2 -8
  5. package/agent/tools/ai_tts.ts +4 -6
  6. package/agent/tools/del_group_notice.ts +4 -6
  7. package/agent/tools/delete_essence_msg.ts +4 -6
  8. package/agent/tools/delete_friend.ts +4 -6
  9. package/agent/tools/download_file.ts +4 -6
  10. package/agent/tools/forward_single_msg.ts +4 -8
  11. package/agent/tools/get_ai_characters.ts +4 -6
  12. package/agent/tools/get_essence_list.ts +4 -6
  13. package/agent/tools/get_friend_msg_history.ts +4 -6
  14. package/agent/tools/get_group_file_url.ts +4 -6
  15. package/agent/tools/get_group_info_ex.ts +4 -6
  16. package/agent/tools/get_group_msg_history.ts +4 -6
  17. package/agent/tools/get_group_notice.ts +4 -6
  18. package/agent/tools/get_group_root_files.ts +4 -6
  19. package/agent/tools/get_group_shut_list.ts +4 -6
  20. package/agent/tools/get_mini_app_ark.ts +4 -6
  21. package/agent/tools/get_user_status.ts +4 -6
  22. package/agent/tools/group_sign.ts +4 -6
  23. package/agent/tools/mark_msg_as_read.ts +4 -6
  24. package/agent/tools/ocr_image.ts +4 -6
  25. package/agent/tools/send_forward_msg.ts +4 -8
  26. package/agent/tools/send_group_notice.ts +4 -6
  27. package/agent/tools/send_like.ts +4 -6
  28. package/agent/tools/send_poke.ts +4 -6
  29. package/agent/tools/set_avatar.ts +4 -6
  30. package/agent/tools/set_emoji_reaction.ts +4 -6
  31. package/agent/tools/set_essence_msg.ts +4 -6
  32. package/agent/tools/set_group_portrait.ts +4 -6
  33. package/agent/tools/set_online_status.ts +4 -6
  34. package/agent/tools/set_profile.ts +4 -6
  35. package/agent/tools/set_signature.ts +4 -6
  36. package/agent/tools/set_title.ts +4 -6
  37. package/agent/tools/translate.ts +4 -6
  38. package/agent/tools/upload_group_file.ts +4 -6
  39. package/lib/{napcat-agent-deps.d.ts → client.d.ts} +15 -14
  40. package/lib/client.js +62 -0
  41. package/lib/http-endpoint.d.ts +6 -6
  42. package/lib/http-endpoint.js +24 -12
  43. package/lib/index.d.ts +1 -1
  44. package/lib/index.js +1 -1
  45. package/lib/napcat-endpoint-commands.d.ts +1 -1
  46. package/lib/side-event-dispatch.d.ts +2 -2
  47. package/lib/side-event-dispatch.js +3 -3
  48. package/lib/ws-endpoint.d.ts +6 -41
  49. package/lib/ws-endpoint.js +26 -157
  50. package/lib/wss-endpoint.d.ts +6 -6
  51. package/lib/wss-endpoint.js +25 -13
  52. package/package.json +15 -14
  53. package/src/client.ts +79 -0
  54. package/src/http-endpoint.ts +31 -20
  55. package/src/index.ts +4 -7
  56. package/src/side-event-dispatch.ts +4 -4
  57. package/src/ws-endpoint.ts +29 -202
  58. package/src/wss-endpoint.ts +32 -21
  59. package/lib/napcat-agent-deps.js +0 -33
  60. package/src/napcat-agent-deps.ts +0 -95
@@ -1,24 +1,28 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
2
+ import { createRecallEndpointControl, } from 'zhin.js/adapter';
1
3
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
2
4
  import { createNapCatEndpointManagement } from './endpoint-management.js';
3
- import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
4
5
  import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
5
6
  import { buildSendAction, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
6
7
  import { receiveNapCatSideEvent } from './side-event-dispatch.js';
7
8
  import { createNapCatContentPort } from './onebot-get-msg.js';
9
+ import { NapcatClient } from './client.js';
8
10
  import { readRequestBody } from './webhook.js';
9
11
  import { verifyNapCatAccessToken } from './wss-auth.js';
10
- export class NapCatHttpEndpoint {
12
+ export class NapCatHttpEndpoint extends Endpoint {
13
+ client = new NapcatClient((action, params) => this.#callApi(action, params));
11
14
  #logger;
12
15
  #options;
13
16
  #inboundDeduper = new InboundMessageDeduper();
14
- management = createNapCatEndpointManagement(this);
15
- content = createNapCatContentPort((action, params) => this.callApi(action, params));
17
+ management = createNapCatEndpointManagement(this.client);
18
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
19
+ content = createNapCatContentPort((action, params) => this.client.callApi(action, params));
16
20
  #callHttpAction;
17
21
  #routeReleases = [];
18
22
  #open = false;
19
23
  #started = false;
20
- #unregisterAgent;
21
24
  constructor(options) {
25
+ super();
22
26
  this.#logger = getAdapterLogger('napcat', options.config.id);
23
27
  this.#options = options;
24
28
  this.#callHttpAction = options.callHttpAction ?? callNapCatHttpAction;
@@ -27,7 +31,6 @@ export class NapCatHttpEndpoint {
27
31
  if (this.#started)
28
32
  return;
29
33
  this.#started = true;
30
- this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.id, this);
31
34
  this.#setupRoutes();
32
35
  this.#logger.info(formatCompact({
33
36
  op: 'listen',
@@ -46,8 +49,6 @@ export class NapCatHttpEndpoint {
46
49
  this.#open = false;
47
50
  for (const release of this.#routeReleases.splice(0))
48
51
  release();
49
- this.#unregisterAgent?.();
50
- this.#unregisterAgent = undefined;
51
52
  this.#inboundDeduper.clear();
52
53
  this.#started = false;
53
54
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
@@ -72,9 +73,9 @@ export class NapCatHttpEndpoint {
72
73
  async recallMessage(messageId) {
73
74
  if (!messageId)
74
75
  return;
75
- await this.callApi('delete_msg', { message_id: Number(messageId) });
76
+ await this.client.callApi('delete_msg', { message_id: Number(messageId) });
76
77
  }
77
- callApi(action, params = {}) {
78
+ #callApi(action, params = {}) {
78
79
  return this.#callHttpAction({
79
80
  http_url: this.#options.config.http_url,
80
81
  access_token: this.#options.config.access_token,
@@ -83,8 +84,14 @@ export class NapCatHttpEndpoint {
83
84
  admit(ev) {
84
85
  if (!this.#open)
85
86
  return;
87
+ void this.emitPlatform(napCatPlatformEventName(ev), ev).catch((error) => {
88
+ this.#logger.warn(formatCompact({
89
+ op: 'napcat_platform_event_failed',
90
+ error: error instanceof Error ? error.message : String(error),
91
+ }));
92
+ });
86
93
  if (!isMessageEvent(ev)) {
87
- receiveNapCatSideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
94
+ receiveNapCatSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, this.client, ev, this.#logger);
88
95
  return;
89
96
  }
90
97
  if (isSelfMessage(ev))
@@ -98,7 +105,7 @@ export class NapCatHttpEndpoint {
98
105
  const conversation = napcatInboundConversation(String(this.#options.id), ev);
99
106
  const nickname = senderNickname(ev);
100
107
  const mentioned = isNapCatBotMentioned(ev);
101
- void this.#options.gateway.receive({
108
+ void this.emit('message.receive', {
102
109
  conversation,
103
110
  message: { conversation, id: msgId },
104
111
  content: formatInboundContent(ev),
@@ -163,3 +170,8 @@ export class NapCatHttpEndpoint {
163
170
  }
164
171
  }
165
172
  }
173
+ function napCatPlatformEventName(ev) {
174
+ return [ev.post_type, ev.message_type ?? ev.notice_type ?? ev.request_type, ev.sub_type]
175
+ .filter(Boolean)
176
+ .join('.');
177
+ }
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, type MessageSegment, type NapCatActionRequest, type NapCatActionResponse, type NapCatAdapterConfig, type NapCatConfigBase, type NapCatEndpointConfig, type NapCatEvent, type NapCatHttpConfig, type NapCatMessageEvent, type NapCatSender, type NapCatWireSegment, type NapCatWsConfig, type NapCatWssConfig, type ParsedSendTarget, type ResolvedNapCatConfig, } from './protocol.js';
2
2
  export { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
3
- export { getEndpoint, getNapcatAgentDeps, registerNapcatAgentEndpoint, setNapcatAgentDeps, type NapcatAgentDeps, type NapcatAgentEndpoint, } from './napcat-agent-deps.js';
3
+ export { napcatClient, type NapcatClient, type NapcatClientEventMap, } from './client.js';
4
4
  export { parseOneBotGetMsgResponse } from './onebot-get-msg.js';
5
5
  export { NapCatWsEndpoint, type NapCatWsEndpointOptions, } from './ws-endpoint.js';
6
6
  export { NapCatWssEndpoint, type NapCatWssEndpointOptions, } from './wss-endpoint.js';
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, } from './protocol.js';
2
2
  export { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
3
- export { getEndpoint, getNapcatAgentDeps, registerNapcatAgentEndpoint, setNapcatAgentDeps, } from './napcat-agent-deps.js';
3
+ export { napcatClient, } from './client.js';
4
4
  export { parseOneBotGetMsgResponse } from './onebot-get-msg.js';
5
5
  export { NapCatWsEndpoint, } from './ws-endpoint.js';
6
6
  export { NapCatWssEndpoint, } from './wss-endpoint.js';
@@ -1 +1 @@
1
- export declare const napcatEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
1
+ export declare const napcatEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
@@ -1,7 +1,7 @@
1
- import type { SideEventGateway } from '@zhin.js/core/runtime';
1
+ import type { EndpointEventEmitter } from 'zhin.js/adapter';
2
2
  import { type getAdapterLogger } from '@zhin.js/logger';
3
3
  import type { NapCatEvent } from './protocol.js';
4
4
  export interface NapCatSideEventCaller {
5
5
  callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
6
6
  }
7
- export declare function receiveNapCatSideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, caller: NapCatSideEventCaller, raw: NapCatEvent, logger: ReturnType<typeof getAdapterLogger>): void;
7
+ export declare function receiveNapCatSideEvent(emit: EndpointEventEmitter, endpointKey: string, caller: NapCatSideEventCaller, raw: NapCatEvent, logger: ReturnType<typeof getAdapterLogger>): void;
@@ -1,14 +1,14 @@
1
1
  import { receiveOneBotLikeSideEvent } from '@zhin.js/core';
2
2
  import { formatCompact } from '@zhin.js/logger';
3
- export function receiveNapCatSideEvent(sideEvents, endpointKey, caller, raw, logger) {
4
- if (!sideEvents)
3
+ export function receiveNapCatSideEvent(emit, endpointKey, caller, raw, logger) {
4
+ if (!emit)
5
5
  return;
6
6
  const record = raw;
7
7
  const postType = String(record.post_type ?? '');
8
8
  const requestType = String(record.request_type ?? '');
9
9
  const isRequest = postType === 'request' || postType.startsWith('request.');
10
10
  const isFriend = requestType === 'friend' || postType.includes('friend');
11
- void receiveOneBotLikeSideEvent(sideEvents, {
11
+ void receiveOneBotLikeSideEvent(emit, {
12
12
  adapter: 'napcat',
13
13
  endpointKey,
14
14
  platform: 'napcat',
@@ -1,18 +1,19 @@
1
- import { type EndpointInstance, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
2
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
1
+ import { Endpoint } from 'zhin.js/adapter';
2
+ import { type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
3
3
  import type { CapabilityId } from 'zhin.js';
4
4
  import { type NapCatEvent, type NapCatWsConfig } from './protocol.js';
5
5
  import { type NapCatWsCreateOptions, type NapCatWsSocket } from './ws-types.js';
6
+ import { NapcatClient } from './client.js';
6
7
  export interface NapCatWsEndpointOptions {
7
8
  readonly id: CapabilityId;
8
- readonly gateway: MessageGateway;
9
- readonly sideEvents?: SideEventGateway;
10
9
  readonly config: NapCatWsConfig;
11
10
  readonly createWebSocket?: (url: string, options: NapCatWsCreateOptions) => NapCatWsSocket;
12
11
  }
13
- export declare class NapCatWsEndpoint implements EndpointInstance {
12
+ export declare class NapCatWsEndpoint extends Endpoint<NapcatClient> {
14
13
  #private;
14
+ readonly client: NapcatClient;
15
15
  readonly management: EndpointManagement;
16
+ readonly control: EndpointControl;
16
17
  readonly content: import("@zhin.js/adapter").EndpointContentPort;
17
18
  constructor(options: NapCatWsEndpointOptions);
18
19
  start(): Promise<void>;
@@ -21,42 +22,6 @@ export declare class NapCatWsEndpoint implements EndpointInstance {
21
22
  stop(): Promise<void>;
22
23
  send({ conversation, payload }: EndpointSendRequest): Promise<string>;
23
24
  recallMessage(messageId: string): Promise<void>;
24
- callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
25
- setTitle(groupId: number, userId: number, title: string, duration?: number): Promise<boolean>;
26
- sendLike(userId: number, times?: number): Promise<unknown>;
27
- deleteFriend(userId: number): Promise<unknown>;
28
- markMsgAsRead(messageId: number): Promise<unknown>;
29
- ocrImage(image: string): Promise<unknown>;
30
- setQQProfile(nickname: string, company?: string, email?: string, college?: string, personalNote?: string): Promise<unknown>;
31
- setGroupPortrait(groupId: number, file: string): Promise<unknown>;
32
- setEssenceMsg(messageId: number): Promise<unknown>;
33
- deleteEssenceMsg(messageId: number): Promise<unknown>;
34
- getEssenceMsgList(groupId: number): Promise<unknown>;
35
- sendGroupSign(groupId: number): Promise<unknown>;
36
- sendGroupNotice(groupId: number, content: string, image?: string): Promise<unknown>;
37
- getGroupNotice(groupId: number): Promise<unknown>;
38
- deleteGroupNotice(groupId: number, noticeId: string): Promise<unknown>;
39
- uploadGroupFile(groupId: number, file: string, name: string, folder?: string): Promise<unknown>;
40
- getGroupRootFiles(groupId: number): Promise<unknown>;
41
- getGroupFileUrl(groupId: number, fileId: string, busid: number): Promise<unknown>;
42
- downloadFile(url: string, threadCount?: number, headers?: string[]): Promise<unknown>;
43
- setOnlineStatus(status: number, extStatus: number): Promise<unknown>;
44
- setQQAvatar(file: string): Promise<unknown>;
45
- forwardFriendSingleMsg(userId: number, messageId: number): Promise<unknown>;
46
- forwardGroupSingleMsg(groupId: number, messageId: number): Promise<unknown>;
47
- translateEn2Zh(sourceText: string): Promise<unknown>;
48
- setMsgEmojiLike(messageId: number, emojiId: string): Promise<unknown>;
49
- sendForwardMsg(messageType: 'private' | 'group', id: number, messages: unknown[]): Promise<unknown>;
50
- getFriendMsgHistory(userId: number, messageSeq?: number, count?: number): Promise<unknown>;
51
- getGroupMsgHistory(groupId: number, messageSeq?: number, count?: number): Promise<unknown>;
52
- setSelfLongnick(longnick: string): Promise<unknown>;
53
- getGroupInfoEx(groupId: number): Promise<unknown>;
54
- sendPoke(userId: number, groupId?: number): Promise<unknown>;
55
- ncGetUserStatus(userId: number): Promise<unknown>;
56
- getGroupShutList(groupId: number): Promise<unknown>;
57
- getMiniAppArk(type: string, title: string, desc: string, picUrl: string, jumpUrl: string): Promise<unknown>;
58
- getAiCharacters(groupId: number): Promise<unknown>;
59
- sendGroupAiRecord(groupId: number, characterId: string, text: string): Promise<unknown>;
60
25
  /** Test / internal: admit a parsed event when the endpoint is open. */
61
26
  admit(ev: NapCatEvent): void;
62
27
  }
@@ -1,29 +1,32 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
1
2
  /**
2
3
  * NapCat WS client endpoint — outbound connect to NapCat.
3
4
  */
4
5
  import WebSocket from 'ws';
5
- import { createEndpointLifecycle, } from 'zhin.js/adapter';
6
+ import { createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
6
7
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
8
  import { createNapCatEndpointManagement } from './endpoint-management.js';
8
- import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
9
9
  import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
10
10
  import { buildSendAction, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
11
11
  import { receiveNapCatSideEvent } from './side-event-dispatch.js';
12
12
  import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, } from './ws-transport.js';
13
13
  import { createNapCatContentPort } from './onebot-get-msg.js';
14
- export class NapCatWsEndpoint {
14
+ import { NapcatClient } from './client.js';
15
+ export class NapCatWsEndpoint extends Endpoint {
16
+ client = new NapcatClient((action, params) => this.#callApi(action, params));
15
17
  #logger;
16
18
  #options;
17
19
  #inboundDeduper = new InboundMessageDeduper();
18
- management = createNapCatEndpointManagement(this);
19
- content = createNapCatContentPort((action, params) => this.callApi(action, params));
20
+ management = createNapCatEndpointManagement(this.client);
21
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
22
+ content = createNapCatContentPort((action, params) => this.client.callApi(action, params));
20
23
  #lifecycle;
21
24
  #ws;
22
25
  #requestId = { value: 0 };
23
26
  #pending = new Map();
24
27
  #open = false;
25
- #unregisterAgent;
26
28
  constructor(options) {
29
+ super();
27
30
  this.#logger = getAdapterLogger('napcat', options.config.id);
28
31
  this.#options = options;
29
32
  this.#lifecycle = createEndpointLifecycle({
@@ -40,16 +43,7 @@ export class NapCatWsEndpoint {
40
43
  async start() {
41
44
  if (this.#lifecycle.started)
42
45
  return;
43
- this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.id, this);
44
- try {
45
- await this.#lifecycle.start((handle) => this.#connect(handle));
46
- }
47
- catch (err) {
48
- // start 失败复位由基座保证;agent 注册/反注册是适配器专有依赖,留在适配器侧
49
- this.#unregisterAgent?.();
50
- this.#unregisterAgent = undefined;
51
- throw err;
52
- }
46
+ await this.#lifecycle.start((handle) => this.#connect(handle));
53
47
  }
54
48
  open() {
55
49
  this.#open = true;
@@ -60,8 +54,6 @@ export class NapCatWsEndpoint {
60
54
  async stop() {
61
55
  this.#open = false;
62
56
  await this.#lifecycle.stop();
63
- this.#unregisterAgent?.();
64
- this.#unregisterAgent = undefined;
65
57
  rejectAllPending(this.#pending);
66
58
  this.#inboundDeduper.clear();
67
59
  if (this.#ws) {
@@ -77,7 +69,7 @@ export class NapCatWsEndpoint {
77
69
  async send({ conversation, payload }) {
78
70
  const message = formatOutboundSegments(payload);
79
71
  const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
80
- const data = await this.callApi(action, params);
72
+ const data = await this.client.callApi(action, params);
81
73
  const messageId = data?.message_id != null ? String(data.message_id) : '';
82
74
  this.#logger.debug(formatCompact({
83
75
  op: 'napcat_send',
@@ -90,151 +82,23 @@ export class NapCatWsEndpoint {
90
82
  async recallMessage(messageId) {
91
83
  if (!messageId)
92
84
  return;
93
- await this.callApi('delete_msg', { message_id: Number(messageId) });
85
+ await this.client.callApi('delete_msg', { message_id: Number(messageId) });
94
86
  }
95
- callApi(action, params = {}) {
87
+ #callApi(action, params = {}) {
96
88
  return callNapCatWsAction(this.#ws, this.#pending, this.#requestId, action, params);
97
89
  }
98
- // ── Agent-facing API wrappers ─────────────────────────────────────
99
- async setTitle(groupId, userId, title, duration = -1) {
100
- await this.callApi('set_group_special_title', {
101
- group_id: groupId,
102
- user_id: userId,
103
- special_title: title,
104
- duration,
105
- });
106
- return true;
107
- }
108
- sendLike(userId, times = 1) {
109
- return this.callApi('send_like', { user_id: userId, times });
110
- }
111
- deleteFriend(userId) {
112
- return this.callApi('delete_friend', { user_id: userId });
113
- }
114
- markMsgAsRead(messageId) {
115
- return this.callApi('mark_msg_as_read', { message_id: messageId });
116
- }
117
- ocrImage(image) {
118
- return this.callApi('ocr_image', { image });
119
- }
120
- setQQProfile(nickname, company, email, college, personalNote) {
121
- return this.callApi('set_qq_profile', {
122
- nickname,
123
- company,
124
- email,
125
- college,
126
- personal_note: personalNote,
127
- });
128
- }
129
- setGroupPortrait(groupId, file) {
130
- return this.callApi('set_group_portrait', { group_id: groupId, file });
131
- }
132
- setEssenceMsg(messageId) {
133
- return this.callApi('set_essence_msg', { message_id: messageId });
134
- }
135
- deleteEssenceMsg(messageId) {
136
- return this.callApi('delete_essence_msg', { message_id: messageId });
137
- }
138
- getEssenceMsgList(groupId) {
139
- return this.callApi('get_essence_msg_list', { group_id: groupId });
140
- }
141
- sendGroupSign(groupId) {
142
- return this.callApi('send_group_sign', { group_id: groupId });
143
- }
144
- sendGroupNotice(groupId, content, image) {
145
- return this.callApi('_send_group_notice', { group_id: groupId, content, image });
146
- }
147
- getGroupNotice(groupId) {
148
- return this.callApi('_get_group_notice', { group_id: groupId });
149
- }
150
- deleteGroupNotice(groupId, noticeId) {
151
- return this.callApi('_del_group_notice', { group_id: groupId, notice_id: noticeId });
152
- }
153
- uploadGroupFile(groupId, file, name, folder) {
154
- return this.callApi('upload_group_file', { group_id: groupId, file, name, folder });
155
- }
156
- getGroupRootFiles(groupId) {
157
- return this.callApi('get_group_root_files', { group_id: groupId });
158
- }
159
- getGroupFileUrl(groupId, fileId, busid) {
160
- return this.callApi('get_group_file_url', { group_id: groupId, file_id: fileId, busid });
161
- }
162
- downloadFile(url, threadCount = 1, headers) {
163
- return this.callApi('download_file', { url, thread_count: threadCount, headers });
164
- }
165
- setOnlineStatus(status, extStatus) {
166
- return this.callApi('set_online_status', { status, ext_status: extStatus });
167
- }
168
- setQQAvatar(file) {
169
- return this.callApi('set_qq_avatar', { file });
170
- }
171
- forwardFriendSingleMsg(userId, messageId) {
172
- return this.callApi('forward_friend_single_msg', { user_id: userId, message_id: messageId });
173
- }
174
- forwardGroupSingleMsg(groupId, messageId) {
175
- return this.callApi('forward_group_single_msg', { group_id: groupId, message_id: messageId });
176
- }
177
- translateEn2Zh(sourceText) {
178
- return this.callApi('translate_en2zh', { source_text: sourceText });
179
- }
180
- setMsgEmojiLike(messageId, emojiId) {
181
- return this.callApi('set_msg_emoji_like', { message_id: messageId, emoji_id: emojiId });
182
- }
183
- sendForwardMsg(messageType, id, messages) {
184
- return this.callApi('send_forward_msg', {
185
- message_type: messageType,
186
- [messageType === 'group' ? 'group_id' : 'user_id']: id,
187
- messages,
188
- });
189
- }
190
- getFriendMsgHistory(userId, messageSeq, count) {
191
- return this.callApi('get_friend_msg_history', {
192
- user_id: userId,
193
- message_seq: messageSeq,
194
- count,
195
- });
196
- }
197
- getGroupMsgHistory(groupId, messageSeq, count) {
198
- return this.callApi('get_group_msg_history', {
199
- group_id: groupId,
200
- message_seq: messageSeq,
201
- count,
202
- });
203
- }
204
- setSelfLongnick(longnick) {
205
- return this.callApi('set_self_longnick', { longNick: longnick });
206
- }
207
- getGroupInfoEx(groupId) {
208
- return this.callApi('get_group_info_ex', { group_id: groupId });
209
- }
210
- sendPoke(userId, groupId) {
211
- return this.callApi('send_poke', { user_id: userId, group_id: groupId });
212
- }
213
- ncGetUserStatus(userId) {
214
- return this.callApi('nc_get_user_status', { user_id: userId });
215
- }
216
- getGroupShutList(groupId) {
217
- return this.callApi('get_group_shut_list', { group_id: groupId });
218
- }
219
- getMiniAppArk(type, title, desc, picUrl, jumpUrl) {
220
- return this.callApi('get_mini_app_ark', { type, title, desc, picUrl, jumpUrl });
221
- }
222
- getAiCharacters(groupId) {
223
- return this.callApi('get_ai_characters', { group_id: groupId });
224
- }
225
- sendGroupAiRecord(groupId, characterId, text) {
226
- return this.callApi('send_group_ai_record', {
227
- group_id: groupId,
228
- character: characterId,
229
- text,
230
- });
231
- }
232
90
  /** Test / internal: admit a parsed event when the endpoint is open. */
233
91
  admit(ev) {
234
92
  if (!this.#open)
235
93
  return;
94
+ void this.emitPlatform(napCatPlatformEventName(ev), ev).catch((error) => {
95
+ this.#logger.warn(formatCompact({
96
+ op: 'napcat_platform_event_failed',
97
+ error: error instanceof Error ? error.message : String(error),
98
+ }));
99
+ });
236
100
  if (!isMessageEvent(ev)) {
237
- receiveNapCatSideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
101
+ receiveNapCatSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, this.client, ev, this.#logger);
238
102
  return;
239
103
  }
240
104
  if (isSelfMessage(ev))
@@ -249,7 +113,7 @@ export class NapCatWsEndpoint {
249
113
  const content = formatInboundContent(ev);
250
114
  const nickname = senderNickname(ev);
251
115
  const mentioned = isNapCatBotMentioned(ev);
252
- void this.#options.gateway.receive({
116
+ void this.emit('message.receive', {
253
117
  conversation,
254
118
  message: { conversation, id: msgId },
255
119
  content,
@@ -370,3 +234,8 @@ export class NapCatWsEndpoint {
370
234
  });
371
235
  }
372
236
  }
237
+ function napCatPlatformEventName(ev) {
238
+ return [ev.post_type, ev.message_type ?? ev.notice_type ?? ev.request_type, ev.sub_type]
239
+ .filter(Boolean)
240
+ .join('.');
241
+ }
@@ -1,18 +1,19 @@
1
- import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
2
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
1
+ import { Endpoint } from 'zhin.js/adapter';
2
+ import { type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
3
3
  import type { HttpHost } from '@zhin.js/host-http';
4
4
  import type { CapabilityId } from 'zhin.js';
5
5
  import { type NapCatEvent, type NapCatWssConfig } from './protocol.js';
6
+ import { NapcatClient } from './client.js';
6
7
  export interface NapCatWssEndpointOptions {
7
8
  readonly id: CapabilityId;
8
- readonly gateway: MessageGateway;
9
- readonly sideEvents?: SideEventGateway;
10
9
  readonly http: HttpHost;
11
10
  readonly config: NapCatWssConfig;
12
11
  }
13
- export declare class NapCatWssEndpoint implements EndpointInstance {
12
+ export declare class NapCatWssEndpoint extends Endpoint<NapcatClient> {
14
13
  #private;
14
+ readonly client: NapcatClient;
15
15
  readonly management: EndpointManagement;
16
+ readonly control: EndpointControl;
16
17
  readonly content: import("@zhin.js/adapter").EndpointContentPort;
17
18
  constructor(options: NapCatWssEndpointOptions);
18
19
  start(): Promise<void>;
@@ -21,6 +22,5 @@ export declare class NapCatWssEndpoint implements EndpointInstance {
21
22
  stop(): Promise<void>;
22
23
  send({ conversation, payload }: EndpointSendRequest): Promise<string>;
23
24
  recallMessage(messageId: string): Promise<void>;
24
- callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
25
25
  admit(ev: NapCatEvent): void;
26
26
  }
@@ -1,22 +1,26 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
1
2
  /**
2
3
  * NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
3
4
  */
4
5
  import { clearInterval } from 'node:timers';
6
+ import { createRecallEndpointControl, } from 'zhin.js/adapter';
5
7
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
6
8
  import { createNapCatEndpointManagement } from './endpoint-management.js';
7
- import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
8
9
  import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
9
10
  import { buildSendAction, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
10
11
  import { receiveNapCatSideEvent } from './side-event-dispatch.js';
11
12
  import { createNapCatContentPort } from './onebot-get-msg.js';
13
+ import { NapcatClient } from './client.js';
12
14
  import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, startNapCatHeartbeat, } from './ws-transport.js';
13
15
  import { verifyNapCatAccessToken } from './wss-auth.js';
14
- export class NapCatWssEndpoint {
16
+ export class NapCatWssEndpoint extends Endpoint {
17
+ client = new NapcatClient((action, params) => this.#callApi(action, params));
15
18
  #logger;
16
19
  #options;
17
20
  #inboundDeduper = new InboundMessageDeduper();
18
- management = createNapCatEndpointManagement(this);
19
- content = createNapCatContentPort((action, params) => this.callApi(action, params));
21
+ management = createNapCatEndpointManagement(this.client);
22
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
23
+ content = createNapCatContentPort((action, params) => this.client.callApi(action, params));
20
24
  #ws;
21
25
  #wsRelease;
22
26
  #heartbeatTimer;
@@ -24,8 +28,8 @@ export class NapCatWssEndpoint {
24
28
  #pending = new Map();
25
29
  #open = false;
26
30
  #started = false;
27
- #unregisterAgent;
28
31
  constructor(options) {
32
+ super();
29
33
  this.#logger = getAdapterLogger('napcat', options.config.id);
30
34
  this.#options = options;
31
35
  }
@@ -33,7 +37,6 @@ export class NapCatWssEndpoint {
33
37
  if (this.#started)
34
38
  return;
35
39
  this.#started = true;
36
- this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.id, this);
37
40
  const handle = this.#options.http.ws(this.#options.config.path);
38
41
  this.#wsRelease = handle.onConnection((connection) => {
39
42
  this.#acceptConnection(connection);
@@ -53,8 +56,6 @@ export class NapCatWssEndpoint {
53
56
  }
54
57
  async stop() {
55
58
  this.#open = false;
56
- this.#unregisterAgent?.();
57
- this.#unregisterAgent = undefined;
58
59
  this.#wsRelease?.();
59
60
  this.#wsRelease = undefined;
60
61
  if (this.#heartbeatTimer) {
@@ -77,22 +78,28 @@ export class NapCatWssEndpoint {
77
78
  async send({ conversation, payload }) {
78
79
  const message = formatOutboundSegments(payload);
79
80
  const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
80
- const data = await this.callApi(action, params);
81
+ const data = await this.client.callApi(action, params);
81
82
  return data?.message_id != null ? String(data.message_id) : '';
82
83
  }
83
84
  async recallMessage(messageId) {
84
85
  if (!messageId)
85
86
  return;
86
- await this.callApi('delete_msg', { message_id: Number(messageId) });
87
+ await this.client.callApi('delete_msg', { message_id: Number(messageId) });
87
88
  }
88
- callApi(action, params = {}) {
89
+ #callApi(action, params = {}) {
89
90
  return callNapCatWsAction(this.#ws, this.#pending, this.#requestId, action, params);
90
91
  }
91
92
  admit(ev) {
92
93
  if (!this.#open)
93
94
  return;
95
+ void this.emitPlatform(napCatPlatformEventName(ev), ev).catch((error) => {
96
+ this.#logger.warn(formatCompact({
97
+ op: 'napcat_platform_event_failed',
98
+ error: error instanceof Error ? error.message : String(error),
99
+ }));
100
+ });
94
101
  if (!isMessageEvent(ev)) {
95
- receiveNapCatSideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
102
+ receiveNapCatSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, this.client, ev, this.#logger);
96
103
  return;
97
104
  }
98
105
  if (isSelfMessage(ev))
@@ -106,7 +113,7 @@ export class NapCatWssEndpoint {
106
113
  const conversation = napcatInboundConversation(String(this.#options.id), ev);
107
114
  const nickname = senderNickname(ev);
108
115
  const mentioned = isNapCatBotMentioned(ev);
109
- void this.#options.gateway.receive({
116
+ void this.emit('message.receive', {
110
117
  conversation,
111
118
  message: { conversation, id: msgId },
112
119
  content: formatInboundContent(ev),
@@ -173,3 +180,8 @@ export class NapCatWssEndpoint {
173
180
  }));
174
181
  }
175
182
  }
183
+ function napCatPlatformEventName(ev) {
184
+ return [ev.post_type, ev.message_type ?? ev.notice_type ?? ev.request_type, ev.sub_type]
185
+ .filter(Boolean)
186
+ .join('.');
187
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-napcat",
3
- "version": "6.0.14",
3
+ "version": "7.0.1",
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,13 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "ws": "^8.21.1",
46
- "@zhin.js/adapter": "1.1.11",
47
- "@zhin.js/core": "1.5.12",
48
- "@zhin.js/host-http": "1.0.11",
46
+ "@zhin.js/adapter": "1.2.1",
47
+ "@zhin.js/core": "1.5.14",
48
+ "@zhin.js/feature-kit": "1.0.13",
49
+ "@zhin.js/host-http": "1.0.13",
49
50
  "@zhin.js/im-contract": "1.0.4",
50
- "@zhin.js/logger": "1.0.76",
51
- "@zhin.js/permission": "1.0.3"
51
+ "@zhin.js/logger": "1.0.77",
52
+ "@zhin.js/permission": "1.0.4"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/node": "^26.1.2",
@@ -56,17 +57,17 @@
56
57
  "typescript": "^6.0.3",
57
58
  "vitest": "^4.1.10",
58
59
  "zod": "^4.4.3",
59
- "@zhin.js/agent": "1.1.14",
60
- "@zhin.js/host-http": "1.0.11",
61
- "zhin.js": "6.0.12"
60
+ "@zhin.js/agent": "1.1.16",
61
+ "@zhin.js/host-http": "1.0.13",
62
+ "zhin.js": "6.0.14"
62
63
  },
63
64
  "peerDependencies": {
64
65
  "zod": "^4.0.0",
65
- "@zhin.js/adapter": "1.1.11",
66
- "@zhin.js/agent": "1.1.14",
67
- "@zhin.js/command": "1.0.15",
68
- "@zhin.js/core": "1.5.12",
69
- "zhin.js": "6.0.12"
66
+ "@zhin.js/adapter": "1.2.1",
67
+ "@zhin.js/agent": "1.1.16",
68
+ "@zhin.js/command": "1.0.16",
69
+ "@zhin.js/core": "1.5.14",
70
+ "zhin.js": "6.0.14"
70
71
  },
71
72
  "peerDependenciesMeta": {
72
73
  "@zhin.js/agent": {