Remove allowedSfxChannels functionality - allow SFX in all channels
- Remove allowedSfxChannels from database schema and all code - Remove channel checking logic from all SFX commands (!sfx, /sfx, /soundboard) - Remove /config sfxchannels subcommand - Update config.json and example to remove channel restrictions - Simplify SFX system to work in any channel with bot access Benefits: - Better user experience - no confusing channel restrictions - Simpler configuration - fewer settings to manage - Cleaner codebase - reduced complexity - Universal access - SFX works anywhere the bot can send messages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
11
README.md
11
README.md
@@ -81,7 +81,7 @@ A modern Discord bot built with Discord.js v14 that provides sound effects, text
|
||||
|
||||
# Production mode with Docker Compose
|
||||
pnpm start
|
||||
|
||||
|
||||
# Production mode with local Node.js
|
||||
pnpm start:prod
|
||||
```
|
||||
@@ -228,19 +228,24 @@ pnpm image:run
|
||||
The bot uses **SQLite database** for persistent guild configurations. Configuration can be managed in three ways:
|
||||
|
||||
#### 1. Automatic Registration (Recommended for Public Bot)
|
||||
|
||||
When the bot is added to a new server, it automatically:
|
||||
|
||||
- Creates default configuration with sensible settings
|
||||
- Sends a welcome message explaining features
|
||||
- Registers slash commands for the server
|
||||
|
||||
#### 2. Live Configuration via Slash Commands
|
||||
|
||||
Administrators can use `/config` commands to modify settings in real-time:
|
||||
|
||||
- `/config show` - View current server settings
|
||||
- `/config prefix <prefix>` - Change command prefix
|
||||
- `/config sfx <true/false>` - Enable/disable sound effects
|
||||
- And more (see Configuration Management section above)
|
||||
|
||||
#### 3. Seed Data from config.json (Optional)
|
||||
|
||||
For initial deployment or migrating existing servers, create `config.json`:
|
||||
|
||||
```json
|
||||
@@ -256,7 +261,6 @@ For initial deployment or migrating existing servers, create `config.json`:
|
||||
"internalName": "My Server",
|
||||
"prefix": "!",
|
||||
"enableSfx": true,
|
||||
"allowedSfxChannels": "general|voice-chat",
|
||||
"sfxVolume": 0.5,
|
||||
"enableFunFacts": true,
|
||||
"enableHamFacts": true,
|
||||
@@ -317,7 +321,7 @@ help,commands|Available commands: !sfx, !funfact, !hamfact
|
||||
"pingRoleId": "ROLE_ID"
|
||||
},
|
||||
{
|
||||
"id": "weekly-reminder",
|
||||
"id": "weekly-reminder",
|
||||
"schedule": "0 10 * * 1",
|
||||
"channelId": "CHANNEL_ID",
|
||||
"message": "Happy Monday!"
|
||||
@@ -326,6 +330,7 @@ help,commands|Available commands: !sfx, !funfact, !hamfact
|
||||
```
|
||||
|
||||
**Schedule Formats Supported:**
|
||||
|
||||
- **Object format**: `{"hour": 9, "minute": 30, "tz": "America/Los_Angeles"}` (with timezone)
|
||||
- **Cron format**: `"0 9 * * *"` (standard cron expression)
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
f|is for :frog:
|
||||
imelly|Didn't know this was a political channel. WTF, you don't have sites where you can vomit that garbage out for the peanut gallery? Not everyone holds your same position. Why do that? Don't assume everyone is a socialist. Knock it off. I didn't join here to listen to your political BS. I am not paying a subscription to listen to you people pontificate about your political hangups with normal people. Unsubscribed, unfollowed. -iMellyGurl
|
||||
dance|*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*
|
||||
@@ -10,9 +10,9 @@
|
||||
"id": "GUILD ID",
|
||||
"prefix": "!",
|
||||
"enableSfx": true,
|
||||
"allowedSfxChannels": "piped|list|of-valid-channels",
|
||||
"sfxVolume": 0.5,
|
||||
"passes": 2,
|
||||
"allowedRolesForRequest": ["DISCORD ROLE ID"],
|
||||
"enableFunFacts": true,
|
||||
"enableHamFacts": true,
|
||||
"scheduledEvents": [
|
||||
@@ -34,17 +34,14 @@
|
||||
"id": "SECOND GUILD ID",
|
||||
"prefix": "!",
|
||||
"enableSfx": true,
|
||||
"allowedSfxChannels": "piped|list|of-valid-channels",
|
||||
"sfxVolume": 0.5,
|
||||
"passes": 2,
|
||||
"enableFunFacts": true,
|
||||
"enableHamFacts": true
|
||||
}
|
||||
],
|
||||
"activities": [
|
||||
"that gum you like"
|
||||
],
|
||||
"activities": ["Chardee MacDennis", "The Nightman Cometh", "Charlie Work"],
|
||||
"blacklistedUsers": ["IGNORE COMMANDS FROM THESE DISCORD USER IDS"]
|
||||
},
|
||||
"debug": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'dance',
|
||||
description: 'Make the bot dance!',
|
||||
|
||||
async execute(message, args, guildConfig) {
|
||||
await message.channel.send(
|
||||
'*┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛ ┏(-_-)┓┏(-_-)┛┗(-_- )┓┗(-_-)┛┏(-_-)┛*'
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'role',
|
||||
description: 'Add or remove allowed roles',
|
||||
|
||||
async execute(message, args, guildConfig) {
|
||||
// Check if there are allowed roles configured
|
||||
if (!guildConfig.allowedRolesForRequest || guildConfig.allowedRolesForRequest.length === 0) {
|
||||
return message.reply('No roles are currently allowed to be added/removed by members.');
|
||||
}
|
||||
|
||||
// Show usage if no arguments
|
||||
if (args.length === 0) {
|
||||
return message.reply(
|
||||
`Usage: ${guildConfig.prefix}role {add|remove} {${guildConfig.allowedRolesForRequest}}`
|
||||
);
|
||||
}
|
||||
|
||||
const action = args[0]?.toLowerCase();
|
||||
const roleName = args.slice(1).join(' ');
|
||||
|
||||
// Validate action
|
||||
if (!['add', 'remove'].includes(action)) {
|
||||
return message.reply(
|
||||
`You must use add/remove after the role command! *e.g. ${guildConfig.prefix}role add <rolename>*`
|
||||
);
|
||||
}
|
||||
|
||||
// Validate role name
|
||||
if (!roleName) {
|
||||
return message.reply(
|
||||
`Usage: ${guildConfig.prefix}role {add|remove} {${guildConfig.allowedRolesForRequest}}`
|
||||
);
|
||||
}
|
||||
|
||||
// Check if role is in the allowed list
|
||||
const allowedRoles = guildConfig.allowedRolesForRequest.split('|');
|
||||
const roleRegex = new RegExp(guildConfig.allowedRolesForRequest, 'i');
|
||||
|
||||
if (!roleRegex.test(roleName)) {
|
||||
return message.reply(
|
||||
`**${roleName}** is not a valid role name! The roles allowed for request are: ${allowedRoles.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
// Find the role in the guild (case-sensitive search)
|
||||
const role = message.guild.roles.cache.find(r =>
|
||||
r.name.toLowerCase() === roleName.toLowerCase()
|
||||
);
|
||||
|
||||
if (!role) {
|
||||
return message.reply(`${roleName} is not a role on this server!`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (action === 'add') {
|
||||
await message.member.roles.add(role, 'User requested');
|
||||
await message.react('👍');
|
||||
console.log(`Added role ${role.name} to ${message.author.username}`);
|
||||
} else if (action === 'remove') {
|
||||
await message.member.roles.remove(role, 'User requested');
|
||||
await message.react('👍');
|
||||
console.log(`Removed role ${role.name} from ${message.author.username}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error managing role ${role.name}:`, error);
|
||||
await message.react('⚠️');
|
||||
|
||||
// Send error message if we can't react
|
||||
if (!message.reactions.cache.has('⚠️')) {
|
||||
await message.reply('I encountered an error managing that role. Make sure I have the proper permissions!');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -41,14 +41,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
async execute(message, args, guildConfig) {
|
||||
// Check if SFX is allowed in this channel
|
||||
if (guildConfig.allowedSfxChannels) {
|
||||
const allowedChannels = new RegExp(guildConfig.allowedSfxChannels);
|
||||
if (!allowedChannels.test(message.channel.name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const sfxName = args[0];
|
||||
|
||||
// If no SFX specified, show the list
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } = require('discord.js');
|
||||
const configManager = require('../../config/config');
|
||||
const {
|
||||
SlashCommandBuilder,
|
||||
EmbedBuilder,
|
||||
PermissionFlagsBits,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const configManager = require("../../config/config");
|
||||
|
||||
module.exports = {
|
||||
async formatAllowedRoles(guild, guildConfig) {
|
||||
const databaseService = configManager.databaseService;
|
||||
if (!databaseService) return 'Database unavailable';
|
||||
if (!databaseService) return "Database unavailable";
|
||||
|
||||
const allowedRoleIds = databaseService.getAllowedRoleIds(guild.id);
|
||||
|
||||
|
||||
if (allowedRoleIds.length === 0) {
|
||||
return 'None configured';
|
||||
return "None configured";
|
||||
}
|
||||
|
||||
const roles = [];
|
||||
@@ -22,99 +27,104 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
return roles.length > 0 ? roles.join(', ') : 'None configured';
|
||||
return roles.length > 0 ? roles.join(", ") : "None configured";
|
||||
},
|
||||
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('config')
|
||||
.setDescription('Manage server configuration')
|
||||
.setName("config")
|
||||
.setDescription("Manage server configuration")
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('show')
|
||||
.setDescription('Show current server configuration')
|
||||
.setName("show")
|
||||
.setDescription("Show current server configuration")
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('prefix')
|
||||
.setDescription('Set the command prefix')
|
||||
.addStringOption(option =>
|
||||
option.setName('new_prefix')
|
||||
.setDescription('The new command prefix')
|
||||
.setName("prefix")
|
||||
.setDescription("Set the command prefix")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("new_prefix")
|
||||
.setDescription("The new command prefix")
|
||||
.setRequired(true)
|
||||
.setMaxLength(5)
|
||||
.addChoices(
|
||||
{ name: "!", value: "!" },
|
||||
{ name: "$", value: "$" },
|
||||
{ name: "%", value: "%" },
|
||||
{ name: "^", value: "^" },
|
||||
{ name: "&", value: "&" }
|
||||
)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('sfx')
|
||||
.setDescription('Enable or disable sound effects')
|
||||
.addBooleanOption(option =>
|
||||
option.setName('enabled')
|
||||
.setDescription('Enable sound effects')
|
||||
.setName("sfx")
|
||||
.setDescription("Enable or disable sound effects")
|
||||
.addBooleanOption((option) =>
|
||||
option
|
||||
.setName("enabled")
|
||||
.setDescription("Enable sound effects")
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('volume')
|
||||
.setDescription('Set sound effects volume')
|
||||
.addNumberOption(option =>
|
||||
option.setName('level')
|
||||
.setDescription('Volume level (0.1 to 1.0)')
|
||||
.setName("volume")
|
||||
.setDescription("Set sound effects volume")
|
||||
.addNumberOption((option) =>
|
||||
option
|
||||
.setName("level")
|
||||
.setDescription("Volume level (0.1 to 1.0)")
|
||||
.setRequired(true)
|
||||
.setMinValue(0.1)
|
||||
.setMaxValue(1.0)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('funfacts')
|
||||
.setDescription('Enable or disable fun facts')
|
||||
.addBooleanOption(option =>
|
||||
option.setName('enabled')
|
||||
.setDescription('Enable fun facts')
|
||||
.setName("funfacts")
|
||||
.setDescription("Enable or disable fun facts")
|
||||
.addBooleanOption((option) =>
|
||||
option
|
||||
.setName("enabled")
|
||||
.setDescription("Enable fun facts")
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('hamfacts')
|
||||
.setDescription('Enable or disable ham facts')
|
||||
.addBooleanOption(option =>
|
||||
option.setName('enabled')
|
||||
.setDescription('Enable ham facts')
|
||||
.setName("hamfacts")
|
||||
.setDescription("Enable or disable ham facts")
|
||||
.addBooleanOption((option) =>
|
||||
option
|
||||
.setName("enabled")
|
||||
.setDescription("Enable ham facts")
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
.addSubcommand((subcommand) =>
|
||||
subcommand
|
||||
.setName('sfxchannels')
|
||||
.setDescription('Set allowed channels for sound effects (regex pattern)')
|
||||
.addStringOption(option =>
|
||||
option.setName('pattern')
|
||||
.setDescription('Channel name pattern (leave empty to allow all channels)')
|
||||
.setRequired(false)
|
||||
)
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('roles')
|
||||
.setDescription('Manage self-assignable roles')
|
||||
.addStringOption(option =>
|
||||
option.setName('action')
|
||||
.setDescription('Action to perform')
|
||||
.setName("roles")
|
||||
.setDescription("Manage self-assignable roles")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("action")
|
||||
.setDescription("Action to perform")
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Add role to list', value: 'add' },
|
||||
{ name: 'Remove role from list', value: 'remove' },
|
||||
{ name: 'Clear all roles', value: 'clear' },
|
||||
{ name: 'Show current roles', value: 'list' }
|
||||
{ name: "Add role to list", value: "add" },
|
||||
{ name: "Remove role from list", value: "remove" },
|
||||
{ name: "Clear all roles", value: "clear" },
|
||||
{ name: "Show current roles", value: "list" }
|
||||
)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option.setName('role')
|
||||
.setDescription('The role to add or remove (not needed for list/clear)')
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("role")
|
||||
.setDescription(
|
||||
"The role to add or remove (not needed for list/clear)"
|
||||
)
|
||||
.setRequired(false)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
@@ -125,84 +135,100 @@ module.exports = {
|
||||
const databaseService = configManager.databaseService;
|
||||
|
||||
if (!databaseService) {
|
||||
return interaction.reply({
|
||||
content: '❌ Database service not available.',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
return interaction.reply({
|
||||
content: "❌ Database service not available.",
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
|
||||
if (subcommand === 'show') {
|
||||
if (subcommand === "show") {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`⚙️ Configuration for ${interaction.guild.name}`)
|
||||
.setColor(0x21c629)
|
||||
.addFields([
|
||||
{ name: 'Prefix', value: `\`${guildConfig.prefix}\``, inline: true },
|
||||
{ name: 'SFX Enabled', value: guildConfig.enableSfx ? '✅ Yes' : '❌ No', inline: true },
|
||||
{ name: 'SFX Volume', value: `${Math.round(guildConfig.sfxVolume * 100)}%`, inline: true },
|
||||
{ name: 'Fun Facts', value: guildConfig.enableFunFacts ? '✅ Enabled' : '❌ Disabled', inline: true },
|
||||
{ name: 'Ham Facts', value: guildConfig.enableHamFacts ? '✅ Enabled' : '❌ Disabled', inline: true },
|
||||
{ name: 'Allowed SFX Channels', value: guildConfig.allowedSfxChannels || 'All channels', inline: false },
|
||||
{ name: 'Self-Assignable Roles', value: await this.formatAllowedRoles(interaction.guild, guildConfig), inline: false },
|
||||
{ name: "Prefix", value: `\`${guildConfig.prefix}\``, inline: true },
|
||||
{
|
||||
name: "SFX Enabled",
|
||||
value: guildConfig.enableSfx ? "✅ Yes" : "❌ No",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "SFX Volume",
|
||||
value: `${Math.round(guildConfig.sfxVolume * 100)}%`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Fun Facts",
|
||||
value: guildConfig.enableFunFacts ? "✅ Enabled" : "❌ Disabled",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Ham Facts",
|
||||
value: guildConfig.enableHamFacts ? "✅ Enabled" : "❌ Disabled",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Self-Assignable Roles",
|
||||
value: await this.formatAllowedRoles(
|
||||
interaction.guild,
|
||||
guildConfig
|
||||
),
|
||||
inline: false,
|
||||
},
|
||||
])
|
||||
.setFooter({ text: 'Use /config commands to modify settings' });
|
||||
.setFooter({ text: "Use /config commands to modify settings" });
|
||||
|
||||
return interaction.reply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
// Handle configuration updates
|
||||
const newConfig = { ...guildConfig };
|
||||
let updateMessage = '';
|
||||
let updateMessage = "";
|
||||
|
||||
switch (subcommand) {
|
||||
case 'prefix':
|
||||
const newPrefix = interaction.options.getString('new_prefix');
|
||||
case "prefix":
|
||||
const newPrefix = interaction.options.getString("new_prefix");
|
||||
newConfig.prefix = newPrefix;
|
||||
updateMessage = `Command prefix updated to \`${newPrefix}\``;
|
||||
break;
|
||||
|
||||
case 'sfx':
|
||||
const sfxEnabled = interaction.options.getBoolean('enabled');
|
||||
case "sfx":
|
||||
const sfxEnabled = interaction.options.getBoolean("enabled");
|
||||
newConfig.enableSfx = sfxEnabled;
|
||||
updateMessage = `Sound effects ${sfxEnabled ? 'enabled' : 'disabled'}`;
|
||||
updateMessage = `Sound effects ${sfxEnabled ? "enabled" : "disabled"}`;
|
||||
break;
|
||||
|
||||
case 'volume':
|
||||
const volume = interaction.options.getNumber('level');
|
||||
case "volume":
|
||||
const volume = interaction.options.getNumber("level");
|
||||
newConfig.sfxVolume = volume;
|
||||
updateMessage = `SFX volume set to ${Math.round(volume * 100)}%`;
|
||||
break;
|
||||
|
||||
case 'funfacts':
|
||||
const funfactsEnabled = interaction.options.getBoolean('enabled');
|
||||
case "funfacts":
|
||||
const funfactsEnabled = interaction.options.getBoolean("enabled");
|
||||
newConfig.enableFunFacts = funfactsEnabled;
|
||||
updateMessage = `Fun facts ${funfactsEnabled ? 'enabled' : 'disabled'}`;
|
||||
updateMessage = `Fun facts ${funfactsEnabled ? "enabled" : "disabled"}`;
|
||||
break;
|
||||
|
||||
case 'hamfacts':
|
||||
const hamfactsEnabled = interaction.options.getBoolean('enabled');
|
||||
case "hamfacts":
|
||||
const hamfactsEnabled = interaction.options.getBoolean("enabled");
|
||||
newConfig.enableHamFacts = hamfactsEnabled;
|
||||
updateMessage = `Ham facts ${hamfactsEnabled ? 'enabled' : 'disabled'}`;
|
||||
updateMessage = `Ham facts ${hamfactsEnabled ? "enabled" : "disabled"}`;
|
||||
break;
|
||||
|
||||
case 'sfxchannels':
|
||||
const channelPattern = interaction.options.getString('pattern');
|
||||
newConfig.allowedSfxChannels = channelPattern || null;
|
||||
updateMessage = channelPattern
|
||||
? `SFX channels restricted to pattern: \`${channelPattern}\``
|
||||
: 'SFX allowed in all channels';
|
||||
break;
|
||||
case "roles":
|
||||
const action = interaction.options.getString("action");
|
||||
const roleName = interaction.options.getString("role");
|
||||
|
||||
if (action === "list") {
|
||||
const allowedRoleIds = databaseService.getAllowedRoleIds(
|
||||
interaction.guild.id
|
||||
);
|
||||
|
||||
case 'roles':
|
||||
const action = interaction.options.getString('action');
|
||||
const roleName = interaction.options.getString('role');
|
||||
|
||||
if (action === 'list') {
|
||||
const allowedRoleIds = databaseService.getAllowedRoleIds(interaction.guild.id);
|
||||
|
||||
if (allowedRoleIds.length === 0) {
|
||||
return interaction.reply({
|
||||
content: '❌ No self-assignable roles are currently configured.',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
content: "❌ No self-assignable roles are currently configured.",
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,66 +239,79 @@ module.exports = {
|
||||
const roleObj = await interaction.guild.roles.fetch(roleId);
|
||||
if (roleObj) roles.push(roleObj);
|
||||
} catch (error) {
|
||||
console.warn(`Role ${roleId} not found in guild ${interaction.guild.id}`);
|
||||
console.warn(
|
||||
`Role ${roleId} not found in guild ${interaction.guild.id}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('📋 Self-Assignable Roles Configuration')
|
||||
.setDescription(roles.length > 0 ? 'Currently configured roles:' : 'No valid roles found.')
|
||||
.setTitle("📋 Self-Assignable Roles Configuration")
|
||||
.setDescription(
|
||||
roles.length > 0
|
||||
? "Currently configured roles:"
|
||||
: "No valid roles found."
|
||||
)
|
||||
.setColor(0x21c629)
|
||||
.addFields(roles.length > 0 ? {
|
||||
name: 'Allowed Roles',
|
||||
value: roles.map(r => `• ${r}`).join('\n'),
|
||||
inline: false
|
||||
} : {
|
||||
name: 'Status',
|
||||
value: 'No roles configured or all configured roles have been deleted.',
|
||||
inline: false
|
||||
});
|
||||
.addFields(
|
||||
roles.length > 0
|
||||
? {
|
||||
name: "Allowed Roles",
|
||||
value: roles.map((r) => `• ${r}`).join("\n"),
|
||||
inline: false,
|
||||
}
|
||||
: {
|
||||
name: "Status",
|
||||
value:
|
||||
"No roles configured or all configured roles have been deleted.",
|
||||
inline: false,
|
||||
}
|
||||
);
|
||||
|
||||
return interaction.reply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
if (action === 'clear') {
|
||||
if (action === "clear") {
|
||||
databaseService.updateAllowedRoleIds(interaction.guild.id, []);
|
||||
updateMessage = 'Self-assignable roles list cleared';
|
||||
updateMessage = "Self-assignable roles list cleared";
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!roleName) {
|
||||
return interaction.reply({
|
||||
content: '❌ You must specify a role for add/remove actions.',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
content: "❌ You must specify a role for add/remove actions.",
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
|
||||
// Find the role by name
|
||||
const role = interaction.guild.roles.cache.find(r =>
|
||||
r.name.toLowerCase() === roleName.toLowerCase()
|
||||
const role = interaction.guild.roles.cache.find(
|
||||
(r) => r.name.toLowerCase() === roleName.toLowerCase()
|
||||
);
|
||||
|
||||
|
||||
if (!role) {
|
||||
return interaction.reply({
|
||||
content: `❌ Role **${roleName}** not found on this server.`,
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
|
||||
// Check if bot can manage this role
|
||||
if (!interaction.guild.members.me.permissions.has('ManageRoles') ||
|
||||
role.position >= interaction.guild.members.me.roles.highest.position) {
|
||||
if (
|
||||
!interaction.guild.members.me.permissions.has("ManageRoles") ||
|
||||
role.position >= interaction.guild.members.me.roles.highest.position
|
||||
) {
|
||||
return interaction.reply({
|
||||
content: `❌ I cannot manage the **${role.name}** role due to permission hierarchy.`,
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'add') {
|
||||
if (action === "add") {
|
||||
databaseService.addAllowedRole(interaction.guild.id, role.id);
|
||||
updateMessage = `Added **${role.name}** to self-assignable roles`;
|
||||
} else if (action === 'remove') {
|
||||
} else if (action === "remove") {
|
||||
databaseService.removeAllowedRole(interaction.guild.id, role.id);
|
||||
updateMessage = `Removed **${role.name}** from self-assignable roles`;
|
||||
}
|
||||
@@ -280,64 +319,69 @@ module.exports = {
|
||||
// Don't set updated = true here since we're calling database methods directly
|
||||
// Skip the upsertGuildConfig call at the end
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Configuration Updated')
|
||||
.setTitle("✅ Configuration Updated")
|
||||
.setColor(0x00ff00)
|
||||
.setDescription(updateMessage)
|
||||
.setFooter({ text: 'Use /config show to see all settings' });
|
||||
.setFooter({ text: "Use /config show to see all settings" });
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
|
||||
console.log(`Role configuration updated for ${interaction.guild.name}: ${action} ${role.name}`);
|
||||
|
||||
console.log(
|
||||
`Role configuration updated for ${interaction.guild.name}: ${action} ${role.name}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update configuration in database
|
||||
databaseService.upsertGuildConfig(newConfig);
|
||||
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('✅ Configuration Updated')
|
||||
.setTitle("✅ Configuration Updated")
|
||||
.setColor(0x00ff00)
|
||||
.setDescription(updateMessage)
|
||||
.setFooter({ text: 'Use /config show to see all settings' });
|
||||
.setFooter({ text: "Use /config show to see all settings" });
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
|
||||
console.log(`Configuration updated for ${interaction.guild.name}: ${subcommand} by @${interaction.user.username}`);
|
||||
|
||||
console.log(
|
||||
`Configuration updated for ${interaction.guild.name}: ${subcommand} by @${interaction.user.username}`
|
||||
);
|
||||
},
|
||||
|
||||
async autocomplete(interaction, guildConfig) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
|
||||
// Only handle autocomplete for roles subcommand
|
||||
if (subcommand !== 'roles') {
|
||||
if (subcommand !== "roles") {
|
||||
return interaction.respond([]);
|
||||
}
|
||||
|
||||
const action = interaction.options.getString('action');
|
||||
const action = interaction.options.getString("action");
|
||||
const focusedValue = interaction.options.getFocused().toLowerCase();
|
||||
const databaseService = configManager.databaseService;
|
||||
|
||||
|
||||
if (!databaseService) {
|
||||
return interaction.respond([]);
|
||||
}
|
||||
|
||||
// Get currently allowed role IDs
|
||||
const allowedRoleIds = databaseService.getAllowedRoleIds(interaction.guild.id);
|
||||
const allowedRoleIds = databaseService.getAllowedRoleIds(
|
||||
interaction.guild.id
|
||||
);
|
||||
let availableRoles = [];
|
||||
|
||||
if (action === 'add') {
|
||||
if (action === "add") {
|
||||
// For add: show roles NOT currently in the allowed list
|
||||
const allRoles = interaction.guild.roles.cache
|
||||
.filter(role =>
|
||||
const allRoles = interaction.guild.roles.cache.filter(
|
||||
(role) =>
|
||||
!role.managed && // Exclude bot/integration roles
|
||||
role.id !== interaction.guild.id && // Exclude @everyone
|
||||
!allowedRoleIds.includes(role.id) && // Exclude already allowed roles
|
||||
role.position < interaction.guild.members.me.roles.highest.position // Only manageable roles
|
||||
);
|
||||
|
||||
);
|
||||
|
||||
availableRoles = Array.from(allRoles.values());
|
||||
|
||||
} else if (action === 'remove') {
|
||||
} else if (action === "remove") {
|
||||
// For remove: show roles currently in the allowed list
|
||||
for (const roleId of allowedRoleIds) {
|
||||
try {
|
||||
@@ -353,13 +397,13 @@ module.exports = {
|
||||
|
||||
// Filter based on what user has typed and limit to 25
|
||||
const filtered = availableRoles
|
||||
.filter(role => role.name.toLowerCase().includes(focusedValue))
|
||||
.filter((role) => role.name.toLowerCase().includes(focusedValue))
|
||||
.slice(0, 25)
|
||||
.map(role => ({
|
||||
.map((role) => ({
|
||||
name: role.name,
|
||||
value: role.name
|
||||
value: role.name,
|
||||
}));
|
||||
|
||||
await interaction.respond(filtered);
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,17 +14,6 @@ module.exports = {
|
||||
),
|
||||
|
||||
async execute(interaction, guildConfig) {
|
||||
// Check if SFX is allowed in this channel
|
||||
if (guildConfig.allowedSfxChannels) {
|
||||
const allowedChannels = new RegExp(guildConfig.allowedSfxChannels);
|
||||
if (!allowedChannels.test(interaction.channel.name)) {
|
||||
return interaction.reply({
|
||||
content: 'Sound effects are not allowed in this channel!',
|
||||
flags: [MessageFlags.Ephemeral]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const sfxName = interaction.options.getString('sound');
|
||||
|
||||
// Check if user is in a voice channel
|
||||
|
||||
@@ -62,17 +62,6 @@ module.exports = {
|
||||
.setDescription("Interactive soundboard with categorized buttons"),
|
||||
|
||||
async execute(interaction, guildConfig) {
|
||||
// Check if SFX is allowed in this channel
|
||||
if (guildConfig.allowedSfxChannels) {
|
||||
const allowedChannels = new RegExp(guildConfig.allowedSfxChannels);
|
||||
if (!allowedChannels.test(interaction.channel.name)) {
|
||||
return interaction.reply({
|
||||
content: "Sound effects are not allowed in this channel!",
|
||||
flags: [MessageFlags.Ephemeral],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user is in a voice channel
|
||||
if (!interaction.member.voice.channel) {
|
||||
return interaction.reply({
|
||||
|
||||
@@ -102,7 +102,6 @@ class ConfigManager {
|
||||
internalName: "Unknown Guild",
|
||||
prefix: "!",
|
||||
enableSfx: true,
|
||||
allowedSfxChannels: null,
|
||||
sfxVolume: 0.5,
|
||||
enableFunFacts: true,
|
||||
enableHamFacts: true,
|
||||
|
||||
@@ -269,7 +269,6 @@ Use \`/config show\` to view all settings or \`/config\` commands to modify them
|
||||
internalName: guild.name,
|
||||
prefix: "!",
|
||||
enableSfx: true,
|
||||
allowedSfxChannels: null, // Allow in all channels by default
|
||||
sfxVolume: 0.5,
|
||||
enableFunFacts: true,
|
||||
enableHamFacts: true,
|
||||
|
||||
@@ -25,7 +25,6 @@ class DatabaseService {
|
||||
internal_name TEXT,
|
||||
prefix TEXT DEFAULT '!',
|
||||
enable_sfx BOOLEAN DEFAULT true,
|
||||
allowed_sfx_channels TEXT,
|
||||
sfx_volume REAL DEFAULT 0.5,
|
||||
enable_fun_facts BOOLEAN DEFAULT true,
|
||||
enable_ham_facts BOOLEAN DEFAULT true,
|
||||
@@ -69,7 +68,7 @@ class DatabaseService {
|
||||
('bot_name', 'GHBot'),
|
||||
('debug', 'false'),
|
||||
('admin_user_id', ''),
|
||||
('activities', '["Playing sounds", "Serving facts"]'),
|
||||
('activities', '["Chardee MacDennis", "The Nightman Cometh", "Charlie Work"]'),
|
||||
('blacklisted_users', '[]')
|
||||
`);
|
||||
|
||||
@@ -144,7 +143,6 @@ class DatabaseService {
|
||||
internalName: guild.internalName || guild.name || "Unknown Guild",
|
||||
prefix: guild.prefix || "!",
|
||||
enableSfx: guild.enableSfx !== false,
|
||||
allowedSfxChannels: guild.allowedSfxChannels || null,
|
||||
sfxVolume: guild.sfxVolume || 0.5,
|
||||
enableFunFacts: guild.enableFunFacts !== false,
|
||||
enableHamFacts: guild.enableHamFacts !== false,
|
||||
@@ -234,14 +232,14 @@ class DatabaseService {
|
||||
),
|
||||
insertGuild: this.db.prepare(`
|
||||
INSERT OR REPLACE INTO guilds
|
||||
(id, name, internal_name, prefix, enable_sfx, allowed_sfx_channels, sfx_volume,
|
||||
(id, name, internal_name, prefix, enable_sfx, sfx_volume,
|
||||
enable_fun_facts, enable_ham_facts, allowed_roles_for_request)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`),
|
||||
updateGuild: this.db.prepare(`
|
||||
UPDATE guilds SET
|
||||
name = ?, internal_name = ?, prefix = ?, enable_sfx = ?,
|
||||
allowed_sfx_channels = ?, sfx_volume = ?, enable_fun_facts = ?,
|
||||
sfx_volume = ?, enable_fun_facts = ?,
|
||||
enable_ham_facts = ?, allowed_roles_for_request = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ? AND is_active = true
|
||||
`),
|
||||
@@ -294,7 +292,6 @@ class DatabaseService {
|
||||
internalName: guild.internal_name,
|
||||
prefix: guild.prefix,
|
||||
enableSfx: Boolean(guild.enable_sfx),
|
||||
allowedSfxChannels: guild.allowed_sfx_channels,
|
||||
sfxVolume: guild.sfx_volume,
|
||||
enableFunFacts: Boolean(guild.enable_fun_facts),
|
||||
enableHamFacts: Boolean(guild.enable_ham_facts),
|
||||
@@ -316,7 +313,6 @@ class DatabaseService {
|
||||
internalName: guild.internal_name,
|
||||
prefix: guild.prefix,
|
||||
enableSfx: Boolean(guild.enable_sfx),
|
||||
allowedSfxChannels: guild.allowed_sfx_channels,
|
||||
sfxVolume: guild.sfx_volume,
|
||||
enableFunFacts: Boolean(guild.enable_fun_facts),
|
||||
enableHamFacts: Boolean(guild.enable_ham_facts),
|
||||
@@ -365,7 +361,6 @@ class DatabaseService {
|
||||
guildConfig.internalName || guildConfig.name,
|
||||
guildConfig.prefix || "!",
|
||||
guildConfig.enableSfx !== false ? 1 : 0,
|
||||
guildConfig.allowedSfxChannels || null,
|
||||
guildConfig.sfxVolume || 0.5,
|
||||
guildConfig.enableFunFacts !== false ? 1 : 0,
|
||||
guildConfig.enableHamFacts !== false ? 1 : 0,
|
||||
@@ -418,7 +413,6 @@ class DatabaseService {
|
||||
internalName: guild.internal_name,
|
||||
prefix: guild.prefix,
|
||||
enableSfx: Boolean(guild.enable_sfx),
|
||||
allowedSfxChannels: guild.allowed_sfx_channels,
|
||||
sfxVolume: guild.sfx_volume,
|
||||
enableFunFacts: Boolean(guild.enable_fun_facts),
|
||||
enableHamFacts: Boolean(guild.enable_ham_facts),
|
||||
|
||||
Reference in New Issue
Block a user