@furlow/discord 1.0.1 → 1.0.3
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/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +30 -16
- package/dist/client/index.js.map +1 -1
- package/dist/index.js +110 -36
- package/dist/index.js.map +1 -1
- package/dist/interactions/index.d.ts +8 -1
- package/dist/interactions/index.js +24 -4
- package/dist/interactions/index.js.map +1 -1
- package/dist/video/index.js +3 -1
- package/dist/video/index.js.map +1 -1
- package/dist/voice/index.js +53 -15
- package/dist/voice/index.js.map +1 -1
- package/package.json +3 -3
package/dist/client/index.d.ts
CHANGED
package/dist/client/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var INTENT_MAP = {
|
|
|
25
25
|
auto_moderation_configuration: GatewayIntentBits.AutoModerationConfiguration,
|
|
26
26
|
auto_moderation_execution: GatewayIntentBits.AutoModerationExecution
|
|
27
27
|
};
|
|
28
|
-
var FurlowClient = class {
|
|
28
|
+
var FurlowClient = class _FurlowClient {
|
|
29
29
|
client;
|
|
30
30
|
token;
|
|
31
31
|
spec;
|
|
@@ -111,14 +111,22 @@ var FurlowClient = class {
|
|
|
111
111
|
}
|
|
112
112
|
return [...intents];
|
|
113
113
|
}
|
|
114
|
+
/** Default timeout for ready event (30 seconds) */
|
|
115
|
+
static READY_TIMEOUT_MS = 3e4;
|
|
114
116
|
/**
|
|
115
117
|
* Start the client
|
|
116
118
|
*/
|
|
117
119
|
async start() {
|
|
118
120
|
await this.client.login(this.token);
|
|
119
121
|
if (!this.client.isReady()) {
|
|
120
|
-
await new Promise((resolve) => {
|
|
121
|
-
|
|
122
|
+
await new Promise((resolve, reject) => {
|
|
123
|
+
const timeoutId = setTimeout(() => {
|
|
124
|
+
reject(new Error("Client ready timeout - Discord client did not become ready within 30 seconds"));
|
|
125
|
+
}, _FurlowClient.READY_TIMEOUT_MS);
|
|
126
|
+
this.client.once("ready", () => {
|
|
127
|
+
clearTimeout(timeoutId);
|
|
128
|
+
resolve();
|
|
129
|
+
});
|
|
122
130
|
});
|
|
123
131
|
}
|
|
124
132
|
await this.applyIdentity();
|
|
@@ -139,13 +147,15 @@ var FurlowClient = class {
|
|
|
139
147
|
if (identity.name && this.client.user?.username !== identity.name) {
|
|
140
148
|
try {
|
|
141
149
|
await this.client.user?.setUsername(identity.name);
|
|
142
|
-
} catch {
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.warn("Failed to set bot username (may be rate limited):", err instanceof Error ? err.message : String(err));
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
if (identity.avatar) {
|
|
146
155
|
try {
|
|
147
156
|
await this.client.user?.setAvatar(identity.avatar);
|
|
148
|
-
} catch {
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.warn("Failed to set bot avatar (may be rate limited):", err instanceof Error ? err.message : String(err));
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
}
|
|
@@ -155,17 +165,21 @@ var FurlowClient = class {
|
|
|
155
165
|
async applyPresence() {
|
|
156
166
|
if (!this.spec.presence) return;
|
|
157
167
|
const presence = this.spec.presence;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
try {
|
|
169
|
+
this.client.user?.setPresence({
|
|
170
|
+
status: presence.status ?? "online",
|
|
171
|
+
activities: presence.activity ? [
|
|
172
|
+
{
|
|
173
|
+
type: this.getActivityType(presence.activity.type),
|
|
174
|
+
name: presence.activity.text,
|
|
175
|
+
url: presence.activity.url,
|
|
176
|
+
state: presence.activity.state
|
|
177
|
+
}
|
|
178
|
+
] : void 0
|
|
179
|
+
});
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error("Failed to set presence:", err);
|
|
182
|
+
}
|
|
169
183
|
}
|
|
170
184
|
/**
|
|
171
185
|
* Get Discord.js activity type
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Discord client wrapper\n */\n\nimport {\n Client,\n GatewayIntentBits,\n Partials,\n type ClientOptions,\n type ClientEvents,\n} from 'discord.js';\nimport type { FurlowSpec, IntentsConfig, GatewayConfig, Identity, Presence } from '@furlow/schema';\n\nexport interface FurlowClientOptions {\n token: string;\n spec: FurlowSpec;\n}\n\nconst INTENT_MAP: Record<string, GatewayIntentBits> = {\n guilds: GatewayIntentBits.Guilds,\n guild_members: GatewayIntentBits.GuildMembers,\n guild_moderation: GatewayIntentBits.GuildModeration,\n guild_emojis_and_stickers: GatewayIntentBits.GuildEmojisAndStickers,\n guild_integrations: GatewayIntentBits.GuildIntegrations,\n guild_webhooks: GatewayIntentBits.GuildWebhooks,\n guild_invites: GatewayIntentBits.GuildInvites,\n guild_voice_states: GatewayIntentBits.GuildVoiceStates,\n guild_presences: GatewayIntentBits.GuildPresences,\n guild_messages: GatewayIntentBits.GuildMessages,\n guild_message_reactions: GatewayIntentBits.GuildMessageReactions,\n guild_message_typing: GatewayIntentBits.GuildMessageTyping,\n direct_messages: GatewayIntentBits.DirectMessages,\n direct_message_reactions: GatewayIntentBits.DirectMessageReactions,\n direct_message_typing: GatewayIntentBits.DirectMessageTyping,\n message_content: GatewayIntentBits.MessageContent,\n guild_scheduled_events: GatewayIntentBits.GuildScheduledEvents,\n auto_moderation_configuration: GatewayIntentBits.AutoModerationConfiguration,\n auto_moderation_execution: GatewayIntentBits.AutoModerationExecution,\n};\n\nexport class FurlowClient {\n private client: Client;\n private token: string;\n private spec: FurlowSpec;\n\n constructor(options: FurlowClientOptions) {\n this.token = options.token;\n this.spec = options.spec;\n\n const intents = this.resolveIntents(options.spec.intents);\n\n const clientOptions: ClientOptions = {\n intents,\n partials: [\n Partials.Message,\n Partials.Channel,\n Partials.Reaction,\n Partials.User,\n Partials.GuildMember,\n ],\n };\n\n this.client = new Client(clientOptions);\n }\n\n /**\n * Resolve intents from spec\n */\n private resolveIntents(config?: IntentsConfig): GatewayIntentBits[] {\n if (!config) {\n // Default intents\n return [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.GuildMembers,\n GatewayIntentBits.MessageContent,\n ];\n }\n\n if (config.auto) {\n // Auto-detect required intents from spec\n return this.autoDetectIntents();\n }\n\n if (config.explicit) {\n return config.explicit\n .map((intent) => INTENT_MAP[intent])\n .filter((i): i is GatewayIntentBits => i !== undefined);\n }\n\n return [GatewayIntentBits.Guilds];\n }\n\n /**\n * Auto-detect required intents from spec\n */\n private autoDetectIntents(): GatewayIntentBits[] {\n const intents = new Set<GatewayIntentBits>();\n\n // Always need Guilds\n intents.add(GatewayIntentBits.Guilds);\n\n // Check events\n if (this.spec.events) {\n for (const handler of this.spec.events) {\n switch (handler.event) {\n case 'message_create':\n case 'message':\n case 'message_update':\n case 'message_delete':\n intents.add(GatewayIntentBits.GuildMessages);\n intents.add(GatewayIntentBits.MessageContent);\n break;\n case 'guild_member_add':\n case 'guild_member_remove':\n case 'guild_member_update':\n case 'member_join':\n case 'member_leave':\n intents.add(GatewayIntentBits.GuildMembers);\n break;\n case 'voice_state_update':\n case 'voice_join':\n case 'voice_leave':\n intents.add(GatewayIntentBits.GuildVoiceStates);\n break;\n case 'message_reaction_add':\n case 'message_reaction_remove':\n intents.add(GatewayIntentBits.GuildMessageReactions);\n break;\n case 'presence_update':\n intents.add(GatewayIntentBits.GuildPresences);\n break;\n }\n }\n }\n\n // Check if commands exist\n if (this.spec.commands?.length) {\n intents.add(GatewayIntentBits.GuildMessages);\n }\n\n // Check if voice features are used\n if (this.spec.voice) {\n intents.add(GatewayIntentBits.GuildVoiceStates);\n }\n\n return [...intents];\n }\n\n /**\n * Start the client\n */\n async start(): Promise<void> {\n await this.client.login(this.token);\n\n // Wait for ready\n if (!this.client.isReady()) {\n await new Promise<void>((resolve) => {\n this.client.once('ready', () => resolve());\n });\n }\n\n // Apply identity\n await this.applyIdentity();\n\n // Apply presence\n await this.applyPresence();\n }\n\n /**\n * Stop the client\n */\n async stop(): Promise<void> {\n await this.client.destroy();\n }\n\n /**\n * Apply bot identity\n */\n private async applyIdentity(): Promise<void> {\n if (!this.spec.identity) return;\n\n const identity = this.spec.identity;\n\n // Set username if different\n if (identity.name && this.client.user?.username !== identity.name) {\n try {\n await this.client.user?.setUsername(identity.name);\n } catch {\n // Username changes are rate limited\n }\n }\n\n // Set avatar\n if (identity.avatar) {\n try {\n await this.client.user?.setAvatar(identity.avatar);\n } catch {\n // Avatar might be rate limited\n }\n }\n }\n\n /**\n * Apply presence\n */\n private async applyPresence(): Promise<void> {\n if (!this.spec.presence) return;\n\n const presence = this.spec.presence;\n\n this.client.user?.setPresence({\n status: presence.status ?? 'online',\n activities: presence.activity\n ? [\n {\n type: this.getActivityType(presence.activity.type),\n name: presence.activity.text,\n url: presence.activity.url,\n state: presence.activity.state,\n },\n ]\n : undefined,\n });\n }\n\n /**\n * Get Discord.js activity type\n */\n private getActivityType(type: string): number {\n const types: Record<string, number> = {\n playing: 0,\n streaming: 1,\n listening: 2,\n watching: 3,\n custom: 4,\n competing: 5,\n };\n return types[type] ?? 0;\n }\n\n /**\n * Register event listener\n */\n on<K extends keyof ClientEvents>(\n event: K,\n listener: (...args: ClientEvents[K]) => void\n ): this {\n this.client.on(event, listener);\n return this;\n }\n\n /**\n * Register one-time event listener\n */\n once<K extends keyof ClientEvents>(\n event: K,\n listener: (...args: ClientEvents[K]) => void\n ): this {\n this.client.once(event, listener);\n return this;\n }\n\n /**\n * Get the underlying Discord.js client\n */\n getClient(): Client {\n return this.client;\n }\n\n /**\n * Get the spec\n */\n getSpec(): FurlowSpec {\n return this.spec;\n }\n\n /**\n * Check if client is ready\n */\n isReady(): boolean {\n return this.client.isReady();\n }\n\n /**\n * Get guild count\n */\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n /**\n * Get user count (approximate)\n */\n get userCount(): number {\n return this.client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0);\n }\n}\n\n/**\n * Create a FURLOW client\n */\nexport function createClient(options: FurlowClientOptions): FurlowClient {\n return new FurlowClient(options);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Discord client wrapper\n */\n\nimport {\n Client,\n GatewayIntentBits,\n Partials,\n type ClientOptions,\n type ClientEvents,\n} from 'discord.js';\nimport type { FurlowSpec, IntentsConfig, GatewayConfig, Identity, Presence } from '@furlow/schema';\n\nexport interface FurlowClientOptions {\n token: string;\n spec: FurlowSpec;\n}\n\nconst INTENT_MAP: Record<string, GatewayIntentBits> = {\n guilds: GatewayIntentBits.Guilds,\n guild_members: GatewayIntentBits.GuildMembers,\n guild_moderation: GatewayIntentBits.GuildModeration,\n guild_emojis_and_stickers: GatewayIntentBits.GuildEmojisAndStickers,\n guild_integrations: GatewayIntentBits.GuildIntegrations,\n guild_webhooks: GatewayIntentBits.GuildWebhooks,\n guild_invites: GatewayIntentBits.GuildInvites,\n guild_voice_states: GatewayIntentBits.GuildVoiceStates,\n guild_presences: GatewayIntentBits.GuildPresences,\n guild_messages: GatewayIntentBits.GuildMessages,\n guild_message_reactions: GatewayIntentBits.GuildMessageReactions,\n guild_message_typing: GatewayIntentBits.GuildMessageTyping,\n direct_messages: GatewayIntentBits.DirectMessages,\n direct_message_reactions: GatewayIntentBits.DirectMessageReactions,\n direct_message_typing: GatewayIntentBits.DirectMessageTyping,\n message_content: GatewayIntentBits.MessageContent,\n guild_scheduled_events: GatewayIntentBits.GuildScheduledEvents,\n auto_moderation_configuration: GatewayIntentBits.AutoModerationConfiguration,\n auto_moderation_execution: GatewayIntentBits.AutoModerationExecution,\n};\n\nexport class FurlowClient {\n private client: Client;\n private token: string;\n private spec: FurlowSpec;\n\n constructor(options: FurlowClientOptions) {\n this.token = options.token;\n this.spec = options.spec;\n\n const intents = this.resolveIntents(options.spec.intents);\n\n const clientOptions: ClientOptions = {\n intents,\n partials: [\n Partials.Message,\n Partials.Channel,\n Partials.Reaction,\n Partials.User,\n Partials.GuildMember,\n ],\n };\n\n this.client = new Client(clientOptions);\n }\n\n /**\n * Resolve intents from spec\n */\n private resolveIntents(config?: IntentsConfig): GatewayIntentBits[] {\n if (!config) {\n // Default intents\n return [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.GuildMembers,\n GatewayIntentBits.MessageContent,\n ];\n }\n\n if (config.auto) {\n // Auto-detect required intents from spec\n return this.autoDetectIntents();\n }\n\n if (config.explicit) {\n return config.explicit\n .map((intent) => INTENT_MAP[intent])\n .filter((i): i is GatewayIntentBits => i !== undefined);\n }\n\n return [GatewayIntentBits.Guilds];\n }\n\n /**\n * Auto-detect required intents from spec\n */\n private autoDetectIntents(): GatewayIntentBits[] {\n const intents = new Set<GatewayIntentBits>();\n\n // Always need Guilds\n intents.add(GatewayIntentBits.Guilds);\n\n // Check events\n if (this.spec.events) {\n for (const handler of this.spec.events) {\n switch (handler.event) {\n case 'message_create':\n case 'message':\n case 'message_update':\n case 'message_delete':\n intents.add(GatewayIntentBits.GuildMessages);\n intents.add(GatewayIntentBits.MessageContent);\n break;\n case 'guild_member_add':\n case 'guild_member_remove':\n case 'guild_member_update':\n case 'member_join':\n case 'member_leave':\n intents.add(GatewayIntentBits.GuildMembers);\n break;\n case 'voice_state_update':\n case 'voice_join':\n case 'voice_leave':\n intents.add(GatewayIntentBits.GuildVoiceStates);\n break;\n case 'message_reaction_add':\n case 'message_reaction_remove':\n intents.add(GatewayIntentBits.GuildMessageReactions);\n break;\n case 'presence_update':\n intents.add(GatewayIntentBits.GuildPresences);\n break;\n }\n }\n }\n\n // Check if commands exist\n if (this.spec.commands?.length) {\n intents.add(GatewayIntentBits.GuildMessages);\n }\n\n // Check if voice features are used\n if (this.spec.voice) {\n intents.add(GatewayIntentBits.GuildVoiceStates);\n }\n\n return [...intents];\n }\n\n /** Default timeout for ready event (30 seconds) */\n private static readonly READY_TIMEOUT_MS = 30000;\n\n /**\n * Start the client\n */\n async start(): Promise<void> {\n await this.client.login(this.token);\n\n // Wait for ready with timeout\n if (!this.client.isReady()) {\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error('Client ready timeout - Discord client did not become ready within 30 seconds'));\n }, FurlowClient.READY_TIMEOUT_MS);\n\n this.client.once('ready', () => {\n clearTimeout(timeoutId);\n resolve();\n });\n });\n }\n\n // Apply identity\n await this.applyIdentity();\n\n // Apply presence\n await this.applyPresence();\n }\n\n /**\n * Stop the client\n */\n async stop(): Promise<void> {\n await this.client.destroy();\n }\n\n /**\n * Apply bot identity\n */\n private async applyIdentity(): Promise<void> {\n if (!this.spec.identity) return;\n\n const identity = this.spec.identity;\n\n // Set username if different\n if (identity.name && this.client.user?.username !== identity.name) {\n try {\n await this.client.user?.setUsername(identity.name);\n } catch (err) {\n // Username changes are rate limited - log but don't fail startup\n console.warn('Failed to set bot username (may be rate limited):', err instanceof Error ? err.message : String(err));\n }\n }\n\n // Set avatar\n if (identity.avatar) {\n try {\n await this.client.user?.setAvatar(identity.avatar);\n } catch (err) {\n // Avatar might be rate limited - log but don't fail startup\n console.warn('Failed to set bot avatar (may be rate limited):', err instanceof Error ? err.message : String(err));\n }\n }\n }\n\n /**\n * Apply presence\n */\n private async applyPresence(): Promise<void> {\n if (!this.spec.presence) return;\n\n const presence = this.spec.presence;\n\n try {\n this.client.user?.setPresence({\n status: presence.status ?? 'online',\n activities: presence.activity\n ? [\n {\n type: this.getActivityType(presence.activity.type),\n name: presence.activity.text,\n url: presence.activity.url,\n state: presence.activity.state,\n },\n ]\n : undefined,\n });\n } catch (err) {\n console.error('Failed to set presence:', err);\n }\n }\n\n /**\n * Get Discord.js activity type\n */\n private getActivityType(type: string): number {\n const types: Record<string, number> = {\n playing: 0,\n streaming: 1,\n listening: 2,\n watching: 3,\n custom: 4,\n competing: 5,\n };\n return types[type] ?? 0;\n }\n\n /**\n * Register event listener\n */\n on<K extends keyof ClientEvents>(\n event: K,\n listener: (...args: ClientEvents[K]) => void\n ): this {\n this.client.on(event, listener);\n return this;\n }\n\n /**\n * Register one-time event listener\n */\n once<K extends keyof ClientEvents>(\n event: K,\n listener: (...args: ClientEvents[K]) => void\n ): this {\n this.client.once(event, listener);\n return this;\n }\n\n /**\n * Get the underlying Discord.js client\n */\n getClient(): Client {\n return this.client;\n }\n\n /**\n * Get the spec\n */\n getSpec(): FurlowSpec {\n return this.spec;\n }\n\n /**\n * Check if client is ready\n */\n isReady(): boolean {\n return this.client.isReady();\n }\n\n /**\n * Get guild count\n */\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n /**\n * Get user count (approximate)\n */\n get userCount(): number {\n return this.client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0);\n }\n}\n\n/**\n * Create a FURLOW client\n */\nexport function createClient(options: FurlowClientOptions): FurlowClient {\n return new FurlowClient(options);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var INTENT_MAP = {
|
|
|
25
25
|
auto_moderation_configuration: GatewayIntentBits.AutoModerationConfiguration,
|
|
26
26
|
auto_moderation_execution: GatewayIntentBits.AutoModerationExecution
|
|
27
27
|
};
|
|
28
|
-
var FurlowClient = class {
|
|
28
|
+
var FurlowClient = class _FurlowClient {
|
|
29
29
|
client;
|
|
30
30
|
token;
|
|
31
31
|
spec;
|
|
@@ -111,14 +111,22 @@ var FurlowClient = class {
|
|
|
111
111
|
}
|
|
112
112
|
return [...intents];
|
|
113
113
|
}
|
|
114
|
+
/** Default timeout for ready event (30 seconds) */
|
|
115
|
+
static READY_TIMEOUT_MS = 3e4;
|
|
114
116
|
/**
|
|
115
117
|
* Start the client
|
|
116
118
|
*/
|
|
117
119
|
async start() {
|
|
118
120
|
await this.client.login(this.token);
|
|
119
121
|
if (!this.client.isReady()) {
|
|
120
|
-
await new Promise((resolve) => {
|
|
121
|
-
|
|
122
|
+
await new Promise((resolve, reject) => {
|
|
123
|
+
const timeoutId = setTimeout(() => {
|
|
124
|
+
reject(new Error("Client ready timeout - Discord client did not become ready within 30 seconds"));
|
|
125
|
+
}, _FurlowClient.READY_TIMEOUT_MS);
|
|
126
|
+
this.client.once("ready", () => {
|
|
127
|
+
clearTimeout(timeoutId);
|
|
128
|
+
resolve();
|
|
129
|
+
});
|
|
122
130
|
});
|
|
123
131
|
}
|
|
124
132
|
await this.applyIdentity();
|
|
@@ -139,13 +147,15 @@ var FurlowClient = class {
|
|
|
139
147
|
if (identity.name && this.client.user?.username !== identity.name) {
|
|
140
148
|
try {
|
|
141
149
|
await this.client.user?.setUsername(identity.name);
|
|
142
|
-
} catch {
|
|
150
|
+
} catch (err) {
|
|
151
|
+
console.warn("Failed to set bot username (may be rate limited):", err instanceof Error ? err.message : String(err));
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
if (identity.avatar) {
|
|
146
155
|
try {
|
|
147
156
|
await this.client.user?.setAvatar(identity.avatar);
|
|
148
|
-
} catch {
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.warn("Failed to set bot avatar (may be rate limited):", err instanceof Error ? err.message : String(err));
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
}
|
|
@@ -155,17 +165,21 @@ var FurlowClient = class {
|
|
|
155
165
|
async applyPresence() {
|
|
156
166
|
if (!this.spec.presence) return;
|
|
157
167
|
const presence = this.spec.presence;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
try {
|
|
169
|
+
this.client.user?.setPresence({
|
|
170
|
+
status: presence.status ?? "online",
|
|
171
|
+
activities: presence.activity ? [
|
|
172
|
+
{
|
|
173
|
+
type: this.getActivityType(presence.activity.type),
|
|
174
|
+
name: presence.activity.text,
|
|
175
|
+
url: presence.activity.url,
|
|
176
|
+
state: presence.activity.state
|
|
177
|
+
}
|
|
178
|
+
] : void 0
|
|
179
|
+
});
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error("Failed to set presence:", err);
|
|
182
|
+
}
|
|
169
183
|
}
|
|
170
184
|
/**
|
|
171
185
|
* Get Discord.js activity type
|
|
@@ -358,6 +372,7 @@ function createGatewayManager(client, options) {
|
|
|
358
372
|
import {
|
|
359
373
|
REST,
|
|
360
374
|
Routes,
|
|
375
|
+
MessageFlags,
|
|
361
376
|
SlashCommandBuilder,
|
|
362
377
|
ContextMenuCommandBuilder,
|
|
363
378
|
ApplicationCommandType
|
|
@@ -373,6 +388,7 @@ var InteractionHandler = class {
|
|
|
373
388
|
modalHandlers = /* @__PURE__ */ new Map();
|
|
374
389
|
userContextHandlers = /* @__PURE__ */ new Map();
|
|
375
390
|
messageContextHandlers = /* @__PURE__ */ new Map();
|
|
391
|
+
autocompleteHandlers = /* @__PURE__ */ new Map();
|
|
376
392
|
constructor(options) {
|
|
377
393
|
this.client = options.client;
|
|
378
394
|
this.clientId = options.clientId;
|
|
@@ -392,8 +408,9 @@ var InteractionHandler = class {
|
|
|
392
408
|
if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) {
|
|
393
409
|
await interaction.reply({
|
|
394
410
|
content: "An error occurred while processing this interaction.",
|
|
395
|
-
|
|
396
|
-
}).catch(() => {
|
|
411
|
+
flags: MessageFlags.Ephemeral
|
|
412
|
+
}).catch((replyError) => {
|
|
413
|
+
console.warn("Failed to send error reply to interaction:", replyError instanceof Error ? replyError.message : String(replyError));
|
|
397
414
|
});
|
|
398
415
|
}
|
|
399
416
|
}
|
|
@@ -433,6 +450,13 @@ var InteractionHandler = class {
|
|
|
433
450
|
if (handler) {
|
|
434
451
|
await handler(interaction);
|
|
435
452
|
}
|
|
453
|
+
} else if (interaction.isAutocomplete()) {
|
|
454
|
+
const focusedOption = interaction.options.getFocused(true);
|
|
455
|
+
const specificKey = `${interaction.commandName}:${focusedOption.name}`;
|
|
456
|
+
const handler = this.autocompleteHandlers.get(specificKey) ?? this.autocompleteHandlers.get(interaction.commandName) ?? this.findPrefixHandler(this.autocompleteHandlers, specificKey);
|
|
457
|
+
if (handler) {
|
|
458
|
+
await handler(interaction);
|
|
459
|
+
}
|
|
436
460
|
}
|
|
437
461
|
}
|
|
438
462
|
/**
|
|
@@ -482,6 +506,14 @@ var InteractionHandler = class {
|
|
|
482
506
|
onMessageContext(name, handler) {
|
|
483
507
|
this.messageContextHandlers.set(name, handler);
|
|
484
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Register an autocomplete handler
|
|
511
|
+
* @param nameOrKey Command name, or "command:option" for option-specific handler
|
|
512
|
+
* @param handler The autocomplete handler
|
|
513
|
+
*/
|
|
514
|
+
onAutocomplete(nameOrKey, handler) {
|
|
515
|
+
this.autocompleteHandlers.set(nameOrKey, handler);
|
|
516
|
+
}
|
|
485
517
|
/**
|
|
486
518
|
* Register slash commands with Discord
|
|
487
519
|
*/
|
|
@@ -556,8 +588,10 @@ var InteractionHandler = class {
|
|
|
556
588
|
addOption(builder, opt) {
|
|
557
589
|
const addMethod = `add${this.getOptionMethodName(opt.type)}Option`;
|
|
558
590
|
if (typeof builder[addMethod] !== "function") {
|
|
559
|
-
|
|
560
|
-
|
|
591
|
+
const validTypes = ["string", "integer", "number", "boolean", "user", "channel", "role", "mentionable", "attachment"];
|
|
592
|
+
throw new Error(
|
|
593
|
+
`Unknown command option type: "${opt.type}" for option "${opt.name}". Valid types are: ${validTypes.join(", ")}`
|
|
594
|
+
);
|
|
561
595
|
}
|
|
562
596
|
builder[addMethod]((optBuilder) => {
|
|
563
597
|
optBuilder.setName(opt.name).setDescription(opt.description);
|
|
@@ -624,8 +658,10 @@ import {
|
|
|
624
658
|
AudioPlayerStatus,
|
|
625
659
|
VoiceConnectionStatus,
|
|
626
660
|
entersState,
|
|
627
|
-
getVoiceConnection
|
|
661
|
+
getVoiceConnection,
|
|
662
|
+
StreamType
|
|
628
663
|
} from "@discordjs/voice";
|
|
664
|
+
import { FFmpeg } from "prism-media";
|
|
629
665
|
var FILTER_ARGS = {
|
|
630
666
|
bassboost: ["-af", "bass=g=10,dynaudnorm=f=200"],
|
|
631
667
|
nightcore: ["-af", "asetrate=44100*1.25,aresample=44100,atempo=1.06"],
|
|
@@ -663,14 +699,23 @@ var VoiceManager = class {
|
|
|
663
699
|
selfDeaf: options.selfDeaf ?? this.config.connection?.self_deaf ?? true,
|
|
664
700
|
selfMute: options.selfMute ?? this.config.connection?.self_mute ?? false
|
|
665
701
|
});
|
|
666
|
-
|
|
702
|
+
try {
|
|
703
|
+
await entersState(connection, VoiceConnectionStatus.Ready, 3e4);
|
|
704
|
+
} catch (err) {
|
|
705
|
+
connection.destroy();
|
|
706
|
+
throw new Error(`Failed to connect to voice channel: ${err instanceof Error ? err.message : String(err)}`);
|
|
707
|
+
}
|
|
667
708
|
const player = createAudioPlayer();
|
|
668
709
|
player.on(AudioPlayerStatus.Idle, () => {
|
|
669
|
-
this.handleTrackEnd(guildId)
|
|
710
|
+
this.handleTrackEnd(guildId).catch((err) => {
|
|
711
|
+
console.error(`Error handling track end in ${guildId}:`, err);
|
|
712
|
+
});
|
|
670
713
|
});
|
|
671
714
|
player.on("error", (error) => {
|
|
672
715
|
console.error(`Audio player error in ${guildId}:`, error);
|
|
673
|
-
this.handleTrackEnd(guildId)
|
|
716
|
+
this.handleTrackEnd(guildId).catch((err) => {
|
|
717
|
+
console.error(`Error handling track end after player error in ${guildId}:`, err);
|
|
718
|
+
});
|
|
674
719
|
});
|
|
675
720
|
connection.subscribe(player);
|
|
676
721
|
this.guildStates.set(guildId, {
|
|
@@ -707,18 +752,45 @@ var VoiceManager = class {
|
|
|
707
752
|
if (!state) {
|
|
708
753
|
throw new Error("Not connected to voice in this guild");
|
|
709
754
|
}
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
755
|
+
const hasFilters = state.filters.size > 0;
|
|
756
|
+
const hasSeek = options.seek && options.seek > 0;
|
|
757
|
+
let resource;
|
|
758
|
+
if (hasFilters || hasSeek) {
|
|
759
|
+
const ffmpegArgs = [
|
|
760
|
+
"-analyzeduration",
|
|
761
|
+
"0",
|
|
762
|
+
"-loglevel",
|
|
763
|
+
"0",
|
|
764
|
+
"-i",
|
|
765
|
+
source
|
|
766
|
+
];
|
|
767
|
+
if (hasSeek) {
|
|
768
|
+
const inputIndex = ffmpegArgs.indexOf("-i");
|
|
769
|
+
ffmpegArgs.splice(inputIndex, 0, "-ss", String(options.seek / 1e3));
|
|
770
|
+
}
|
|
771
|
+
if (hasFilters) {
|
|
772
|
+
const filterArgs = this.buildFilterArgs(state.filters);
|
|
773
|
+
ffmpegArgs.push(...filterArgs);
|
|
774
|
+
}
|
|
775
|
+
ffmpegArgs.push(
|
|
776
|
+
"-f",
|
|
777
|
+
"s16le",
|
|
778
|
+
"-ar",
|
|
779
|
+
"48000",
|
|
780
|
+
"-ac",
|
|
781
|
+
"2",
|
|
782
|
+
"pipe:1"
|
|
783
|
+
);
|
|
784
|
+
const ffmpegStream = new FFmpeg({ args: ffmpegArgs });
|
|
785
|
+
resource = createAudioResource(ffmpegStream, {
|
|
786
|
+
inputType: StreamType.Raw,
|
|
787
|
+
inlineVolume: true
|
|
788
|
+
});
|
|
789
|
+
} else {
|
|
790
|
+
resource = createAudioResource(source, {
|
|
791
|
+
inlineVolume: true
|
|
792
|
+
});
|
|
717
793
|
}
|
|
718
|
-
const resource = createAudioResource(source, {
|
|
719
|
-
inlineVolume: true,
|
|
720
|
-
inputType: ffmpegArgs.length > 2 ? void 0 : void 0
|
|
721
|
-
});
|
|
722
794
|
const volume = options.volume ?? state.volume;
|
|
723
795
|
resource.volume?.setVolume(volume / 100);
|
|
724
796
|
state.currentResource = resource;
|
|
@@ -1103,7 +1175,9 @@ var VideoManager = class {
|
|
|
1103
1175
|
*/
|
|
1104
1176
|
setupListener() {
|
|
1105
1177
|
this.client.on("voiceStateUpdate", (oldState, newState) => {
|
|
1106
|
-
this.handleVoiceStateUpdate(oldState, newState)
|
|
1178
|
+
this.handleVoiceStateUpdate(oldState, newState).catch((err) => {
|
|
1179
|
+
console.error("Error handling voice state update for stream detection:", err);
|
|
1180
|
+
});
|
|
1107
1181
|
});
|
|
1108
1182
|
}
|
|
1109
1183
|
/**
|