@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/LICENSE +21 -0
- package/README.md +202 -0
- package/lib/adapter.d.ts +175 -0
- package/lib/adapter.js +897 -0
- package/lib/bot.d.ts +293 -0
- package/lib/bot.js +526 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +8 -0
- package/lib/lite/bot.d.ts +305 -0
- package/lib/lite/bot.js +527 -0
- package/lib/lite/gateway.d.ts +119 -0
- package/lib/lite/gateway.js +349 -0
- package/lib/lite/index.d.ts +148 -0
- package/lib/lite/index.js +244 -0
- package/lib/lite/interactions.d.ts +134 -0
- package/lib/lite/interactions.js +238 -0
- package/lib/lite/rest.d.ts +102 -0
- package/lib/lite/rest.js +292 -0
- package/lib/types.d.ts +106 -0
- package/lib/types.js +64 -0
- package/package.json +65 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Lite - 轻量版 Discord 客户端
|
|
3
|
+
*
|
|
4
|
+
* 特性:
|
|
5
|
+
* - 使用原生 fetch,无外部依赖
|
|
6
|
+
* - 自动检测运行时环境
|
|
7
|
+
* - Node.js: 支持 Gateway WebSocket
|
|
8
|
+
* - Cloudflare Workers / Vercel: 支持 Interactions Webhook
|
|
9
|
+
*
|
|
10
|
+
* @example Node.js Gateway 模式
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { DiscordLite, GatewayIntents } from './lite';
|
|
13
|
+
*
|
|
14
|
+
* const client = new DiscordLite({
|
|
15
|
+
* token: 'your-bot-token',
|
|
16
|
+
* intents: GatewayIntents.Guilds | GatewayIntents.GuildMessages | GatewayIntents.MessageContent,
|
|
17
|
+
* mode: 'gateway',
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* client.on('messageCreate', (message) => {
|
|
21
|
+
* console.log(message.content);
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* await client.start();
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Cloudflare Workers Webhook 模式
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { DiscordLite, InteractionsHandler } from './lite';
|
|
30
|
+
*
|
|
31
|
+
* const handler = new InteractionsHandler({
|
|
32
|
+
* publicKey: 'your-public-key',
|
|
33
|
+
* token: 'your-bot-token',
|
|
34
|
+
* applicationId: 'your-app-id',
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* handler.onCommand('hello', async (interaction) => {
|
|
38
|
+
* return InteractionsHandler.messageResponse('Hello, World!');
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* export default {
|
|
42
|
+
* fetch: (request) => handler.handleRequest(request),
|
|
43
|
+
* };
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
import { EventEmitter } from 'events';
|
|
47
|
+
import { DiscordREST } from './rest.js';
|
|
48
|
+
import { DiscordGateway, GatewayIntents } from './gateway.js';
|
|
49
|
+
import { InteractionsHandler } from './interactions.js';
|
|
50
|
+
// 重新导出
|
|
51
|
+
export { DiscordREST } from './rest.js';
|
|
52
|
+
export { DiscordGateway, GatewayIntents, GatewayOpcodes } from './gateway.js';
|
|
53
|
+
export { InteractionsHandler, InteractionType, InteractionCallbackType, verifyInteractionSignature } from './interactions.js';
|
|
54
|
+
export { DiscordLiteBot } from './bot.js';
|
|
55
|
+
/**
|
|
56
|
+
* 检测当前运行时环境
|
|
57
|
+
*/
|
|
58
|
+
export function detectRuntime() {
|
|
59
|
+
// Cloudflare Workers
|
|
60
|
+
if (typeof globalThis.caches !== 'undefined' && typeof globalThis.WebSocketPair !== 'undefined') {
|
|
61
|
+
return 'cloudflare';
|
|
62
|
+
}
|
|
63
|
+
// Vercel Edge Runtime
|
|
64
|
+
if (typeof globalThis.EdgeRuntime !== 'undefined') {
|
|
65
|
+
return 'vercel';
|
|
66
|
+
}
|
|
67
|
+
// Deno
|
|
68
|
+
if (typeof globalThis.Deno !== 'undefined') {
|
|
69
|
+
return 'deno';
|
|
70
|
+
}
|
|
71
|
+
// Bun
|
|
72
|
+
if (typeof globalThis.Bun !== 'undefined') {
|
|
73
|
+
return 'bun';
|
|
74
|
+
}
|
|
75
|
+
// Node.js
|
|
76
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
77
|
+
return 'node';
|
|
78
|
+
}
|
|
79
|
+
// Browser
|
|
80
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
81
|
+
return 'browser';
|
|
82
|
+
}
|
|
83
|
+
return 'unknown';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 检测是否支持 WebSocket Gateway
|
|
87
|
+
*/
|
|
88
|
+
export function supportsGateway() {
|
|
89
|
+
const runtime = detectRuntime();
|
|
90
|
+
return ['node', 'bun', 'deno'].includes(runtime);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Discord Lite 统一客户端
|
|
94
|
+
*/
|
|
95
|
+
export class DiscordLite extends EventEmitter {
|
|
96
|
+
options;
|
|
97
|
+
gateway = null;
|
|
98
|
+
interactions = null;
|
|
99
|
+
rest;
|
|
100
|
+
runtime;
|
|
101
|
+
mode;
|
|
102
|
+
user = null;
|
|
103
|
+
constructor(options) {
|
|
104
|
+
super();
|
|
105
|
+
this.options = options;
|
|
106
|
+
this.runtime = detectRuntime();
|
|
107
|
+
this.rest = new DiscordREST({ token: options.token, proxy: options.proxy });
|
|
108
|
+
// 确定运行模式
|
|
109
|
+
if (options.mode === 'auto' || !options.mode) {
|
|
110
|
+
this.mode = supportsGateway() ? 'gateway' : 'interactions';
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this.mode = options.mode;
|
|
114
|
+
}
|
|
115
|
+
console.log(`[DiscordLite] 运行时: ${this.runtime}, 模式: ${this.mode}`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 启动客户端(Gateway 模式)
|
|
119
|
+
*/
|
|
120
|
+
async start() {
|
|
121
|
+
if (this.mode !== 'gateway') {
|
|
122
|
+
throw new Error('start() 仅支持 Gateway 模式,Interactions 模式请使用 handleRequest()');
|
|
123
|
+
}
|
|
124
|
+
if (!supportsGateway()) {
|
|
125
|
+
throw new Error(`当前运行时 ${this.runtime} 不支持 Gateway 模式`);
|
|
126
|
+
}
|
|
127
|
+
const intents = this.options.intents ?? (GatewayIntents.Guilds |
|
|
128
|
+
GatewayIntents.GuildMessages |
|
|
129
|
+
GatewayIntents.DirectMessages |
|
|
130
|
+
GatewayIntents.MessageContent);
|
|
131
|
+
this.gateway = new DiscordGateway({
|
|
132
|
+
token: this.options.token,
|
|
133
|
+
intents,
|
|
134
|
+
proxy: this.options.proxy,
|
|
135
|
+
});
|
|
136
|
+
// 转发事件
|
|
137
|
+
this.gateway.on('ready', (user) => {
|
|
138
|
+
this.user = user;
|
|
139
|
+
this.emit('ready', user);
|
|
140
|
+
});
|
|
141
|
+
this.gateway.on('messageCreate', (message) => this.emit('messageCreate', message));
|
|
142
|
+
this.gateway.on('messageUpdate', (message) => this.emit('messageUpdate', message));
|
|
143
|
+
this.gateway.on('messageDelete', (data) => this.emit('messageDelete', data));
|
|
144
|
+
this.gateway.on('guildCreate', (guild) => this.emit('guildCreate', guild));
|
|
145
|
+
this.gateway.on('guildDelete', (guild) => this.emit('guildDelete', guild));
|
|
146
|
+
this.gateway.on('guildMemberAdd', (member) => this.emit('guildMemberAdd', member));
|
|
147
|
+
this.gateway.on('guildMemberRemove', (member) => this.emit('guildMemberRemove', member));
|
|
148
|
+
this.gateway.on('interactionCreate', (interaction) => this.emit('interactionCreate', interaction));
|
|
149
|
+
this.gateway.on('dispatch', (event, data) => this.emit('dispatch', event, data));
|
|
150
|
+
this.gateway.on('error', (error) => this.emit('error', error));
|
|
151
|
+
this.gateway.on('close', (code, reason) => this.emit('close', code, reason));
|
|
152
|
+
await this.gateway.connect();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 停止客户端
|
|
156
|
+
*/
|
|
157
|
+
stop() {
|
|
158
|
+
if (this.gateway) {
|
|
159
|
+
this.gateway.disconnect();
|
|
160
|
+
this.gateway = null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 初始化 Interactions 处理器
|
|
165
|
+
*/
|
|
166
|
+
initInteractions() {
|
|
167
|
+
if (!this.options.publicKey || !this.options.applicationId) {
|
|
168
|
+
throw new Error('Interactions 模式需要 publicKey 和 applicationId');
|
|
169
|
+
}
|
|
170
|
+
this.interactions = new InteractionsHandler({
|
|
171
|
+
publicKey: this.options.publicKey,
|
|
172
|
+
token: this.options.token,
|
|
173
|
+
applicationId: this.options.applicationId,
|
|
174
|
+
});
|
|
175
|
+
return this.interactions;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 处理 HTTP 请求(Interactions 模式)
|
|
179
|
+
*/
|
|
180
|
+
async handleRequest(request) {
|
|
181
|
+
if (!this.interactions) {
|
|
182
|
+
this.initInteractions();
|
|
183
|
+
}
|
|
184
|
+
return this.interactions.handleRequest(request);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 获取 REST 客户端
|
|
188
|
+
*/
|
|
189
|
+
getREST() {
|
|
190
|
+
return this.rest;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 获取当前用户
|
|
194
|
+
*/
|
|
195
|
+
getUser() {
|
|
196
|
+
return this.user;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 获取当前运行时
|
|
200
|
+
*/
|
|
201
|
+
getRuntime() {
|
|
202
|
+
return this.runtime;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 获取当前模式
|
|
206
|
+
*/
|
|
207
|
+
getMode() {
|
|
208
|
+
return this.mode;
|
|
209
|
+
}
|
|
210
|
+
// ============================================
|
|
211
|
+
// 便捷方法
|
|
212
|
+
// ============================================
|
|
213
|
+
/** 发送消息 */
|
|
214
|
+
async sendMessage(channelId, content) {
|
|
215
|
+
return this.rest.createMessage(channelId, content);
|
|
216
|
+
}
|
|
217
|
+
/** 编辑消息 */
|
|
218
|
+
async editMessage(channelId, messageId, content) {
|
|
219
|
+
return this.rest.editMessage(channelId, messageId, content);
|
|
220
|
+
}
|
|
221
|
+
/** 删除消息 */
|
|
222
|
+
async deleteMessage(channelId, messageId) {
|
|
223
|
+
return this.rest.deleteMessage(channelId, messageId);
|
|
224
|
+
}
|
|
225
|
+
/** 获取消息 */
|
|
226
|
+
async getMessage(channelId, messageId) {
|
|
227
|
+
return this.rest.getMessage(channelId, messageId);
|
|
228
|
+
}
|
|
229
|
+
/** 获取服务器 */
|
|
230
|
+
async getGuild(guildId) {
|
|
231
|
+
return this.rest.getGuild(guildId);
|
|
232
|
+
}
|
|
233
|
+
/** 获取服务器成员 */
|
|
234
|
+
async getGuildMember(guildId, userId) {
|
|
235
|
+
return this.rest.getGuildMember(guildId, userId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 创建 Discord Lite 客户端的便捷方法
|
|
240
|
+
*/
|
|
241
|
+
export function createClient(options) {
|
|
242
|
+
return new DiscordLite(options);
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Interactions Webhook 处理器
|
|
3
|
+
* 用于 Cloudflare Workers / Vercel 等 Serverless 环境
|
|
4
|
+
*/
|
|
5
|
+
import { DiscordREST } from './rest.js';
|
|
6
|
+
export declare enum InteractionType {
|
|
7
|
+
Ping = 1,
|
|
8
|
+
ApplicationCommand = 2,
|
|
9
|
+
MessageComponent = 3,
|
|
10
|
+
ApplicationCommandAutocomplete = 4,
|
|
11
|
+
ModalSubmit = 5
|
|
12
|
+
}
|
|
13
|
+
export declare enum InteractionCallbackType {
|
|
14
|
+
Pong = 1,
|
|
15
|
+
ChannelMessageWithSource = 4,
|
|
16
|
+
DeferredChannelMessageWithSource = 5,
|
|
17
|
+
DeferredUpdateMessage = 6,
|
|
18
|
+
UpdateMessage = 7,
|
|
19
|
+
ApplicationCommandAutocompleteResult = 8,
|
|
20
|
+
Modal = 9,
|
|
21
|
+
PremiumRequired = 10
|
|
22
|
+
}
|
|
23
|
+
export interface InteractionWebhookOptions {
|
|
24
|
+
publicKey: string;
|
|
25
|
+
token: string;
|
|
26
|
+
applicationId: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 验证 Discord Interaction 请求签名
|
|
30
|
+
* 使用 Web Crypto API,兼容所有运行时
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyInteractionSignature(publicKey: string, signature: string, timestamp: string, body: string): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Discord Interactions Webhook 处理器
|
|
35
|
+
*/
|
|
36
|
+
export declare class InteractionsHandler {
|
|
37
|
+
private publicKey;
|
|
38
|
+
private token;
|
|
39
|
+
private applicationId;
|
|
40
|
+
private rest;
|
|
41
|
+
private handlers;
|
|
42
|
+
constructor(options: InteractionWebhookOptions);
|
|
43
|
+
/**
|
|
44
|
+
* 注册命令处理器
|
|
45
|
+
*/
|
|
46
|
+
onCommand(name: string, handler: (interaction: any) => Promise<any>): void;
|
|
47
|
+
/**
|
|
48
|
+
* 注册消息组件处理器
|
|
49
|
+
*/
|
|
50
|
+
onComponent(customId: string, handler: (interaction: any) => Promise<any>): void;
|
|
51
|
+
/**
|
|
52
|
+
* 注册模态框提交处理器
|
|
53
|
+
*/
|
|
54
|
+
onModalSubmit(customId: string, handler: (interaction: any) => Promise<any>): void;
|
|
55
|
+
/**
|
|
56
|
+
* 处理 HTTP 请求
|
|
57
|
+
* 适用于 Cloudflare Workers / Vercel Edge Functions
|
|
58
|
+
*/
|
|
59
|
+
handleRequest(request: Request): Promise<Response>;
|
|
60
|
+
/**
|
|
61
|
+
* 处理 Interaction
|
|
62
|
+
*/
|
|
63
|
+
handleInteraction(interaction: any): Promise<any>;
|
|
64
|
+
/**
|
|
65
|
+
* 默认响应
|
|
66
|
+
*/
|
|
67
|
+
private defaultResponse;
|
|
68
|
+
/**
|
|
69
|
+
* 创建延迟响应
|
|
70
|
+
*/
|
|
71
|
+
static deferResponse(ephemeral?: boolean): {
|
|
72
|
+
type: InteractionCallbackType;
|
|
73
|
+
data: {
|
|
74
|
+
flags: number;
|
|
75
|
+
} | {
|
|
76
|
+
flags?: undefined;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* 创建消息响应
|
|
81
|
+
*/
|
|
82
|
+
static messageResponse(content: string | {
|
|
83
|
+
content?: string;
|
|
84
|
+
embeds?: any[];
|
|
85
|
+
components?: any[];
|
|
86
|
+
}, ephemeral?: boolean): {
|
|
87
|
+
type: InteractionCallbackType;
|
|
88
|
+
data: {
|
|
89
|
+
flags: number;
|
|
90
|
+
content?: string;
|
|
91
|
+
embeds?: any[];
|
|
92
|
+
components?: any[];
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* 创建更新消息响应
|
|
97
|
+
*/
|
|
98
|
+
static updateResponse(content: string | {
|
|
99
|
+
content?: string;
|
|
100
|
+
embeds?: any[];
|
|
101
|
+
components?: any[];
|
|
102
|
+
}): {
|
|
103
|
+
type: InteractionCallbackType;
|
|
104
|
+
data: {
|
|
105
|
+
content?: string;
|
|
106
|
+
embeds?: any[];
|
|
107
|
+
components?: any[];
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* 创建模态框响应
|
|
112
|
+
*/
|
|
113
|
+
static modalResponse(customId: string, title: string, components: any[]): {
|
|
114
|
+
type: InteractionCallbackType;
|
|
115
|
+
data: {
|
|
116
|
+
custom_id: string;
|
|
117
|
+
title: string;
|
|
118
|
+
components: any[];
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* 获取 REST 客户端
|
|
123
|
+
*/
|
|
124
|
+
getREST(): DiscordREST;
|
|
125
|
+
/**
|
|
126
|
+
* 编辑后续消息(用于延迟响应后)
|
|
127
|
+
*/
|
|
128
|
+
editFollowup(interactionToken: string, content: any): Promise<any>;
|
|
129
|
+
/**
|
|
130
|
+
* 发送后续消息
|
|
131
|
+
*/
|
|
132
|
+
sendFollowup(interactionToken: string, content: any): Promise<any>;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=interactions.d.ts.map
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord Interactions Webhook 处理器
|
|
3
|
+
* 用于 Cloudflare Workers / Vercel 等 Serverless 环境
|
|
4
|
+
*/
|
|
5
|
+
import { DiscordREST } from './rest.js';
|
|
6
|
+
// Interaction Types
|
|
7
|
+
export var InteractionType;
|
|
8
|
+
(function (InteractionType) {
|
|
9
|
+
InteractionType[InteractionType["Ping"] = 1] = "Ping";
|
|
10
|
+
InteractionType[InteractionType["ApplicationCommand"] = 2] = "ApplicationCommand";
|
|
11
|
+
InteractionType[InteractionType["MessageComponent"] = 3] = "MessageComponent";
|
|
12
|
+
InteractionType[InteractionType["ApplicationCommandAutocomplete"] = 4] = "ApplicationCommandAutocomplete";
|
|
13
|
+
InteractionType[InteractionType["ModalSubmit"] = 5] = "ModalSubmit";
|
|
14
|
+
})(InteractionType || (InteractionType = {}));
|
|
15
|
+
// Interaction Callback Types
|
|
16
|
+
export var InteractionCallbackType;
|
|
17
|
+
(function (InteractionCallbackType) {
|
|
18
|
+
InteractionCallbackType[InteractionCallbackType["Pong"] = 1] = "Pong";
|
|
19
|
+
InteractionCallbackType[InteractionCallbackType["ChannelMessageWithSource"] = 4] = "ChannelMessageWithSource";
|
|
20
|
+
InteractionCallbackType[InteractionCallbackType["DeferredChannelMessageWithSource"] = 5] = "DeferredChannelMessageWithSource";
|
|
21
|
+
InteractionCallbackType[InteractionCallbackType["DeferredUpdateMessage"] = 6] = "DeferredUpdateMessage";
|
|
22
|
+
InteractionCallbackType[InteractionCallbackType["UpdateMessage"] = 7] = "UpdateMessage";
|
|
23
|
+
InteractionCallbackType[InteractionCallbackType["ApplicationCommandAutocompleteResult"] = 8] = "ApplicationCommandAutocompleteResult";
|
|
24
|
+
InteractionCallbackType[InteractionCallbackType["Modal"] = 9] = "Modal";
|
|
25
|
+
InteractionCallbackType[InteractionCallbackType["PremiumRequired"] = 10] = "PremiumRequired";
|
|
26
|
+
})(InteractionCallbackType || (InteractionCallbackType = {}));
|
|
27
|
+
/**
|
|
28
|
+
* 验证 Discord Interaction 请求签名
|
|
29
|
+
* 使用 Web Crypto API,兼容所有运行时
|
|
30
|
+
*/
|
|
31
|
+
export async function verifyInteractionSignature(publicKey, signature, timestamp, body) {
|
|
32
|
+
try {
|
|
33
|
+
// 将 hex 字符串转换为 Uint8Array
|
|
34
|
+
const hexToUint8Array = (hex) => {
|
|
35
|
+
const matches = hex.match(/.{1,2}/g);
|
|
36
|
+
return new Uint8Array(matches ? matches.map(byte => parseInt(byte, 16)) : []);
|
|
37
|
+
};
|
|
38
|
+
const publicKeyBytes = hexToUint8Array(publicKey);
|
|
39
|
+
const signatureBytes = hexToUint8Array(signature);
|
|
40
|
+
const messageBytes = new TextEncoder().encode(timestamp + body);
|
|
41
|
+
// 使用 Web Crypto API(兼容 Node.js 和 Cloudflare Workers)
|
|
42
|
+
const cryptoKey = await crypto.subtle.importKey('raw', publicKeyBytes, { name: 'Ed25519', namedCurve: 'Ed25519' }, false, ['verify']);
|
|
43
|
+
return await crypto.subtle.verify('Ed25519', cryptoKey, signatureBytes, messageBytes);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('签名验证失败:', error);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Discord Interactions Webhook 处理器
|
|
52
|
+
*/
|
|
53
|
+
export class InteractionsHandler {
|
|
54
|
+
publicKey;
|
|
55
|
+
token;
|
|
56
|
+
applicationId;
|
|
57
|
+
rest;
|
|
58
|
+
handlers = new Map();
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.publicKey = options.publicKey;
|
|
61
|
+
this.token = options.token;
|
|
62
|
+
this.applicationId = options.applicationId;
|
|
63
|
+
this.rest = new DiscordREST({ token: options.token });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 注册命令处理器
|
|
67
|
+
*/
|
|
68
|
+
onCommand(name, handler) {
|
|
69
|
+
this.handlers.set(`command:${name}`, handler);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 注册消息组件处理器
|
|
73
|
+
*/
|
|
74
|
+
onComponent(customId, handler) {
|
|
75
|
+
this.handlers.set(`component:${customId}`, handler);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 注册模态框提交处理器
|
|
79
|
+
*/
|
|
80
|
+
onModalSubmit(customId, handler) {
|
|
81
|
+
this.handlers.set(`modal:${customId}`, handler);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 处理 HTTP 请求
|
|
85
|
+
* 适用于 Cloudflare Workers / Vercel Edge Functions
|
|
86
|
+
*/
|
|
87
|
+
async handleRequest(request) {
|
|
88
|
+
// 验证签名
|
|
89
|
+
const signature = request.headers.get('x-signature-ed25519');
|
|
90
|
+
const timestamp = request.headers.get('x-signature-timestamp');
|
|
91
|
+
const body = await request.text();
|
|
92
|
+
if (!signature || !timestamp) {
|
|
93
|
+
return new Response('Missing signature', { status: 401 });
|
|
94
|
+
}
|
|
95
|
+
const isValid = await verifyInteractionSignature(this.publicKey, signature, timestamp, body);
|
|
96
|
+
if (!isValid) {
|
|
97
|
+
return new Response('Invalid signature', { status: 401 });
|
|
98
|
+
}
|
|
99
|
+
// 解析并处理 Interaction
|
|
100
|
+
const interaction = JSON.parse(body);
|
|
101
|
+
const response = await this.handleInteraction(interaction);
|
|
102
|
+
return new Response(JSON.stringify(response), {
|
|
103
|
+
headers: { 'Content-Type': 'application/json' },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 处理 Interaction
|
|
108
|
+
*/
|
|
109
|
+
async handleInteraction(interaction) {
|
|
110
|
+
const { type, data } = interaction;
|
|
111
|
+
// Ping/Pong 健康检查
|
|
112
|
+
if (type === InteractionType.Ping) {
|
|
113
|
+
return { type: InteractionCallbackType.Pong };
|
|
114
|
+
}
|
|
115
|
+
// 应用命令
|
|
116
|
+
if (type === InteractionType.ApplicationCommand) {
|
|
117
|
+
const handler = this.handlers.get(`command:${data.name}`);
|
|
118
|
+
if (handler) {
|
|
119
|
+
return handler(interaction);
|
|
120
|
+
}
|
|
121
|
+
return this.defaultResponse('命令未找到');
|
|
122
|
+
}
|
|
123
|
+
// 消息组件
|
|
124
|
+
if (type === InteractionType.MessageComponent) {
|
|
125
|
+
// 尝试精确匹配
|
|
126
|
+
let handler = this.handlers.get(`component:${data.custom_id}`);
|
|
127
|
+
// 尝试前缀匹配
|
|
128
|
+
if (!handler) {
|
|
129
|
+
for (const [key, h] of this.handlers) {
|
|
130
|
+
if (key.startsWith('component:') && data.custom_id.startsWith(key.slice(10))) {
|
|
131
|
+
handler = h;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (handler) {
|
|
137
|
+
return handler(interaction);
|
|
138
|
+
}
|
|
139
|
+
return this.defaultResponse('组件处理器未找到');
|
|
140
|
+
}
|
|
141
|
+
// 模态框提交
|
|
142
|
+
if (type === InteractionType.ModalSubmit) {
|
|
143
|
+
const handler = this.handlers.get(`modal:${data.custom_id}`);
|
|
144
|
+
if (handler) {
|
|
145
|
+
return handler(interaction);
|
|
146
|
+
}
|
|
147
|
+
return this.defaultResponse('模态框处理器未找到');
|
|
148
|
+
}
|
|
149
|
+
// 自动补全
|
|
150
|
+
if (type === InteractionType.ApplicationCommandAutocomplete) {
|
|
151
|
+
const handler = this.handlers.get(`autocomplete:${data.name}`);
|
|
152
|
+
if (handler) {
|
|
153
|
+
return handler(interaction);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
type: InteractionCallbackType.ApplicationCommandAutocompleteResult,
|
|
157
|
+
data: { choices: [] },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return this.defaultResponse('未知的 Interaction 类型');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 默认响应
|
|
164
|
+
*/
|
|
165
|
+
defaultResponse(message) {
|
|
166
|
+
return {
|
|
167
|
+
type: InteractionCallbackType.ChannelMessageWithSource,
|
|
168
|
+
data: {
|
|
169
|
+
content: message,
|
|
170
|
+
flags: 64, // Ephemeral
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 创建延迟响应
|
|
176
|
+
*/
|
|
177
|
+
static deferResponse(ephemeral = false) {
|
|
178
|
+
return {
|
|
179
|
+
type: InteractionCallbackType.DeferredChannelMessageWithSource,
|
|
180
|
+
data: ephemeral ? { flags: 64 } : {},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 创建消息响应
|
|
185
|
+
*/
|
|
186
|
+
static messageResponse(content, ephemeral = false) {
|
|
187
|
+
const data = typeof content === 'string' ? { content } : content;
|
|
188
|
+
return {
|
|
189
|
+
type: InteractionCallbackType.ChannelMessageWithSource,
|
|
190
|
+
data: {
|
|
191
|
+
...data,
|
|
192
|
+
flags: ephemeral ? 64 : 0,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 创建更新消息响应
|
|
198
|
+
*/
|
|
199
|
+
static updateResponse(content) {
|
|
200
|
+
const data = typeof content === 'string' ? { content } : content;
|
|
201
|
+
return {
|
|
202
|
+
type: InteractionCallbackType.UpdateMessage,
|
|
203
|
+
data,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 创建模态框响应
|
|
208
|
+
*/
|
|
209
|
+
static modalResponse(customId, title, components) {
|
|
210
|
+
return {
|
|
211
|
+
type: InteractionCallbackType.Modal,
|
|
212
|
+
data: {
|
|
213
|
+
custom_id: customId,
|
|
214
|
+
title,
|
|
215
|
+
components,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 获取 REST 客户端
|
|
221
|
+
*/
|
|
222
|
+
getREST() {
|
|
223
|
+
return this.rest;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 编辑后续消息(用于延迟响应后)
|
|
227
|
+
*/
|
|
228
|
+
async editFollowup(interactionToken, content) {
|
|
229
|
+
return this.rest.editOriginalInteractionResponse(this.applicationId, interactionToken, content);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 发送后续消息
|
|
233
|
+
*/
|
|
234
|
+
async sendFollowup(interactionToken, content) {
|
|
235
|
+
return this.rest.createFollowupMessage(this.applicationId, interactionToken, content);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=interactions.js.map
|