@zhin.js/adapter-onebot11 1.0.55 → 1.0.56
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 +7 -0
- package/README.md +35 -40
- package/lib/adapter.d.ts +30 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +125 -0
- package/lib/adapter.js.map +1 -0
- package/lib/bot-ws-client.d.ts +39 -0
- package/lib/bot-ws-client.d.ts.map +1 -0
- package/lib/bot-ws-client.js +370 -0
- package/lib/bot-ws-client.js.map +1 -0
- package/lib/bot-ws-server.d.ts +41 -0
- package/lib/bot-ws-server.d.ts.map +1 -0
- package/lib/bot-ws-server.js +308 -0
- package/lib/bot-ws-server.js.map +1 -0
- package/lib/index.d.ts +11 -172
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +12 -827
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +54 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +5 -0
- package/lib/types.js.map +1 -0
- package/package.json +4 -4
- package/src/adapter.ts +147 -0
- package/src/bot-ws-client.ts +399 -0
- package/src/bot-ws-server.ts +350 -0
- package/src/index.ts +21 -968
- package/src/types.ts +58 -0
package/src/index.ts
CHANGED
|
@@ -1,983 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
SendContent,
|
|
13
|
-
Tool,
|
|
14
|
-
ToolPermissionLevel,
|
|
15
|
-
Notice,
|
|
16
|
-
Request,
|
|
17
|
-
createGroupManagementTools,
|
|
18
|
-
GROUP_MANAGEMENT_SKILL_KEYWORDS,
|
|
19
|
-
GROUP_MANAGEMENT_SKILL_TAGS,
|
|
20
|
-
type IGroupManagement,
|
|
21
|
-
} from 'zhin.js';
|
|
22
|
-
import type { Router } from '@zhin.js/http'
|
|
23
|
-
import { IncomingMessage } from "http";
|
|
24
|
-
import { clearInterval } from "node:timers";
|
|
1
|
+
/**
|
|
2
|
+
* OneBot11 适配器入口:单一适配器,支持正向 WS / 反向 WS(connection: ws | wss)
|
|
3
|
+
*/
|
|
4
|
+
import { usePlugin, type Plugin, type Context } from 'zhin.js';
|
|
5
|
+
import type { Router } from '@zhin.js/http';
|
|
6
|
+
import { OneBot11Adapter } from './adapter.js';
|
|
7
|
+
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
export { OneBot11WsClient } from './bot-ws-client.js';
|
|
10
|
+
export { OneBot11WsServer } from './bot-ws-server.js';
|
|
11
|
+
export { OneBot11Adapter, type OneBot11Bot } from './adapter.js';
|
|
25
12
|
|
|
26
|
-
// 类型扩展 - 使用 zhin.js 模式
|
|
27
13
|
declare module 'zhin.js' {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'onebot11.wss': OneBot11WssAdapter;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// OneBot11 发送者权限信息
|
|
36
|
-
export interface OneBot11SenderInfo {
|
|
37
|
-
id: string;
|
|
38
|
-
name: string;
|
|
39
|
-
/** 群角色 */
|
|
40
|
-
role?: 'owner' | 'admin' | 'member';
|
|
41
|
-
/** 是否为群主 */
|
|
42
|
-
isOwner?: boolean;
|
|
43
|
-
/** 是否为管理员 */
|
|
44
|
-
isAdmin?: boolean;
|
|
45
|
-
/** 群名片 */
|
|
46
|
-
card?: string;
|
|
47
|
-
/** 头衔 */
|
|
48
|
-
title?: string;
|
|
49
|
-
}
|
|
50
|
-
// ============================================================================
|
|
51
|
-
// OneBot11 配置和类型
|
|
52
|
-
// ============================================================================
|
|
53
|
-
|
|
54
|
-
export interface OneBot11Config {
|
|
55
|
-
context: 'onebot11';
|
|
56
|
-
name: string;
|
|
57
|
-
type: string
|
|
58
|
-
access_token?: string;
|
|
59
|
-
}
|
|
60
|
-
export interface OneBot11WsClientConfig extends OneBot11Config {
|
|
61
|
-
type: 'ws'
|
|
62
|
-
url: string;
|
|
63
|
-
reconnect_interval?: number;
|
|
64
|
-
heartbeat_interval?: number;
|
|
65
|
-
}
|
|
66
|
-
export interface OneBot11WsServerConfig extends OneBot11Config {
|
|
67
|
-
type: 'ws_reverse'
|
|
68
|
-
path: string
|
|
69
|
-
heartbeat_interval?: number;
|
|
70
|
-
}
|
|
71
|
-
export interface OneBot11HTTPConfig extends OneBot11Config {
|
|
72
|
-
type: 'http_sse'
|
|
73
|
-
port: number
|
|
74
|
-
path: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface OneBot11Message {
|
|
78
|
-
post_type: string;
|
|
79
|
-
self_id: string
|
|
80
|
-
message_type?: string;
|
|
81
|
-
sub_type?: string;
|
|
82
|
-
message_id: number;
|
|
83
|
-
user_id: number;
|
|
84
|
-
group_id?: number;
|
|
85
|
-
message: Array<{
|
|
86
|
-
type: string;
|
|
87
|
-
data: Record<string, any>;
|
|
88
|
-
}>;
|
|
89
|
-
raw_message: string;
|
|
90
|
-
time: number;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface ApiResponse<T = any> {
|
|
94
|
-
status: string;
|
|
95
|
-
retcode: number;
|
|
96
|
-
data: T;
|
|
97
|
-
echo?: string;
|
|
98
|
-
}
|
|
99
|
-
const plugin = usePlugin();
|
|
100
|
-
const { provide, useContext } = plugin;
|
|
101
|
-
|
|
102
|
-
// ============================================================================
|
|
103
|
-
// OneBot11 适配器实现
|
|
104
|
-
// ============================================================================
|
|
105
|
-
|
|
106
|
-
export class OneBot11WsClient extends EventEmitter implements Bot<OneBot11WsClientConfig, OneBot11Message> {
|
|
107
|
-
$connected: boolean
|
|
108
|
-
private ws?: WebSocket;
|
|
109
|
-
private reconnectTimer?: NodeJS.Timeout;
|
|
110
|
-
private heartbeatTimer?: NodeJS.Timeout;
|
|
111
|
-
private requestId = 0;
|
|
112
|
-
private pendingRequests = new Map<string, {
|
|
113
|
-
resolve: (value: any) => void;
|
|
114
|
-
reject: (error: Error) => void;
|
|
115
|
-
timeout: NodeJS.Timeout;
|
|
116
|
-
}>();
|
|
117
|
-
|
|
118
|
-
constructor(public adapter: OneBot11Adapter, public $config: OneBot11WsClientConfig) {
|
|
119
|
-
super();
|
|
120
|
-
this.$connected = false
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
get $id() {
|
|
124
|
-
return this.$config.name;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
async $connect(): Promise<void> {
|
|
129
|
-
return new Promise((resolve, reject) => {
|
|
130
|
-
let wsUrl = this.$config.url;
|
|
131
|
-
const headers: Record<string, string> = {};
|
|
132
|
-
|
|
133
|
-
if (this.$config.access_token) {
|
|
134
|
-
headers['Authorization'] = `Bearer ${this.$config.access_token}`;
|
|
135
|
-
}
|
|
136
|
-
this.ws = new WebSocket(wsUrl, { headers });
|
|
137
|
-
|
|
138
|
-
this.ws.on('open', () => {
|
|
139
|
-
this.$connected = true;
|
|
140
|
-
if (!this.$config.access_token) plugin.logger.warn(`missing 'access_token', your OneBot protocol is not safely`)
|
|
141
|
-
this.startHeartbeat();
|
|
142
|
-
resolve();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
this.ws.on('message', (data) => {
|
|
146
|
-
try {
|
|
147
|
-
const message = JSON.parse(data.toString());
|
|
148
|
-
this.handleWebSocketMessage(message);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
this.emit('error', error)
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
this.ws.on('close', (code, reason) => {
|
|
155
|
-
this.$connected = false
|
|
156
|
-
reject({ code, reason })
|
|
157
|
-
this.scheduleReconnect();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
this.ws.on('error', (error) => {
|
|
161
|
-
reject(error);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async $disconnect(): Promise<void> {
|
|
167
|
-
if (this.reconnectTimer) {
|
|
168
|
-
clearTimeout(this.reconnectTimer);
|
|
169
|
-
this.reconnectTimer = undefined;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (this.heartbeatTimer) {
|
|
173
|
-
clearInterval(this.heartbeatTimer);
|
|
174
|
-
this.heartbeatTimer = undefined;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// 清理所有待处理的请求
|
|
178
|
-
for (const [id, request] of this.pendingRequests) {
|
|
179
|
-
clearTimeout(request.timeout);
|
|
180
|
-
request.reject(new Error('Connection closed'));
|
|
181
|
-
}
|
|
182
|
-
this.pendingRequests.clear();
|
|
183
|
-
|
|
184
|
-
if (this.ws) {
|
|
185
|
-
this.ws.close();
|
|
186
|
-
this.ws = undefined;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
$formatMessage(onebotMsg: OneBot11Message) {
|
|
190
|
-
const message = Message.from(onebotMsg, {
|
|
191
|
-
$id: onebotMsg.message_id.toString(),
|
|
192
|
-
$adapter: 'onebot11',
|
|
193
|
-
$bot: `${this.$config.name}`,
|
|
194
|
-
$sender: {
|
|
195
|
-
id: onebotMsg.user_id.toString(),
|
|
196
|
-
name: onebotMsg.user_id.toString()
|
|
197
|
-
},
|
|
198
|
-
$channel: {
|
|
199
|
-
id: (onebotMsg.group_id || onebotMsg.user_id).toString(),
|
|
200
|
-
type: onebotMsg.group_id ? 'group' : 'private'
|
|
201
|
-
},
|
|
202
|
-
$content: onebotMsg.message,
|
|
203
|
-
$raw: onebotMsg.raw_message,
|
|
204
|
-
$timestamp: onebotMsg.time,
|
|
205
|
-
$recall: async () => {
|
|
206
|
-
await this.$recallMessage(message.$id)
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
$reply: async (content: MessageSegment[], quote?: boolean | string): Promise<string> => {
|
|
210
|
-
if (quote) content.unshift({ type: 'reply', data: { message_id: message.$id } })
|
|
211
|
-
return await this.adapter.sendMessage({
|
|
212
|
-
...message.$channel,
|
|
213
|
-
context: 'onebot11',
|
|
214
|
-
bot: `${this.$config.name}`,
|
|
215
|
-
content
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
})
|
|
219
|
-
return message
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async $sendMessage(options: SendOptions): Promise<string> {
|
|
223
|
-
const messageData: any = {
|
|
224
|
-
message: options.content
|
|
225
|
-
};
|
|
226
|
-
if (options.type === 'group') {
|
|
227
|
-
const result = await this.callApi('send_group_msg', {
|
|
228
|
-
group_id: parseInt(options.id),
|
|
229
|
-
...messageData
|
|
230
|
-
});
|
|
231
|
-
plugin.logger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(options.content)}`)
|
|
232
|
-
return result.message_id.toString();
|
|
233
|
-
} else if (options.type === 'private') {
|
|
234
|
-
const result = await this.callApi('send_private_msg', {
|
|
235
|
-
user_id: parseInt(options.id),
|
|
236
|
-
...messageData
|
|
237
|
-
});
|
|
238
|
-
plugin.logger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(options.content)}`)
|
|
239
|
-
return result.message_id.toString();
|
|
240
|
-
} else {
|
|
241
|
-
throw new Error('Either group_id or user_id must be provided');
|
|
14
|
+
namespace Plugin {
|
|
15
|
+
interface Contexts {
|
|
16
|
+
router: import('@zhin.js/http').Router;
|
|
242
17
|
}
|
|
243
|
-
return '';
|
|
244
18
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
message_id: parseInt(id)
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ==================== 群管理 API ====================
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* 踢出群成员
|
|
255
|
-
*/
|
|
256
|
-
async kickMember(groupId: number, userId: number, rejectAddRequest: boolean = false): Promise<boolean> {
|
|
257
|
-
try {
|
|
258
|
-
await this.callApi('set_group_kick', {
|
|
259
|
-
group_id: groupId,
|
|
260
|
-
user_id: userId,
|
|
261
|
-
reject_add_request: rejectAddRequest,
|
|
262
|
-
});
|
|
263
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} 踢出成员 ${userId}(群 ${groupId})`);
|
|
264
|
-
return true;
|
|
265
|
-
} catch (error) {
|
|
266
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 踢出成员失败:`, error);
|
|
267
|
-
throw error;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 禁言群成员
|
|
273
|
-
*/
|
|
274
|
-
async muteMember(groupId: number, userId: number, duration: number = 600): Promise<boolean> {
|
|
275
|
-
try {
|
|
276
|
-
await this.callApi('set_group_ban', {
|
|
277
|
-
group_id: groupId,
|
|
278
|
-
user_id: userId,
|
|
279
|
-
duration,
|
|
280
|
-
});
|
|
281
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} ${duration > 0 ? `禁言成员 ${userId} ${duration}秒` : `解除成员 ${userId} 禁言`}(群 ${groupId})`);
|
|
282
|
-
return true;
|
|
283
|
-
} catch (error) {
|
|
284
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 禁言操作失败:`, error);
|
|
285
|
-
throw error;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* 全员禁言
|
|
291
|
-
*/
|
|
292
|
-
async muteAll(groupId: number, enable: boolean = true): Promise<boolean> {
|
|
293
|
-
try {
|
|
294
|
-
await this.callApi('set_group_whole_ban', {
|
|
295
|
-
group_id: groupId,
|
|
296
|
-
enable,
|
|
297
|
-
});
|
|
298
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} ${enable ? '开启' : '关闭'}全员禁言(群 ${groupId})`);
|
|
299
|
-
return true;
|
|
300
|
-
} catch (error) {
|
|
301
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 全员禁言操作失败:`, error);
|
|
302
|
-
throw error;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 设置管理员
|
|
308
|
-
*/
|
|
309
|
-
async setAdmin(groupId: number, userId: number, enable: boolean = true): Promise<boolean> {
|
|
310
|
-
try {
|
|
311
|
-
await this.callApi('set_group_admin', {
|
|
312
|
-
group_id: groupId,
|
|
313
|
-
user_id: userId,
|
|
314
|
-
enable,
|
|
315
|
-
});
|
|
316
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} ${enable ? '设置' : '取消'}管理员 ${userId}(群 ${groupId})`);
|
|
317
|
-
return true;
|
|
318
|
-
} catch (error) {
|
|
319
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 设置管理员失败:`, error);
|
|
320
|
-
throw error;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* 设置群名片
|
|
326
|
-
*/
|
|
327
|
-
async setCard(groupId: number, userId: number, card: string): Promise<boolean> {
|
|
328
|
-
try {
|
|
329
|
-
await this.callApi('set_group_card', {
|
|
330
|
-
group_id: groupId,
|
|
331
|
-
user_id: userId,
|
|
332
|
-
card,
|
|
333
|
-
});
|
|
334
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} 设置成员 ${userId} 群名片为 "${card}"(群 ${groupId})`);
|
|
335
|
-
return true;
|
|
336
|
-
} catch (error) {
|
|
337
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 设置群名片失败:`, error);
|
|
338
|
-
throw error;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* 设置群头衔
|
|
344
|
-
*/
|
|
345
|
-
async setTitle(groupId: number, userId: number, title: string, duration: number = -1): Promise<boolean> {
|
|
346
|
-
try {
|
|
347
|
-
await this.callApi('set_group_special_title', {
|
|
348
|
-
group_id: groupId,
|
|
349
|
-
user_id: userId,
|
|
350
|
-
special_title: title,
|
|
351
|
-
duration,
|
|
352
|
-
});
|
|
353
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} 设置成员 ${userId} 头衔为 "${title}"(群 ${groupId})`);
|
|
354
|
-
return true;
|
|
355
|
-
} catch (error) {
|
|
356
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 设置头衔失败:`, error);
|
|
357
|
-
throw error;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* 设置群名
|
|
363
|
-
*/
|
|
364
|
-
async setGroupName(groupId: number, name: string): Promise<boolean> {
|
|
365
|
-
try {
|
|
366
|
-
await this.callApi('set_group_name', {
|
|
367
|
-
group_id: groupId,
|
|
368
|
-
group_name: name,
|
|
369
|
-
});
|
|
370
|
-
plugin.logger.info(`OneBot11 Bot ${this.$id} 设置群名为 "${name}"(群 ${groupId})`);
|
|
371
|
-
return true;
|
|
372
|
-
} catch (error) {
|
|
373
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 设置群名失败:`, error);
|
|
374
|
-
throw error;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* 获取群成员列表
|
|
380
|
-
*/
|
|
381
|
-
async getMemberList(groupId: number): Promise<any[]> {
|
|
382
|
-
try {
|
|
383
|
-
return await this.callApi('get_group_member_list', { group_id: groupId });
|
|
384
|
-
} catch (error) {
|
|
385
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 获取群成员列表失败:`, error);
|
|
386
|
-
throw error;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* 获取群信息
|
|
392
|
-
*/
|
|
393
|
-
async getGroupInfo(groupId: number): Promise<any> {
|
|
394
|
-
try {
|
|
395
|
-
return await this.callApi('get_group_info', { group_id: groupId });
|
|
396
|
-
} catch (error) {
|
|
397
|
-
plugin.logger.error(`OneBot11 Bot ${this.$id} 获取群信息失败:`, error);
|
|
398
|
-
throw error;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private async callApi(action: string, params: any = {}): Promise<any> {
|
|
403
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
404
|
-
throw new Error('WebSocket is not connected');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const echo = `req_${++this.requestId}`;
|
|
408
|
-
const message = {
|
|
409
|
-
action,
|
|
410
|
-
params,
|
|
411
|
-
echo
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
return new Promise((resolve, reject) => {
|
|
415
|
-
const timeout = setTimeout(() => {
|
|
416
|
-
this.pendingRequests.delete(echo);
|
|
417
|
-
reject(new Error(`API call timeout: ${action}`));
|
|
418
|
-
}, 30000); // 30秒超时
|
|
419
|
-
|
|
420
|
-
this.pendingRequests.set(echo, { resolve, reject, timeout });
|
|
421
|
-
this.ws!.send(JSON.stringify(message));
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
private handleWebSocketMessage(message: any): void {
|
|
426
|
-
// 处理API响应
|
|
427
|
-
if (message.echo && this.pendingRequests.has(message.echo)) {
|
|
428
|
-
const request = this.pendingRequests.get(message.echo)!;
|
|
429
|
-
this.pendingRequests.delete(message.echo);
|
|
430
|
-
clearTimeout(request.timeout);
|
|
431
|
-
|
|
432
|
-
const response = message as ApiResponse;
|
|
433
|
-
if (response.status === 'ok') {
|
|
434
|
-
return request.resolve(response.data);
|
|
435
|
-
}
|
|
436
|
-
return request.reject(new Error(`API error: ${response.retcode}`));
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// 处理事件消息
|
|
440
|
-
if (message.post_type === 'message') {
|
|
441
|
-
this.handleOneBot11Message(message);
|
|
442
|
-
} else if (message.post_type === 'notice') {
|
|
443
|
-
this.handleOneBot11Notice(message);
|
|
444
|
-
} else if (message.post_type === 'request') {
|
|
445
|
-
this.handleOneBot11Request(message);
|
|
446
|
-
} else if (message.post_type === 'meta_event' && message.meta_event_type === 'heartbeat') {
|
|
447
|
-
// 心跳消息,暂时忽略
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
private handleOneBot11Notice(event: any): void {
|
|
452
|
-
const noticeTypeMap: Record<string, string> = {
|
|
453
|
-
group_increase: 'group_member_increase',
|
|
454
|
-
group_decrease: 'group_member_decrease',
|
|
455
|
-
group_admin: 'group_admin_change',
|
|
456
|
-
group_ban: 'group_ban',
|
|
457
|
-
group_recall: 'group_recall',
|
|
458
|
-
friend_recall: 'friend_recall',
|
|
459
|
-
friend_add: 'friend_add',
|
|
460
|
-
notify: event.sub_type === 'poke'
|
|
461
|
-
? (event.group_id ? 'group_poke' : 'friend_poke')
|
|
462
|
-
: `notify_${event.sub_type}`,
|
|
463
|
-
group_upload: 'group_upload',
|
|
464
|
-
};
|
|
465
|
-
const $type = noticeTypeMap[event.notice_type] || event.notice_type;
|
|
466
|
-
const isGroup = !!event.group_id;
|
|
467
|
-
const notice = Notice.from(event, {
|
|
468
|
-
$id: `${event.time}_${event.notice_type}_${event.group_id || event.user_id}`,
|
|
469
|
-
$adapter: 'onebot11',
|
|
470
|
-
$bot: this.$config.name,
|
|
471
|
-
$type,
|
|
472
|
-
$subType: event.sub_type,
|
|
473
|
-
$channel: {
|
|
474
|
-
id: (event.group_id || event.user_id)?.toString() || '',
|
|
475
|
-
type: isGroup ? 'group' : 'private',
|
|
476
|
-
},
|
|
477
|
-
$operator: event.operator_id ? { id: event.operator_id.toString(), name: event.operator_id.toString() } : undefined,
|
|
478
|
-
$target: event.user_id ? { id: event.user_id.toString(), name: event.user_id.toString() } : undefined,
|
|
479
|
-
$timestamp: event.time || Math.floor(Date.now() / 1000),
|
|
480
|
-
});
|
|
481
|
-
this.adapter.emit('notice.receive', notice);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
private handleOneBot11Request(event: any): void {
|
|
485
|
-
const typeMap: Record<string, string> = {
|
|
486
|
-
friend: 'friend_add',
|
|
487
|
-
group: event.sub_type === 'invite' ? 'group_invite' : 'group_add',
|
|
488
|
-
};
|
|
489
|
-
const $type = typeMap[event.request_type] || event.request_type;
|
|
490
|
-
const request = Request.from(event, {
|
|
491
|
-
$id: event.flag || `${event.time}_${event.request_type}_${event.user_id}`,
|
|
492
|
-
$adapter: 'onebot11',
|
|
493
|
-
$bot: this.$config.name,
|
|
494
|
-
$type,
|
|
495
|
-
$subType: event.sub_type,
|
|
496
|
-
$channel: {
|
|
497
|
-
id: (event.group_id || event.user_id)?.toString() || '',
|
|
498
|
-
type: event.group_id ? 'group' : 'private',
|
|
499
|
-
},
|
|
500
|
-
$sender: { id: event.user_id?.toString() || '', name: event.user_id?.toString() || '' },
|
|
501
|
-
$comment: event.comment,
|
|
502
|
-
$timestamp: event.time || Math.floor(Date.now() / 1000),
|
|
503
|
-
$approve: async (remark?: string) => {
|
|
504
|
-
await this.callApi(
|
|
505
|
-
event.request_type === 'friend' ? 'set_friend_add_request' : 'set_group_add_request',
|
|
506
|
-
{ flag: event.flag, approve: true, remark },
|
|
507
|
-
);
|
|
508
|
-
},
|
|
509
|
-
$reject: async (reason?: string) => {
|
|
510
|
-
await this.callApi(
|
|
511
|
-
event.request_type === 'friend' ? 'set_friend_add_request' : 'set_group_add_request',
|
|
512
|
-
{ flag: event.flag, approve: false, reason },
|
|
513
|
-
);
|
|
514
|
-
},
|
|
515
|
-
});
|
|
516
|
-
this.adapter.emit('request.receive', request);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
private handleOneBot11Message(onebotMsg: OneBot11Message): void {
|
|
520
|
-
const message = this.$formatMessage(onebotMsg);
|
|
521
|
-
this.adapter.emit('message.receive', message);
|
|
522
|
-
plugin.logger.debug(`${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}):${segment.raw(message.$content)}`);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
private startHeartbeat(): void {
|
|
526
|
-
const interval = this.$config.heartbeat_interval || 30000;
|
|
527
|
-
this.heartbeatTimer = setInterval(() => {
|
|
528
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
529
|
-
this.ws.ping();
|
|
530
|
-
}
|
|
531
|
-
}, interval);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
private scheduleReconnect(): void {
|
|
535
|
-
if (this.reconnectTimer) {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const interval = this.$config.reconnect_interval || 5000;
|
|
540
|
-
this.reconnectTimer = setTimeout(async () => {
|
|
541
|
-
this.reconnectTimer = undefined;
|
|
542
|
-
try {
|
|
543
|
-
await this.$connect();
|
|
544
|
-
} catch (error) {
|
|
545
|
-
this.emit('error', new Error(`Reconnection failed: ${error}`));
|
|
546
|
-
this.scheduleReconnect();
|
|
547
|
-
}
|
|
548
|
-
}, interval);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
export class OneBot11WsServer extends EventEmitter implements Bot<OneBot11WsServerConfig, OneBot11Message> {
|
|
552
|
-
$connected: boolean
|
|
553
|
-
#wss?: WebSocketServer
|
|
554
|
-
#clientMap: Map<string, WebSocket> = new Map<string, WebSocket>()
|
|
555
|
-
private heartbeatTimer?: NodeJS.Timeout;
|
|
556
|
-
private requestId = 0;
|
|
557
|
-
private pendingRequests = new Map<string, {
|
|
558
|
-
resolve: (value: any) => void;
|
|
559
|
-
reject: (error: Error) => void;
|
|
560
|
-
timeout: NodeJS.Timeout;
|
|
561
|
-
}>();
|
|
562
|
-
|
|
563
|
-
get $id() {
|
|
564
|
-
return this.$config.name;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
constructor(public adapter: OneBot11WssAdapter, public router: Router, public $config: OneBot11WsServerConfig) {
|
|
568
|
-
super();
|
|
569
|
-
this.$connected = false
|
|
570
|
-
}
|
|
571
|
-
async $connect(): Promise<void> {
|
|
572
|
-
if (!this.$config.access_token) plugin.logger.warn(`missing 'access_token', your OneBot protocol is not safely`)
|
|
573
|
-
this.#wss = this.router.ws(this.$config.path, {
|
|
574
|
-
verifyClient: (info: { origin: string; secure: boolean; req: IncomingMessage }) => {
|
|
575
|
-
const {
|
|
576
|
-
req: { headers },
|
|
577
|
-
} = info;
|
|
578
|
-
const authorization = headers['authorization'] || '';
|
|
579
|
-
if (this.$config.access_token && authorization !== `Bearer ${this.$config.access_token}`) {
|
|
580
|
-
plugin.logger.error('鉴权失败');
|
|
581
|
-
return false;
|
|
582
|
-
}
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
})
|
|
586
|
-
this.$connected = true;
|
|
587
|
-
plugin.logger.info(`ws server start at path:${this.$config.path}`)
|
|
588
|
-
this.#wss.on('connection', (client, req) => {
|
|
589
|
-
this.startHeartbeat();
|
|
590
|
-
plugin.logger.info(`已连接到协议端:${req.socket.remoteAddress}`);
|
|
591
|
-
client.on('error', err => {
|
|
592
|
-
plugin.logger.error('连接出错:', err);
|
|
593
|
-
});
|
|
594
|
-
client.on('close', code => {
|
|
595
|
-
plugin.logger.error(`与连接端(${req.socket.remoteAddress})断开,错误码:${code}`);
|
|
596
|
-
for (const [key, value] of this.#clientMap) {
|
|
597
|
-
if (client === value) this.#clientMap.delete(key)
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
client.on('message', (data) => {
|
|
601
|
-
try {
|
|
602
|
-
const message = JSON.parse(data.toString());
|
|
603
|
-
this.handleWebSocketMessage(client, message);
|
|
604
|
-
} catch (error) {
|
|
605
|
-
this.emit('error', error)
|
|
606
|
-
}
|
|
607
|
-
})
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
async $disconnect(): Promise<void> {
|
|
612
|
-
this.#wss?.close();
|
|
613
|
-
if (this.heartbeatTimer) {
|
|
614
|
-
clearInterval(this.heartbeatTimer)
|
|
615
|
-
delete this.heartbeatTimer;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
$formatMessage(onebotMsg: OneBot11Message) {
|
|
619
|
-
const message = Message.from(onebotMsg, {
|
|
620
|
-
$id: onebotMsg.message_id.toString(),
|
|
621
|
-
$adapter: 'onebot11',
|
|
622
|
-
$bot: `${this.$config.name}`,
|
|
623
|
-
$sender: {
|
|
624
|
-
id: onebotMsg.user_id.toString(),
|
|
625
|
-
name: onebotMsg.user_id.toString()
|
|
626
|
-
},
|
|
627
|
-
$channel: {
|
|
628
|
-
id: [onebotMsg.self_id, (onebotMsg.group_id || onebotMsg.user_id)].join(':'),
|
|
629
|
-
type: onebotMsg.group_id ? 'group' : 'private'
|
|
630
|
-
},
|
|
631
|
-
$content: onebotMsg.message,
|
|
632
|
-
$raw: onebotMsg.raw_message,
|
|
633
|
-
$timestamp: onebotMsg.time,
|
|
634
|
-
$recall: async () => {
|
|
635
|
-
await this.$recallMessage(message.$id)
|
|
636
|
-
},
|
|
637
|
-
$reply: async (content: SendContent, quote?: boolean | string): Promise<string> => {
|
|
638
|
-
if (!Array.isArray(content)) content = [content];
|
|
639
|
-
if (quote) content.unshift({ type: 'reply', data: { message_id: message.$id } })
|
|
640
|
-
return await this.$sendMessage({
|
|
641
|
-
...message.$channel,
|
|
642
|
-
context: 'onebot11',
|
|
643
|
-
bot: `${this.$config.name}`,
|
|
644
|
-
content
|
|
645
|
-
})
|
|
646
|
-
}
|
|
647
|
-
})
|
|
648
|
-
return message
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
async $sendMessage(options: SendOptions): Promise<string> {
|
|
652
|
-
const messageData: any = {
|
|
653
|
-
message: options.content
|
|
654
|
-
};
|
|
655
|
-
if (options.type === 'group') {
|
|
656
|
-
const [self_id, id] = options.id.split(':')
|
|
657
|
-
const result = await this.callApi(self_id, 'send_group_msg', {
|
|
658
|
-
group_id: parseInt(id),
|
|
659
|
-
...messageData
|
|
660
|
-
});
|
|
661
|
-
plugin.logger.debug(`${this.$config.name} send ${options.type}(${id}):${segment.raw(options.content)}`)
|
|
662
|
-
return result.message_id.toString();
|
|
663
|
-
} else if (options.type === 'private') {
|
|
664
|
-
const [self_id, id] = options.id.split(':')
|
|
665
|
-
const result = await this.callApi(self_id, 'send_private_msg', {
|
|
666
|
-
user_id: parseInt(id),
|
|
667
|
-
...messageData
|
|
668
|
-
});
|
|
669
|
-
plugin.logger.debug(`${this.$config.name} send ${options.type}(${id}):${segment.raw(options.content)}`)
|
|
670
|
-
return result.message_id.toString();
|
|
671
|
-
} else {
|
|
672
|
-
throw new Error('Either group_id or user_id must be provided');
|
|
673
|
-
}
|
|
674
|
-
return '';
|
|
675
|
-
}
|
|
676
|
-
async $recallMessage(id: string): Promise<void> {
|
|
677
|
-
const [self_id, message_id] = id.split(':')
|
|
678
|
-
await this.callApi(self_id, 'delete_msg', {
|
|
679
|
-
message_id: parseInt(message_id)
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
private async callApi(self_id: string, action: string, params: any = {}): Promise<any> {
|
|
683
|
-
const client = this.#clientMap.get(self_id)
|
|
684
|
-
if (!client || client.readyState !== WebSocket.OPEN) {
|
|
685
|
-
throw new Error('WebSocket is not connected');
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
const echo = `req_${++this.requestId}`;
|
|
689
|
-
const message = {
|
|
690
|
-
action,
|
|
691
|
-
params,
|
|
692
|
-
echo
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
return new Promise((resolve, reject) => {
|
|
696
|
-
const timeout = setTimeout(() => {
|
|
697
|
-
this.pendingRequests.delete(echo);
|
|
698
|
-
reject(new Error(`API call timeout: ${action}`));
|
|
699
|
-
}, 30000); // 30秒超时
|
|
700
|
-
|
|
701
|
-
this.pendingRequests.set(echo, { resolve, reject, timeout });
|
|
702
|
-
client.send(JSON.stringify(message));
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
private handleWebSocketMessage(client: WebSocket, message: any): void {
|
|
707
|
-
// 处理API响应
|
|
708
|
-
if (message.echo && this.pendingRequests.has(message.echo)) {
|
|
709
|
-
const request = this.pendingRequests.get(message.echo)!;
|
|
710
|
-
this.pendingRequests.delete(message.echo);
|
|
711
|
-
clearTimeout(request.timeout);
|
|
712
|
-
|
|
713
|
-
const response = message as ApiResponse;
|
|
714
|
-
if (response.status === 'ok') {
|
|
715
|
-
return request.resolve(response.data);
|
|
716
|
-
}
|
|
717
|
-
return request.reject(new Error(`API error: ${response.retcode}`));
|
|
718
|
-
}
|
|
719
|
-
switch (message.post_type) {
|
|
720
|
-
case 'message':
|
|
721
|
-
return this.handleMessage(message);
|
|
722
|
-
case 'notice':
|
|
723
|
-
return this.handleNotice(message);
|
|
724
|
-
case 'request':
|
|
725
|
-
return this.handleRequest(message);
|
|
726
|
-
case 'meta_event':
|
|
727
|
-
return this.handleMetaEvent(client, message)
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
private handleMetaEvent(client: WebSocket, message: any) {
|
|
731
|
-
switch (message.sub_type) {
|
|
732
|
-
case 'heartbeat':
|
|
733
|
-
break;
|
|
734
|
-
case 'connect':
|
|
735
|
-
this.#clientMap.set(message.self_id, client);
|
|
736
|
-
plugin.logger.info(`client ${message.self_id} of ${this.$config.name} by ${this.$config.context} connected`)
|
|
737
|
-
break;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
private handleMessage(onebotMsg: OneBot11Message): void {
|
|
741
|
-
const message = this.$formatMessage(onebotMsg);
|
|
742
|
-
this.adapter.emit('message.receive', message);
|
|
743
|
-
plugin.logger.debug(`${this.$config.name} recv ${message.$channel.type}(${onebotMsg.group_id || onebotMsg.user_id}):${segment.raw(message.$content)}`);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
private handleNotice(event: any): void {
|
|
747
|
-
const noticeTypeMap: Record<string, string> = {
|
|
748
|
-
group_increase: 'group_member_increase',
|
|
749
|
-
group_decrease: 'group_member_decrease',
|
|
750
|
-
group_admin: 'group_admin_change',
|
|
751
|
-
group_ban: 'group_ban',
|
|
752
|
-
group_recall: 'group_recall',
|
|
753
|
-
friend_recall: 'friend_recall',
|
|
754
|
-
friend_add: 'friend_add',
|
|
755
|
-
notify: event.sub_type === 'poke'
|
|
756
|
-
? (event.group_id ? 'group_poke' : 'friend_poke')
|
|
757
|
-
: `notify_${event.sub_type}`,
|
|
758
|
-
group_upload: 'group_upload',
|
|
759
|
-
};
|
|
760
|
-
const $type = noticeTypeMap[event.notice_type] || event.notice_type;
|
|
761
|
-
const isGroup = !!event.group_id;
|
|
762
|
-
const notice = Notice.from(event, {
|
|
763
|
-
$id: `${event.self_id}:${event.time}_${event.notice_type}_${event.group_id || event.user_id}`,
|
|
764
|
-
$adapter: 'onebot11',
|
|
765
|
-
$bot: this.$config.name,
|
|
766
|
-
$type,
|
|
767
|
-
$subType: event.sub_type,
|
|
768
|
-
$channel: {
|
|
769
|
-
id: [event.self_id, (event.group_id || event.user_id)].join(':'),
|
|
770
|
-
type: isGroup ? 'group' : 'private',
|
|
771
|
-
},
|
|
772
|
-
$operator: event.operator_id ? { id: event.operator_id.toString(), name: event.operator_id.toString() } : undefined,
|
|
773
|
-
$target: event.user_id ? { id: event.user_id.toString(), name: event.user_id.toString() } : undefined,
|
|
774
|
-
$timestamp: event.time || Math.floor(Date.now() / 1000),
|
|
775
|
-
});
|
|
776
|
-
this.adapter.emit('notice.receive', notice);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
private handleRequest(event: any): void {
|
|
780
|
-
const self_id = event.self_id?.toString() || '';
|
|
781
|
-
const typeMap: Record<string, string> = {
|
|
782
|
-
friend: 'friend_add',
|
|
783
|
-
group: event.sub_type === 'invite' ? 'group_invite' : 'group_add',
|
|
784
|
-
};
|
|
785
|
-
const $type = typeMap[event.request_type] || event.request_type;
|
|
786
|
-
const request = Request.from(event, {
|
|
787
|
-
$id: event.flag || `${self_id}:${event.time}_${event.request_type}_${event.user_id}`,
|
|
788
|
-
$adapter: 'onebot11',
|
|
789
|
-
$bot: this.$config.name,
|
|
790
|
-
$type,
|
|
791
|
-
$subType: event.sub_type,
|
|
792
|
-
$channel: {
|
|
793
|
-
id: [self_id, (event.group_id || event.user_id)].join(':'),
|
|
794
|
-
type: event.group_id ? 'group' : 'private',
|
|
795
|
-
},
|
|
796
|
-
$sender: { id: event.user_id?.toString() || '', name: event.user_id?.toString() || '' },
|
|
797
|
-
$comment: event.comment,
|
|
798
|
-
$timestamp: event.time || Math.floor(Date.now() / 1000),
|
|
799
|
-
$approve: async (remark?: string) => {
|
|
800
|
-
await this.callApi(
|
|
801
|
-
self_id,
|
|
802
|
-
event.request_type === 'friend' ? 'set_friend_add_request' : 'set_group_add_request',
|
|
803
|
-
{ flag: event.flag, approve: true, remark },
|
|
804
|
-
);
|
|
805
|
-
},
|
|
806
|
-
$reject: async (reason?: string) => {
|
|
807
|
-
await this.callApi(
|
|
808
|
-
self_id,
|
|
809
|
-
event.request_type === 'friend' ? 'set_friend_add_request' : 'set_group_add_request',
|
|
810
|
-
{ flag: event.flag, approve: false, reason },
|
|
811
|
-
);
|
|
812
|
-
},
|
|
813
|
-
});
|
|
814
|
-
this.adapter.emit('request.receive', request);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
private startHeartbeat(): void {
|
|
818
|
-
const interval = this.$config.heartbeat_interval || 30000;
|
|
819
|
-
this.heartbeatTimer = setInterval(() => {
|
|
820
|
-
for (const client of this.#wss?.clients || []) {
|
|
821
|
-
if (client && client.readyState === WebSocket.OPEN) {
|
|
822
|
-
client.ping();
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}, interval);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// 定义 Adapter 类
|
|
830
|
-
class OneBot11Adapter extends Adapter<OneBot11WsClient> {
|
|
831
|
-
constructor(plugin: Plugin) {
|
|
832
|
-
super(plugin, 'onebot11', []);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
createBot(config: OneBot11WsClientConfig): OneBot11WsClient {
|
|
836
|
-
return new OneBot11WsClient(this, config);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// ── IGroupManagement 标准群管方法 ──────────────────────────────────
|
|
840
|
-
|
|
841
|
-
async kickMember(botId: string, sceneId: string, userId: string) {
|
|
842
|
-
const bot = this.bots.get(botId);
|
|
843
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
844
|
-
return bot.kickMember(Number(sceneId), Number(userId), false);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
async muteMember(botId: string, sceneId: string, userId: string, duration = 600) {
|
|
848
|
-
const bot = this.bots.get(botId);
|
|
849
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
850
|
-
return bot.muteMember(Number(sceneId), Number(userId), duration);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
async muteAll(botId: string, sceneId: string, enable = true) {
|
|
854
|
-
const bot = this.bots.get(botId);
|
|
855
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
856
|
-
return bot.muteAll(Number(sceneId), enable);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
async setAdmin(botId: string, sceneId: string, userId: string, enable = true) {
|
|
860
|
-
const bot = this.bots.get(botId);
|
|
861
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
862
|
-
return bot.setAdmin(Number(sceneId), Number(userId), enable);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
async setMemberNickname(botId: string, sceneId: string, userId: string, nickname: string) {
|
|
866
|
-
const bot = this.bots.get(botId);
|
|
867
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
868
|
-
return bot.setCard(Number(sceneId), Number(userId), nickname);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
async setGroupName(botId: string, sceneId: string, name: string) {
|
|
872
|
-
const bot = this.bots.get(botId);
|
|
873
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
874
|
-
return bot.setGroupName(Number(sceneId), name);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
async listMembers(botId: string, sceneId: string) {
|
|
878
|
-
const bot = this.bots.get(botId);
|
|
879
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
880
|
-
const members = await bot.getMemberList(Number(sceneId));
|
|
881
|
-
return {
|
|
882
|
-
members: members.map((m: any) => ({
|
|
883
|
-
user_id: m.user_id, nickname: m.nickname, card: m.card,
|
|
884
|
-
role: m.role, title: m.title,
|
|
885
|
-
})),
|
|
886
|
-
count: members.length,
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
async getGroupInfo(botId: string, sceneId: string) {
|
|
891
|
-
const bot = this.bots.get(botId);
|
|
892
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
893
|
-
return bot.getGroupInfo(Number(sceneId));
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// ── 生命周期 ───────────────────────────────────────────────────────
|
|
897
|
-
|
|
898
|
-
async start(): Promise<void> {
|
|
899
|
-
this.registerOneBot11PlatformTools();
|
|
900
|
-
const groupTools = createGroupManagementTools(this as unknown as IGroupManagement, this.name);
|
|
901
|
-
groupTools.forEach((t) => this.addTool(t));
|
|
902
|
-
this.declareSkill({
|
|
903
|
-
description:
|
|
904
|
-
'OneBot11 协议群管理:踢人、禁言、封禁、设管理员、改群名、查成员等。仅有昵称时请先 list_members 获取 user_id 再执行操作。',
|
|
905
|
-
keywords: GROUP_MANAGEMENT_SKILL_KEYWORDS,
|
|
906
|
-
tags: GROUP_MANAGEMENT_SKILL_TAGS,
|
|
907
|
-
});
|
|
908
|
-
await super.start();
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
/**
|
|
912
|
-
* 注册 OneBot11 平台特有工具(头衔等)
|
|
913
|
-
*/
|
|
914
|
-
private registerOneBot11PlatformTools(): void {
|
|
915
|
-
// 设置头衔工具(OneBot11 平台特有)
|
|
916
|
-
this.addTool({
|
|
917
|
-
name: 'onebot11_set_title',
|
|
918
|
-
description: '设置群成员的专属头衔(需要群主权限)',
|
|
919
|
-
parameters: {
|
|
920
|
-
type: 'object',
|
|
921
|
-
properties: {
|
|
922
|
-
bot: { type: 'string', description: 'Bot 名称' },
|
|
923
|
-
group_id: { type: 'number', description: '群号' },
|
|
924
|
-
user_id: { type: 'number', description: '成员 QQ 号' },
|
|
925
|
-
title: { type: 'string', description: '头衔名称' },
|
|
926
|
-
},
|
|
927
|
-
required: ['bot', 'group_id', 'user_id', 'title'],
|
|
928
|
-
},
|
|
929
|
-
platforms: ['onebot11'],
|
|
930
|
-
scopes: ['group'],
|
|
931
|
-
permissionLevel: 'group_owner',
|
|
932
|
-
execute: async (args) => {
|
|
933
|
-
const { bot: botId, group_id, user_id, title } = args;
|
|
934
|
-
const bot = this.bots.get(botId);
|
|
935
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
936
|
-
const success = await bot.setTitle(group_id, user_id, title);
|
|
937
|
-
return { success, message: success ? `已将 ${user_id} 的头衔设为 "${title}"` : '操作失败' };
|
|
938
|
-
},
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
class OneBot11WssAdapter extends Adapter<OneBot11WsServer> {
|
|
944
|
-
#router: Router;
|
|
945
|
-
|
|
946
|
-
constructor(plugin: Plugin, router: Router) {
|
|
947
|
-
super(plugin, 'onebot11.wss', []);
|
|
948
|
-
this.#router = router;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
createBot(config: OneBot11WsServerConfig): OneBot11WsServer {
|
|
952
|
-
return new OneBot11WsServer(this, this.#router, config);
|
|
19
|
+
interface Adapters {
|
|
20
|
+
onebot11: OneBot11Adapter;
|
|
953
21
|
}
|
|
954
22
|
}
|
|
955
23
|
|
|
956
|
-
|
|
24
|
+
const { provide } = usePlugin();
|
|
957
25
|
provide({
|
|
958
|
-
name:
|
|
959
|
-
description:
|
|
26
|
+
name: 'onebot11',
|
|
27
|
+
description: 'OneBot11 协议适配器(正向 WS / 反向 WS)',
|
|
960
28
|
mounted: async (p: Plugin) => {
|
|
961
29
|
const adapter = new OneBot11Adapter(p);
|
|
962
30
|
await adapter.start();
|
|
963
31
|
return adapter;
|
|
964
32
|
},
|
|
965
|
-
dispose: async (adapter) => {
|
|
33
|
+
dispose: async (adapter: OneBot11Adapter) => {
|
|
966
34
|
await adapter.stop();
|
|
967
35
|
},
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
useContext('router', (router) => {
|
|
971
|
-
provide({
|
|
972
|
-
name: "onebot11.wss",
|
|
973
|
-
description: "OneBot11 WebSocket Server Adapter",
|
|
974
|
-
mounted: async (p: Plugin) => {
|
|
975
|
-
const adapter = new OneBot11WssAdapter(p, router);
|
|
976
|
-
await adapter.start();
|
|
977
|
-
return adapter;
|
|
978
|
-
},
|
|
979
|
-
dispose: async (adapter) => {
|
|
980
|
-
await adapter.stop();
|
|
981
|
-
},
|
|
982
|
-
});
|
|
983
|
-
});
|
|
36
|
+
} as unknown as Context<'onebot11'>);
|