napcat-sdk 0.1.0 → 0.1.2
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/dist/index.cjs +116 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +264 -287
- package/dist/index.d.mts +264 -287
- package/dist/index.mjs +116 -153
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -3
- package/readme.md +70 -0
- package/.node-version +0 -1
- package/src/index.ts +0 -4
- package/src/logger.ts +0 -27
- package/src/napcat.ts +0 -497
- package/src/onebot.ts +0 -1381
- package/src/segment.ts +0 -59
- package/src/types.ts +0 -34
- package/tsconfig.json +0 -3
- package/tsdown.config.ts +0 -12
package/readme.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# NapCat SDK for TypeScript
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
The NapCat SDK for TypeScript allows developers to easily integrate NapCat's functionalities into their TypeScript applications. This SDK provides a set of tools and utilities to interact with NapCat services seamlessly.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
You can install the NapCat SDK via npm. Run the following command in your terminal:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm install napcat-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
To connect to NapCat, you need to create an instance of the NapCat client. Here's a simple example:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { NapCat, segment } from 'napcat-sdk'
|
|
21
|
+
|
|
22
|
+
// 1. Create a new NapCat client instance
|
|
23
|
+
const napcat = new NapCat({
|
|
24
|
+
// protocol: 'ws', // Optional: specify the protocol (default is 'ws')
|
|
25
|
+
// host: 'localhost', // Optional: specify a custom host
|
|
26
|
+
// port: 3333, // Optional: specify a custom port
|
|
27
|
+
token: 'here-your-auth-token', // Required: your authentication token
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// 2. Subscribe to events
|
|
31
|
+
napcat.on('message', (event) => {
|
|
32
|
+
// replay is a method to send a message quickly, optional with reply mark
|
|
33
|
+
event.reply('Hello from NapCat SDK!', true) // true is for reply mark
|
|
34
|
+
|
|
35
|
+
// you can call all the NapCat api through `napcat.api()` method
|
|
36
|
+
const { value } = await napcat.api<{ value: unknown }>('awesome-function')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// you can also listen to specific message sub-types
|
|
40
|
+
napcat.on('message.group', async (event) => {
|
|
41
|
+
// all methods of a message event are available
|
|
42
|
+
await event.addEssence(event.message_id)
|
|
43
|
+
await event.recall()
|
|
44
|
+
|
|
45
|
+
// You can also interact with group instance to do some operations
|
|
46
|
+
await event.group.setTitle(114514, 'Special Title')
|
|
47
|
+
|
|
48
|
+
// message to send is allowed to be an array of segments
|
|
49
|
+
await event.reply(['Hi! ', napcat.segment.face(66)])
|
|
50
|
+
|
|
51
|
+
// or just use napcat to send messages
|
|
52
|
+
await napcat.sendGroupMsg(event.group_id, 'Hello Group!')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// and more events...
|
|
56
|
+
napcat.on('notice', (event) => {})
|
|
57
|
+
napcat.on('notice.group', (event) => {})
|
|
58
|
+
napcat.on('request', (event) => {})
|
|
59
|
+
napcat.on('request.group.invite', (event) => {
|
|
60
|
+
// approve the group invite request, or event.reject() to reject
|
|
61
|
+
event.approve()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// close the connection when needed
|
|
65
|
+
napcat.close()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT License © 2025-PRESENT Viki
|
package/.node-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
24.11.1
|
package/src/index.ts
DELETED
package/src/logger.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
|
|
2
|
-
|
|
3
|
-
export type Logger = Record<LogLevel, (...args: unknown[]) => void>
|
|
4
|
-
|
|
5
|
-
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
6
|
-
fatal: 0,
|
|
7
|
-
error: 1,
|
|
8
|
-
warn: 2,
|
|
9
|
-
info: 3,
|
|
10
|
-
debug: 4,
|
|
11
|
-
trace: 5,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const LOG_LEVEL_KEYS = Object.keys(LOG_LEVELS) as LogLevel[]
|
|
15
|
-
|
|
16
|
-
const noop = () => {}
|
|
17
|
-
|
|
18
|
-
export const ABSTRACT_LOGGER: Logger = Object.fromEntries(LOG_LEVEL_KEYS.map((level) => [level, noop])) as Logger
|
|
19
|
-
|
|
20
|
-
export const CONSOLE_LOGGER: Logger = Object.fromEntries(
|
|
21
|
-
LOG_LEVEL_KEYS.map((level) => [
|
|
22
|
-
level,
|
|
23
|
-
(...args: unknown[]) => {
|
|
24
|
-
console[level === 'fatal' ? 'error' : level === 'trace' ? 'debug' : level](...args)
|
|
25
|
-
},
|
|
26
|
-
]),
|
|
27
|
-
) as Logger
|
package/src/napcat.ts
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto'
|
|
2
|
-
import mitt from 'mitt'
|
|
3
|
-
import pkg from '../package.json' with { type: 'json' }
|
|
4
|
-
import { segment } from './segment'
|
|
5
|
-
import { CONSOLE_LOGGER, ABSTRACT_LOGGER } from './logger'
|
|
6
|
-
import { NAPCAT_NOTICE_EVENT_MAP, NAPCAT_NOTICE_NOTIFY_MAP } from './onebot'
|
|
7
|
-
|
|
8
|
-
import type { Emitter } from 'mitt'
|
|
9
|
-
import type { Logger } from './logger'
|
|
10
|
-
import type { EventMap, MiokiOptions, OptionalProps } from './types'
|
|
11
|
-
import type {
|
|
12
|
-
API,
|
|
13
|
-
Friend,
|
|
14
|
-
FriendWithInfo,
|
|
15
|
-
Group,
|
|
16
|
-
GroupMessageEvent,
|
|
17
|
-
GroupWithInfo,
|
|
18
|
-
NormalizedElementToSend,
|
|
19
|
-
PrivateMessageEvent,
|
|
20
|
-
Sendable,
|
|
21
|
-
} from './onebot'
|
|
22
|
-
|
|
23
|
-
export const name = pkg.name
|
|
24
|
-
export const version = pkg.version
|
|
25
|
-
|
|
26
|
-
export { CONSOLE_LOGGER, ABSTRACT_LOGGER, pkg as PKG }
|
|
27
|
-
|
|
28
|
-
export const DEFAULT_NAPCAT_OPTIONS = {
|
|
29
|
-
protocol: 'ws',
|
|
30
|
-
host: 'localhost',
|
|
31
|
-
port: 3333,
|
|
32
|
-
logger: ABSTRACT_LOGGER,
|
|
33
|
-
} satisfies Required<OptionalProps<MiokiOptions>>
|
|
34
|
-
|
|
35
|
-
export class NapCat {
|
|
36
|
-
/** WebSocket 实例 */
|
|
37
|
-
#ws: WebSocket | null = null
|
|
38
|
-
/** 事件发射器 */
|
|
39
|
-
#event: Emitter<EventMap & Record<string | symbol, unknown>> = mitt()
|
|
40
|
-
/** Echo 事件发射器 */
|
|
41
|
-
#echoEvent: Emitter<Record<string, unknown>> = mitt()
|
|
42
|
-
|
|
43
|
-
constructor(private readonly options: MiokiOptions) {}
|
|
44
|
-
|
|
45
|
-
/** 配置项 */
|
|
46
|
-
get #config(): Required<MiokiOptions> {
|
|
47
|
-
return {
|
|
48
|
-
protocol: this.options.protocol || DEFAULT_NAPCAT_OPTIONS.protocol,
|
|
49
|
-
host: this.options.host || DEFAULT_NAPCAT_OPTIONS.host,
|
|
50
|
-
port: this.options.port || DEFAULT_NAPCAT_OPTIONS.port,
|
|
51
|
-
logger: this.options.logger || DEFAULT_NAPCAT_OPTIONS.logger,
|
|
52
|
-
token: this.options.token,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** WebSocket 实例 */
|
|
57
|
-
get ws(): WebSocket {
|
|
58
|
-
if (!this.#ws) {
|
|
59
|
-
this.logger.error('WebSocket is not connected.')
|
|
60
|
-
throw new Error('WebSocket is not connected.')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return this.#ws
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** 日志记录器 */
|
|
67
|
-
get logger(): Logger {
|
|
68
|
-
return this.#config.logger
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** 消息段构建器 */
|
|
72
|
-
get segment(): typeof segment {
|
|
73
|
-
return segment
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** 生成唯一的 echo ID */
|
|
77
|
-
#echoId() {
|
|
78
|
-
return crypto.randomBytes(16).toString('hex')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** 构建 WebSocket 连接地址 */
|
|
82
|
-
#buildWsUrl(): string {
|
|
83
|
-
return `${this.#config.protocol}://${this.#config.host}:${this.#config.port}?access_token=${this.#config.token}`
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** 包装回复消息 */
|
|
87
|
-
#wrapReply(sendable: Sendable | Sendable[], message_id?: number, reply?: boolean): Sendable[] {
|
|
88
|
-
const sendableList = typeof sendable === 'string' ? [sendable] : [sendable].flat()
|
|
89
|
-
|
|
90
|
-
if (reply && message_id) {
|
|
91
|
-
return [segment.reply(String(message_id)), ...sendableList]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return sendableList
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** 确保 WebSocket 已连接 */
|
|
98
|
-
#ensureWsConnection(ws: WebSocket | null): asserts ws is WebSocket {
|
|
99
|
-
if (!ws) {
|
|
100
|
-
this.logger.error('WebSocket is not connected.')
|
|
101
|
-
throw new Error('WebSocket is not connected.')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (ws.readyState !== WebSocket.OPEN) {
|
|
105
|
-
this.logger.error('WebSocket is not open.')
|
|
106
|
-
throw new Error('WebSocket is not open.')
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** 标准化可发送消息元素 */
|
|
111
|
-
#normalizeSendable(msg: Sendable | Sendable[]): NormalizedElementToSend[] {
|
|
112
|
-
return [msg].flat(2).map((item) => {
|
|
113
|
-
if (typeof item === 'string') {
|
|
114
|
-
return { type: 'text', data: { text: item } }
|
|
115
|
-
}
|
|
116
|
-
if (item.type === 'at') {
|
|
117
|
-
return { type: 'at', data: { qq: String(item.qq) } }
|
|
118
|
-
}
|
|
119
|
-
const { type, ...data } = item
|
|
120
|
-
return { type, data } as NormalizedElementToSend
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** 等待服务器响应操作 */
|
|
125
|
-
#waitForAction<T extends any>(echoId: string) {
|
|
126
|
-
const eventName = `echo#${echoId}`
|
|
127
|
-
|
|
128
|
-
return new Promise<T>((resolve, reject) => {
|
|
129
|
-
const handle = (data: any) => {
|
|
130
|
-
if (!data || data.echo !== echoId) return
|
|
131
|
-
|
|
132
|
-
this.#echoEvent.off(eventName, handle)
|
|
133
|
-
|
|
134
|
-
if (data.retcode === 0) {
|
|
135
|
-
resolve(data.data as T)
|
|
136
|
-
} else {
|
|
137
|
-
reject(`Server Error: ${data.message}`)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.#echoEvent.on(eventName, handle)
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** 构建群对象 */
|
|
146
|
-
#buildGroup<T extends object>(group_id: number, group_name: string = '', extraInfo: T = {} as T): Group & T {
|
|
147
|
-
return {
|
|
148
|
-
...extraInfo,
|
|
149
|
-
group_id,
|
|
150
|
-
group_name,
|
|
151
|
-
doSign: () => this.api('set_group_sign', { group_id }),
|
|
152
|
-
getInfo: () => this.api('get_group_info', { group_id }),
|
|
153
|
-
getMemberList: async () => this.api('get_group_member_list', { group_id }),
|
|
154
|
-
getMemberInfo: (user_id: number) => this.api('get_group_member_info', { group_id, user_id }),
|
|
155
|
-
setTitle: (title: string) => this.api('set_group_special_title', { group_id, title }),
|
|
156
|
-
setCard: (user_id: number, card: string) => this.api('set_group_card', { group_id, user_id, card }),
|
|
157
|
-
addEssence: (message_id: string) => this.api('set_essence_msg', { message_id }),
|
|
158
|
-
delEssence: (message_id: string) => this.api('delete_essence_msg', { message_id }),
|
|
159
|
-
recall: (message_id: number) => this.api('delete_msg', { message_id }),
|
|
160
|
-
banMember: (user_id: number, duration: number) => this.api('set_group_ban', { group_id, user_id, duration }),
|
|
161
|
-
sendMsg: (sendable: Sendable | Sendable[]) => this.sendGroupMsg(group_id, sendable),
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** 构建好友对象 */
|
|
166
|
-
#buildFriend<T extends object>(user_id: number, nickname: string = '', extraInfo: T = {} as T): Friend & T {
|
|
167
|
-
return {
|
|
168
|
-
...extraInfo,
|
|
169
|
-
user_id,
|
|
170
|
-
nickname,
|
|
171
|
-
delete: (block?: boolean, both?: boolean) =>
|
|
172
|
-
this.api('delete_friend', { user_id, temp_block: block, temp_both_del: both }),
|
|
173
|
-
sendMsg: (sendable: Sendable | Sendable[]) => this.sendPrivateMsg(user_id, sendable),
|
|
174
|
-
getInfo: () => this.api('get_stranger_info', { user_id }),
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/** 构建群消息事件 */
|
|
179
|
-
#buildPrivateMessageEvent(event: PrivateMessageEvent) {
|
|
180
|
-
return {
|
|
181
|
-
...event,
|
|
182
|
-
message: (event.message || []).map((el: any) => ({ type: el.type, ...el.data })),
|
|
183
|
-
friend: this.#buildFriend(event.user_id, event.sender?.nickname || ''),
|
|
184
|
-
recall: () => this.api('delete_msg', { message_id: event.message_id }),
|
|
185
|
-
reply: (sendable: Sendable | Sendable[], reply = false) =>
|
|
186
|
-
this.sendPrivateMsg(event.user_id, this.#wrapReply(sendable, event.message_id, reply)),
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** 构建群消息事件对象 */
|
|
191
|
-
#buildGroupMessageEvent(event: GroupMessageEvent) {
|
|
192
|
-
return {
|
|
193
|
-
...event,
|
|
194
|
-
message: (event.message || []).map((el: any) => ({ type: el.type, ...el.data })),
|
|
195
|
-
group: this.#buildGroup(event.group_id, event.group?.group_name || ''),
|
|
196
|
-
recall: () => this.api('delete_msg', { message_id: event.message_id }),
|
|
197
|
-
addReaction: (id: string) =>
|
|
198
|
-
this.api('set_msg_emoji_like', { message_id: event.message_id, emoji_id: id, set: true }),
|
|
199
|
-
delReaction: (id: string) =>
|
|
200
|
-
this.api('set_msg_emoji_like', { message_id: event.message_id, emoji_id: id, set: false }),
|
|
201
|
-
addEssence: () => this.api('set_essence_msg', { message_id: event.message_id }),
|
|
202
|
-
delEssence: () => this.api('delete_essence_msg', { message_id: event.message_id }),
|
|
203
|
-
reply: (sendable: Sendable | Sendable[], reply = false) =>
|
|
204
|
-
this.sendGroupMsg(event.group_id, this.#wrapReply(sendable, event.message_id, reply)),
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** 绑定内部事件处理器 */
|
|
209
|
-
#bindInternalEvents(data: any) {
|
|
210
|
-
if (data.echo) {
|
|
211
|
-
this.#echoEvent.emit(`echo#${data.echo}`, data)
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (data.post_type) {
|
|
216
|
-
switch (data.post_type) {
|
|
217
|
-
case 'meta_event': {
|
|
218
|
-
this.logger.trace(`received meta_event: ${JSON.stringify(data)}`)
|
|
219
|
-
this.#event.emit('meta_event', data)
|
|
220
|
-
|
|
221
|
-
if (data.meta_event_type) {
|
|
222
|
-
this.#event.emit(`meta_event.${data.meta_event_type}`, data)
|
|
223
|
-
if (data.sub_type) {
|
|
224
|
-
this.#event.emit(`meta_event.${data.meta_event_type}.${data.sub_type}`, data)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
break
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
case 'message': {
|
|
232
|
-
if (data.message_type === 'private') {
|
|
233
|
-
data = this.#buildPrivateMessageEvent(data)
|
|
234
|
-
} else {
|
|
235
|
-
data = this.#buildGroupMessageEvent(data)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
this.#event.emit('message', data)
|
|
239
|
-
|
|
240
|
-
switch (data.message_type) {
|
|
241
|
-
case 'private': {
|
|
242
|
-
this.logger.trace(`received private message: ${JSON.stringify(data)}`)
|
|
243
|
-
this.#event.emit('message.private', data)
|
|
244
|
-
this.#event.emit(`message.private.${data.sub_type}`, data)
|
|
245
|
-
|
|
246
|
-
break
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
case 'group': {
|
|
250
|
-
this.logger.trace(`received group message: ${JSON.stringify(data)}`)
|
|
251
|
-
this.#event.emit('message.group', data)
|
|
252
|
-
this.#event.emit(`message.group.${data.sub_type}`, data)
|
|
253
|
-
|
|
254
|
-
break
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
default: {
|
|
258
|
-
this.logger.debug(`received unknown message type: ${JSON.stringify(data)}`)
|
|
259
|
-
|
|
260
|
-
break
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
break
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
case 'message_sent': {
|
|
268
|
-
this.logger.trace(`received message_sent: ${JSON.stringify(data)}`)
|
|
269
|
-
this.#event.emit('message_sent', data)
|
|
270
|
-
|
|
271
|
-
if (data.message_type) {
|
|
272
|
-
this.#event.emit(`message_sent.${data.message_type}`, data)
|
|
273
|
-
if (data.sub_type) {
|
|
274
|
-
this.#event.emit(`message_sent.${data.message_type}.${data.sub_type}`, data)
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
break
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
case 'notice': {
|
|
282
|
-
this.logger.trace(`received notice: ${JSON.stringify(data)}`)
|
|
283
|
-
|
|
284
|
-
if (!data.notice_type) {
|
|
285
|
-
this.logger.debug(`received unknown notice type: ${JSON.stringify(data)}`)
|
|
286
|
-
break
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const isNotify = data.notice_type === 'notify'
|
|
290
|
-
const isPoke = data.sub_type === 'poke'
|
|
291
|
-
const isGroup = !!data.group_id
|
|
292
|
-
|
|
293
|
-
const { notice_type, sub_type } = isNotify
|
|
294
|
-
? isPoke
|
|
295
|
-
? { notice_type: isGroup ? 'group' : 'friend', sub_type: 'poke' }
|
|
296
|
-
: NAPCAT_NOTICE_NOTIFY_MAP[data.sub_type] || {}
|
|
297
|
-
: NAPCAT_NOTICE_EVENT_MAP[data.notice_type] || {}
|
|
298
|
-
|
|
299
|
-
data.original_notice_type = data.notice_type
|
|
300
|
-
data.notice_type = notice_type || data.notice_type
|
|
301
|
-
|
|
302
|
-
if (data.sub_type && data.sub_type !== sub_type) {
|
|
303
|
-
data.action_type = data.sub_type
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
data.sub_type = sub_type || data.sub_type
|
|
307
|
-
|
|
308
|
-
if (isGroup) {
|
|
309
|
-
data.group = this.#buildGroup(data.group_id, data.group_name || '')
|
|
310
|
-
} else {
|
|
311
|
-
data.friend = this.#buildFriend(data.user_id, data.nickname || '')
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
this.#event.emit('notice', data)
|
|
315
|
-
|
|
316
|
-
if (notice_type) {
|
|
317
|
-
this.#event.emit(`notice.${notice_type}`, data)
|
|
318
|
-
if (sub_type) {
|
|
319
|
-
this.#event.emit(`notice.${notice_type}.${sub_type}`, data)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
break
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
case 'request': {
|
|
327
|
-
this.logger.trace(`received request: ${JSON.stringify(data)}`)
|
|
328
|
-
|
|
329
|
-
if (data.request_type === 'friend') {
|
|
330
|
-
data.reject = () => this.api('set_friend_request', { flag: data.flag, approve: false })
|
|
331
|
-
data.approve = () => this.api('set_friend_request', { flag: data.flag, approve: true })
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (data.request_type === 'group') {
|
|
335
|
-
data.reject = (reason?: string) =>
|
|
336
|
-
this.api('set_group_add_request', { flag: data.flag, approve: false, reason })
|
|
337
|
-
data.approve = () => this.api('set_group_add_request', { flag: data.flag, approve: true })
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
this.#event.emit('request', data)
|
|
341
|
-
|
|
342
|
-
if (data.request_type) {
|
|
343
|
-
this.#event.emit(`request.${data.request_type}`, data)
|
|
344
|
-
if (data.sub_type) {
|
|
345
|
-
this.#event.emit(`request.${data.request_type}.${data.sub_type}`, data)
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
break
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
default: {
|
|
353
|
-
this.logger.debug(`received: ${JSON.stringify(data)}`)
|
|
354
|
-
this.#event.emit(data.post_type, data)
|
|
355
|
-
return
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** 获取一个群的信息,可以用于发送群消息等操作 */
|
|
364
|
-
async pickGroup(group_id: number): Promise<GroupWithInfo> {
|
|
365
|
-
const groupInfo = await this.api<ReturnType<Group['getInfo']>>('get_group_info', { group_id })
|
|
366
|
-
return this.#buildGroup(group_id, groupInfo.group_name, groupInfo)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/** 获取一个好友的信息,可以用于发送私聊消息等操作 */
|
|
370
|
-
async pickFriend(user_id: number): Promise<FriendWithInfo> {
|
|
371
|
-
const friendInfo = await this.api<ReturnType<Friend['getInfo']>>('get_stranger_info', { user_id })
|
|
372
|
-
return this.#buildFriend(user_id, friendInfo.nickname, friendInfo)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* 注册一次性事件监听器
|
|
377
|
-
*/
|
|
378
|
-
once<T extends keyof EventMap>(type: T, handler: (event: EventMap[NoInfer<T>]) => void) {
|
|
379
|
-
const onceHandler = (event: EventMap[NoInfer<T>]) => {
|
|
380
|
-
handler(event)
|
|
381
|
-
this.#event.off(type, onceHandler)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
this.logger.debug(`registering once: ${String(type)}`)
|
|
385
|
-
this.#event.on(type, onceHandler)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* 注册事件监听器,支持主类型或者点分子类型
|
|
390
|
-
*
|
|
391
|
-
* 如: `notice`、`message.private`、`request.group.invite` 等
|
|
392
|
-
*
|
|
393
|
-
* 如果需要移除监听器,请调用 `off` 方法
|
|
394
|
-
*/
|
|
395
|
-
on<T extends keyof EventMap>(type: T, handler: (event: EventMap[NoInfer<T>]) => void) {
|
|
396
|
-
this.logger.debug(`registering: ${String(type)}`)
|
|
397
|
-
this.#event.on(type, handler)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* 移除事件监听器
|
|
402
|
-
*/
|
|
403
|
-
off<T extends keyof EventMap>(type: T, handler: (event: EventMap[NoInfer<T>]) => void) {
|
|
404
|
-
this.logger.debug(`unregistering: ${String(type)}`)
|
|
405
|
-
this.#event.off(type, handler)
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
api<T extends any>(action: API | (string & {}), params: Record<string, any> = {}): Promise<T> {
|
|
409
|
-
this.#ensureWsConnection(this.#ws)
|
|
410
|
-
this.logger.debug(`calling api action: ${action} with params: ${JSON.stringify(params)}`)
|
|
411
|
-
const echo = this.#echoId()
|
|
412
|
-
this.#ws.send(JSON.stringify({ echo, action, params }))
|
|
413
|
-
return this.#waitForAction<T>(echo)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* 发送私聊消息
|
|
418
|
-
*/
|
|
419
|
-
sendPrivateMsg(user_id: number, sendable: Sendable | Sendable[]) {
|
|
420
|
-
return this.api<{ message_id: number }>('send_private_msg', {
|
|
421
|
-
user_id,
|
|
422
|
-
message: this.#normalizeSendable(sendable),
|
|
423
|
-
})
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* 发送群消息
|
|
428
|
-
*/
|
|
429
|
-
sendGroupMsg(group_id: number, sendable: Sendable | Sendable[]) {
|
|
430
|
-
return this.api<{ message_id: number }>('send_group_msg', {
|
|
431
|
-
group_id,
|
|
432
|
-
message: this.#normalizeSendable(sendable),
|
|
433
|
-
})
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/** 启动 NapCat SDK 实例,建立 WebSocket 连接 */
|
|
437
|
-
async bootstrap() {
|
|
438
|
-
const { logger: _, ...config } = this.#config
|
|
439
|
-
|
|
440
|
-
this.logger.info(`bootstrap with config: ${JSON.stringify(config)}`)
|
|
441
|
-
|
|
442
|
-
return new Promise<void>((resolve, reject) => {
|
|
443
|
-
const ws = new WebSocket(this.#buildWsUrl())
|
|
444
|
-
|
|
445
|
-
ws.onmessage = (event) => {
|
|
446
|
-
const data = (() => {
|
|
447
|
-
try {
|
|
448
|
-
return JSON.parse(event.data)
|
|
449
|
-
} catch {
|
|
450
|
-
return null
|
|
451
|
-
}
|
|
452
|
-
})() as any
|
|
453
|
-
|
|
454
|
-
if (!data) {
|
|
455
|
-
this.logger.warn(`received non-json message: ${event.data}`)
|
|
456
|
-
return
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
this.#event.emit('ws.message', data)
|
|
460
|
-
this.#bindInternalEvents(data)
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
ws.onclose = () => {
|
|
464
|
-
this.logger.info('closed')
|
|
465
|
-
this.#event.emit('ws.close')
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
ws.onerror = (error) => {
|
|
469
|
-
this.logger.error(`error: ${error}`)
|
|
470
|
-
this.#event.emit('ws.error', error)
|
|
471
|
-
reject(error)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
ws.onopen = () => {
|
|
475
|
-
this.logger.info('connected')
|
|
476
|
-
this.#event.emit('ws.open')
|
|
477
|
-
resolve()
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
this.#ws = ws
|
|
481
|
-
|
|
482
|
-
this.logger.trace(`WebSocket instance created: ${this.#ws}`)
|
|
483
|
-
})
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/** 销毁 NapCat SDK 实例,关闭 WebSocket 连接 */
|
|
487
|
-
async destroy() {
|
|
488
|
-
if (this.#ws) {
|
|
489
|
-
this.logger.info('destroying NapCat SDK instance...')
|
|
490
|
-
this.#ws.close()
|
|
491
|
-
this.#ws = null
|
|
492
|
-
this.logger.info('NapCat SDK instance destroyed.')
|
|
493
|
-
} else {
|
|
494
|
-
this.logger.warn('NapCat SDK instance is not initialized.')
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|