feat: Introduce a dedicated autocomplete handler for commands and refactor the inventory use command to utilize it.

This commit is contained in:
syntaxbullet
2025-12-18 14:34:47 +01:00
parent 3a96b67e89
commit 8c1f80981b
3 changed files with 37 additions and 37 deletions

View File

@@ -19,34 +19,6 @@ export const use = createCommand({
.setAutocomplete(true)
),
execute: async (interaction) => {
if (!interaction.isChatInputCommand()) {
if (interaction.isAutocomplete()) {
const focusedValue = interaction.options.getFocused();
const userId = interaction.user.id;
// Fetch owned items that are usable
const userInventory = await DrizzleClient.query.inventory.findMany({
where: eq(inventory.userId, BigInt(userId)),
with: {
item: true
},
limit: 10
});
const filtered = userInventory.filter(entry => {
const matchName = entry.item.name.toLowerCase().includes(focusedValue.toLowerCase());
const usageData = entry.item.usageData as ItemUsageData | null;
const isUsable = usageData && usageData.effects && usageData.effects.length > 0;
return matchName && isUsable;
});
await interaction.respond(
filtered.map(entry => ({ name: `${entry.item.name} (${entry.quantity})`, value: entry.item.id }))
);
}
return;
}
await interaction.deferReply();
const itemId = interaction.options.getNumber("item", true);
@@ -55,14 +27,6 @@ export const use = createCommand({
try {
const result = await inventoryService.useItem(user.id, itemId);
// Check for side effects like Role assignment that need Discord API access
// The service returns the usageData, so we can re-check simple effects or just check the results log?
// Actually, we put "TEMP_ROLE" inside results log, AND we can check usageData here for strict role assignment if we want to separate concerns.
// But for now, let's rely on the service to have handled database state, and we handle Discord state here if needed?
// WAIT - I put the role assignment placeholder in the service but it returned a result string.
// The service cannot assign the role directly because it doesn't have the member object easily (requires fetching).
// So we should iterate results or usageData here.
const usageData = result.usageData;
if (usageData) {
for (const effect of usageData.effects) {
@@ -91,5 +55,29 @@ export const use = createCommand({
} catch (error: any) {
await interaction.editReply({ embeds: [createErrorEmbed(error.message)] });
}
},
autocomplete: async (interaction) => {
const focusedValue = interaction.options.getFocused();
const userId = interaction.user.id;
// Fetch owned items that are usable
const userInventory = await DrizzleClient.query.inventory.findMany({
where: eq(inventory.userId, BigInt(userId)),
with: {
item: true
},
limit: 10
});
const filtered = userInventory.filter(entry => {
const matchName = entry.item.name.toLowerCase().includes(focusedValue.toLowerCase());
const usageData = entry.item.usageData as ItemUsageData | null;
const isUsable = usageData && usageData.effects && usageData.effects.length > 0;
return matchName && isUsable;
});
await interaction.respond(
filtered.map(entry => ({ name: `${entry.item.name} (${entry.quantity})`, value: entry.item.id }))
);
}
});

View File

@@ -19,6 +19,17 @@ const event: Event<Events.InteractionCreate> = {
}
}
if (interaction.isAutocomplete()) {
const command = KyokoClient.commands.get(interaction.commandName);
if (!command || !command.autocomplete) return;
try {
await command.autocomplete(interaction);
} catch (error) {
console.error(`Error handling autocomplete for ${interaction.commandName}:`, error);
}
return;
}
if (!interaction.isChatInputCommand()) return;
const command = KyokoClient.commands.get(interaction.commandName);

View File

@@ -1,8 +1,9 @@
import type { ChatInputCommandInteraction, ClientEvents, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js";
import type { AutocompleteInteraction, ChatInputCommandInteraction, ClientEvents, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js";
export interface Command {
data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder | SlashCommandSubcommandsOnlyBuilder;
execute: (interaction: ChatInputCommandInteraction) => Promise<void> | void;
autocomplete?: (interaction: AutocompleteInteraction) => Promise<void> | void;
category?: string;
}