Add smart contextual autocomplete for role management
- Add context-aware autocomplete for /role add and /role remove commands - /role add only shows roles user doesn't have that are self-assignable - /role remove only shows roles user currently has that are self-manageable - Filter autocomplete based on user input with 25 result limit - Convert role options from RoleOption to StringOption with autocomplete - Prevent users from seeing irrelevant role choices User Experience Improvements: - Smart role suggestions based on current user state - No more trying to add roles you already have - No more seeing roles you can't manage - Clean, focused autocomplete interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,20 +9,22 @@ module.exports = {
|
|||||||
subcommand
|
subcommand
|
||||||
.setName('add')
|
.setName('add')
|
||||||
.setDescription('Add a role to yourself')
|
.setDescription('Add a role to yourself')
|
||||||
.addRoleOption(option =>
|
.addStringOption(option =>
|
||||||
option.setName('role')
|
option.setName('role')
|
||||||
.setDescription('The role to add')
|
.setDescription('The role to add')
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
|
.setAutocomplete(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.addSubcommand(subcommand =>
|
.addSubcommand(subcommand =>
|
||||||
subcommand
|
subcommand
|
||||||
.setName('remove')
|
.setName('remove')
|
||||||
.setDescription('Remove a role from yourself')
|
.setDescription('Remove a role from yourself')
|
||||||
.addRoleOption(option =>
|
.addStringOption(option =>
|
||||||
option.setName('role')
|
option.setName('role')
|
||||||
.setDescription('The role to remove')
|
.setDescription('The role to remove')
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
|
.setAutocomplete(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.addSubcommand(subcommand =>
|
.addSubcommand(subcommand =>
|
||||||
@@ -91,7 +93,19 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle add/remove subcommands
|
// Handle add/remove subcommands
|
||||||
const targetRole = interaction.options.getRole('role');
|
const roleName = interaction.options.getString('role');
|
||||||
|
|
||||||
|
// Find the role by name
|
||||||
|
const targetRole = interaction.guild.roles.cache.find(r =>
|
||||||
|
r.name.toLowerCase() === roleName.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetRole) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: `❌ Role **${roleName}** not found on this server.`,
|
||||||
|
flags: [MessageFlags.Ephemeral]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the role is in the allowed list
|
// Check if the role is in the allowed list
|
||||||
if (!allowedRoleIds.includes(targetRole.id)) {
|
if (!allowedRoleIds.includes(targetRole.id)) {
|
||||||
@@ -178,5 +192,61 @@ module.exports = {
|
|||||||
flags: [MessageFlags.Ephemeral]
|
flags: [MessageFlags.Ephemeral]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async autocomplete(interaction, guildConfig) {
|
||||||
|
const subcommand = interaction.options.getSubcommand();
|
||||||
|
const focusedValue = interaction.options.getFocused().toLowerCase();
|
||||||
|
const databaseService = configManager.databaseService;
|
||||||
|
|
||||||
|
if (!databaseService) {
|
||||||
|
return interaction.respond([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get allowed role IDs for this guild
|
||||||
|
const allowedRoleIds = databaseService.getAllowedRoleIds(interaction.guild.id);
|
||||||
|
|
||||||
|
if (allowedRoleIds.length === 0) {
|
||||||
|
return interaction.respond([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let availableRoles = [];
|
||||||
|
|
||||||
|
if (subcommand === 'add') {
|
||||||
|
// For add: show roles user doesn't have that are self-assignable
|
||||||
|
for (const roleId of allowedRoleIds) {
|
||||||
|
try {
|
||||||
|
const role = await interaction.guild.roles.fetch(roleId);
|
||||||
|
if (role && !interaction.member.roles.cache.has(roleId)) {
|
||||||
|
availableRoles.push(role);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Role doesn't exist anymore, skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (subcommand === 'remove') {
|
||||||
|
// For remove: show roles user currently has that are self-assignable
|
||||||
|
for (const roleId of allowedRoleIds) {
|
||||||
|
try {
|
||||||
|
const role = await interaction.guild.roles.fetch(roleId);
|
||||||
|
if (role && interaction.member.roles.cache.has(roleId)) {
|
||||||
|
availableRoles.push(role);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Role doesn't exist anymore, skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter based on what user has typed and limit to 25
|
||||||
|
const filtered = availableRoles
|
||||||
|
.filter(role => role.name.toLowerCase().includes(focusedValue))
|
||||||
|
.slice(0, 25)
|
||||||
|
.map(role => ({
|
||||||
|
name: role.name,
|
||||||
|
value: role.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
await interaction.respond(filtered);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user