@zhin.js/adapter-napcat 1.1.18 → 1.1.19
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 +39 -0
- package/README.md +8 -5
- package/adapters/napcat/index.js +2 -29
- package/adapters/napcat/index.ts +3 -35
- package/agents/napcat/skills/napcat-account/tools/set_profile/index.js +1 -1
- package/agents/napcat/skills/napcat-account/tools/set_profile/index.ts +1 -1
- package/agents/napcat/skills/napcat-group-files-history/tools/upload_group_file/index.js +1 -1
- package/agents/napcat/skills/napcat-group-files-history/tools/upload_group_file/index.ts +1 -1
- package/agents/napcat/skills/napcat-group-settings/tools/set_title/index.js +1 -1
- package/agents/napcat/skills/napcat-group-settings/tools/set_title/index.ts +1 -1
- package/agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.js +1 -1
- package/agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.ts +1 -1
- package/commands/napcat/endpoint/definition.js +0 -2
- package/commands/napcat/endpoint/definition.ts +0 -2
- package/lib/adapter.d.ts +23 -0
- package/lib/adapter.js +42 -0
- package/lib/client.d.ts +53 -16
- package/lib/client.js +53 -10
- package/lib/console.d.ts +1 -1
- package/lib/console.js +2 -5
- package/lib/endpoint-management.d.ts +2 -2
- package/lib/endpoint-management.js +13 -13
- package/lib/http-endpoint.d.ts +5 -15
- package/lib/http-endpoint.js +44 -36
- package/lib/identity.d.ts +3 -0
- package/lib/identity.js +32 -0
- package/lib/index.d.ts +3 -2
- package/lib/index.js +2 -2
- package/lib/onebot-get-msg.d.ts +2 -2
- package/lib/onebot-get-msg.js +2 -2
- package/lib/protocol.d.ts +5 -0
- package/lib/protocol.js +23 -11
- package/lib/ws-endpoint.d.ts +6 -16
- package/lib/ws-endpoint.js +57 -48
- package/lib/ws-transport.d.ts +18 -12
- package/lib/ws-transport.js +28 -72
- package/lib/ws-types.d.ts +0 -5
- package/lib/wss-endpoint.d.ts +7 -15
- package/lib/wss-endpoint.js +56 -47
- package/package.json +18 -18
- package/plugin.js +0 -3
- package/src/adapter.ts +45 -0
- package/src/client.ts +91 -19
- package/src/console.ts +2 -5
- package/src/endpoint-management.ts +19 -15
- package/src/http-endpoint.ts +47 -45
- package/src/identity.ts +29 -0
- package/src/index.ts +9 -5
- package/src/onebot-get-msg.ts +4 -4
- package/src/protocol.ts +26 -14
- package/src/ws-endpoint.ts +61 -54
- package/src/ws-transport.ts +40 -101
- package/src/ws-types.ts +0 -6
- package/src/wss-endpoint.ts +60 -54
- package/lib/napcat-runtime-state.d.ts +0 -1
- package/lib/napcat-runtime-state.js +0 -6
- package/src/napcat-runtime-state.ts +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function createNapCatEndpointManagement(endpoint) {
|
|
2
2
|
return Object.freeze({
|
|
3
|
-
async listFriends() {
|
|
4
|
-
const data = await endpoint.
|
|
3
|
+
async listFriends(request = {}) {
|
|
4
|
+
const data = await endpoint.requestApi({ action: 'get_friend_list', signal: request.signal });
|
|
5
5
|
return toArray(data).map((value) => {
|
|
6
6
|
const friend = asRecord(value);
|
|
7
7
|
return {
|
|
@@ -11,8 +11,8 @@ export function createNapCatEndpointManagement(endpoint) {
|
|
|
11
11
|
};
|
|
12
12
|
});
|
|
13
13
|
},
|
|
14
|
-
async listGroups() {
|
|
15
|
-
const data = await endpoint.
|
|
14
|
+
async listGroups(request = {}) {
|
|
15
|
+
const data = await endpoint.requestApi({ action: 'get_group_list', signal: request.signal });
|
|
16
16
|
return toArray(data).map((value) => {
|
|
17
17
|
const group = asRecord(value);
|
|
18
18
|
return {
|
|
@@ -21,17 +21,17 @@ export function createNapCatEndpointManagement(endpoint) {
|
|
|
21
21
|
};
|
|
22
22
|
});
|
|
23
23
|
},
|
|
24
|
-
async listGroupMembers(groupId) {
|
|
25
|
-
const data = await endpoint.
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
async listGroupMembers({ groupId, signal }) {
|
|
25
|
+
const data = await endpoint.requestApi({ action: 'get_group_member_list', params: {
|
|
26
|
+
group_id: toNumberId(groupId, 'group_id'),
|
|
27
|
+
}, signal });
|
|
28
28
|
return toArray(data);
|
|
29
29
|
},
|
|
30
|
-
async sendGroupNotice(groupId, content) {
|
|
31
|
-
await endpoint.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
async sendGroupNotice({ groupId, content, signal }) {
|
|
31
|
+
await endpoint.requestApi({ action: '_send_group_notice', params: {
|
|
32
|
+
group_id: toNumberId(groupId, 'group_id'),
|
|
33
|
+
content,
|
|
34
|
+
}, signal });
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
37
|
}
|
package/lib/http-endpoint.d.ts
CHANGED
|
@@ -1,27 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { endpointSpec, type Endpoint, type EndpointSpecification } from 'zhin.js/adapter';
|
|
2
2
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
3
3
|
import type { CapabilityId } from 'zhin.js';
|
|
4
|
-
import { callNapCatHttpAction, type
|
|
4
|
+
import { callNapCatHttpAction, type NapCatHttpConfig } from './protocol.js';
|
|
5
5
|
import { NapcatClient } from './client.js';
|
|
6
6
|
export interface NapCatHttpEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
8
9
|
readonly http: HttpHost;
|
|
9
10
|
readonly config: NapCatHttpConfig;
|
|
10
11
|
readonly callHttpAction?: typeof callNapCatHttpAction;
|
|
11
12
|
}
|
|
12
|
-
export declare class NapCatHttpEndpoint extends
|
|
13
|
+
export declare class NapCatHttpEndpoint extends NapcatClient implements Endpoint {
|
|
13
14
|
#private;
|
|
14
|
-
readonly
|
|
15
|
-
readonly management: EndpointManagement;
|
|
16
|
-
readonly control: EndpointControl;
|
|
17
|
-
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
15
|
+
readonly [endpointSpec]: EndpointSpecification;
|
|
18
16
|
constructor(options: NapCatHttpEndpointOptions);
|
|
19
|
-
get transportState(): EndpointTransportState;
|
|
20
|
-
start(): Promise<void>;
|
|
21
|
-
open(): void;
|
|
22
|
-
close(): void;
|
|
23
|
-
stop(): Promise<void>;
|
|
24
|
-
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
25
|
-
recallMessage(messageId: string): Promise<void>;
|
|
26
|
-
admit(ev: NapCatEvent): void;
|
|
27
17
|
}
|
package/lib/http-endpoint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
-
import {
|
|
2
|
+
import { createEndpointSpecification, endpointSpec, createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
3
3
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
4
4
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
5
5
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
@@ -9,29 +9,41 @@ import { createNapCatContentPort } from './onebot-get-msg.js';
|
|
|
9
9
|
import { NapcatClient } from './client.js';
|
|
10
10
|
import { readRequestBody } from './webhook.js';
|
|
11
11
|
import { verifyNapCatAccessToken } from './wss-auth.js';
|
|
12
|
-
export class NapCatHttpEndpoint extends
|
|
13
|
-
|
|
12
|
+
export class NapCatHttpEndpoint extends NapcatClient {
|
|
13
|
+
[endpointSpec];
|
|
14
14
|
#logger;
|
|
15
15
|
#options;
|
|
16
16
|
#inboundDeduper = new InboundMessageDeduper();
|
|
17
|
-
management = createNapCatEndpointManagement(this
|
|
18
|
-
control = createRecallEndpointControl((id) => this
|
|
19
|
-
content = createNapCatContentPort(
|
|
20
|
-
#callHttpAction;
|
|
17
|
+
#management = createNapCatEndpointManagement(this);
|
|
18
|
+
#control = createRecallEndpointControl((id, signal) => this.#recallMessage(id, signal));
|
|
19
|
+
#content = createNapCatContentPort(request => this.requestApi(request));
|
|
21
20
|
#routeReleases = [];
|
|
22
21
|
#open = false;
|
|
23
22
|
#started = false;
|
|
24
23
|
#stopped = false;
|
|
25
24
|
constructor(options) {
|
|
26
|
-
super();
|
|
25
|
+
super({ config: options.config, httpAction: options.callHttpAction ?? callNapCatHttpAction, signal: options.signal });
|
|
27
26
|
this.#logger = getAdapterLogger('napcat', options.config.id);
|
|
28
27
|
this.#options = options;
|
|
29
|
-
|
|
28
|
+
const readTransportState = () => this.#transportState;
|
|
29
|
+
this[endpointSpec] = createEndpointSpecification(this, {
|
|
30
|
+
name: options.config.id,
|
|
31
|
+
start: () => this.#start(), open: () => this.#openEndpoint(),
|
|
32
|
+
close: () => this.#close(), stop: () => this.#stop(),
|
|
33
|
+
send: request => this.#send(request),
|
|
34
|
+
management: this.#management, control: this.#control, content: this.#content,
|
|
35
|
+
get transportState() { return readTransportState(); },
|
|
36
|
+
});
|
|
37
|
+
this.on('event', event => {
|
|
38
|
+
void this[endpointSpec].events.run(() => this.#admit(event)).catch((error) => {
|
|
39
|
+
this.#logger.debug(formatCompact({ op: 'napcat_receive_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
30
42
|
}
|
|
31
|
-
get transportState() {
|
|
43
|
+
get #transportState() {
|
|
32
44
|
return this.#stopped ? 'stopped' : this.#started ? 'open' : 'idle';
|
|
33
45
|
}
|
|
34
|
-
async start() {
|
|
46
|
+
async #start() {
|
|
35
47
|
if (this.#started)
|
|
36
48
|
return;
|
|
37
49
|
this.#stopped = false;
|
|
@@ -44,13 +56,13 @@ export class NapCatHttpEndpoint extends Endpoint {
|
|
|
44
56
|
path: this.#options.config.post_path,
|
|
45
57
|
}));
|
|
46
58
|
}
|
|
47
|
-
|
|
59
|
+
#openEndpoint() {
|
|
48
60
|
this.#open = true;
|
|
49
61
|
}
|
|
50
|
-
close() {
|
|
62
|
+
#close() {
|
|
51
63
|
this.#open = false;
|
|
52
64
|
}
|
|
53
|
-
async stop() {
|
|
65
|
+
async #stop() {
|
|
54
66
|
this.#stopped = true;
|
|
55
67
|
this.#open = false;
|
|
56
68
|
for (const release of this.#routeReleases.splice(0))
|
|
@@ -59,14 +71,10 @@ export class NapCatHttpEndpoint extends Endpoint {
|
|
|
59
71
|
this.#started = false;
|
|
60
72
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
61
73
|
}
|
|
62
|
-
async send({ conversation, payload }) {
|
|
74
|
+
async #send({ conversation, payload, signal }) {
|
|
63
75
|
const message = formatOutboundSegments(payload);
|
|
64
76
|
const { action, params } = buildSendAction(napcatOutboundTarget(conversation), message);
|
|
65
|
-
const
|
|
66
|
-
http_url: this.#options.config.http_url,
|
|
67
|
-
access_token: this.#options.config.access_token,
|
|
68
|
-
}, action, params);
|
|
69
|
-
const data = resp.data;
|
|
77
|
+
const data = await this.requestApi({ action, params, signal });
|
|
70
78
|
const rawId = data?.message_id;
|
|
71
79
|
if ((typeof rawId !== 'number' && typeof rawId !== 'string') || String(rawId).trim() === '' || (typeof rawId === 'number' && !Number.isFinite(rawId))) {
|
|
72
80
|
throw new EndpointDeliveryError('delivery_unconfirmed', 'Message delivery is unconfirmed: platform returned no message_id', 'unknown');
|
|
@@ -80,28 +88,23 @@ export class NapCatHttpEndpoint extends Endpoint {
|
|
|
80
88
|
}));
|
|
81
89
|
return messageId;
|
|
82
90
|
}
|
|
83
|
-
async recallMessage(messageId) {
|
|
91
|
+
async #recallMessage(messageId, signal) {
|
|
84
92
|
if (!messageId)
|
|
85
93
|
return;
|
|
86
|
-
await this.
|
|
94
|
+
await this.requestApi({ action: 'delete_msg', params: { message_id: Number(messageId) }, signal });
|
|
87
95
|
}
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
access_token: this.#options.config.access_token,
|
|
92
|
-
}, action, params).then((resp) => resp.data);
|
|
93
|
-
}
|
|
94
|
-
admit(ev) {
|
|
95
|
-
if (!this.#open)
|
|
96
|
+
#admit(ev) {
|
|
97
|
+
const nativeEvent = ev;
|
|
98
|
+
if (!this.#started)
|
|
96
99
|
return;
|
|
97
|
-
void this.
|
|
100
|
+
void this[endpointSpec].events.platform(napCatPlatformEventName(ev), ev).catch((error) => {
|
|
98
101
|
this.#logger.warn(formatCompact({
|
|
99
102
|
op: 'napcat_platform_event_failed',
|
|
100
103
|
error: error instanceof Error ? error.message : String(error),
|
|
101
104
|
}));
|
|
102
105
|
});
|
|
103
106
|
if (!isMessageEvent(ev)) {
|
|
104
|
-
receiveNapCatSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, this
|
|
107
|
+
receiveNapCatSideEvent((name, payload) => this[endpointSpec].events.emit(name, payload), this.#options.config.id, this, ev, this.#logger);
|
|
105
108
|
return;
|
|
106
109
|
}
|
|
107
110
|
if (isSelfMessage(ev))
|
|
@@ -112,10 +115,12 @@ export class NapCatHttpEndpoint extends Endpoint {
|
|
|
112
115
|
if (Array.isArray(ev.message) || typeof ev.message === 'string') {
|
|
113
116
|
ev = { ...ev, message: normalizeMessage(ev.message) };
|
|
114
117
|
}
|
|
115
|
-
const conversation = napcatInboundConversation(String(this.#options.id), ev);
|
|
118
|
+
const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
116
119
|
const nickname = senderNickname(ev);
|
|
117
120
|
const mentioned = isNapCatBotMentioned(ev);
|
|
118
|
-
void this.
|
|
121
|
+
void this[endpointSpec].events.message({
|
|
122
|
+
event: nativeEvent,
|
|
123
|
+
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
119
124
|
conversation,
|
|
120
125
|
message: { conversation, id: msgId },
|
|
121
126
|
content: formatInboundContent(ev),
|
|
@@ -166,8 +171,11 @@ export class NapCatHttpEndpoint extends Endpoint {
|
|
|
166
171
|
response.end(JSON.stringify({ message: 'Invalid JSON' }));
|
|
167
172
|
return;
|
|
168
173
|
}
|
|
169
|
-
if (this.#open)
|
|
170
|
-
this.
|
|
174
|
+
if (this.#open) {
|
|
175
|
+
void this[endpointSpec].events.run(() => this.ingest(ev)).catch((error) => {
|
|
176
|
+
this.#logger.debug(formatCompact({ op: 'napcat_receive_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
171
179
|
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
172
180
|
response.end(JSON.stringify({ status: 'ok' }));
|
|
173
181
|
}
|
package/lib/identity.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import { buildWsConnectOptions, readNapCatActionResponse } from './protocol.js';
|
|
3
|
+
import { createNapCatRpc } from './ws-transport.js';
|
|
4
|
+
/** Separate read-only connection: never changes credentials or state of the running Endpoint. */
|
|
5
|
+
export async function probeNapcatIdentity(config, signal) {
|
|
6
|
+
signal.throwIfAborted();
|
|
7
|
+
const { url, headers } = buildWsConnectOptions(config);
|
|
8
|
+
const socket = new WebSocket(url, { headers });
|
|
9
|
+
const rpc = createNapCatRpc({ endpointId: config.id, signal, socket: () => socket });
|
|
10
|
+
let rejectOpen = () => undefined;
|
|
11
|
+
const abort = () => { rejectOpen(signal.reason); rpc.disconnect(); socket.terminate(); };
|
|
12
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
13
|
+
socket.on('message', data => rpc.receive(data, () => undefined));
|
|
14
|
+
socket.on('close', () => { rejectOpen(new Error('Identity connection closed')); rpc.disconnect(); });
|
|
15
|
+
try {
|
|
16
|
+
await new Promise((resolve, reject) => { rejectOpen = reject; socket.once('open', resolve); socket.on('error', reject); });
|
|
17
|
+
signal.throwIfAborted();
|
|
18
|
+
const response = await rpc.call({ action: 'get_login_info', signal });
|
|
19
|
+
readNapCatActionResponse(response);
|
|
20
|
+
signal.throwIfAborted();
|
|
21
|
+
const data = response.data;
|
|
22
|
+
if (!data || typeof data !== 'object' || !('user_id' in data)
|
|
23
|
+
|| (typeof data.user_id !== 'string' && typeof data.user_id !== 'number') || !String(data.user_id).trim() || String(data.user_id) === '0')
|
|
24
|
+
return undefined;
|
|
25
|
+
return `qq:${data.user_id}`;
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
signal.removeEventListener('abort', abort);
|
|
29
|
+
rpc.dispose();
|
|
30
|
+
socket.terminate();
|
|
31
|
+
}
|
|
32
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, type MessageSegment, type NapCatActionRequest, type NapCatActionResponse, 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
|
+
export { NapcatAdapter } from './adapter.js';
|
|
2
3
|
export { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
3
|
-
export {
|
|
4
|
+
export { type NapcatClient, type NapcatClientEventMap, type NapcatClientOptions, type NapcatMemberTitleRequest, type NapcatProfileRequest, type NapcatGroupFileRequest, type NapcatMiniAppRequest, } from './client.js';
|
|
4
5
|
export { parseOneBotGetMsgResponse } from './onebot-get-msg.js';
|
|
5
6
|
export { NapCatWsEndpoint, type NapCatWsEndpointOptions, } from './ws-endpoint.js';
|
|
6
7
|
export { NapCatWssEndpoint, type NapCatWssEndpointOptions, } from './wss-endpoint.js';
|
|
7
8
|
export { NapCatHttpEndpoint, type NapCatHttpEndpointOptions, } from './http-endpoint.js';
|
|
8
9
|
export type { NapCatWsSocket, NapCatWsCreateOptions } from './ws-types.js';
|
|
9
|
-
export {
|
|
10
|
+
export { createNapCatRpc, decodeWsPayload, type NapCatRpc, type NapCatRpcOptions, } from './ws-transport.js';
|
|
10
11
|
export { verifyNapCatAccessToken } from './wss-auth.js';
|
|
11
12
|
export { readRequestBody } from './webhook.js';
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, } from './protocol.js';
|
|
2
|
+
export { NapcatAdapter } from './adapter.js';
|
|
2
3
|
export { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.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';
|
|
7
7
|
export { NapCatHttpEndpoint, } from './http-endpoint.js';
|
|
8
|
-
export {
|
|
8
|
+
export { createNapCatRpc, decodeWsPayload, } from './ws-transport.js';
|
|
9
9
|
export { verifyNapCatAccessToken } from './wss-auth.js';
|
|
10
10
|
export { readRequestBody } from './webhook.js';
|
package/lib/onebot-get-msg.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export interface NapcatMessageLookup {
|
|
|
12
12
|
time?: number;
|
|
13
13
|
}
|
|
14
14
|
export declare function parseOneBotGetMsgResponse(messageId: string, data: unknown): NapcatMessageLookup;
|
|
15
|
-
export declare function createNapCatContentPort(callApi: (
|
|
16
|
-
import type { EndpointContentPort } from 'zhin.js/adapter';
|
|
15
|
+
export declare function createNapCatContentPort(callApi: (request: PendingRpcRequest) => Promise<unknown>): EndpointContentPort;
|
|
16
|
+
import type { PendingRpcRequest, EndpointContentPort } from 'zhin.js/adapter';
|
package/lib/onebot-get-msg.js
CHANGED
|
@@ -35,7 +35,7 @@ export function createNapCatContentPort(callApi) {
|
|
|
35
35
|
: Object.freeze({ status: 'resolved', reference, value: reference.media });
|
|
36
36
|
}
|
|
37
37
|
if (reference.kind === 'message') {
|
|
38
|
-
const parsed = parseOneBotGetMsgResponse(reference.message.id, await callApi('get_msg', { message_id: reference.message.id }));
|
|
38
|
+
const parsed = parseOneBotGetMsgResponse(reference.message.id, await callApi({ action: 'get_msg', params: { message_id: reference.message.id }, signal: context.signal }));
|
|
39
39
|
context.signal.throwIfAborted();
|
|
40
40
|
const value = Object.freeze({
|
|
41
41
|
ref: reference.message,
|
|
@@ -45,7 +45,7 @@ export function createNapCatContentPort(callApi) {
|
|
|
45
45
|
});
|
|
46
46
|
return Object.freeze({ status: 'resolved', reference, value });
|
|
47
47
|
}
|
|
48
|
-
const raw = await callApi('get_forward_msg', { message_id: reference.forwardId, id: reference.forwardId });
|
|
48
|
+
const raw = await callApi({ action: 'get_forward_msg', params: { message_id: reference.forwardId, id: reference.forwardId }, signal: context.signal });
|
|
49
49
|
context.signal.throwIfAborted();
|
|
50
50
|
const nodes = Array.isArray(raw.messages) ? raw.messages.slice(0, context.maxEntries) : [];
|
|
51
51
|
const value = Object.freeze(nodes.map((node) => {
|
package/lib/protocol.d.ts
CHANGED
|
@@ -88,6 +88,10 @@ export interface NapCatActionResponse {
|
|
|
88
88
|
message?: string;
|
|
89
89
|
wording?: string;
|
|
90
90
|
}
|
|
91
|
+
/** OneBot 11: async/1 is submitted without a known result, not a rejection.
|
|
92
|
+
* https://github.com/botuniverse/onebot-11/blob/master/communication/http.md#响应
|
|
93
|
+
*/
|
|
94
|
+
export declare function readNapCatActionResponse(value: unknown): NapCatActionResponse;
|
|
91
95
|
export interface NapCatWireSegment {
|
|
92
96
|
readonly type: string;
|
|
93
97
|
readonly data?: Record<string, unknown>;
|
|
@@ -139,6 +143,7 @@ export declare function buildWsConnectOptions(config: NapCatWsConfig): {
|
|
|
139
143
|
export interface NapCatHttpOptions {
|
|
140
144
|
readonly http_url: string;
|
|
141
145
|
readonly access_token?: string;
|
|
146
|
+
readonly signal?: AbortSignal;
|
|
142
147
|
}
|
|
143
148
|
/**
|
|
144
149
|
* 向 NapCat HTTP API 发送动作请求:POST {http_url}/{action}。
|
package/lib/protocol.js
CHANGED
|
@@ -6,6 +6,21 @@
|
|
|
6
6
|
import { EndpointDeliveryError, isMediaRef } from '@zhin.js/im-contract';
|
|
7
7
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
8
8
|
const logger = getLogger('napcat');
|
|
9
|
+
/** OneBot 11: async/1 is submitted without a known result, not a rejection.
|
|
10
|
+
* https://github.com/botuniverse/onebot-11/blob/master/communication/http.md#响应
|
|
11
|
+
*/
|
|
12
|
+
export function readNapCatActionResponse(value) {
|
|
13
|
+
if (!value || typeof value !== 'object' || !('status' in value) || !('retcode' in value)
|
|
14
|
+
|| !Number.isInteger(value.retcode))
|
|
15
|
+
throw new EndpointDeliveryError('delivery_unconfirmed', 'NapCat returned an invalid action response', 'unknown');
|
|
16
|
+
const response = value;
|
|
17
|
+
if (response.status === 'failed' && response.retcode !== 0 && response.retcode !== 1) {
|
|
18
|
+
throw new EndpointDeliveryError('platform_rejected', `NapCat action failed [${response.retcode}]: ${response.message || response.wording || 'unknown'}`, 'rejected');
|
|
19
|
+
}
|
|
20
|
+
if (response.status !== 'ok' || response.retcode !== 0)
|
|
21
|
+
throw new EndpointDeliveryError('delivery_unconfirmed', 'NapCat did not confirm the action result', 'unknown');
|
|
22
|
+
return response;
|
|
23
|
+
}
|
|
9
24
|
function normalizeConnection(connection) {
|
|
10
25
|
if (connection === 'wss' || connection === 'http')
|
|
11
26
|
return connection;
|
|
@@ -299,25 +314,22 @@ export async function callNapCatHttpAction(options, action, params = {}) {
|
|
|
299
314
|
headers.Authorization = `Bearer ${options.access_token}`;
|
|
300
315
|
}
|
|
301
316
|
const res = await fetch(url, {
|
|
302
|
-
signal: AbortSignal.timeout(30_000),
|
|
317
|
+
signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000),
|
|
303
318
|
method: 'POST',
|
|
304
319
|
headers,
|
|
305
320
|
body: JSON.stringify(params),
|
|
306
321
|
});
|
|
307
322
|
const text = await res.text();
|
|
308
|
-
if (res.status
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
if (res.status !== 200) {
|
|
324
|
+
const rejected = [400, 401, 403, 404, 406, 429].includes(res.status);
|
|
325
|
+
throw new EndpointDeliveryError(rejected ? 'platform_rejected' : 'delivery_unconfirmed', `NapCat HTTP ${res.status}: ${text}`, rejected ? 'rejected' : 'unknown');
|
|
326
|
+
}
|
|
312
327
|
let data;
|
|
313
328
|
try {
|
|
314
329
|
data = JSON.parse(text);
|
|
315
330
|
}
|
|
316
|
-
catch {
|
|
317
|
-
throw new
|
|
318
|
-
}
|
|
319
|
-
if (data.status !== 'ok' && data.retcode !== 0) {
|
|
320
|
-
throw new EndpointDeliveryError('platform_rejected', `NapCat HTTP action failed [${data.retcode}]: ${data.message || data.wording || 'unknown'}`, 'rejected');
|
|
331
|
+
catch (cause) {
|
|
332
|
+
throw new EndpointDeliveryError('delivery_unconfirmed', 'NapCat returned invalid JSON', 'unknown', { cause });
|
|
321
333
|
}
|
|
322
|
-
return data;
|
|
334
|
+
return readNapCatActionResponse(data);
|
|
323
335
|
}
|
package/lib/ws-endpoint.d.ts
CHANGED
|
@@ -1,27 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { endpointSpec, type Endpoint, type EndpointSpecification } from 'zhin.js/adapter';
|
|
2
2
|
import type { CapabilityId } from 'zhin.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type NapCatWsConfig } from './protocol.js';
|
|
4
4
|
import { type NapCatWsCreateOptions, type NapCatWsSocket } from './ws-types.js';
|
|
5
5
|
import { NapcatClient } from './client.js';
|
|
6
6
|
export interface NapCatWsEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
8
9
|
readonly config: NapCatWsConfig;
|
|
9
10
|
readonly createWebSocket?: (url: string, options: NapCatWsCreateOptions) => NapCatWsSocket;
|
|
10
11
|
}
|
|
11
|
-
export declare class NapCatWsEndpoint extends
|
|
12
|
+
export declare class NapCatWsEndpoint extends NapcatClient implements Endpoint {
|
|
12
13
|
#private;
|
|
13
|
-
readonly
|
|
14
|
-
readonly management: EndpointManagement;
|
|
15
|
-
readonly control: EndpointControl;
|
|
16
|
-
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
14
|
+
readonly [endpointSpec]: EndpointSpecification;
|
|
17
15
|
constructor(options: NapCatWsEndpointOptions);
|
|
18
|
-
get
|
|
19
|
-
start(): Promise<void>;
|
|
20
|
-
open(): void;
|
|
21
|
-
close(): void;
|
|
22
|
-
stop(): Promise<void>;
|
|
23
|
-
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
24
|
-
recallMessage(messageId: string): Promise<void>;
|
|
25
|
-
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
26
|
-
admit(ev: NapCatEvent): void;
|
|
16
|
+
protected get actionSocket(): NapCatWsSocket | undefined;
|
|
27
17
|
}
|