@onebots/adapter-kook 1.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/LICENSE +21 -0
- package/README.md +133 -0
- package/lib/adapter.d.ts +137 -0
- package/lib/adapter.js +817 -0
- package/lib/bot.d.ts +155 -0
- package/lib/bot.js +403 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +3 -0
- package/lib/types.d.ts +240 -0
- package/lib/types.js +20 -0
- package/lib/utils.d.ts +93 -0
- package/lib/utils.js +240 -0
- package/package.json +39 -0
package/lib/adapter.js
ADDED
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KOOK (开黑了) 适配器
|
|
3
|
+
* 继承 Adapter 基类,实现 KOOK 平台功能
|
|
4
|
+
* - 消息收发(sendMessage)
|
|
5
|
+
* - 服务器管理(getGroupList, getGroupInfo 等,对应 KOOK 的服务器)
|
|
6
|
+
* - 频道管理(getChannelList, getChannelInfo 等)
|
|
7
|
+
* - 用户管理(getFriendList, getUserInfo 等)
|
|
8
|
+
*/
|
|
9
|
+
import { Account, AdapterRegistry, AccountStatus } from "onebots";
|
|
10
|
+
import { Adapter } from "onebots";
|
|
11
|
+
import { KookBot } from "./bot.js";
|
|
12
|
+
import { parseKMarkdown, mentionUser, mentionAll, mentionHere } from "./utils.js";
|
|
13
|
+
export class KookAdapter extends Adapter {
|
|
14
|
+
constructor(app) {
|
|
15
|
+
super(app, "kook");
|
|
16
|
+
this.icon = "https://www.kookapp.cn/favicon.ico";
|
|
17
|
+
}
|
|
18
|
+
// ============================================
|
|
19
|
+
// 消息相关方法
|
|
20
|
+
// ============================================
|
|
21
|
+
/**
|
|
22
|
+
* 发送消息
|
|
23
|
+
* KOOK 支持频道消息和私聊消息
|
|
24
|
+
*/
|
|
25
|
+
async sendMessage(uin, params) {
|
|
26
|
+
const account = this.getAccount(uin);
|
|
27
|
+
if (!account)
|
|
28
|
+
throw new Error(`Account ${uin} not found`);
|
|
29
|
+
const bot = account.client;
|
|
30
|
+
const { scene_id, scene_type, message } = params;
|
|
31
|
+
// 解析消息内容
|
|
32
|
+
let content = '';
|
|
33
|
+
let messageType = 9; // 默认使用 KMarkdown
|
|
34
|
+
for (const seg of message) {
|
|
35
|
+
if (typeof seg === 'string') {
|
|
36
|
+
content += seg;
|
|
37
|
+
}
|
|
38
|
+
else if (seg.type === 'text') {
|
|
39
|
+
content += seg.data.text || '';
|
|
40
|
+
}
|
|
41
|
+
else if (seg.type === 'at') {
|
|
42
|
+
if (seg.data.qq === 'all') {
|
|
43
|
+
content += mentionAll();
|
|
44
|
+
}
|
|
45
|
+
else if (seg.data.qq === 'here') {
|
|
46
|
+
content += mentionHere();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
content += mentionUser(seg.data.qq || seg.data.id || '');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (seg.type === 'image') {
|
|
53
|
+
// 图片消息需要单独发送
|
|
54
|
+
if (seg.data.url) {
|
|
55
|
+
content += `[图片](${seg.data.url})`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (seg.type === 'face') {
|
|
59
|
+
// 表情
|
|
60
|
+
content += `:${seg.data.id || 'smile'}:`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let result;
|
|
64
|
+
if (scene_type === 'private' || scene_type === 'direct') {
|
|
65
|
+
// 私聊消息
|
|
66
|
+
result = await bot.sendDirectMessage(scene_id.string, content);
|
|
67
|
+
}
|
|
68
|
+
else if (scene_type === 'channel') {
|
|
69
|
+
// 频道消息
|
|
70
|
+
result = await bot.sendChannelMessage(scene_id.string, content);
|
|
71
|
+
}
|
|
72
|
+
else if (scene_type === 'group') {
|
|
73
|
+
// 群组消息也发送到频道
|
|
74
|
+
result = await bot.sendChannelMessage(scene_id.string, content);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`KOOK 不支持的消息场景类型: ${scene_type}`);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
message_id: this.createId(result.msg_id),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 删除/撤回消息
|
|
85
|
+
*/
|
|
86
|
+
async deleteMessage(uin, params) {
|
|
87
|
+
const account = this.getAccount(uin);
|
|
88
|
+
if (!account)
|
|
89
|
+
throw new Error(`Account ${uin} not found`);
|
|
90
|
+
const bot = account.client;
|
|
91
|
+
const msgId = params.message_id.string;
|
|
92
|
+
// 根据场景类型删除消息
|
|
93
|
+
// 需要从消息中获取 channel_id,这里简化处理
|
|
94
|
+
// 实际应该从消息缓存或数据库中获取
|
|
95
|
+
const channelId = params.scene_id?.string || '';
|
|
96
|
+
if (channelId) {
|
|
97
|
+
await bot.deleteMessage(channelId, msgId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 获取消息
|
|
102
|
+
*/
|
|
103
|
+
async getMessage(uin, params) {
|
|
104
|
+
const account = this.getAccount(uin);
|
|
105
|
+
if (!account)
|
|
106
|
+
throw new Error(`Account ${uin} not found`);
|
|
107
|
+
const bot = account.client;
|
|
108
|
+
const msgId = params.message_id.string;
|
|
109
|
+
const channelId = params.scene_id?.string || '';
|
|
110
|
+
const msg = await bot.getMessage(channelId, msgId);
|
|
111
|
+
return {
|
|
112
|
+
message_id: this.createId(msg.id),
|
|
113
|
+
time: msg.create_at,
|
|
114
|
+
sender: {
|
|
115
|
+
scene_type: 'channel',
|
|
116
|
+
sender_id: this.createId(msg.author.id),
|
|
117
|
+
scene_id: this.createId(msg.id),
|
|
118
|
+
sender_name: msg.author.username,
|
|
119
|
+
scene_name: '',
|
|
120
|
+
},
|
|
121
|
+
message: [{
|
|
122
|
+
type: msg.type === 9 ? 'text' : 'text',
|
|
123
|
+
data: { text: parseKMarkdown(msg.content) },
|
|
124
|
+
}],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 更新消息
|
|
129
|
+
*/
|
|
130
|
+
async updateMessage(uin, params) {
|
|
131
|
+
const account = this.getAccount(uin);
|
|
132
|
+
if (!account)
|
|
133
|
+
throw new Error(`Account ${uin} not found`);
|
|
134
|
+
const bot = account.client;
|
|
135
|
+
const msgId = params.message_id.string;
|
|
136
|
+
// 解析消息内容
|
|
137
|
+
let content = '';
|
|
138
|
+
for (const seg of params.message) {
|
|
139
|
+
if (typeof seg === 'string') {
|
|
140
|
+
content += seg;
|
|
141
|
+
}
|
|
142
|
+
else if (seg.type === 'text') {
|
|
143
|
+
content += seg.data.text || '';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// 更新消息需要 channelId,但参数中没有,尝试从消息中获取
|
|
147
|
+
// 如果无法获取,则使用第一个可用的频道(简化处理)
|
|
148
|
+
const channelId = params.scene_id?.string || '';
|
|
149
|
+
if (!channelId) {
|
|
150
|
+
throw new Error('更新消息需要 channel_id,但参数中未提供');
|
|
151
|
+
}
|
|
152
|
+
await bot.updateMessage(channelId, msgId, content);
|
|
153
|
+
}
|
|
154
|
+
// ============================================
|
|
155
|
+
// 用户相关方法
|
|
156
|
+
// ============================================
|
|
157
|
+
/**
|
|
158
|
+
* 获取机器人自身信息
|
|
159
|
+
*/
|
|
160
|
+
async getLoginInfo(uin) {
|
|
161
|
+
const account = this.getAccount(uin);
|
|
162
|
+
if (!account)
|
|
163
|
+
throw new Error(`Account ${uin} not found`);
|
|
164
|
+
const bot = account.client;
|
|
165
|
+
const me = await bot.getMe();
|
|
166
|
+
return {
|
|
167
|
+
user_id: this.createId(me.id),
|
|
168
|
+
user_name: me.username,
|
|
169
|
+
user_displayname: me.nickname,
|
|
170
|
+
avatar: me.avatar,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 获取用户信息
|
|
175
|
+
*/
|
|
176
|
+
async getUserInfo(uin, params) {
|
|
177
|
+
const account = this.getAccount(uin);
|
|
178
|
+
if (!account)
|
|
179
|
+
throw new Error(`Account ${uin} not found`);
|
|
180
|
+
const bot = account.client;
|
|
181
|
+
const userId = params.user_id.string;
|
|
182
|
+
const user = await bot.getUser(userId);
|
|
183
|
+
return {
|
|
184
|
+
user_id: this.createId(user.id),
|
|
185
|
+
user_name: user.username,
|
|
186
|
+
user_displayname: user.nickname,
|
|
187
|
+
avatar: user.avatar,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// ============================================
|
|
191
|
+
// 好友(私聊会话)相关方法
|
|
192
|
+
// ============================================
|
|
193
|
+
/**
|
|
194
|
+
* 获取好友列表(私聊会话列表)
|
|
195
|
+
*/
|
|
196
|
+
async getFriendList(uin, params) {
|
|
197
|
+
const account = this.getAccount(uin);
|
|
198
|
+
if (!account)
|
|
199
|
+
throw new Error(`Account ${uin} not found`);
|
|
200
|
+
const bot = account.client;
|
|
201
|
+
const friends = [];
|
|
202
|
+
let page = 1;
|
|
203
|
+
// KOOK 不提供私聊会话列表 API,返回空数组
|
|
204
|
+
// 如果需要获取私聊用户,需要通过其他方式(如消息记录)
|
|
205
|
+
return [];
|
|
206
|
+
return friends;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 获取好友信息
|
|
210
|
+
*/
|
|
211
|
+
async getFriendInfo(uin, params) {
|
|
212
|
+
const account = this.getAccount(uin);
|
|
213
|
+
if (!account)
|
|
214
|
+
throw new Error(`Account ${uin} not found`);
|
|
215
|
+
const bot = account.client;
|
|
216
|
+
const userId = params.user_id.string;
|
|
217
|
+
const user = await bot.getUser(userId);
|
|
218
|
+
return {
|
|
219
|
+
user_id: this.createId(user.id),
|
|
220
|
+
user_name: user.username,
|
|
221
|
+
remark: user.nickname,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// ============================================
|
|
225
|
+
// 群组(服务器)相关方法
|
|
226
|
+
// ============================================
|
|
227
|
+
/**
|
|
228
|
+
* 获取群列表(服务器列表)
|
|
229
|
+
*/
|
|
230
|
+
async getGroupList(uin, params) {
|
|
231
|
+
const account = this.getAccount(uin);
|
|
232
|
+
if (!account)
|
|
233
|
+
throw new Error(`Account ${uin} not found`);
|
|
234
|
+
const bot = account.client;
|
|
235
|
+
const groups = [];
|
|
236
|
+
let page = 1;
|
|
237
|
+
do {
|
|
238
|
+
const result = await bot.getGuildList(page, 50);
|
|
239
|
+
for (const guild of result.items) {
|
|
240
|
+
groups.push({
|
|
241
|
+
group_id: this.createId(guild.id),
|
|
242
|
+
group_name: guild.name,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (page >= result.meta.page_total)
|
|
246
|
+
break;
|
|
247
|
+
page++;
|
|
248
|
+
} while (true);
|
|
249
|
+
return groups;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 获取群信息(服务器信息)
|
|
253
|
+
*/
|
|
254
|
+
async getGroupInfo(uin, params) {
|
|
255
|
+
const account = this.getAccount(uin);
|
|
256
|
+
if (!account)
|
|
257
|
+
throw new Error(`Account ${uin} not found`);
|
|
258
|
+
const bot = account.client;
|
|
259
|
+
const guildId = params.group_id.string;
|
|
260
|
+
const guild = await bot.getGuild(guildId);
|
|
261
|
+
return {
|
|
262
|
+
group_id: this.createId(guild.id),
|
|
263
|
+
group_name: guild.name,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 退出群组(退出服务器)
|
|
268
|
+
*/
|
|
269
|
+
async leaveGroup(uin, params) {
|
|
270
|
+
const account = this.getAccount(uin);
|
|
271
|
+
if (!account)
|
|
272
|
+
throw new Error(`Account ${uin} not found`);
|
|
273
|
+
const bot = account.client;
|
|
274
|
+
await bot.leaveGuild(params.group_id.string);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* 获取群成员列表(服务器成员列表)
|
|
278
|
+
*/
|
|
279
|
+
async getGroupMemberList(uin, params) {
|
|
280
|
+
const account = this.getAccount(uin);
|
|
281
|
+
if (!account)
|
|
282
|
+
throw new Error(`Account ${uin} not found`);
|
|
283
|
+
const bot = account.client;
|
|
284
|
+
const guildId = params.group_id.string;
|
|
285
|
+
const members = [];
|
|
286
|
+
let page = 1;
|
|
287
|
+
do {
|
|
288
|
+
const result = await bot.getGuildMemberList(guildId, undefined, undefined, undefined, undefined, undefined, undefined, page, 50);
|
|
289
|
+
for (const user of result.items) {
|
|
290
|
+
members.push({
|
|
291
|
+
group_id: params.group_id,
|
|
292
|
+
user_id: this.createId(user.id),
|
|
293
|
+
user_name: user.username,
|
|
294
|
+
card: user.nickname,
|
|
295
|
+
role: 'member',
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (page >= result.meta.page_total)
|
|
299
|
+
break;
|
|
300
|
+
page++;
|
|
301
|
+
} while (true);
|
|
302
|
+
return members;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* 获取群成员信息
|
|
306
|
+
*/
|
|
307
|
+
async getGroupMemberInfo(uin, params) {
|
|
308
|
+
const account = this.getAccount(uin);
|
|
309
|
+
if (!account)
|
|
310
|
+
throw new Error(`Account ${uin} not found`);
|
|
311
|
+
const bot = account.client;
|
|
312
|
+
const guildId = params.group_id.string;
|
|
313
|
+
const userId = params.user_id.string;
|
|
314
|
+
const user = await bot.getUser(userId, guildId);
|
|
315
|
+
return {
|
|
316
|
+
group_id: params.group_id,
|
|
317
|
+
user_id: this.createId(user.id),
|
|
318
|
+
user_name: user.username,
|
|
319
|
+
card: user.nickname,
|
|
320
|
+
role: 'member',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 踢出群成员
|
|
325
|
+
*/
|
|
326
|
+
async kickGroupMember(uin, params) {
|
|
327
|
+
const account = this.getAccount(uin);
|
|
328
|
+
if (!account)
|
|
329
|
+
throw new Error(`Account ${uin} not found`);
|
|
330
|
+
const bot = account.client;
|
|
331
|
+
await bot.kickGuildMember(params.group_id.string, params.user_id.string);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* 设置群名片(设置服务器昵称)
|
|
335
|
+
*/
|
|
336
|
+
async setGroupCard(uin, params) {
|
|
337
|
+
const account = this.getAccount(uin);
|
|
338
|
+
if (!account)
|
|
339
|
+
throw new Error(`Account ${uin} not found`);
|
|
340
|
+
const bot = account.client;
|
|
341
|
+
await bot.setGuildNickname(params.group_id.string, params.card, params.user_id.string);
|
|
342
|
+
}
|
|
343
|
+
// ============================================
|
|
344
|
+
// 频道相关方法
|
|
345
|
+
// ============================================
|
|
346
|
+
/**
|
|
347
|
+
* 获取频道列表
|
|
348
|
+
*/
|
|
349
|
+
async getChannelList(uin, params) {
|
|
350
|
+
const account = this.getAccount(uin);
|
|
351
|
+
if (!account)
|
|
352
|
+
throw new Error(`Account ${uin} not found`);
|
|
353
|
+
const bot = account.client;
|
|
354
|
+
const guildId = params?.guild_id?.string;
|
|
355
|
+
if (!guildId)
|
|
356
|
+
throw new Error('guild_id is required');
|
|
357
|
+
const channels = [];
|
|
358
|
+
let page = 1;
|
|
359
|
+
do {
|
|
360
|
+
const result = await bot.getChannelList(guildId, undefined, page, 50);
|
|
361
|
+
for (const channel of result.items) {
|
|
362
|
+
channels.push({
|
|
363
|
+
channel_id: this.createId(channel.id),
|
|
364
|
+
channel_name: channel.name,
|
|
365
|
+
channel_type: channel.type,
|
|
366
|
+
parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (page >= result.meta.page_total)
|
|
370
|
+
break;
|
|
371
|
+
page++;
|
|
372
|
+
} while (true);
|
|
373
|
+
return channels;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* 获取频道信息
|
|
377
|
+
*/
|
|
378
|
+
async getChannelInfo(uin, params) {
|
|
379
|
+
const account = this.getAccount(uin);
|
|
380
|
+
if (!account)
|
|
381
|
+
throw new Error(`Account ${uin} not found`);
|
|
382
|
+
const bot = account.client;
|
|
383
|
+
const channel = await bot.getChannel(params.channel_id.string);
|
|
384
|
+
return {
|
|
385
|
+
channel_id: this.createId(channel.id),
|
|
386
|
+
channel_name: channel.name,
|
|
387
|
+
channel_type: channel.type,
|
|
388
|
+
parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 创建频道
|
|
393
|
+
*/
|
|
394
|
+
async createChannel(uin, params) {
|
|
395
|
+
const account = this.getAccount(uin);
|
|
396
|
+
if (!account)
|
|
397
|
+
throw new Error(`Account ${uin} not found`);
|
|
398
|
+
const bot = account.client;
|
|
399
|
+
const channel = await bot.createChannel(params.guild_id.string, params.channel_name, params.channel_type, params.parent_id?.string);
|
|
400
|
+
return {
|
|
401
|
+
channel_id: this.createId(channel.id),
|
|
402
|
+
channel_name: channel.name,
|
|
403
|
+
channel_type: channel.type,
|
|
404
|
+
parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* 更新频道
|
|
409
|
+
*/
|
|
410
|
+
async updateChannel(uin, params) {
|
|
411
|
+
const account = this.getAccount(uin);
|
|
412
|
+
if (!account)
|
|
413
|
+
throw new Error(`Account ${uin} not found`);
|
|
414
|
+
const bot = account.client;
|
|
415
|
+
await bot.updateChannel(params.channel_id.string, params.channel_name);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* 删除频道
|
|
419
|
+
*/
|
|
420
|
+
async deleteChannel(uin, params) {
|
|
421
|
+
const account = this.getAccount(uin);
|
|
422
|
+
if (!account)
|
|
423
|
+
throw new Error(`Account ${uin} not found`);
|
|
424
|
+
const bot = account.client;
|
|
425
|
+
await bot.deleteChannel(params.channel_id.string);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* 获取频道成员列表
|
|
429
|
+
*/
|
|
430
|
+
async getChannelMemberList(uin, params) {
|
|
431
|
+
const account = this.getAccount(uin);
|
|
432
|
+
if (!account)
|
|
433
|
+
throw new Error(`Account ${uin} not found`);
|
|
434
|
+
const bot = account.client;
|
|
435
|
+
const result = await bot.getChannelUserList(params.channel_id.string);
|
|
436
|
+
return result.items.map(user => ({
|
|
437
|
+
channel_id: params.channel_id,
|
|
438
|
+
user_id: this.createId(user.id),
|
|
439
|
+
user_name: user.username,
|
|
440
|
+
role: 'member',
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
// ============================================
|
|
444
|
+
// 服务器(公会)相关方法
|
|
445
|
+
// ============================================
|
|
446
|
+
/**
|
|
447
|
+
* 获取服务器列表
|
|
448
|
+
*/
|
|
449
|
+
async getGuildList(uin) {
|
|
450
|
+
const account = this.getAccount(uin);
|
|
451
|
+
if (!account)
|
|
452
|
+
throw new Error(`Account ${uin} not found`);
|
|
453
|
+
const bot = account.client;
|
|
454
|
+
const guilds = [];
|
|
455
|
+
let page = 1;
|
|
456
|
+
do {
|
|
457
|
+
const result = await bot.getGuildList(page, 50);
|
|
458
|
+
for (const guild of result.items) {
|
|
459
|
+
guilds.push({
|
|
460
|
+
guild_id: this.createId(guild.id),
|
|
461
|
+
guild_name: guild.name,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
if (page >= result.meta.page_total)
|
|
465
|
+
break;
|
|
466
|
+
page++;
|
|
467
|
+
} while (true);
|
|
468
|
+
return guilds;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* 获取服务器信息
|
|
472
|
+
*/
|
|
473
|
+
async getGuildInfo(uin, params) {
|
|
474
|
+
const account = this.getAccount(uin);
|
|
475
|
+
if (!account)
|
|
476
|
+
throw new Error(`Account ${uin} not found`);
|
|
477
|
+
const bot = account.client;
|
|
478
|
+
const guild = await bot.getGuild(params.guild_id.string);
|
|
479
|
+
return {
|
|
480
|
+
guild_id: this.createId(guild.id),
|
|
481
|
+
guild_name: guild.name,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* 获取服务器成员信息
|
|
486
|
+
*/
|
|
487
|
+
async getGuildMemberInfo(uin, params) {
|
|
488
|
+
const account = this.getAccount(uin);
|
|
489
|
+
if (!account)
|
|
490
|
+
throw new Error(`Account ${uin} not found`);
|
|
491
|
+
const bot = account.client;
|
|
492
|
+
const user = await bot.getUser(params.user_id.string, params.guild_id.string);
|
|
493
|
+
return {
|
|
494
|
+
guild_id: params.guild_id,
|
|
495
|
+
user_id: this.createId(user.id),
|
|
496
|
+
user_name: user.username,
|
|
497
|
+
nickname: user.nickname,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
// ============================================
|
|
501
|
+
// 系统相关方法
|
|
502
|
+
// ============================================
|
|
503
|
+
/**
|
|
504
|
+
* 获取版本信息
|
|
505
|
+
*/
|
|
506
|
+
async getVersion(uin) {
|
|
507
|
+
return {
|
|
508
|
+
app_name: 'onebots KOOK Adapter',
|
|
509
|
+
app_version: '1.0.0',
|
|
510
|
+
impl: 'kook',
|
|
511
|
+
version: '1.0.0',
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* 获取运行状态
|
|
516
|
+
*/
|
|
517
|
+
async getStatus(uin) {
|
|
518
|
+
const account = this.getAccount(uin);
|
|
519
|
+
return {
|
|
520
|
+
online: account?.status === AccountStatus.Online,
|
|
521
|
+
good: account?.status === AccountStatus.Online,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
// ============================================
|
|
525
|
+
// 账号创建
|
|
526
|
+
// ============================================
|
|
527
|
+
createAccount(config) {
|
|
528
|
+
const kookConfig = {
|
|
529
|
+
account_id: config.account_id,
|
|
530
|
+
token: config.token,
|
|
531
|
+
verifyToken: config.verifyToken,
|
|
532
|
+
encryptKey: config.encryptKey,
|
|
533
|
+
mode: config.mode || 'websocket',
|
|
534
|
+
};
|
|
535
|
+
const bot = new KookBot(kookConfig);
|
|
536
|
+
const account = new Account(this, bot, config);
|
|
537
|
+
// Webhook 路由
|
|
538
|
+
if (kookConfig.mode === 'webhook') {
|
|
539
|
+
this.app.router.post(`${account.path}/webhook`, bot.handleWebhook.bind(bot));
|
|
540
|
+
}
|
|
541
|
+
// 监听 Bot 事件
|
|
542
|
+
bot.on('ready', () => {
|
|
543
|
+
this.logger.info(`KOOK Bot ${config.account_id} 已就绪`);
|
|
544
|
+
});
|
|
545
|
+
bot.on('error', (error) => {
|
|
546
|
+
this.logger.error(`KOOK Bot ${config.account_id} 错误:`, error);
|
|
547
|
+
});
|
|
548
|
+
// 监听频道消息
|
|
549
|
+
bot.on('channel_message', (event) => {
|
|
550
|
+
// 忽略自己发送的消息
|
|
551
|
+
const me = bot.getCachedMe();
|
|
552
|
+
if (me && event.author_id === me.id)
|
|
553
|
+
return;
|
|
554
|
+
// 打印消息接收日志
|
|
555
|
+
const content = event.content || '';
|
|
556
|
+
const contentPreview = content.length > 100 ? content.substring(0, 100) + '...' : content;
|
|
557
|
+
const channelId = event.channel_id || '';
|
|
558
|
+
this.logger.info(`[KOOK] 收到频道消息 | 消息ID: ${event.msg_id} | 频道: ${channelId} | ` +
|
|
559
|
+
`发送者: ${event.extra?.author?.username || event.author_id} | 内容: ${contentPreview}`);
|
|
560
|
+
// 构建消息段
|
|
561
|
+
const messageSegments = [];
|
|
562
|
+
const rawContent = event.content || '';
|
|
563
|
+
// 根据消息类型处理
|
|
564
|
+
switch (event.type) {
|
|
565
|
+
case 1: // 文字消息
|
|
566
|
+
case 9: // KMarkdown
|
|
567
|
+
messageSegments.push({
|
|
568
|
+
type: 'text',
|
|
569
|
+
data: { text: parseKMarkdown(rawContent) },
|
|
570
|
+
});
|
|
571
|
+
break;
|
|
572
|
+
case 2: // 图片
|
|
573
|
+
messageSegments.push({
|
|
574
|
+
type: 'image',
|
|
575
|
+
data: { url: rawContent },
|
|
576
|
+
});
|
|
577
|
+
break;
|
|
578
|
+
case 3: // 视频
|
|
579
|
+
messageSegments.push({
|
|
580
|
+
type: 'video',
|
|
581
|
+
data: { url: rawContent },
|
|
582
|
+
});
|
|
583
|
+
break;
|
|
584
|
+
case 4: // 文件
|
|
585
|
+
messageSegments.push({
|
|
586
|
+
type: 'file',
|
|
587
|
+
data: { url: rawContent },
|
|
588
|
+
});
|
|
589
|
+
break;
|
|
590
|
+
case 8: // 音频
|
|
591
|
+
messageSegments.push({
|
|
592
|
+
type: 'audio',
|
|
593
|
+
data: { url: rawContent },
|
|
594
|
+
});
|
|
595
|
+
break;
|
|
596
|
+
case 10: // 卡片消息
|
|
597
|
+
messageSegments.push({
|
|
598
|
+
type: 'card',
|
|
599
|
+
data: { content: rawContent },
|
|
600
|
+
});
|
|
601
|
+
break;
|
|
602
|
+
default:
|
|
603
|
+
messageSegments.push({
|
|
604
|
+
type: 'text',
|
|
605
|
+
data: { text: rawContent },
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
// 处理 @
|
|
609
|
+
if (event.extra?.mention) {
|
|
610
|
+
for (const userId of event.extra.mention) {
|
|
611
|
+
messageSegments.unshift({
|
|
612
|
+
type: 'at',
|
|
613
|
+
data: { qq: userId },
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
// 转换为 CommonEvent 格式
|
|
618
|
+
const commonEvent = {
|
|
619
|
+
id: this.createId(event.msg_id),
|
|
620
|
+
timestamp: event.msg_timestamp,
|
|
621
|
+
platform: 'kook',
|
|
622
|
+
bot_id: this.createId(config.account_id),
|
|
623
|
+
type: 'message',
|
|
624
|
+
message_type: 'channel',
|
|
625
|
+
sender: {
|
|
626
|
+
id: this.createId(event.author_id),
|
|
627
|
+
name: event.extra?.author?.username || event.author_id,
|
|
628
|
+
avatar: event.extra?.author?.avatar,
|
|
629
|
+
},
|
|
630
|
+
group: {
|
|
631
|
+
id: this.createId(event.extra?.guild_id || event.channel_id || ''),
|
|
632
|
+
name: '',
|
|
633
|
+
},
|
|
634
|
+
message_id: this.createId(event.msg_id),
|
|
635
|
+
raw_message: rawContent,
|
|
636
|
+
message: messageSegments,
|
|
637
|
+
};
|
|
638
|
+
// 派发到协议层
|
|
639
|
+
account.dispatch(commonEvent);
|
|
640
|
+
});
|
|
641
|
+
// 监听私聊消息
|
|
642
|
+
bot.on('direct_message', (event) => {
|
|
643
|
+
// 忽略自己发送的消息
|
|
644
|
+
const me = bot.getCachedMe();
|
|
645
|
+
if (me && event.author_id === me.id)
|
|
646
|
+
return;
|
|
647
|
+
// 打印消息接收日志
|
|
648
|
+
const content = event.content || '';
|
|
649
|
+
const contentPreview = content.length > 100 ? content.substring(0, 100) + '...' : content;
|
|
650
|
+
this.logger.info(`[KOOK] 收到私聊消息 | 消息ID: ${event.msg_id} | ` +
|
|
651
|
+
`发送者: ${event.extra?.author?.username || event.author_id} | 内容: ${contentPreview}`);
|
|
652
|
+
// 构建消息段
|
|
653
|
+
const messageSegments = [];
|
|
654
|
+
const rawContent = event.content || '';
|
|
655
|
+
switch (event.type) {
|
|
656
|
+
case 1:
|
|
657
|
+
case 9:
|
|
658
|
+
messageSegments.push({
|
|
659
|
+
type: 'text',
|
|
660
|
+
data: { text: parseKMarkdown(rawContent) },
|
|
661
|
+
});
|
|
662
|
+
break;
|
|
663
|
+
case 2:
|
|
664
|
+
messageSegments.push({
|
|
665
|
+
type: 'image',
|
|
666
|
+
data: { url: rawContent },
|
|
667
|
+
});
|
|
668
|
+
break;
|
|
669
|
+
default:
|
|
670
|
+
messageSegments.push({
|
|
671
|
+
type: 'text',
|
|
672
|
+
data: { text: rawContent },
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
// 转换为 CommonEvent 格式
|
|
676
|
+
const commonEvent = {
|
|
677
|
+
id: this.createId(event.msg_id),
|
|
678
|
+
timestamp: event.msg_timestamp,
|
|
679
|
+
platform: 'kook',
|
|
680
|
+
bot_id: this.createId(config.account_id),
|
|
681
|
+
type: 'message',
|
|
682
|
+
message_type: 'private',
|
|
683
|
+
sender: {
|
|
684
|
+
id: this.createId(event.author_id),
|
|
685
|
+
name: event.extra?.author?.username || event.author_id,
|
|
686
|
+
avatar: event.extra?.author?.avatar,
|
|
687
|
+
},
|
|
688
|
+
message_id: this.createId(event.msg_id),
|
|
689
|
+
raw_message: rawContent,
|
|
690
|
+
message: messageSegments,
|
|
691
|
+
};
|
|
692
|
+
// 派发到协议层
|
|
693
|
+
account.dispatch(commonEvent);
|
|
694
|
+
});
|
|
695
|
+
// 监听成员加入服务器
|
|
696
|
+
bot.on('system.joined_guild', (event) => {
|
|
697
|
+
this.logger.info(`用户加入服务器: ${event.extra?.body?.user_id}`);
|
|
698
|
+
const commonEvent = {
|
|
699
|
+
id: this.createId(event.msg_id),
|
|
700
|
+
timestamp: event.msg_timestamp,
|
|
701
|
+
platform: 'kook',
|
|
702
|
+
bot_id: this.createId(config.account_id),
|
|
703
|
+
type: 'notice',
|
|
704
|
+
notice_type: 'group_increase',
|
|
705
|
+
user: {
|
|
706
|
+
id: this.createId(event.extra?.body?.user_id || ''),
|
|
707
|
+
name: '',
|
|
708
|
+
},
|
|
709
|
+
group: {
|
|
710
|
+
id: this.createId(event.target_id),
|
|
711
|
+
name: '',
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
account.dispatch(commonEvent);
|
|
715
|
+
});
|
|
716
|
+
// 监听成员离开服务器
|
|
717
|
+
bot.on('system.exited_guild', (event) => {
|
|
718
|
+
this.logger.info(`用户离开服务器: ${event.extra?.body?.user_id}`);
|
|
719
|
+
const commonEvent = {
|
|
720
|
+
id: this.createId(event.msg_id),
|
|
721
|
+
timestamp: event.msg_timestamp,
|
|
722
|
+
platform: 'kook',
|
|
723
|
+
bot_id: this.createId(config.account_id),
|
|
724
|
+
type: 'notice',
|
|
725
|
+
notice_type: 'group_decrease',
|
|
726
|
+
user: {
|
|
727
|
+
id: this.createId(event.extra?.body?.user_id || ''),
|
|
728
|
+
name: '',
|
|
729
|
+
},
|
|
730
|
+
group: {
|
|
731
|
+
id: this.createId(event.target_id),
|
|
732
|
+
name: '',
|
|
733
|
+
},
|
|
734
|
+
};
|
|
735
|
+
account.dispatch(commonEvent);
|
|
736
|
+
});
|
|
737
|
+
// 监听表情回应
|
|
738
|
+
bot.on('system.added_reaction', (event) => {
|
|
739
|
+
this.logger.debug(`添加表情回应:`, event);
|
|
740
|
+
const commonEvent = {
|
|
741
|
+
id: this.createId(event.msg_id),
|
|
742
|
+
timestamp: event.msg_timestamp,
|
|
743
|
+
platform: 'kook',
|
|
744
|
+
bot_id: this.createId(config.account_id),
|
|
745
|
+
type: 'notice',
|
|
746
|
+
notice_type: 'custom',
|
|
747
|
+
sub_type: 'reaction_add',
|
|
748
|
+
user: {
|
|
749
|
+
id: this.createId(event.extra?.body?.user_id || ''),
|
|
750
|
+
name: '',
|
|
751
|
+
},
|
|
752
|
+
message_id: event.extra?.body?.msg_id,
|
|
753
|
+
emoji: event.extra?.body?.emoji,
|
|
754
|
+
};
|
|
755
|
+
account.dispatch(commonEvent);
|
|
756
|
+
});
|
|
757
|
+
// 监听消息更新
|
|
758
|
+
bot.on('system.updated_message', (event) => {
|
|
759
|
+
this.logger.debug(`消息更新:`, event);
|
|
760
|
+
const commonEvent = {
|
|
761
|
+
id: this.createId(event.msg_id),
|
|
762
|
+
timestamp: event.msg_timestamp,
|
|
763
|
+
platform: 'kook',
|
|
764
|
+
bot_id: this.createId(config.account_id),
|
|
765
|
+
type: 'notice',
|
|
766
|
+
notice_type: 'custom',
|
|
767
|
+
sub_type: 'message_update',
|
|
768
|
+
message_id: event.extra?.body?.msg_id,
|
|
769
|
+
content: event.extra?.body?.content,
|
|
770
|
+
};
|
|
771
|
+
account.dispatch(commonEvent);
|
|
772
|
+
});
|
|
773
|
+
// 监听消息删除
|
|
774
|
+
bot.on('system.deleted_message', (event) => {
|
|
775
|
+
this.logger.debug(`消息删除:`, event);
|
|
776
|
+
const commonEvent = {
|
|
777
|
+
id: this.createId(event.msg_id),
|
|
778
|
+
timestamp: event.msg_timestamp,
|
|
779
|
+
platform: 'kook',
|
|
780
|
+
bot_id: this.createId(config.account_id),
|
|
781
|
+
type: 'notice',
|
|
782
|
+
notice_type: 'custom',
|
|
783
|
+
sub_type: 'message_delete',
|
|
784
|
+
message_id: event.extra?.body?.msg_id,
|
|
785
|
+
};
|
|
786
|
+
account.dispatch(commonEvent);
|
|
787
|
+
});
|
|
788
|
+
// 启动时初始化 Bot
|
|
789
|
+
account.on('start', async () => {
|
|
790
|
+
try {
|
|
791
|
+
await bot.start();
|
|
792
|
+
account.status = AccountStatus.Online;
|
|
793
|
+
const me = bot.getCachedMe();
|
|
794
|
+
account.nickname = me?.username || 'KOOK Bot';
|
|
795
|
+
account.avatar = me?.avatar || this.icon;
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
this.logger.error(`启动 KOOK Bot 失败:`, error);
|
|
799
|
+
account.status = AccountStatus.OffLine;
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
account.on('stop', async () => {
|
|
803
|
+
await bot.stop();
|
|
804
|
+
account.status = AccountStatus.OffLine;
|
|
805
|
+
});
|
|
806
|
+
return account;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
AdapterRegistry.register('kook', KookAdapter, {
|
|
810
|
+
name: 'kook',
|
|
811
|
+
displayName: 'KOOK官方机器人',
|
|
812
|
+
description: 'KOOK官方机器人适配器,支持频道、群聊和私聊',
|
|
813
|
+
icon: 'https://www.kookapp.cn/favicon.ico',
|
|
814
|
+
homepage: 'https://www.kookapp.cn/',
|
|
815
|
+
author: '凉菜',
|
|
816
|
+
});
|
|
817
|
+
//# sourceMappingURL=adapter.js.map
|