@zhin.js/adapter-napcat 3.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +47 -98
- package/adapters/napcat.ts +39 -0
- package/agent/tools/ai_tts.ts +2 -2
- package/agent/tools/del_group_notice.ts +2 -2
- package/agent/tools/delete_essence_msg.ts +2 -2
- package/agent/tools/delete_friend.ts +2 -2
- package/agent/tools/download_file.ts +2 -2
- package/agent/tools/forward_single_msg.ts +2 -2
- package/agent/tools/get_ai_characters.ts +2 -2
- package/agent/tools/get_essence_list.ts +2 -2
- package/agent/tools/get_friend_msg_history.ts +2 -2
- package/agent/tools/get_group_file_url.ts +2 -2
- package/agent/tools/get_group_info_ex.ts +2 -2
- package/agent/tools/get_group_msg_history.ts +2 -2
- package/agent/tools/get_group_notice.ts +2 -2
- package/agent/tools/get_group_root_files.ts +2 -2
- package/agent/tools/get_group_shut_list.ts +2 -2
- package/agent/tools/get_mini_app_ark.ts +2 -2
- package/agent/tools/get_user_status.ts +2 -2
- package/agent/tools/group_sign.ts +2 -2
- package/agent/tools/mark_msg_as_read.ts +2 -2
- package/agent/tools/ocr_image.ts +2 -2
- package/agent/tools/send_forward_msg.ts +2 -2
- package/agent/tools/send_group_notice.ts +2 -2
- package/agent/tools/send_like.ts +2 -2
- package/agent/tools/send_poke.ts +2 -2
- package/agent/tools/set_avatar.ts +2 -2
- package/agent/tools/set_emoji_reaction.ts +2 -2
- package/agent/tools/set_essence_msg.ts +2 -2
- package/agent/tools/set_group_portrait.ts +2 -2
- package/agent/tools/set_online_status.ts +2 -2
- package/agent/tools/set_profile.ts +2 -2
- package/agent/tools/set_signature.ts +2 -2
- package/agent/tools/set_title.ts +2 -2
- package/agent/tools/translate.ts +2 -2
- package/agent/tools/upload_group_file.ts +2 -2
- package/lib/http-endpoint.d.ts +26 -0
- package/lib/http-endpoint.js +147 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +10 -0
- package/lib/napcat-agent-deps.d.ts +51 -0
- package/lib/napcat-agent-deps.js +33 -0
- package/lib/napcat-inbound.d.ts +28 -0
- package/lib/{src/napcat-inbound.js → napcat-inbound.js} +28 -13
- package/lib/onebot-get-msg.d.ts +14 -0
- package/lib/{src/onebot-get-msg.js → onebot-get-msg.js} +0 -1
- package/lib/protocol.d.ts +151 -0
- package/lib/protocol.js +217 -0
- package/lib/webhook.d.ts +2 -0
- package/lib/webhook.js +14 -0
- package/lib/ws-endpoint.d.ts +61 -0
- package/lib/ws-endpoint.js +357 -0
- package/lib/ws-transport.d.ts +13 -0
- package/lib/ws-transport.js +75 -0
- package/lib/ws-types.d.ts +17 -0
- package/lib/ws-types.js +1 -0
- package/lib/wss-auth.d.ts +2 -0
- package/lib/wss-auth.js +16 -0
- package/lib/wss-endpoint.d.ts +25 -0
- package/lib/wss-endpoint.js +157 -0
- package/package.json +44 -33
- package/plugin.ts +13 -0
- package/schema.json +74 -0
- package/src/http-endpoint.ts +197 -0
- package/src/index.ts +66 -81
- package/src/napcat-agent-deps.ts +81 -15
- package/src/napcat-inbound.ts +34 -10
- package/src/onebot-get-msg.ts +8 -2
- package/src/protocol.ts +373 -0
- package/src/webhook.ts +16 -0
- package/src/ws-endpoint.ts +452 -0
- package/src/ws-transport.ts +105 -0
- package/src/ws-types.ts +20 -0
- package/src/wss-auth.ts +17 -0
- package/src/wss-endpoint.ts +205 -0
- package/client/NapCatManagement.tsx +0 -113
- package/client/index.tsx +0 -11
- package/client/tsconfig.json +0 -7
- package/client/utils/api.ts +0 -30
- package/dist/index.js +0 -27
- package/lib/agent/tools/ai_tts.js +0 -21
- package/lib/agent/tools/ai_tts.js.map +0 -1
- package/lib/agent/tools/del_group_notice.js +0 -22
- package/lib/agent/tools/del_group_notice.js.map +0 -1
- package/lib/agent/tools/delete_essence_msg.js +0 -21
- package/lib/agent/tools/delete_essence_msg.js.map +0 -1
- package/lib/agent/tools/delete_friend.js +0 -20
- package/lib/agent/tools/delete_friend.js.map +0 -1
- package/lib/agent/tools/download_file.js +0 -19
- package/lib/agent/tools/download_file.js.map +0 -1
- package/lib/agent/tools/forward_single_msg.js +0 -24
- package/lib/agent/tools/forward_single_msg.js.map +0 -1
- package/lib/agent/tools/get_ai_characters.js +0 -19
- package/lib/agent/tools/get_ai_characters.js.map +0 -1
- package/lib/agent/tools/get_essence_list.js +0 -19
- package/lib/agent/tools/get_essence_list.js.map +0 -1
- package/lib/agent/tools/get_friend_msg_history.js +0 -20
- package/lib/agent/tools/get_friend_msg_history.js.map +0 -1
- package/lib/agent/tools/get_group_file_url.js +0 -21
- package/lib/agent/tools/get_group_file_url.js.map +0 -1
- package/lib/agent/tools/get_group_info_ex.js +0 -19
- package/lib/agent/tools/get_group_info_ex.js.map +0 -1
- package/lib/agent/tools/get_group_msg_history.js +0 -21
- package/lib/agent/tools/get_group_msg_history.js.map +0 -1
- package/lib/agent/tools/get_group_notice.js +0 -19
- package/lib/agent/tools/get_group_notice.js.map +0 -1
- package/lib/agent/tools/get_group_root_files.js +0 -19
- package/lib/agent/tools/get_group_root_files.js.map +0 -1
- package/lib/agent/tools/get_group_shut_list.js +0 -19
- package/lib/agent/tools/get_group_shut_list.js.map +0 -1
- package/lib/agent/tools/get_mini_app_ark.js +0 -22
- package/lib/agent/tools/get_mini_app_ark.js.map +0 -1
- package/lib/agent/tools/get_user_status.js +0 -18
- package/lib/agent/tools/get_user_status.js.map +0 -1
- package/lib/agent/tools/group_sign.js +0 -20
- package/lib/agent/tools/group_sign.js.map +0 -1
- package/lib/agent/tools/mark_msg_as_read.js +0 -19
- package/lib/agent/tools/mark_msg_as_read.js.map +0 -1
- package/lib/agent/tools/ocr_image.js +0 -18
- package/lib/agent/tools/ocr_image.js.map +0 -1
- package/lib/agent/tools/send_forward_msg.js +0 -21
- package/lib/agent/tools/send_forward_msg.js.map +0 -1
- package/lib/agent/tools/send_group_notice.js +0 -23
- package/lib/agent/tools/send_group_notice.js.map +0 -1
- package/lib/agent/tools/send_like.js +0 -20
- package/lib/agent/tools/send_like.js.map +0 -1
- package/lib/agent/tools/send_poke.js +0 -20
- package/lib/agent/tools/send_poke.js.map +0 -1
- package/lib/agent/tools/set_avatar.js +0 -19
- package/lib/agent/tools/set_avatar.js.map +0 -1
- package/lib/agent/tools/set_emoji_reaction.js +0 -20
- package/lib/agent/tools/set_emoji_reaction.js.map +0 -1
- package/lib/agent/tools/set_essence_msg.js +0 -21
- package/lib/agent/tools/set_essence_msg.js.map +0 -1
- package/lib/agent/tools/set_group_portrait.js +0 -22
- package/lib/agent/tools/set_group_portrait.js.map +0 -1
- package/lib/agent/tools/set_online_status.js +0 -20
- package/lib/agent/tools/set_online_status.js.map +0 -1
- package/lib/agent/tools/set_profile.js +0 -23
- package/lib/agent/tools/set_profile.js.map +0 -1
- package/lib/agent/tools/set_signature.js +0 -19
- package/lib/agent/tools/set_signature.js.map +0 -1
- package/lib/agent/tools/set_title.js +0 -23
- package/lib/agent/tools/set_title.js.map +0 -1
- package/lib/agent/tools/translate.js +0 -18
- package/lib/agent/tools/translate.js.map +0 -1
- package/lib/agent/tools/upload_group_file.js +0 -23
- package/lib/agent/tools/upload_group_file.js.map +0 -1
- package/lib/src/adapter.js +0 -102
- package/lib/src/adapter.js.map +0 -1
- package/lib/src/agent-prompt.js +0 -106
- package/lib/src/agent-prompt.js.map +0 -1
- package/lib/src/endpoint-base.js +0 -315
- package/lib/src/endpoint-base.js.map +0 -1
- package/lib/src/endpoint-http.js +0 -106
- package/lib/src/endpoint-http.js.map +0 -1
- package/lib/src/endpoint-ws-client.js +0 -139
- package/lib/src/endpoint-ws-client.js.map +0 -1
- package/lib/src/endpoint-ws-server.js +0 -137
- package/lib/src/endpoint-ws-server.js.map +0 -1
- package/lib/src/index.js +0 -58
- package/lib/src/index.js.map +0 -1
- package/lib/src/napcat-agent-deps.js +0 -19
- package/lib/src/napcat-agent-deps.js.map +0 -1
- package/lib/src/napcat-inbound.js.map +0 -1
- package/lib/src/onebot-get-msg.js.map +0 -1
- package/lib/src/routes.js +0 -61
- package/lib/src/routes.js.map +0 -1
- package/lib/src/segment-mapper.js +0 -2
- package/lib/src/segment-mapper.js.map +0 -1
- package/lib/src/types.js +0 -6
- package/lib/src/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -109
- package/src/agent-prompt.ts +0 -115
- package/src/endpoint-base.ts +0 -362
- package/src/endpoint-http.ts +0 -95
- package/src/endpoint-ws-client.ts +0 -137
- package/src/endpoint-ws-server.ts +0 -136
- package/src/routes.ts +0 -61
- package/src/segment-mapper.ts +0 -1
- package/src/types.ts +0 -266
package/lib/protocol.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NapCat protocol helpers (OneBot 11 + NapCat extensions).
|
|
3
|
+
* No legacy Adapter/Endpoint / segment-mapper.
|
|
4
|
+
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
5
|
+
*/
|
|
6
|
+
function normalizeConnection(connection) {
|
|
7
|
+
if (connection === 'wss' || connection === 'http')
|
|
8
|
+
return connection;
|
|
9
|
+
return 'ws';
|
|
10
|
+
}
|
|
11
|
+
export function resolveNapCatConfig(config = {}) {
|
|
12
|
+
const entry = config.endpoints?.find((item) => item.context === 'napcat');
|
|
13
|
+
const connection = normalizeConnection(config.connection ?? entry?.connection);
|
|
14
|
+
const name = (typeof config.name === 'string' && config.name)
|
|
15
|
+
|| (typeof entry?.name === 'string' && entry.name)
|
|
16
|
+
|| process.env.NAPCAT_BOT_NAME
|
|
17
|
+
|| 'napcat-bot';
|
|
18
|
+
const access_token = config.access_token ?? entry?.access_token;
|
|
19
|
+
if (connection === 'ws') {
|
|
20
|
+
const url = config.url ?? entry?.url;
|
|
21
|
+
if (!url) {
|
|
22
|
+
throw new TypeError('NapCat connection:ws requires url (plugins.<key>.url or endpoints with context: napcat)');
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
context: 'napcat',
|
|
26
|
+
connection: 'ws',
|
|
27
|
+
name,
|
|
28
|
+
access_token,
|
|
29
|
+
url,
|
|
30
|
+
reconnect_interval: config.reconnect_interval ?? entry?.reconnect_interval ?? 5000,
|
|
31
|
+
heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (connection === 'wss') {
|
|
35
|
+
const path = config.path ?? entry?.path;
|
|
36
|
+
if (!path)
|
|
37
|
+
throw new TypeError('NapCat connection:wss requires path');
|
|
38
|
+
return {
|
|
39
|
+
context: 'napcat',
|
|
40
|
+
connection: 'wss',
|
|
41
|
+
name,
|
|
42
|
+
access_token,
|
|
43
|
+
path,
|
|
44
|
+
heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (connection === 'http') {
|
|
48
|
+
const http_url = config.http_url ?? entry?.http_url;
|
|
49
|
+
const post_path = config.post_path ?? entry?.post_path;
|
|
50
|
+
if (!http_url || !post_path) {
|
|
51
|
+
throw new TypeError('NapCat connection:http requires http_url and post_path');
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
context: 'napcat',
|
|
55
|
+
connection: 'http',
|
|
56
|
+
name,
|
|
57
|
+
access_token,
|
|
58
|
+
http_url,
|
|
59
|
+
post_path,
|
|
60
|
+
poll_interval: config.poll_interval ?? entry?.poll_interval ?? 30_000,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
throw new TypeError(`Unknown NapCat connection: ${String(connection)}`);
|
|
64
|
+
}
|
|
65
|
+
export function isMessageEvent(ev) {
|
|
66
|
+
return (ev.post_type === 'message' || ev.post_type === 'message_sent')
|
|
67
|
+
&& ev.message_id != null;
|
|
68
|
+
}
|
|
69
|
+
export function getChannelId(ev) {
|
|
70
|
+
if (ev.message_type === 'group' && ev.group_id != null)
|
|
71
|
+
return String(ev.group_id);
|
|
72
|
+
if (ev.group_id != null && ev.message_type !== 'private')
|
|
73
|
+
return String(ev.group_id);
|
|
74
|
+
if (ev.user_id != null)
|
|
75
|
+
return String(ev.user_id);
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
export function formatInboundTarget(ev) {
|
|
79
|
+
const messageType = ev.message_type === 'group' || (ev.group_id != null && ev.message_type !== 'private')
|
|
80
|
+
? 'group'
|
|
81
|
+
: 'private';
|
|
82
|
+
return `${messageType}:${getChannelId(ev)}`;
|
|
83
|
+
}
|
|
84
|
+
export function parseSendTarget(target) {
|
|
85
|
+
const sep = target.indexOf(':');
|
|
86
|
+
if (sep <= 0) {
|
|
87
|
+
return { message_type: 'private', id: target };
|
|
88
|
+
}
|
|
89
|
+
const head = target.slice(0, sep);
|
|
90
|
+
const rest = target.slice(sep + 1);
|
|
91
|
+
if (head === 'group')
|
|
92
|
+
return { message_type: 'group', id: rest };
|
|
93
|
+
if (head === 'private')
|
|
94
|
+
return { message_type: 'private', id: rest };
|
|
95
|
+
return { message_type: 'private', id: target };
|
|
96
|
+
}
|
|
97
|
+
export function formatInboundContent(ev) {
|
|
98
|
+
if (typeof ev.raw_message === 'string' && ev.raw_message)
|
|
99
|
+
return ev.raw_message;
|
|
100
|
+
if (Array.isArray(ev.message)) {
|
|
101
|
+
return ev.message
|
|
102
|
+
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
|
103
|
+
.join('');
|
|
104
|
+
}
|
|
105
|
+
if (typeof ev.message === 'string')
|
|
106
|
+
return ev.message;
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 入站 sender 语义:必须是用户 ID(Runtime Message contract)。
|
|
111
|
+
* 显示名走 metadata.nickname,见 senderNickname。
|
|
112
|
+
*/
|
|
113
|
+
export function senderUserId(ev) {
|
|
114
|
+
return ev.user_id != null ? String(ev.user_id) : '';
|
|
115
|
+
}
|
|
116
|
+
/** 显示名(群名片 card 优先于 nickname),没有则 undefined。 */
|
|
117
|
+
export function senderNickname(ev) {
|
|
118
|
+
const name = ev.sender?.card || ev.sender?.nickname;
|
|
119
|
+
return typeof name === 'string' && name ? name : undefined;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Wire-encode an already-rendered outbound payload into OneBot message segments.
|
|
123
|
+
* Segment canonicalization is intentionally not done here.
|
|
124
|
+
*/
|
|
125
|
+
export function formatOutboundSegments(payload) {
|
|
126
|
+
if (typeof payload === 'string') {
|
|
127
|
+
return [{ type: 'text', data: { text: payload } }];
|
|
128
|
+
}
|
|
129
|
+
const items = Array.isArray(payload)
|
|
130
|
+
? payload
|
|
131
|
+
: payload && typeof payload === 'object' && 'type' in payload
|
|
132
|
+
? [payload]
|
|
133
|
+
: [];
|
|
134
|
+
if (items.length === 0) {
|
|
135
|
+
const text = payload == null
|
|
136
|
+
? ''
|
|
137
|
+
: typeof payload === 'object'
|
|
138
|
+
? JSON.stringify(payload)
|
|
139
|
+
: String(payload);
|
|
140
|
+
return [{ type: 'text', data: { text } }];
|
|
141
|
+
}
|
|
142
|
+
const segs = [];
|
|
143
|
+
for (const item of items) {
|
|
144
|
+
if (typeof item === 'string') {
|
|
145
|
+
segs.push({ type: 'text', data: { text: item } });
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
segs.push({ type: item.type, data: item.data ?? {} });
|
|
149
|
+
}
|
|
150
|
+
return segs.length ? segs : [{ type: 'text', data: { text: '' } }];
|
|
151
|
+
}
|
|
152
|
+
export function buildSendAction(target, message) {
|
|
153
|
+
const parsed = parseSendTarget(target);
|
|
154
|
+
if (parsed.message_type === 'group') {
|
|
155
|
+
return {
|
|
156
|
+
action: 'send_group_msg',
|
|
157
|
+
params: {
|
|
158
|
+
group_id: Number(parsed.id) || parsed.id,
|
|
159
|
+
message,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
action: 'send_private_msg',
|
|
165
|
+
params: {
|
|
166
|
+
user_id: Number(parsed.id) || parsed.id,
|
|
167
|
+
message,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** Build WS connect URL + headers (access_token via Bearer + query). */
|
|
172
|
+
export function buildWsConnectOptions(config) {
|
|
173
|
+
const headers = {};
|
|
174
|
+
let connectUrl = config.url;
|
|
175
|
+
if (config.access_token) {
|
|
176
|
+
headers.Authorization = `Bearer ${config.access_token}`;
|
|
177
|
+
const url = new URL(config.url);
|
|
178
|
+
url.searchParams.set('access_token', config.access_token);
|
|
179
|
+
connectUrl = url.toString();
|
|
180
|
+
}
|
|
181
|
+
const safeUrl = new URL(connectUrl);
|
|
182
|
+
safeUrl.searchParams.delete('access_token');
|
|
183
|
+
return { url: connectUrl, headers, safeUrl: safeUrl.toString() };
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 向 NapCat HTTP API 发送动作请求:POST {http_url}/{action}。
|
|
187
|
+
* 供 connection:http 出站与纯协议测试使用。
|
|
188
|
+
*/
|
|
189
|
+
export async function callNapCatHttpAction(options, action, params = {}) {
|
|
190
|
+
const base = options.http_url.replace(/\/$/, '');
|
|
191
|
+
const url = `${base}/${action}`;
|
|
192
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
193
|
+
if (options.access_token) {
|
|
194
|
+
headers.Authorization = `Bearer ${options.access_token}`;
|
|
195
|
+
}
|
|
196
|
+
const res = await fetch(url, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers,
|
|
199
|
+
body: JSON.stringify(params),
|
|
200
|
+
});
|
|
201
|
+
const text = await res.text();
|
|
202
|
+
if (res.status === 401)
|
|
203
|
+
throw new Error(`NapCat HTTP auth failed: ${text}`);
|
|
204
|
+
if (res.status !== 200)
|
|
205
|
+
throw new Error(`NapCat HTTP ${res.status}: ${text}`);
|
|
206
|
+
let data;
|
|
207
|
+
try {
|
|
208
|
+
data = JSON.parse(text);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
throw new Error(`NapCat HTTP invalid response: ${text.slice(0, 200)}`);
|
|
212
|
+
}
|
|
213
|
+
if (data.status !== 'ok' && data.retcode !== 0) {
|
|
214
|
+
throw new Error(`NapCat HTTP action failed [${data.retcode}]: ${data.message || data.wording || 'unknown'}`);
|
|
215
|
+
}
|
|
216
|
+
return data;
|
|
217
|
+
}
|
package/lib/webhook.d.ts
ADDED
package/lib/webhook.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export async function readRequestBody(request) {
|
|
2
|
+
const chunks = [];
|
|
3
|
+
let size = 0;
|
|
4
|
+
for await (const chunk of request) {
|
|
5
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
6
|
+
size += buffer.length;
|
|
7
|
+
if (size > 1_048_576) {
|
|
8
|
+
request.destroy();
|
|
9
|
+
throw new Error('Request body exceeds 1MB');
|
|
10
|
+
}
|
|
11
|
+
chunks.push(buffer);
|
|
12
|
+
}
|
|
13
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
14
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
2
|
+
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
|
+
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
4
|
+
import { type NapCatEvent, type NapCatWsConfig } from './protocol.js';
|
|
5
|
+
import { type NapCatWsCreateOptions, type NapCatWsSocket } from './ws-types.js';
|
|
6
|
+
export interface NapCatWsEndpointOptions {
|
|
7
|
+
readonly id: CapabilityId;
|
|
8
|
+
readonly gateway: MessageGateway;
|
|
9
|
+
readonly config: NapCatWsConfig;
|
|
10
|
+
readonly createWebSocket?: (url: string, options: NapCatWsCreateOptions) => NapCatWsSocket;
|
|
11
|
+
}
|
|
12
|
+
export declare class NapCatWsEndpoint implements EndpointInstance {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(options: NapCatWsEndpointOptions);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
open(): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
send({ target, payload }: {
|
|
20
|
+
readonly target: string;
|
|
21
|
+
readonly payload: unknown;
|
|
22
|
+
}): Promise<string>;
|
|
23
|
+
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
24
|
+
setTitle(groupId: number, userId: number, title: string, duration?: number): Promise<boolean>;
|
|
25
|
+
sendLike(userId: number, times?: number): Promise<unknown>;
|
|
26
|
+
deleteFriend(userId: number): Promise<unknown>;
|
|
27
|
+
markMsgAsRead(messageId: number): Promise<unknown>;
|
|
28
|
+
ocrImage(image: string): Promise<unknown>;
|
|
29
|
+
setQQProfile(nickname: string, company?: string, email?: string, college?: string, personalNote?: string): Promise<unknown>;
|
|
30
|
+
setGroupPortrait(groupId: number, file: string): Promise<unknown>;
|
|
31
|
+
setEssenceMsg(messageId: number): Promise<unknown>;
|
|
32
|
+
deleteEssenceMsg(messageId: number): Promise<unknown>;
|
|
33
|
+
getEssenceMsgList(groupId: number): Promise<unknown>;
|
|
34
|
+
sendGroupSign(groupId: number): Promise<unknown>;
|
|
35
|
+
sendGroupNotice(groupId: number, content: string, image?: string): Promise<unknown>;
|
|
36
|
+
getGroupNotice(groupId: number): Promise<unknown>;
|
|
37
|
+
deleteGroupNotice(groupId: number, noticeId: string): Promise<unknown>;
|
|
38
|
+
uploadGroupFile(groupId: number, file: string, name: string, folder?: string): Promise<unknown>;
|
|
39
|
+
getGroupRootFiles(groupId: number): Promise<unknown>;
|
|
40
|
+
getGroupFileUrl(groupId: number, fileId: string, busid: number): Promise<unknown>;
|
|
41
|
+
downloadFile(url: string, threadCount?: number, headers?: string[]): Promise<unknown>;
|
|
42
|
+
setOnlineStatus(status: number, extStatus: number): Promise<unknown>;
|
|
43
|
+
setQQAvatar(file: string): Promise<unknown>;
|
|
44
|
+
forwardFriendSingleMsg(userId: number, messageId: number): Promise<unknown>;
|
|
45
|
+
forwardGroupSingleMsg(groupId: number, messageId: number): Promise<unknown>;
|
|
46
|
+
translateEn2Zh(sourceText: string): Promise<unknown>;
|
|
47
|
+
setMsgEmojiLike(messageId: number, emojiId: string): Promise<unknown>;
|
|
48
|
+
sendForwardMsg(messageType: 'private' | 'group', id: number, messages: unknown[]): Promise<unknown>;
|
|
49
|
+
getFriendMsgHistory(userId: number, messageSeq?: number, count?: number): Promise<unknown>;
|
|
50
|
+
getGroupMsgHistory(groupId: number, messageSeq?: number, count?: number): Promise<unknown>;
|
|
51
|
+
setSelfLongnick(longnick: string): Promise<unknown>;
|
|
52
|
+
getGroupInfoEx(groupId: number): Promise<unknown>;
|
|
53
|
+
sendPoke(userId: number, groupId?: number): Promise<unknown>;
|
|
54
|
+
ncGetUserStatus(userId: number): Promise<unknown>;
|
|
55
|
+
getGroupShutList(groupId: number): Promise<unknown>;
|
|
56
|
+
getMiniAppArk(type: string, title: string, desc: string, picUrl: string, jumpUrl: string): Promise<unknown>;
|
|
57
|
+
getAiCharacters(groupId: number): Promise<unknown>;
|
|
58
|
+
sendGroupAiRecord(groupId: number, characterId: string, text: string): Promise<unknown>;
|
|
59
|
+
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
60
|
+
admit(ev: NapCatEvent): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NapCat WS client endpoint — outbound connect to NapCat.
|
|
3
|
+
*/
|
|
4
|
+
import WebSocket from 'ws';
|
|
5
|
+
import { clearInterval, clearTimeout, setTimeout } from 'node:timers';
|
|
6
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
7
|
+
import { registerNapcatAgentEndpoint } from './napcat-agent-deps.js';
|
|
8
|
+
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
9
|
+
import { buildSendAction, buildWsConnectOptions, formatInboundContent, formatInboundTarget, formatOutboundSegments, isMessageEvent, senderNickname, senderUserId, } from './protocol.js';
|
|
10
|
+
import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, startNapCatHeartbeat, } from './ws-transport.js';
|
|
11
|
+
const logger = getLogger('napcat');
|
|
12
|
+
export class NapCatWsEndpoint {
|
|
13
|
+
#options;
|
|
14
|
+
#inboundDeduper = new InboundMessageDeduper();
|
|
15
|
+
#ws;
|
|
16
|
+
#reconnectTimer;
|
|
17
|
+
#heartbeatTimer;
|
|
18
|
+
#requestId = { value: 0 };
|
|
19
|
+
#pending = new Map();
|
|
20
|
+
#open = false;
|
|
21
|
+
#started = false;
|
|
22
|
+
#stopping = false;
|
|
23
|
+
#unregisterAgent;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.#options = options;
|
|
26
|
+
}
|
|
27
|
+
async start() {
|
|
28
|
+
if (this.#started)
|
|
29
|
+
return;
|
|
30
|
+
this.#started = true;
|
|
31
|
+
this.#stopping = false;
|
|
32
|
+
this.#unregisterAgent = registerNapcatAgentEndpoint(this.#options.config.name, this);
|
|
33
|
+
try {
|
|
34
|
+
await this.#connect();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
this.#started = false;
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
open() {
|
|
42
|
+
this.#open = true;
|
|
43
|
+
}
|
|
44
|
+
close() {
|
|
45
|
+
this.#open = false;
|
|
46
|
+
}
|
|
47
|
+
async stop() {
|
|
48
|
+
this.#open = false;
|
|
49
|
+
this.#stopping = true;
|
|
50
|
+
this.#started = false;
|
|
51
|
+
this.#unregisterAgent?.();
|
|
52
|
+
this.#unregisterAgent = undefined;
|
|
53
|
+
if (this.#reconnectTimer) {
|
|
54
|
+
clearTimeout(this.#reconnectTimer);
|
|
55
|
+
this.#reconnectTimer = undefined;
|
|
56
|
+
}
|
|
57
|
+
if (this.#heartbeatTimer) {
|
|
58
|
+
clearInterval(this.#heartbeatTimer);
|
|
59
|
+
this.#heartbeatTimer = undefined;
|
|
60
|
+
}
|
|
61
|
+
rejectAllPending(this.#pending);
|
|
62
|
+
this.#inboundDeduper.clear();
|
|
63
|
+
if (this.#ws) {
|
|
64
|
+
try {
|
|
65
|
+
this.#ws.close();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* ignore */
|
|
69
|
+
}
|
|
70
|
+
this.#ws = undefined;
|
|
71
|
+
}
|
|
72
|
+
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
|
|
73
|
+
}
|
|
74
|
+
async send({ target, payload }) {
|
|
75
|
+
const message = formatOutboundSegments(payload);
|
|
76
|
+
const { action, params } = buildSendAction(target, message);
|
|
77
|
+
const data = await this.callApi(action, params);
|
|
78
|
+
const messageId = data?.message_id != null ? String(data.message_id) : '';
|
|
79
|
+
logger.debug(formatCompact({
|
|
80
|
+
op: 'napcat_send',
|
|
81
|
+
endpoint: this.#options.config.name,
|
|
82
|
+
target,
|
|
83
|
+
messageId,
|
|
84
|
+
}));
|
|
85
|
+
return messageId;
|
|
86
|
+
}
|
|
87
|
+
callApi(action, params = {}) {
|
|
88
|
+
return callNapCatWsAction(this.#ws, this.#pending, this.#requestId, action, params);
|
|
89
|
+
}
|
|
90
|
+
// ── Agent-facing API wrappers ─────────────────────────────────────
|
|
91
|
+
async setTitle(groupId, userId, title, duration = -1) {
|
|
92
|
+
await this.callApi('set_group_special_title', {
|
|
93
|
+
group_id: groupId,
|
|
94
|
+
user_id: userId,
|
|
95
|
+
special_title: title,
|
|
96
|
+
duration,
|
|
97
|
+
});
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
sendLike(userId, times = 1) {
|
|
101
|
+
return this.callApi('send_like', { user_id: userId, times });
|
|
102
|
+
}
|
|
103
|
+
deleteFriend(userId) {
|
|
104
|
+
return this.callApi('delete_friend', { user_id: userId });
|
|
105
|
+
}
|
|
106
|
+
markMsgAsRead(messageId) {
|
|
107
|
+
return this.callApi('mark_msg_as_read', { message_id: messageId });
|
|
108
|
+
}
|
|
109
|
+
ocrImage(image) {
|
|
110
|
+
return this.callApi('ocr_image', { image });
|
|
111
|
+
}
|
|
112
|
+
setQQProfile(nickname, company, email, college, personalNote) {
|
|
113
|
+
return this.callApi('set_qq_profile', {
|
|
114
|
+
nickname,
|
|
115
|
+
company,
|
|
116
|
+
email,
|
|
117
|
+
college,
|
|
118
|
+
personal_note: personalNote,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
setGroupPortrait(groupId, file) {
|
|
122
|
+
return this.callApi('set_group_portrait', { group_id: groupId, file });
|
|
123
|
+
}
|
|
124
|
+
setEssenceMsg(messageId) {
|
|
125
|
+
return this.callApi('set_essence_msg', { message_id: messageId });
|
|
126
|
+
}
|
|
127
|
+
deleteEssenceMsg(messageId) {
|
|
128
|
+
return this.callApi('delete_essence_msg', { message_id: messageId });
|
|
129
|
+
}
|
|
130
|
+
getEssenceMsgList(groupId) {
|
|
131
|
+
return this.callApi('get_essence_msg_list', { group_id: groupId });
|
|
132
|
+
}
|
|
133
|
+
sendGroupSign(groupId) {
|
|
134
|
+
return this.callApi('send_group_sign', { group_id: groupId });
|
|
135
|
+
}
|
|
136
|
+
sendGroupNotice(groupId, content, image) {
|
|
137
|
+
return this.callApi('_send_group_notice', { group_id: groupId, content, image });
|
|
138
|
+
}
|
|
139
|
+
getGroupNotice(groupId) {
|
|
140
|
+
return this.callApi('_get_group_notice', { group_id: groupId });
|
|
141
|
+
}
|
|
142
|
+
deleteGroupNotice(groupId, noticeId) {
|
|
143
|
+
return this.callApi('_del_group_notice', { group_id: groupId, notice_id: noticeId });
|
|
144
|
+
}
|
|
145
|
+
uploadGroupFile(groupId, file, name, folder) {
|
|
146
|
+
return this.callApi('upload_group_file', { group_id: groupId, file, name, folder });
|
|
147
|
+
}
|
|
148
|
+
getGroupRootFiles(groupId) {
|
|
149
|
+
return this.callApi('get_group_root_files', { group_id: groupId });
|
|
150
|
+
}
|
|
151
|
+
getGroupFileUrl(groupId, fileId, busid) {
|
|
152
|
+
return this.callApi('get_group_file_url', { group_id: groupId, file_id: fileId, busid });
|
|
153
|
+
}
|
|
154
|
+
downloadFile(url, threadCount = 1, headers) {
|
|
155
|
+
return this.callApi('download_file', { url, thread_count: threadCount, headers });
|
|
156
|
+
}
|
|
157
|
+
setOnlineStatus(status, extStatus) {
|
|
158
|
+
return this.callApi('set_online_status', { status, ext_status: extStatus });
|
|
159
|
+
}
|
|
160
|
+
setQQAvatar(file) {
|
|
161
|
+
return this.callApi('set_qq_avatar', { file });
|
|
162
|
+
}
|
|
163
|
+
forwardFriendSingleMsg(userId, messageId) {
|
|
164
|
+
return this.callApi('forward_friend_single_msg', { user_id: userId, message_id: messageId });
|
|
165
|
+
}
|
|
166
|
+
forwardGroupSingleMsg(groupId, messageId) {
|
|
167
|
+
return this.callApi('forward_group_single_msg', { group_id: groupId, message_id: messageId });
|
|
168
|
+
}
|
|
169
|
+
translateEn2Zh(sourceText) {
|
|
170
|
+
return this.callApi('translate_en2zh', { source_text: sourceText });
|
|
171
|
+
}
|
|
172
|
+
setMsgEmojiLike(messageId, emojiId) {
|
|
173
|
+
return this.callApi('set_msg_emoji_like', { message_id: messageId, emoji_id: emojiId });
|
|
174
|
+
}
|
|
175
|
+
sendForwardMsg(messageType, id, messages) {
|
|
176
|
+
return this.callApi('send_forward_msg', {
|
|
177
|
+
message_type: messageType,
|
|
178
|
+
[messageType === 'group' ? 'group_id' : 'user_id']: id,
|
|
179
|
+
messages,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
getFriendMsgHistory(userId, messageSeq, count) {
|
|
183
|
+
return this.callApi('get_friend_msg_history', {
|
|
184
|
+
user_id: userId,
|
|
185
|
+
message_seq: messageSeq,
|
|
186
|
+
count,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
getGroupMsgHistory(groupId, messageSeq, count) {
|
|
190
|
+
return this.callApi('get_group_msg_history', {
|
|
191
|
+
group_id: groupId,
|
|
192
|
+
message_seq: messageSeq,
|
|
193
|
+
count,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
setSelfLongnick(longnick) {
|
|
197
|
+
return this.callApi('set_self_longnick', { longNick: longnick });
|
|
198
|
+
}
|
|
199
|
+
getGroupInfoEx(groupId) {
|
|
200
|
+
return this.callApi('get_group_info_ex', { group_id: groupId });
|
|
201
|
+
}
|
|
202
|
+
sendPoke(userId, groupId) {
|
|
203
|
+
return this.callApi('send_poke', { user_id: userId, group_id: groupId });
|
|
204
|
+
}
|
|
205
|
+
ncGetUserStatus(userId) {
|
|
206
|
+
return this.callApi('nc_get_user_status', { user_id: userId });
|
|
207
|
+
}
|
|
208
|
+
getGroupShutList(groupId) {
|
|
209
|
+
return this.callApi('get_group_shut_list', { group_id: groupId });
|
|
210
|
+
}
|
|
211
|
+
getMiniAppArk(type, title, desc, picUrl, jumpUrl) {
|
|
212
|
+
return this.callApi('get_mini_app_ark', { type, title, desc, picUrl, jumpUrl });
|
|
213
|
+
}
|
|
214
|
+
getAiCharacters(groupId) {
|
|
215
|
+
return this.callApi('get_ai_characters', { group_id: groupId });
|
|
216
|
+
}
|
|
217
|
+
sendGroupAiRecord(groupId, characterId, text) {
|
|
218
|
+
return this.callApi('send_group_ai_record', {
|
|
219
|
+
group_id: groupId,
|
|
220
|
+
character: characterId,
|
|
221
|
+
text,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
225
|
+
admit(ev) {
|
|
226
|
+
if (!this.#open || !isMessageEvent(ev))
|
|
227
|
+
return;
|
|
228
|
+
if (isSelfMessage(ev))
|
|
229
|
+
return;
|
|
230
|
+
const msgId = String(ev.message_id);
|
|
231
|
+
if (!this.#inboundDeduper.shouldProcess(msgId))
|
|
232
|
+
return;
|
|
233
|
+
if (Array.isArray(ev.message) || typeof ev.message === 'string') {
|
|
234
|
+
ev = { ...ev, message: normalizeMessage(ev.message) };
|
|
235
|
+
}
|
|
236
|
+
const target = formatInboundTarget(ev);
|
|
237
|
+
const content = formatInboundContent(ev);
|
|
238
|
+
const nickname = senderNickname(ev);
|
|
239
|
+
const mentioned = isNapCatBotMentioned(ev);
|
|
240
|
+
void this.#options.gateway.receive({
|
|
241
|
+
adapter: this.#options.id,
|
|
242
|
+
target,
|
|
243
|
+
content,
|
|
244
|
+
sender: senderUserId(ev),
|
|
245
|
+
id: msgId,
|
|
246
|
+
metadata: Object.freeze({
|
|
247
|
+
message_type: ev.message_type,
|
|
248
|
+
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
249
|
+
group_id: ev.group_id != null ? String(ev.group_id) : undefined,
|
|
250
|
+
endpoint: this.#options.config.name,
|
|
251
|
+
time: ev.time,
|
|
252
|
+
self_id: ev.self_id != null ? String(ev.self_id) : undefined,
|
|
253
|
+
role: ev.sender?.role,
|
|
254
|
+
...(nickname ? { nickname } : {}),
|
|
255
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
256
|
+
}),
|
|
257
|
+
}).catch((err) => {
|
|
258
|
+
logger.warn(formatCompact({
|
|
259
|
+
op: 'napcat_gateway_receive_failed',
|
|
260
|
+
target,
|
|
261
|
+
error: err instanceof Error ? err.message : String(err),
|
|
262
|
+
}));
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async #connect() {
|
|
266
|
+
const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
|
|
267
|
+
const create = this.#options.createWebSocket
|
|
268
|
+
?? ((connectUrl, options) => new WebSocket(connectUrl, { headers: options.headers }));
|
|
269
|
+
await new Promise((resolve, reject) => {
|
|
270
|
+
let settled = false;
|
|
271
|
+
const ws = create(url, { headers });
|
|
272
|
+
this.#ws = ws;
|
|
273
|
+
ws.on('open', () => {
|
|
274
|
+
if (settled)
|
|
275
|
+
return;
|
|
276
|
+
settled = true;
|
|
277
|
+
if (!this.#options.config.access_token) {
|
|
278
|
+
logger.warn(formatCompact({
|
|
279
|
+
endpoint: this.#options.config.name,
|
|
280
|
+
ok: false,
|
|
281
|
+
error: 'missing access_token',
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
logger.debug(formatCompact({
|
|
285
|
+
endpoint: this.#options.config.name,
|
|
286
|
+
mode: 'ws',
|
|
287
|
+
url: safeUrl,
|
|
288
|
+
}));
|
|
289
|
+
this.#heartbeatTimer = this.#stopping
|
|
290
|
+
? this.#heartbeatTimer
|
|
291
|
+
: startNapCatHeartbeat(this.#ws, this.#options.config.heartbeat_interval, this.#heartbeatTimer);
|
|
292
|
+
resolve();
|
|
293
|
+
});
|
|
294
|
+
ws.on('message', (data) => {
|
|
295
|
+
handleNapCatWsMessage(data, {
|
|
296
|
+
endpointName: this.#options.config.name,
|
|
297
|
+
pending: this.#pending,
|
|
298
|
+
admit: (event) => this.admit(event),
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
ws.on('close', (code, reason) => {
|
|
302
|
+
const reasonStr = typeof reason === 'string'
|
|
303
|
+
? reason
|
|
304
|
+
: Buffer.isBuffer(reason)
|
|
305
|
+
? reason.toString()
|
|
306
|
+
: String(reason ?? '');
|
|
307
|
+
const codeNum = typeof code === 'number' ? code : Number(code ?? 0);
|
|
308
|
+
const codeHint = codeNum === 1005
|
|
309
|
+
? ' [no status]'
|
|
310
|
+
: codeNum === 1006
|
|
311
|
+
? ' [abnormal]'
|
|
312
|
+
: '';
|
|
313
|
+
logger.warn(formatCompact({
|
|
314
|
+
op: 'disconnect',
|
|
315
|
+
endpoint: this.#options.config.name,
|
|
316
|
+
code: codeNum,
|
|
317
|
+
error: `${reasonStr || 'closed'}${codeHint}`,
|
|
318
|
+
reconnect_ms: this.#options.config.reconnect_interval,
|
|
319
|
+
}));
|
|
320
|
+
if (!settled) {
|
|
321
|
+
settled = true;
|
|
322
|
+
reject(new Error(`NapCat WS closed: ${codeNum} ${reasonStr}`));
|
|
323
|
+
}
|
|
324
|
+
this.#scheduleReconnect();
|
|
325
|
+
});
|
|
326
|
+
ws.on('error', (err) => {
|
|
327
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
328
|
+
logger.warn(formatCompact({
|
|
329
|
+
op: 'ws_error',
|
|
330
|
+
endpoint: this.#options.config.name,
|
|
331
|
+
ok: false,
|
|
332
|
+
error: error.message,
|
|
333
|
+
}));
|
|
334
|
+
if (!settled) {
|
|
335
|
+
settled = true;
|
|
336
|
+
reject(error);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
#scheduleReconnect() {
|
|
342
|
+
if (this.#stopping || !this.#started || this.#reconnectTimer)
|
|
343
|
+
return;
|
|
344
|
+
const delay = this.#options.config.reconnect_interval;
|
|
345
|
+
this.#reconnectTimer = setTimeout(() => {
|
|
346
|
+
this.#reconnectTimer = undefined;
|
|
347
|
+
void this.#connect().catch((err) => {
|
|
348
|
+
logger.warn(formatCompact({
|
|
349
|
+
op: 'reconnect',
|
|
350
|
+
endpoint: this.#options.config.name,
|
|
351
|
+
ok: false,
|
|
352
|
+
error: err instanceof Error ? err.message : String(err),
|
|
353
|
+
}));
|
|
354
|
+
});
|
|
355
|
+
}, delay);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type NapCatEvent } from './protocol.js';
|
|
2
|
+
import { type NapCatPendingAction, type NapCatWsSocket } from './ws-types.js';
|
|
3
|
+
export declare function decodeWsPayload(data: unknown): string;
|
|
4
|
+
export declare function handleNapCatWsMessage(data: unknown, options: {
|
|
5
|
+
readonly endpointName: string;
|
|
6
|
+
readonly pending: Map<string, NapCatPendingAction>;
|
|
7
|
+
readonly admit: (ev: NapCatEvent) => void;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function callNapCatWsAction(ws: NapCatWsSocket | undefined, pending: Map<string, NapCatPendingAction>, requestId: {
|
|
10
|
+
value: number;
|
|
11
|
+
}, action: string, params: Record<string, unknown>): Promise<unknown>;
|
|
12
|
+
export declare function startNapCatHeartbeat(ws: NapCatWsSocket | undefined, intervalMs: number, existingTimer?: NodeJS.Timeout): NodeJS.Timeout;
|
|
13
|
+
export declare function rejectAllPending(pending: Map<string, NapCatPendingAction>, message?: string): void;
|