@onebots/adapter-discord 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/lib/adapter.js ADDED
@@ -0,0 +1,897 @@
1
+ /**
2
+ * Discord 适配器
3
+ * 轻量版实现,直接封装 Discord API
4
+ */
5
+ import { Account, AdapterRegistry, AccountStatus } from "onebots";
6
+ import { Adapter } from "onebots";
7
+ import { DiscordBot } from "./bot.js";
8
+ import { ChannelType } from "./types.js";
9
+ export class DiscordAdapter extends Adapter {
10
+ constructor(app) {
11
+ super(app, "discord");
12
+ this.icon = "https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png";
13
+ }
14
+ // ============================================
15
+ // 消息相关方法
16
+ // ============================================
17
+ /**
18
+ * 发送消息
19
+ * 支持私聊(DM)、群组(Guild)和频道(Channel)消息
20
+ */
21
+ async sendMessage(uin, params) {
22
+ const account = this.getAccount(uin);
23
+ if (!account)
24
+ throw new Error(`Account ${uin} not found`);
25
+ const bot = account.client;
26
+ const { scene_id, scene_type, message } = params;
27
+ let messageId;
28
+ const channelId = scene_id.string;
29
+ // 构建消息内容
30
+ const { content, embeds } = this.buildDiscordMessage(message);
31
+ try {
32
+ let sentMessage;
33
+ if (scene_type === "private") {
34
+ // 私信消息 - scene_id 是用户 ID
35
+ sentMessage = await bot.sendDM(channelId, { content, embeds });
36
+ }
37
+ else if (scene_type === "channel" || scene_type === "group") {
38
+ // 频道消息 - scene_id 是频道 ID
39
+ sentMessage = await bot.sendMessage(channelId, { content, embeds });
40
+ }
41
+ else {
42
+ throw new Error(`不支持的消息类型: ${scene_type}`);
43
+ }
44
+ messageId = sentMessage.id;
45
+ }
46
+ catch (error) {
47
+ this.logger.error(`发送消息失败:`, error);
48
+ throw error;
49
+ }
50
+ return {
51
+ message_id: this.createId(messageId),
52
+ };
53
+ }
54
+ /**
55
+ * 删除/撤回消息
56
+ */
57
+ async deleteMessage(uin, params) {
58
+ const account = this.getAccount(uin);
59
+ if (!account)
60
+ throw new Error(`Account ${uin} not found`);
61
+ const bot = account.client;
62
+ const messageId = params.message_id.string;
63
+ const channelId = params.scene_id?.string;
64
+ if (!channelId) {
65
+ throw new Error('删除消息需要提供 scene_id (频道ID)');
66
+ }
67
+ await bot.deleteMessage(channelId, messageId);
68
+ }
69
+ /**
70
+ * 获取消息
71
+ */
72
+ async getMessage(uin, params) {
73
+ const account = this.getAccount(uin);
74
+ if (!account)
75
+ throw new Error(`Account ${uin} not found`);
76
+ const bot = account.client;
77
+ const messageId = params.message_id.string;
78
+ const channelId = params.scene_id?.string;
79
+ if (!channelId) {
80
+ throw new Error('获取消息需要提供 scene_id (频道ID)');
81
+ }
82
+ const message = await bot.getMessage(channelId, messageId);
83
+ return this.convertMessageToInfo(message);
84
+ }
85
+ /**
86
+ * 获取历史消息
87
+ */
88
+ async getMessageHistory(uin, params) {
89
+ const account = this.getAccount(uin);
90
+ if (!account)
91
+ throw new Error(`Account ${uin} not found`);
92
+ const bot = account.client;
93
+ const channelId = params.scene_id.string;
94
+ const limit = params.limit || 50;
95
+ const messages = await bot.getMessageHistory(channelId, limit);
96
+ return [...messages.values()].map(msg => this.convertMessageToInfo(msg));
97
+ }
98
+ // ============================================
99
+ // 用户相关方法
100
+ // ============================================
101
+ /**
102
+ * 获取机器人信息
103
+ */
104
+ async getLoginInfo(uin) {
105
+ const account = this.getAccount(uin);
106
+ if (!account)
107
+ throw new Error(`Account ${uin} not found`);
108
+ const bot = account.client;
109
+ const user = bot.getBotUser();
110
+ if (!user) {
111
+ throw new Error('Bot 未就绪');
112
+ }
113
+ return {
114
+ user_id: this.createId(user.id),
115
+ user_name: user.username,
116
+ user_displayname: user.global_name || user.username,
117
+ avatar: user.displayAvatarURL(),
118
+ };
119
+ }
120
+ /**
121
+ * 获取用户信息
122
+ */
123
+ async getUserInfo(uin, params) {
124
+ const account = this.getAccount(uin);
125
+ if (!account)
126
+ throw new Error(`Account ${uin} not found`);
127
+ const bot = account.client;
128
+ const userId = params.user_id.string;
129
+ const user = await bot.getUser(userId);
130
+ return {
131
+ user_id: this.createId(user.id),
132
+ user_name: user.username,
133
+ user_displayname: user.global_name || user.username,
134
+ avatar: user.displayAvatarURL(),
135
+ };
136
+ }
137
+ // ============================================
138
+ // 好友相关方法
139
+ // Discord 没有好友系统,返回空列表
140
+ // ============================================
141
+ /**
142
+ * 获取好友列表
143
+ * Discord 没有传统好友系统,返回空列表
144
+ */
145
+ async getFriendList(uin, params) {
146
+ return [];
147
+ }
148
+ /**
149
+ * 获取好友信息
150
+ * Discord 没有传统好友系统,返回用户信息
151
+ */
152
+ async getFriendInfo(uin, params) {
153
+ const account = this.getAccount(uin);
154
+ if (!account)
155
+ throw new Error(`Account ${uin} not found`);
156
+ const bot = account.client;
157
+ const userId = params.user_id.string;
158
+ const user = await bot.getUser(userId);
159
+ return {
160
+ user_id: this.createId(user.id),
161
+ user_name: user.username,
162
+ };
163
+ }
164
+ // ============================================
165
+ // 群组(服务器/Guild)相关方法
166
+ // ============================================
167
+ /**
168
+ * 获取群列表(服务器列表)
169
+ */
170
+ async getGroupList(uin, params) {
171
+ const account = this.getAccount(uin);
172
+ if (!account)
173
+ throw new Error(`Account ${uin} not found`);
174
+ const bot = account.client;
175
+ const guilds = bot.getGuilds();
176
+ return [...guilds.values()].map(guild => ({
177
+ group_id: this.createId(guild.id),
178
+ group_name: guild.name,
179
+ member_count: guild.member_count,
180
+ }));
181
+ }
182
+ /**
183
+ * 获取群信息(服务器信息)
184
+ */
185
+ async getGroupInfo(uin, params) {
186
+ const account = this.getAccount(uin);
187
+ if (!account)
188
+ throw new Error(`Account ${uin} not found`);
189
+ const bot = account.client;
190
+ const guildId = params.group_id.string;
191
+ const guild = await bot.getGuild(guildId);
192
+ return {
193
+ group_id: this.createId(guild.id),
194
+ group_name: guild.name,
195
+ member_count: guild.member_count,
196
+ };
197
+ }
198
+ /**
199
+ * 退出群组(服务器)
200
+ */
201
+ async leaveGroup(uin, params) {
202
+ const account = this.getAccount(uin);
203
+ if (!account)
204
+ throw new Error(`Account ${uin} not found`);
205
+ const bot = account.client;
206
+ const guildId = params.group_id.string;
207
+ // 使用 REST API 离开服务器
208
+ await bot.getREST().request(`/users/@me/guilds/${guildId}`, {
209
+ method: 'DELETE',
210
+ });
211
+ }
212
+ /**
213
+ * 获取群成员列表
214
+ */
215
+ async getGroupMemberList(uin, params) {
216
+ const account = this.getAccount(uin);
217
+ if (!account)
218
+ throw new Error(`Account ${uin} not found`);
219
+ const bot = account.client;
220
+ const guildId = params.group_id.string;
221
+ const members = await bot.getGuildMembers(guildId);
222
+ return [...members.values()].map(member => ({
223
+ group_id: params.group_id,
224
+ user_id: this.createId(member.user.id),
225
+ user_name: member.user.username,
226
+ card: member.nick || undefined,
227
+ role: this.getMemberRole(member),
228
+ }));
229
+ }
230
+ /**
231
+ * 获取群成员信息
232
+ */
233
+ async getGroupMemberInfo(uin, params) {
234
+ const account = this.getAccount(uin);
235
+ if (!account)
236
+ throw new Error(`Account ${uin} not found`);
237
+ const bot = account.client;
238
+ const guildId = params.group_id.string;
239
+ const userId = params.user_id.string;
240
+ const member = await bot.getGuildMember(guildId, userId);
241
+ return {
242
+ group_id: params.group_id,
243
+ user_id: this.createId(member.user.id),
244
+ user_name: member.user.username,
245
+ card: member.nick || undefined,
246
+ role: this.getMemberRole(member),
247
+ };
248
+ }
249
+ /**
250
+ * 踢出群成员
251
+ */
252
+ async kickGroupMember(uin, params) {
253
+ const account = this.getAccount(uin);
254
+ if (!account)
255
+ throw new Error(`Account ${uin} not found`);
256
+ const bot = account.client;
257
+ const guildId = params.group_id.string;
258
+ const userId = params.user_id.string;
259
+ await bot.kickMember(guildId, userId, 'Kicked via onebots');
260
+ }
261
+ /**
262
+ * 群成员禁言(超时)
263
+ */
264
+ async muteGroupMember(uin, params) {
265
+ const account = this.getAccount(uin);
266
+ if (!account)
267
+ throw new Error(`Account ${uin} not found`);
268
+ const bot = account.client;
269
+ const guildId = params.group_id.string;
270
+ const userId = params.user_id.string;
271
+ const duration = params.duration;
272
+ if (duration === 0) {
273
+ // 解除禁言
274
+ await bot.removeTimeout(guildId, userId);
275
+ }
276
+ else {
277
+ // 设置超时
278
+ await bot.timeoutMember(guildId, userId, duration);
279
+ }
280
+ }
281
+ /**
282
+ * 设置群名片(昵称)
283
+ */
284
+ async setGroupCard(uin, params) {
285
+ const account = this.getAccount(uin);
286
+ if (!account)
287
+ throw new Error(`Account ${uin} not found`);
288
+ const bot = account.client;
289
+ const guildId = params.group_id.string;
290
+ const userId = params.user_id.string;
291
+ await bot.setMemberNickname(guildId, userId, params.card || null);
292
+ }
293
+ /**
294
+ * 发送群消息表情回应
295
+ */
296
+ async sendGroupMessageReaction(uin, params) {
297
+ const account = this.getAccount(uin);
298
+ if (!account)
299
+ throw new Error(`Account ${uin} not found`);
300
+ const bot = account.client;
301
+ const channelId = params.group_id.string;
302
+ const messageId = params.message_id.string;
303
+ // Discord 使用 Unicode emoji 或自定义 emoji 格式
304
+ const emoji = String.fromCodePoint(params.face_id);
305
+ await bot.addReaction(channelId, messageId, emoji);
306
+ }
307
+ // ============================================
308
+ // 频道相关方法
309
+ // ============================================
310
+ /**
311
+ * 获取频道服务器信息
312
+ */
313
+ async getGuildInfo(uin, params) {
314
+ const account = this.getAccount(uin);
315
+ if (!account)
316
+ throw new Error(`Account ${uin} not found`);
317
+ const bot = account.client;
318
+ const guildId = params.guild_id.string;
319
+ const guild = await bot.getGuild(guildId);
320
+ return {
321
+ guild_id: this.createId(guild.id),
322
+ guild_name: guild.name,
323
+ guild_display_name: guild.name,
324
+ };
325
+ }
326
+ /**
327
+ * 获取频道服务器列表
328
+ */
329
+ async getGuildList(uin) {
330
+ const account = this.getAccount(uin);
331
+ if (!account)
332
+ throw new Error(`Account ${uin} not found`);
333
+ const bot = account.client;
334
+ const guilds = bot.getGuilds();
335
+ return [...guilds.values()].map(guild => ({
336
+ guild_id: this.createId(guild.id),
337
+ guild_name: guild.name,
338
+ guild_display_name: guild.name,
339
+ }));
340
+ }
341
+ /**
342
+ * 获取频道成员信息
343
+ */
344
+ async getGuildMemberInfo(uin, params) {
345
+ const account = this.getAccount(uin);
346
+ if (!account)
347
+ throw new Error(`Account ${uin} not found`);
348
+ const bot = account.client;
349
+ const guildId = params.guild_id.string;
350
+ const userId = params.user_id.string;
351
+ const member = await bot.getGuildMember(guildId, userId);
352
+ return {
353
+ guild_id: params.guild_id,
354
+ user_id: this.createId(member.user.id),
355
+ user_name: member.user.username,
356
+ nickname: member.nick || undefined,
357
+ role: this.getMemberRole(member),
358
+ };
359
+ }
360
+ /**
361
+ * 获取频道信息
362
+ */
363
+ async getChannelInfo(uin, params) {
364
+ const account = this.getAccount(uin);
365
+ if (!account)
366
+ throw new Error(`Account ${uin} not found`);
367
+ const bot = account.client;
368
+ const channelId = params.channel_id.string;
369
+ const channel = await bot.getChannel(channelId);
370
+ if (!channel) {
371
+ throw new Error(`频道 ${channelId} 不存在`);
372
+ }
373
+ return {
374
+ channel_id: this.createId(channel.id),
375
+ channel_name: channel.name || '',
376
+ channel_type: channel.type,
377
+ parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
378
+ };
379
+ }
380
+ /**
381
+ * 获取频道列表
382
+ */
383
+ async getChannelList(uin, params) {
384
+ const account = this.getAccount(uin);
385
+ if (!account)
386
+ throw new Error(`Account ${uin} not found`);
387
+ if (!params?.guild_id) {
388
+ throw new Error('获取频道列表需要提供 guild_id');
389
+ }
390
+ const bot = account.client;
391
+ const guildId = params.guild_id.string;
392
+ const channels = await bot.getGuildChannels(guildId);
393
+ return [...channels.values()].map(channel => ({
394
+ channel_id: this.createId(channel.id),
395
+ channel_name: channel.name || '',
396
+ channel_type: channel.type,
397
+ parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
398
+ }));
399
+ }
400
+ /**
401
+ * 创建频道
402
+ */
403
+ async createChannel(uin, params) {
404
+ const account = this.getAccount(uin);
405
+ if (!account)
406
+ throw new Error(`Account ${uin} not found`);
407
+ const bot = account.client;
408
+ const guildId = params.guild_id.string;
409
+ const channel = await bot.createTextChannel(guildId, params.channel_name, {
410
+ parent: params.parent_id?.string,
411
+ });
412
+ return {
413
+ channel_id: this.createId(channel.id),
414
+ channel_name: channel.name || '',
415
+ channel_type: channel.type,
416
+ parent_id: channel.parent_id ? this.createId(channel.parent_id) : undefined,
417
+ };
418
+ }
419
+ /**
420
+ * 删除频道
421
+ */
422
+ async deleteChannel(uin, params) {
423
+ const account = this.getAccount(uin);
424
+ if (!account)
425
+ throw new Error(`Account ${uin} not found`);
426
+ const bot = account.client;
427
+ const channelId = params.channel_id.string;
428
+ await bot.deleteChannel(channelId);
429
+ }
430
+ /**
431
+ * 更新频道
432
+ */
433
+ async updateChannel(uin, params) {
434
+ const account = this.getAccount(uin);
435
+ if (!account)
436
+ throw new Error(`Account ${uin} not found`);
437
+ const bot = account.client;
438
+ const channelId = params.channel_id.string;
439
+ await bot.updateChannel(channelId, {
440
+ name: params.channel_name,
441
+ parent: params.parent_id?.string,
442
+ });
443
+ }
444
+ // ============================================
445
+ // 频道成员相关方法
446
+ // ============================================
447
+ /**
448
+ * 获取频道成员信息
449
+ */
450
+ async getChannelMemberInfo(uin, params) {
451
+ const account = this.getAccount(uin);
452
+ if (!account)
453
+ throw new Error(`Account ${uin} not found`);
454
+ const bot = account.client;
455
+ const channelId = params.channel_id.string;
456
+ const userId = params.user_id.string;
457
+ // 获取频道所属的服务器
458
+ const channel = await bot.getChannel(channelId);
459
+ if (!channel || !channel.guild_id) {
460
+ throw new Error(`频道 ${channelId} 不属于任何服务器`);
461
+ }
462
+ const member = await bot.getGuildMember(channel.guild_id, userId);
463
+ return {
464
+ channel_id: params.channel_id,
465
+ user_id: this.createId(member.user.id),
466
+ user_name: member.user.username,
467
+ role: this.getMemberRole(member),
468
+ };
469
+ }
470
+ /**
471
+ * 获取频道成员列表
472
+ */
473
+ async getChannelMemberList(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 channelId = params.channel_id.string;
479
+ // 获取频道所属的服务器
480
+ const channel = await bot.getChannel(channelId);
481
+ if (!channel || !channel.guild_id) {
482
+ throw new Error(`频道 ${channelId} 不属于任何服务器`);
483
+ }
484
+ const members = await bot.getGuildMembers(channel.guild_id);
485
+ return [...members.values()].map(member => ({
486
+ channel_id: params.channel_id,
487
+ user_id: this.createId(member.user.id),
488
+ user_name: member.user.username,
489
+ role: this.getMemberRole(member),
490
+ }));
491
+ }
492
+ /**
493
+ * 踢出频道成员
494
+ */
495
+ async kickChannelMember(uin, params) {
496
+ const account = this.getAccount(uin);
497
+ if (!account)
498
+ throw new Error(`Account ${uin} not found`);
499
+ const bot = account.client;
500
+ const channelId = params.channel_id.string;
501
+ const userId = params.user_id.string;
502
+ // 获取频道所属的服务器
503
+ const channel = await bot.getChannel(channelId);
504
+ if (!channel || !channel.guild_id) {
505
+ throw new Error(`频道 ${channelId} 不属于任何服务器`);
506
+ }
507
+ await bot.kickMember(channel.guild_id, userId);
508
+ }
509
+ /**
510
+ * 设置频道成员禁言
511
+ */
512
+ async setChannelMemberMute(uin, params) {
513
+ const account = this.getAccount(uin);
514
+ if (!account)
515
+ throw new Error(`Account ${uin} not found`);
516
+ const bot = account.client;
517
+ const channelId = params.channel_id.string;
518
+ const userId = params.user_id.string;
519
+ // 获取频道所属的服务器
520
+ const channel = await bot.getChannel(channelId);
521
+ if (!channel || !channel.guild_id) {
522
+ throw new Error(`频道 ${channelId} 不属于任何服务器`);
523
+ }
524
+ if (params.mute) {
525
+ // Discord 超时最长 28 天
526
+ await bot.timeoutMember(channel.guild_id, userId, 28 * 24 * 60 * 60);
527
+ }
528
+ else {
529
+ await bot.removeTimeout(channel.guild_id, userId);
530
+ }
531
+ }
532
+ // ============================================
533
+ // 媒体资源相关方法
534
+ // ============================================
535
+ /**
536
+ * 检查是否可以发送图片
537
+ */
538
+ async canSendImage(uin) {
539
+ return true; // Discord 支持图片发送
540
+ }
541
+ /**
542
+ * 检查是否可以发送语音
543
+ */
544
+ async canSendRecord(uin) {
545
+ return true; // Discord 支持音频文件发送
546
+ }
547
+ // ============================================
548
+ // 系统相关方法
549
+ // ============================================
550
+ /**
551
+ * 获取版本信息
552
+ */
553
+ async getVersion(uin) {
554
+ return {
555
+ app_name: 'onebots-discord',
556
+ app_version: '1.0.0',
557
+ impl: 'discord-lite',
558
+ version: '1.0.0',
559
+ };
560
+ }
561
+ /**
562
+ * 获取运行状态
563
+ */
564
+ async getStatus(uin) {
565
+ const account = this.getAccount(uin);
566
+ if (!account) {
567
+ return { good: false };
568
+ }
569
+ const bot = account.client;
570
+ return {
571
+ online: bot.isReady(),
572
+ good: bot.isReady(),
573
+ };
574
+ }
575
+ // ============================================
576
+ // 账号创建
577
+ // ============================================
578
+ createAccount(config) {
579
+ const discordConfig = {
580
+ account_id: config.account_id,
581
+ token: config.token,
582
+ intents: config.intents,
583
+ presence: config.presence,
584
+ proxy: config.proxy,
585
+ };
586
+ const bot = new DiscordBot(discordConfig);
587
+ const account = new Account(this, bot, config);
588
+ // 监听 Bot 事件
589
+ bot.on('ready', (user) => {
590
+ this.logger.info(`Discord Bot ${user.tag} 已就绪`);
591
+ account.status = AccountStatus.Online;
592
+ account.nickname = user.username;
593
+ account.avatar = user.displayAvatarURL();
594
+ });
595
+ bot.on('error', (error) => {
596
+ this.logger.error(`Discord Bot 错误:`, error);
597
+ });
598
+ // 监听消息事件
599
+ bot.on('messageCreate', (message) => {
600
+ // 忽略机器人自己的消息
601
+ if (message.author.bot)
602
+ return;
603
+ // 打印消息接收日志
604
+ const content = message.content || '';
605
+ const contentPreview = content.length > 100 ? content.substring(0, 100) + '...' : content;
606
+ const channelType = message.channel.type === ChannelType.DM ? '私聊' : message.channel.type === ChannelType.GuildText ? '频道' : '群组';
607
+ this.logger.info(`[DISCORD] 收到${channelType}消息 | 消息ID: ${message.id} | 频道: ${message.channel.id} | ` +
608
+ `发送者: ${message.author.username}(${message.author.id}) | 内容: ${contentPreview}`);
609
+ // 构建消息段
610
+ const messageSegments = [];
611
+ // 文本内容
612
+ if (message.content) {
613
+ messageSegments.push({
614
+ type: 'text',
615
+ data: { text: message.content }
616
+ });
617
+ }
618
+ // 附件(图片、文件等)
619
+ for (const attachment of message.attachments || []) {
620
+ if (attachment.content_type?.startsWith('image/')) {
621
+ messageSegments.push({
622
+ type: 'image',
623
+ data: {
624
+ file: attachment.id,
625
+ url: attachment.url,
626
+ filename: attachment.filename,
627
+ }
628
+ });
629
+ }
630
+ else if (attachment.content_type?.startsWith('audio/')) {
631
+ messageSegments.push({
632
+ type: 'voice',
633
+ data: {
634
+ file: attachment.id,
635
+ url: attachment.url,
636
+ filename: attachment.filename,
637
+ }
638
+ });
639
+ }
640
+ else if (attachment.content_type?.startsWith('video/')) {
641
+ messageSegments.push({
642
+ type: 'video',
643
+ data: {
644
+ file: attachment.id,
645
+ url: attachment.url,
646
+ filename: attachment.filename,
647
+ }
648
+ });
649
+ }
650
+ else {
651
+ messageSegments.push({
652
+ type: 'file',
653
+ data: {
654
+ file: attachment.id,
655
+ url: attachment.url,
656
+ filename: attachment.filename,
657
+ }
658
+ });
659
+ }
660
+ }
661
+ // @提及
662
+ for (const mention of message.mentions || []) {
663
+ messageSegments.push({
664
+ type: 'at',
665
+ data: {
666
+ qq: mention.id,
667
+ name: mention.username,
668
+ }
669
+ });
670
+ }
671
+ // 确定消息类型
672
+ let messageType;
673
+ let group;
674
+ if (message.channel.type === ChannelType.DM) {
675
+ messageType = 'private';
676
+ }
677
+ else if (message.channel.type === ChannelType.GroupDM) {
678
+ messageType = 'group';
679
+ }
680
+ else {
681
+ messageType = 'channel';
682
+ if (message.guild) {
683
+ group = {
684
+ id: this.createId(message.guild.id),
685
+ name: message.guild.name,
686
+ };
687
+ }
688
+ }
689
+ // 转换为 CommonEvent 格式
690
+ const commonEvent = {
691
+ id: this.createId(message.id),
692
+ timestamp: message.createdTimestamp,
693
+ platform: 'discord',
694
+ bot_id: this.createId(config.account_id),
695
+ type: 'message',
696
+ message_type: messageType,
697
+ sender: {
698
+ id: this.createId(message.author.id),
699
+ name: message.author.username,
700
+ avatar: message.author.displayAvatarURL(),
701
+ },
702
+ group,
703
+ message_id: this.createId(message.id),
704
+ raw_message: message.content,
705
+ message: messageSegments,
706
+ };
707
+ // 派发到协议层
708
+ account.dispatch(commonEvent);
709
+ });
710
+ // 监听成员加入事件
711
+ bot.on('guildMemberAdd', (member) => {
712
+ this.logger.info(`成员加入: ${member.user.username} -> ${member.guild?.name}`);
713
+ const commonEvent = {
714
+ id: this.createId(Date.now().toString()),
715
+ timestamp: Date.now(),
716
+ platform: 'discord',
717
+ bot_id: this.createId(config.account_id),
718
+ type: 'notice',
719
+ notice_type: 'group_increase',
720
+ user: {
721
+ id: this.createId(member.user.id),
722
+ name: member.user.username,
723
+ avatar: member.user.displayAvatarURL(),
724
+ },
725
+ group: member.guild ? {
726
+ id: this.createId(member.guild.id),
727
+ name: member.guild.name,
728
+ } : undefined,
729
+ };
730
+ account.dispatch(commonEvent);
731
+ });
732
+ // 监听成员离开事件
733
+ bot.on('guildMemberRemove', (member) => {
734
+ this.logger.info(`成员离开: ${member.user?.username} <- ${member.guild?.name}`);
735
+ const commonEvent = {
736
+ id: this.createId(Date.now().toString()),
737
+ timestamp: Date.now(),
738
+ platform: 'discord',
739
+ bot_id: this.createId(config.account_id),
740
+ type: 'notice',
741
+ notice_type: 'group_decrease',
742
+ user: {
743
+ id: this.createId(member.user?.id || 'unknown'),
744
+ name: member.user?.username || 'Unknown',
745
+ avatar: member.user?.displayAvatarURL?.(),
746
+ },
747
+ group: member.guild ? {
748
+ id: this.createId(member.guild.id),
749
+ name: member.guild.name,
750
+ } : undefined,
751
+ };
752
+ account.dispatch(commonEvent);
753
+ });
754
+ // 启动时初始化 Bot
755
+ account.on('start', async () => {
756
+ try {
757
+ await bot.start();
758
+ }
759
+ catch (error) {
760
+ this.logger.error(`启动 Discord Bot 失败:`, error);
761
+ account.status = AccountStatus.OffLine;
762
+ }
763
+ });
764
+ account.on('stop', async () => {
765
+ await bot.stop();
766
+ account.status = AccountStatus.OffLine;
767
+ });
768
+ return account;
769
+ }
770
+ // ============================================
771
+ // 辅助方法
772
+ // ============================================
773
+ /**
774
+ * 构建 Discord 消息内容
775
+ */
776
+ buildDiscordMessage(message) {
777
+ let content = '';
778
+ const embeds = [];
779
+ for (const seg of message) {
780
+ switch (seg.type) {
781
+ case 'text':
782
+ content += seg.data.text || '';
783
+ break;
784
+ case 'at':
785
+ if (seg.data.qq === 'all') {
786
+ content += '@everyone';
787
+ }
788
+ else {
789
+ content += `<@${seg.data.qq}>`;
790
+ }
791
+ break;
792
+ case 'image':
793
+ if (seg.data.url) {
794
+ // 轻量版:将图片作为 Embed 发送
795
+ embeds.push({
796
+ image: { url: seg.data.url },
797
+ });
798
+ }
799
+ break;
800
+ case 'share':
801
+ // 使用 Embed 展示分享链接
802
+ const shareEmbed = {
803
+ title: seg.data.title || '分享链接',
804
+ url: seg.data.url,
805
+ description: seg.data.content || '',
806
+ };
807
+ if (seg.data.image) {
808
+ shareEmbed.image = { url: seg.data.image };
809
+ }
810
+ embeds.push(shareEmbed);
811
+ break;
812
+ case 'face':
813
+ // Discord 使用 Unicode emoji
814
+ if (seg.data.id) {
815
+ try {
816
+ content += String.fromCodePoint(parseInt(seg.data.id));
817
+ }
818
+ catch {
819
+ content += `[表情:${seg.data.id}]`;
820
+ }
821
+ }
822
+ break;
823
+ default:
824
+ // 未知类型,转为文本
825
+ if (seg.data.text) {
826
+ content += seg.data.text;
827
+ }
828
+ }
829
+ }
830
+ return { content, embeds };
831
+ }
832
+ /**
833
+ * 转换消息为 MessageInfo
834
+ */
835
+ convertMessageToInfo(message) {
836
+ const segments = [];
837
+ if (message.content) {
838
+ segments.push({
839
+ type: 'text',
840
+ data: { text: message.content }
841
+ });
842
+ }
843
+ for (const attachment of message.attachments || []) {
844
+ if (attachment.content_type?.startsWith('image/')) {
845
+ segments.push({
846
+ type: 'image',
847
+ data: { file: attachment.id, url: attachment.url }
848
+ });
849
+ }
850
+ else {
851
+ segments.push({
852
+ type: 'file',
853
+ data: { file: attachment.id, url: attachment.url }
854
+ });
855
+ }
856
+ }
857
+ // 确定场景类型
858
+ let sceneType;
859
+ if (message.channel.type === ChannelType.DM) {
860
+ sceneType = 'private';
861
+ }
862
+ else {
863
+ sceneType = 'channel';
864
+ }
865
+ return {
866
+ message_id: this.createId(message.id),
867
+ time: Math.floor(message.createdTimestamp / 1000),
868
+ sender: {
869
+ scene_type: sceneType,
870
+ sender_id: this.createId(message.author.id),
871
+ scene_id: this.createId(message.channel.id),
872
+ sender_name: message.author.username,
873
+ scene_name: 'DM',
874
+ },
875
+ message: segments,
876
+ };
877
+ }
878
+ /**
879
+ * 获取成员角色
880
+ */
881
+ getMemberRole(member) {
882
+ // 轻量版简化处理:根据角色数量判断
883
+ if (member.roles && member.roles.length > 2) {
884
+ return 'admin';
885
+ }
886
+ return 'member';
887
+ }
888
+ }
889
+ AdapterRegistry.register('discord', DiscordAdapter, {
890
+ name: 'discord',
891
+ displayName: 'Discord官方机器人',
892
+ description: 'Discord官方机器人适配器(轻量版),支持频道、群聊和私聊',
893
+ icon: 'https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png',
894
+ homepage: 'https://discord.com/',
895
+ author: '凉菜',
896
+ });
897
+ //# sourceMappingURL=adapter.js.map