@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/bot.js ADDED
@@ -0,0 +1,526 @@
1
+ /**
2
+ * Discord Bot 客户端
3
+ * 轻量版实现,直接封装 Discord API,支持 Node.js 和 Cloudflare Workers
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { DiscordLite, GatewayIntents } from './lite/index.js';
7
+ /**
8
+ * Discord Bot
9
+ * 基于轻量客户端实现,兼容 Node.js 和 Cloudflare Workers
10
+ */
11
+ export class DiscordBot extends EventEmitter {
12
+ client;
13
+ config;
14
+ ready = false;
15
+ user = null;
16
+ guilds = new Map();
17
+ constructor(config) {
18
+ super();
19
+ this.config = config;
20
+ // 解析 intents
21
+ const intents = this.parseIntents(config.intents);
22
+ const clientOptions = {
23
+ token: config.token,
24
+ intents,
25
+ proxy: config.proxy,
26
+ mode: 'gateway',
27
+ };
28
+ this.client = new DiscordLite(clientOptions);
29
+ this.setupEventListeners();
30
+ }
31
+ /**
32
+ * 解析 intents 配置
33
+ */
34
+ parseIntents(intentsConfig) {
35
+ if (!intentsConfig || intentsConfig.length === 0) {
36
+ // 默认 intents
37
+ return (GatewayIntents.Guilds |
38
+ GatewayIntents.GuildMessages |
39
+ GatewayIntents.GuildMembers |
40
+ GatewayIntents.GuildMessageReactions |
41
+ GatewayIntents.DirectMessages |
42
+ GatewayIntents.DirectMessageReactions |
43
+ GatewayIntents.MessageContent |
44
+ GatewayIntents.GuildVoiceStates |
45
+ GatewayIntents.GuildPresences);
46
+ }
47
+ let result = 0;
48
+ const intentMap = {
49
+ 'Guilds': GatewayIntents.Guilds,
50
+ 'GuildMembers': GatewayIntents.GuildMembers,
51
+ 'GuildModeration': GatewayIntents.GuildModeration,
52
+ 'GuildEmojisAndStickers': GatewayIntents.GuildEmojisAndStickers,
53
+ 'GuildIntegrations': GatewayIntents.GuildIntegrations,
54
+ 'GuildWebhooks': GatewayIntents.GuildWebhooks,
55
+ 'GuildInvites': GatewayIntents.GuildInvites,
56
+ 'GuildVoiceStates': GatewayIntents.GuildVoiceStates,
57
+ 'GuildPresences': GatewayIntents.GuildPresences,
58
+ 'GuildMessages': GatewayIntents.GuildMessages,
59
+ 'GuildMessageReactions': GatewayIntents.GuildMessageReactions,
60
+ 'GuildMessageTyping': GatewayIntents.GuildMessageTyping,
61
+ 'DirectMessages': GatewayIntents.DirectMessages,
62
+ 'DirectMessageReactions': GatewayIntents.DirectMessageReactions,
63
+ 'DirectMessageTyping': GatewayIntents.DirectMessageTyping,
64
+ 'MessageContent': GatewayIntents.MessageContent,
65
+ 'GuildScheduledEvents': GatewayIntents.GuildScheduledEvents,
66
+ };
67
+ for (const intent of intentsConfig) {
68
+ if (intent in intentMap) {
69
+ result |= intentMap[intent];
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ /**
75
+ * 设置事件监听器
76
+ */
77
+ setupEventListeners() {
78
+ this.client.on('ready', (user) => {
79
+ this.ready = true;
80
+ this.user = this.wrapUser(user);
81
+ this.emit('ready', this.user);
82
+ });
83
+ this.client.on('messageCreate', (message) => {
84
+ this.emit('messageCreate', this.wrapMessage(message));
85
+ });
86
+ this.client.on('messageUpdate', (message) => {
87
+ this.emit('messageUpdate', null, this.wrapMessage(message));
88
+ });
89
+ this.client.on('messageDelete', (data) => {
90
+ this.emit('messageDelete', data);
91
+ });
92
+ this.client.on('guildCreate', (guild) => {
93
+ this.guilds.set(guild.id, guild);
94
+ this.emit('guildCreate', guild);
95
+ });
96
+ this.client.on('guildDelete', (guild) => {
97
+ this.guilds.delete(guild.id);
98
+ this.emit('guildDelete', guild);
99
+ });
100
+ this.client.on('guildMemberAdd', (member) => {
101
+ this.emit('guildMemberAdd', this.wrapMember(member));
102
+ });
103
+ this.client.on('guildMemberRemove', (member) => {
104
+ this.emit('guildMemberRemove', this.wrapMember(member));
105
+ });
106
+ this.client.on('interactionCreate', (interaction) => {
107
+ this.emit('interactionCreate', interaction);
108
+ });
109
+ this.client.on('error', (error) => {
110
+ this.emit('error', error);
111
+ });
112
+ this.client.on('close', (code, reason) => {
113
+ this.ready = false;
114
+ this.emit('close', code, reason);
115
+ });
116
+ }
117
+ // ============================================
118
+ // 生命周期管理
119
+ // ============================================
120
+ /**
121
+ * 启动 Bot
122
+ */
123
+ async start() {
124
+ try {
125
+ await this.client.start();
126
+ }
127
+ catch (error) {
128
+ this.emit('error', error);
129
+ throw error;
130
+ }
131
+ }
132
+ /**
133
+ * 停止 Bot
134
+ */
135
+ async stop() {
136
+ this.ready = false;
137
+ this.client.stop();
138
+ this.emit('stopped');
139
+ }
140
+ /**
141
+ * 获取 Bot 是否就绪
142
+ */
143
+ isReady() {
144
+ return this.ready;
145
+ }
146
+ // ============================================
147
+ // 消息相关方法
148
+ // ============================================
149
+ /**
150
+ * 发送消息到频道
151
+ */
152
+ async sendMessage(channelId, content) {
153
+ const body = typeof content === 'string' ? { content } : content;
154
+ const result = await this.getREST().createMessage(channelId, body);
155
+ return this.wrapMessage(result);
156
+ }
157
+ /**
158
+ * 发送私信
159
+ */
160
+ async sendDM(userId, content) {
161
+ // 首先创建 DM 频道
162
+ const dmChannel = await this.getREST().request('/users/@me/channels', {
163
+ method: 'POST',
164
+ body: { recipient_id: userId },
165
+ });
166
+ return this.sendMessage(dmChannel.id, content);
167
+ }
168
+ /**
169
+ * 发送 Embed 消息
170
+ */
171
+ async sendEmbed(channelId, embeds) {
172
+ return this.sendMessage(channelId, { embeds });
173
+ }
174
+ /**
175
+ * 发送带附件的消息
176
+ */
177
+ async sendWithAttachments(channelId, content, _attachments) {
178
+ // 轻量版暂不支持附件上传,仅发送文本
179
+ return this.sendMessage(channelId, content);
180
+ }
181
+ /**
182
+ * 编辑消息
183
+ */
184
+ async editMessage(channelId, messageId, content) {
185
+ const result = await this.getREST().editMessage(channelId, messageId, content);
186
+ return this.wrapMessage(result);
187
+ }
188
+ /**
189
+ * 删除消息
190
+ */
191
+ async deleteMessage(channelId, messageId) {
192
+ await this.getREST().deleteMessage(channelId, messageId);
193
+ }
194
+ /**
195
+ * 获取消息
196
+ */
197
+ async getMessage(channelId, messageId) {
198
+ const result = await this.getREST().getMessage(channelId, messageId);
199
+ return this.wrapMessage(result);
200
+ }
201
+ /**
202
+ * 获取消息历史
203
+ */
204
+ async getMessageHistory(channelId, limit = 50, before) {
205
+ const query = { limit: String(limit) };
206
+ if (before)
207
+ query.before = before;
208
+ const messages = await this.getREST().getMessages(channelId, query);
209
+ const map = new Map();
210
+ for (const msg of messages) {
211
+ map.set(msg.id, this.wrapMessage(msg));
212
+ }
213
+ return map;
214
+ }
215
+ /**
216
+ * 添加消息反应
217
+ */
218
+ async addReaction(channelId, messageId, emoji) {
219
+ const encodedEmoji = encodeURIComponent(emoji);
220
+ await this.getREST().request(`/channels/${channelId}/messages/${messageId}/reactions/${encodedEmoji}/@me`, {
221
+ method: 'PUT',
222
+ });
223
+ }
224
+ /**
225
+ * 移除消息反应
226
+ */
227
+ async removeReaction(channelId, messageId, emoji, userId) {
228
+ const encodedEmoji = encodeURIComponent(emoji);
229
+ const target = userId || '@me';
230
+ await this.getREST().request(`/channels/${channelId}/messages/${messageId}/reactions/${encodedEmoji}/${target}`, {
231
+ method: 'DELETE',
232
+ });
233
+ }
234
+ // ============================================
235
+ // 用户相关方法
236
+ // ============================================
237
+ /**
238
+ * 获取机器人信息
239
+ */
240
+ getBotUser() {
241
+ return this.user;
242
+ }
243
+ /**
244
+ * 获取用户信息
245
+ */
246
+ async getUser(userId) {
247
+ const result = await this.getREST().getUser(userId);
248
+ return this.wrapUser(result);
249
+ }
250
+ /**
251
+ * 获取成员信息
252
+ */
253
+ async getMember(guildId, userId) {
254
+ const result = await this.getREST().getGuildMember(guildId, userId);
255
+ return this.wrapMember(result);
256
+ }
257
+ // ============================================
258
+ // 服务器(Guild)相关方法
259
+ // ============================================
260
+ /**
261
+ * 获取服务器列表
262
+ */
263
+ getGuilds() {
264
+ return this.guilds;
265
+ }
266
+ /**
267
+ * 获取服务器信息
268
+ */
269
+ async getGuild(guildId) {
270
+ const result = await this.getREST().getGuild(guildId);
271
+ this.guilds.set(result.id, result);
272
+ return result;
273
+ }
274
+ /**
275
+ * 获取服务器成员列表
276
+ */
277
+ async getGuildMembers(guildId, limit) {
278
+ const query = {};
279
+ if (limit)
280
+ query.limit = String(limit);
281
+ const members = await this.getREST().getGuildMembers(guildId, query);
282
+ const map = new Map();
283
+ for (const member of members) {
284
+ map.set(member.user.id, this.wrapMember(member));
285
+ }
286
+ return map;
287
+ }
288
+ /**
289
+ * 获取服务器成员信息
290
+ */
291
+ async getGuildMember(guildId, userId) {
292
+ const result = await this.getREST().getGuildMember(guildId, userId);
293
+ return this.wrapMember(result);
294
+ }
295
+ /**
296
+ * 踢出成员
297
+ */
298
+ async kickMember(guildId, userId, _reason) {
299
+ await this.getREST().removeGuildMember(guildId, userId);
300
+ }
301
+ /**
302
+ * 封禁成员
303
+ */
304
+ async banMember(guildId, userId, options) {
305
+ await this.getREST().banGuildMember(guildId, userId, {
306
+ delete_message_seconds: options?.deleteMessageSeconds,
307
+ });
308
+ }
309
+ /**
310
+ * 解除封禁
311
+ */
312
+ async unbanMember(guildId, userId, _reason) {
313
+ await this.getREST().request(`/guilds/${guildId}/bans/${userId}`, {
314
+ method: 'DELETE',
315
+ });
316
+ }
317
+ /**
318
+ * 禁言成员(超时)
319
+ */
320
+ async timeoutMember(guildId, userId, duration, _reason) {
321
+ const until = new Date(Date.now() + duration * 1000).toISOString();
322
+ const result = await this.getREST().request(`/guilds/${guildId}/members/${userId}`, {
323
+ method: 'PATCH',
324
+ body: { communication_disabled_until: until },
325
+ });
326
+ return this.wrapMember(result);
327
+ }
328
+ /**
329
+ * 解除禁言
330
+ */
331
+ async removeTimeout(guildId, userId, _reason) {
332
+ const result = await this.getREST().request(`/guilds/${guildId}/members/${userId}`, {
333
+ method: 'PATCH',
334
+ body: { communication_disabled_until: null },
335
+ });
336
+ return this.wrapMember(result);
337
+ }
338
+ /**
339
+ * 修改成员昵称
340
+ */
341
+ async setMemberNickname(guildId, userId, nickname, _reason) {
342
+ const result = await this.getREST().request(`/guilds/${guildId}/members/${userId}`, {
343
+ method: 'PATCH',
344
+ body: { nick: nickname },
345
+ });
346
+ return this.wrapMember(result);
347
+ }
348
+ /**
349
+ * 添加角色
350
+ */
351
+ async addRole(guildId, userId, roleId, _reason) {
352
+ await this.getREST().request(`/guilds/${guildId}/members/${userId}/roles/${roleId}`, {
353
+ method: 'PUT',
354
+ });
355
+ return this.getGuildMember(guildId, userId);
356
+ }
357
+ /**
358
+ * 移除角色
359
+ */
360
+ async removeRole(guildId, userId, roleId, _reason) {
361
+ await this.getREST().request(`/guilds/${guildId}/members/${userId}/roles/${roleId}`, {
362
+ method: 'DELETE',
363
+ });
364
+ return this.getGuildMember(guildId, userId);
365
+ }
366
+ // ============================================
367
+ // 频道相关方法
368
+ // ============================================
369
+ /**
370
+ * 获取频道
371
+ */
372
+ async getChannel(channelId) {
373
+ try {
374
+ return await this.getREST().getChannel(channelId);
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
380
+ /**
381
+ * 获取服务器频道列表
382
+ */
383
+ async getGuildChannels(guildId) {
384
+ const channels = await this.getREST().request(`/guilds/${guildId}/channels`);
385
+ const map = new Map();
386
+ for (const channel of channels) {
387
+ map.set(channel.id, channel);
388
+ }
389
+ return map;
390
+ }
391
+ /**
392
+ * 创建文本频道
393
+ */
394
+ async createTextChannel(guildId, name, options) {
395
+ return this.getREST().request(`/guilds/${guildId}/channels`, {
396
+ method: 'POST',
397
+ body: {
398
+ name,
399
+ type: 0, // GUILD_TEXT
400
+ topic: options?.topic,
401
+ parent_id: options?.parent,
402
+ nsfw: options?.nsfw,
403
+ },
404
+ });
405
+ }
406
+ /**
407
+ * 删除频道
408
+ */
409
+ async deleteChannel(channelId) {
410
+ await this.getREST().request(`/channels/${channelId}`, {
411
+ method: 'DELETE',
412
+ });
413
+ }
414
+ /**
415
+ * 更新频道
416
+ */
417
+ async updateChannel(channelId, options) {
418
+ return this.getREST().request(`/channels/${channelId}`, {
419
+ method: 'PATCH',
420
+ body: {
421
+ name: options.name,
422
+ topic: options.topic,
423
+ nsfw: options.nsfw,
424
+ parent_id: options.parent,
425
+ },
426
+ });
427
+ }
428
+ // ============================================
429
+ // 角色相关方法
430
+ // ============================================
431
+ /**
432
+ * 获取服务器角色列表
433
+ */
434
+ async getGuildRoles(guildId) {
435
+ const roles = await this.getREST().request(`/guilds/${guildId}/roles`);
436
+ const map = new Map();
437
+ for (const role of roles) {
438
+ map.set(role.id, role);
439
+ }
440
+ return map;
441
+ }
442
+ /**
443
+ * 获取角色信息
444
+ */
445
+ async getRole(guildId, roleId) {
446
+ const roles = await this.getGuildRoles(guildId);
447
+ return roles.get(roleId) || null;
448
+ }
449
+ /**
450
+ * 创建角色
451
+ */
452
+ async createRole(guildId, options) {
453
+ return this.getREST().request(`/guilds/${guildId}/roles`, {
454
+ method: 'POST',
455
+ body: {
456
+ name: options.name,
457
+ color: options.color,
458
+ hoist: options.hoist,
459
+ mentionable: options.mentionable,
460
+ permissions: options.permissions?.toString(),
461
+ },
462
+ });
463
+ }
464
+ /**
465
+ * 删除角色
466
+ */
467
+ async deleteRole(guildId, roleId) {
468
+ await this.getREST().request(`/guilds/${guildId}/roles/${roleId}`, {
469
+ method: 'DELETE',
470
+ });
471
+ }
472
+ // ============================================
473
+ // 工具方法
474
+ // ============================================
475
+ /**
476
+ * 获取 REST 客户端
477
+ */
478
+ getREST() {
479
+ return this.client.getREST();
480
+ }
481
+ /**
482
+ * 获取原始 Discord Lite 客户端
483
+ */
484
+ getClient() {
485
+ return this.client;
486
+ }
487
+ /**
488
+ * 包装用户对象
489
+ */
490
+ wrapUser(user) {
491
+ return {
492
+ ...user,
493
+ global_name: user.global_name || user.globalName,
494
+ displayAvatarURL: () => {
495
+ if (user.avatar) {
496
+ return `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`;
497
+ }
498
+ const defaultAvatar = parseInt(user.discriminator || '0') % 5;
499
+ return `https://cdn.discordapp.com/embed/avatars/${defaultAvatar}.png`;
500
+ },
501
+ tag: `${user.username}#${user.discriminator || '0'}`,
502
+ };
503
+ }
504
+ /**
505
+ * 包装成员对象
506
+ */
507
+ wrapMember(member) {
508
+ return {
509
+ ...member,
510
+ user: this.wrapUser(member.user),
511
+ };
512
+ }
513
+ /**
514
+ * 包装消息对象
515
+ */
516
+ wrapMessage(message) {
517
+ return {
518
+ ...message,
519
+ createdTimestamp: new Date(message.timestamp).getTime(),
520
+ channel: { id: message.channel_id, type: message.guild_id ? 0 : 1 },
521
+ guild: message.guild_id ? { id: message.guild_id } : undefined,
522
+ author: this.wrapUser(message.author),
523
+ };
524
+ }
525
+ }
526
+ //# sourceMappingURL=bot.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export type { DiscordConfig, ProxyConfig, GatewayIntentName, PresenceStatus } from './types.js';
2
+ export { ChannelType, MessageType, ActivityType } from './types.js';
3
+ export * from './adapter.js';
4
+ export { DiscordBot } from './bot.js';
5
+ export type { DiscordUser, DiscordMessage, DiscordGuild, DiscordChannel, DiscordMember, DiscordAttachment } from './bot.js';
6
+ export * from './lite/index.js';
7
+ //# sourceMappingURL=index.d.ts.map
package/lib/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { ChannelType, MessageType, ActivityType } from './types.js';
2
+ // 导出适配器
3
+ export * from './adapter.js';
4
+ // 导出 Bot
5
+ export { DiscordBot } from './bot.js';
6
+ // 导出轻量版客户端(用于独立使用或 Serverless)
7
+ export * from './lite/index.js';
8
+ //# sourceMappingURL=index.js.map