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