CHECKPOINT: Interactive soundboard and refactored SFX system
Major Features Implemented: - Complete Discord.js v14 modernization from v12 with hybrid command system - SQLite database for dynamic guild configuration management - Interactive soundboard with categorized button interface (/soundboard) - Three-tier SFX interface: prefix (!sfx), autocomplete (/sfx), and visual soundboard - Auto-registration system for public bot distribution - Soft delete guild management preserving configurations Technical Improvements: - Refactored SFX playing into reusable service methods (playSFXInteraction/playSFXMessage) - Smart markdown chunking that respects code block boundaries - High-performance caching for 275+ sound effects with autocomplete optimization - Modern Discord.js v14 patterns (MessageFlags.Ephemeral, proper intents) - Fixed security vulnerability in @discordjs/opus with pnpm overrides - Docker deployment with Node 20 and npm for reliable SQLite compilation Interactive Soundboard Features: - Category-based navigation with buttons (GENERAL, NERDS, TWIN PEAKS, etc.) - Pagination support for large categories (16 sounds per page, 4 per row) - Real-time status updates (Playing → Finished playing) - Navigation buttons (Previous/Next/Back to Categories) - Ephemeral responses for clean chat experience Database System: - Auto-migration from config.json to SQLite on first run - /config slash commands for live server configuration - Scheduled events with timezone support (object and cron formats) - Guild auto-registration with welcome messages for new servers Current State: Fully functional modern Discord bot ready for public distribution
This commit is contained in:
@@ -26,64 +26,17 @@ module.exports = {
|
||||
}
|
||||
|
||||
const sfxName = interaction.options.getString('sound');
|
||||
|
||||
// Log the slash command SFX request
|
||||
console.log(
|
||||
`/sfx '${sfxName}' requested in ${guildConfig.internalName || interaction.guild.name}#${interaction.channel.name} from @${interaction.user.username}`
|
||||
);
|
||||
|
||||
// Check if SFX exists
|
||||
if (!sfxManager.hasSFX(sfxName)) {
|
||||
return interaction.reply({
|
||||
content: 'This sound effect does not exist!',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is in a voice channel
|
||||
const member = interaction.member;
|
||||
if (!member.voice.channel) {
|
||||
if (!interaction.member.voice.channel) {
|
||||
return interaction.reply({
|
||||
content: 'You need to be in a voice channel to use this command!',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
});
|
||||
}
|
||||
|
||||
// Defer the reply as joining voice might take a moment
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
// Join the voice channel
|
||||
await voiceService.join(member.voice.channel);
|
||||
|
||||
// Get the SFX file path
|
||||
const sfxPath = sfxManager.getSFXPath(sfxName);
|
||||
|
||||
// Play the sound effect
|
||||
await voiceService.play(
|
||||
interaction.guild.id,
|
||||
sfxPath,
|
||||
{
|
||||
volume: guildConfig.sfxVolume || 0.5
|
||||
}
|
||||
);
|
||||
|
||||
// Update the reply
|
||||
await interaction.editReply(`Playing sound effect: **${sfxName}**`);
|
||||
|
||||
// Leave the voice channel after playing
|
||||
setTimeout(() => {
|
||||
voiceService.leave(interaction.guild.id);
|
||||
}, 500);
|
||||
|
||||
console.log(`✅ Successfully played /sfx '${sfxName}'`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error playing /sfx '${sfxName}':`, error);
|
||||
await interaction.editReply({
|
||||
content: "I couldn't play that sound effect. Make sure I have permission to join your voice channel!"
|
||||
});
|
||||
}
|
||||
// Use the reusable SFX playing method
|
||||
await sfxManager.playSFXInteraction(interaction, sfxName, guildConfig, 'slash');
|
||||
},
|
||||
|
||||
async autocomplete(interaction, guildConfig) {
|
||||
|
||||
Reference in New Issue
Block a user