Qué es autocomplete
Autocomplete es un tipo de interacción de Discord que se dispara cada vez que un usuario escribe en una opción de comando marcada con autocomplete: true. En lugar de esperar a que el usuario envíe el comando completo, Discord envía una AutocompleteInteraction que contiene el texto parcial que el usuario escribió hasta ese momento. El bot responde con hasta 25 sugerencias que Discord muestra como un desplegable debajo del campo de entrada. El usuario puede seleccionar una sugerencia o seguir escribiendo, y Discord envía nuevas interacciones de autocomplete a medida que cambia la entrada.
En Spraxium, autocomplete se maneja con una clase handler dedicada decorada con @SlashAutocompleteHandler. Este handler está separado del handler del comando porque las interacciones de autocomplete llegan antes de que el comando sea enviado y tienen un tipo de interacción distinto. El handler de autocomplete recibe la entrada parcial mediante el decorador de parámetro @SlashFocused y responde con sugerencias filtradas a través del objeto AutocompleteInteraction inyectado por @Ctx.
El sistema de autocomplete se integra con el mismo contenedor DI, registro de módulos y manejo de excepciones que los command handlers. Las clases handler de autocomplete pueden inyectar servicios para obtener sugerencias desde bases de datos, APIs o fuentes de datos en caché. Los errores durante la evaluación de autocomplete son capturados por el exception handler y no derriban el proceso del bot.
Habilitar autocomplete en una opción
Autocomplete se habilita del lado de la definición del comando estableciendo autocomplete: true en la configuración de la opción. Este flag indica a Discord que envíe interacciones de autocomplete cuando el usuario enfoca la entrada de la opción. Solo las opciones String, Integer y Number soportan autocomplete. Cuando autocomplete es true, la propiedad choices se ignora porque Discord no permite choices estáticos y autocomplete dinámico en la misma opción.
import { SlashCommand, SlashOption } from '@spraxium/common';
@SlashCommand({
name: 'search',
description: 'Search for an item in the database.',
})
export class SearchCommand {
@SlashOption.String('query', {
description: 'Search query',
required: true,
autocomplete: true,
})
build() {}
}La clase handler de autocomplete
El decorador @SlashAutocompleteHandler marca una clase como proveedor de autocomplete para una opción específica de un comando específico. Acepta dos argumentos: la clase @SlashCommand propietaria de la opción y el nombre de la opción como string. La clase debe exponer un método handle que reciba el valor enfocado y la interacción, procese la entrada y responda con sugerencias.
import { Ctx, SlashAutocompleteHandler, SlashFocused } from '@spraxium/common';
import type { AutocompleteInteraction } from 'discord.js';
import { SearchCommand } from '../commands/search.command';
@SlashAutocompleteHandler(SearchCommand, 'query')
export class SearchAutocomplete {
async handle(
@SlashFocused() query: string,
@Ctx() interaction: AutocompleteInteraction,
): Promise<void> {
const results = await this.fetchSuggestions(query);
await interaction.respond(
results.map((r) => ({ name: r.label, value: r.id })),
);
}
private async fetchSuggestions(query: string) {
return [
{ label: 'Item One', id: 'item-1' },
{ label: 'Item Two', id: 'item-2' },
].filter((item) => item.label.toLowerCase().includes(query.toLowerCase()));
}
}El decorador @SlashAutocompleteHandler toma la referencia de clase del comando como primer argumento y el nombre de opción como segundo. Durante la carga del módulo, el framework lee estos metadatos y asocia el handler con la opción exacta a la que debe responder. Cada handler se limita a un comando y una opción, por lo que debes crear una clase separada para cada opción que requiera autocomplete.
Inyección de parámetros en handlers de autocomplete
Los handlers de autocomplete usan dos decoradores de parámetro: @SlashFocused() y @Ctx(). El decorador @SlashFocused inyecta el valor parcial que el usuario escribió hasta ese momento. Para opciones String esto es un string; para opciones Integer y Number puede ser string o number según el estado actual de la entrada. El decorador @Ctx inyecta el objeto AutocompleteInteraction, que proporciona el método respond para enviar sugerencias de vuelta a Discord.
| Decorador | Valor inyectado | Tipo |
|---|---|---|
| @SlashFocused() | El texto parcial que el usuario escribió en el campo de opción enfocado. | string or number |
| @Ctx() | El objeto de Discord AutocompleteInteraction en bruto. | AutocompleteInteraction |
El orden de parámetros no importa. El SlashBinder lee los índices de metadatos de ambos decoradores y construye el arreglo de parámetros por posición. Si colocas @SlashFocused en índice 0 y @Ctx en índice 1, o al revés, el framework inyecta los valores correctos en las posiciones correctas.
Responde dentro de tres segundos
Discord exige que las respuestas de autocomplete lleguen dentro de tres segundos desde que se envía la interacción. Si tu handler excede ese límite, el cliente de Discord muestra una lista de sugerencias vacía o fallida y el usuario no puede seleccionar ninguna opción.
Patrones de filtrado y respuesta
Discord impone un máximo de 25 sugerencias por respuesta de autocomplete. Tu handler debe filtrar y recortar resultados antes de llamar a interaction.respond(). Cada sugerencia es un objeto con name (mostrado al usuario, máximo 100 caracteres) y value (enviado al command handler cuando se selecciona).
import { Ctx, SlashAutocompleteHandler, SlashFocused } from '@spraxium/common';
import type { AutocompleteInteraction } from 'discord.js';
import { PingCommand } from '../commands/ping.command';
const SERVICES = [
{ name: 'Gateway', value: 'gateway' },
{ name: 'REST API', value: 'rest-api' },
{ name: 'Voice', value: 'voice' },
{ name: 'CDN', value: 'cdn' },
{ name: 'Database', value: 'database' },
];
@SlashAutocompleteHandler(PingCommand, 'service')
export class PingServiceAutocomplete {
async handle(
@SlashFocused() focused: string,
@Ctx() interaction: AutocompleteInteraction,
): Promise<void> {
const filtered = SERVICES.filter((s) =>
s.name.toLowerCase().includes(String(focused).toLowerCase()),
);
await interaction.respond(filtered.slice(0, 25));
}
}Autocomplete con inyección de dependencias
Los handlers de autocomplete se instancian mediante el contenedor DI, por lo que pueden inyectar servicios igual que los command handlers. Esto permite patrones donde las sugerencias de autocomplete provienen de base de datos, caché o APIs externas. La clase handler declara parámetros de constructor, y el contenedor DI los resuelve desde los providers del módulo durante la carga del módulo.
import { Ctx, SlashAutocompleteHandler, SlashFocused } from '@spraxium/common';
import type { AutocompleteInteraction } from 'discord.js';
import { PlayCommand } from '../commands/play.command';
import { MusicService } from '../../services/music.service';
@SlashAutocompleteHandler(PlayCommand, 'song')
export class SongSearchAutocomplete {
constructor(private readonly music: MusicService) {}
async handle(
@SlashFocused() focused: string,
@Ctx() interaction: AutocompleteInteraction,
): Promise<void> {
const results = await this.music.searchTracks(String(focused));
await interaction.respond(
results.slice(0, 25).map((track) => ({
name: `${track.artist} - ${track.title}`.slice(0, 100),
value: track.uri,
})),
);
}
}Registro de módulo para handlers de autocomplete
Los handlers de autocomplete se registran en el arreglo handlers del módulo junto con los command handlers. El framework detecta metadatos @SlashAutocompleteHandler durante la carga del módulo y conecta el handler con su comando y opción automáticamente. No necesitas arreglos separados ni configuración especial para handlers de autocomplete.
import { Module } from '@spraxium/common';
import { PingCommand } from './commands/ping.command';
import { PingHandler } from './handlers/ping.handler';
import { PingServiceAutocomplete } from './handlers/ping-service.autocomplete';
@Module({
commands: [PingCommand],
handlers: [PingHandler, PingServiceAutocomplete],
})
export class SlashModule {}Checklist de diseño para autocomplete
Recorta siempre a 25 resultados
Discord rechaza respuestas de autocomplete con más de 25 choices. Aplica
.slice(0, 25) a todo arreglo de respuesta, incluso si esperas menos
resultados. Esto evita fallos inesperados cuando los datos crecen más allá
de tus expectativas iniciales.
Trunca nombres de sugerencia a 100 caracteres
Discord limita el campo name a 100 caracteres. Cuando las sugerencias
provienen de contenido generado por usuarios o fuentes externas, aplica
.slice(0, 100) al nombre para evitar rechazo de la API.
Convierte focused a string antes de filtrar
Para opciones Integer y Number, el valor enfocado puede llegar como
number o como string parcial que aún no es un número válido. Usa
String(focused) antes de operaciones de string como toLowerCase() o
includes() para evitar errores de tipo.
Responde rápido con datos en caché o precomputados
Discord espera respuestas de autocomplete dentro de tres segundos. Para sugerencias respaldadas por base de datos, considera cachear consultas frecuentes o usar índices de búsqueda optimizados. Para listas estáticas, define los datos como constantes a nivel de módulo para evitar asignaciones repetidas.
Devuelve arreglo vacío cuando no exista handler
Si una opción de comando tiene autocomplete: true pero no hay handler
registrado, el binder envía un arreglo vacío automáticamente. Sin embargo,
si creas un handler que no responde, el usuario ve un estado de carga
perpetuo. Llama siempre a interaction.respond() en todas las rutas de
código, incluso con un arreglo vacío.