add autocomplete

This commit is contained in:
Max Isom 2022-01-19 18:15:12 -06:00
parent 43baa57c51
commit 7901fcce3d
No known key found for this signature in database
GPG key ID: 25C9B1A7F6798880
4 changed files with 63 additions and 19 deletions

View file

@ -87,29 +87,42 @@ export default class {
}); });
this.client.on('interactionCreate', async interaction => { this.client.on('interactionCreate', async interaction => {
if (!interaction.isButton()) { try {
return; if (interaction.isButton()) {
}
const command = this.commandsByButtonId.get(interaction.customId); const command = this.commandsByButtonId.get(interaction.customId);
if (!command) { if (!command) {
return; return;
} }
try {
if (command.handleButtonInteraction) { if (command.handleButtonInteraction) {
await command.handleButtonInteraction(interaction); await command.handleButtonInteraction(interaction);
} }
}
if (interaction.isAutocomplete()) {
const command = this.commandsByName.get(interaction.commandName);
if (!command) {
return;
}
if (command.handleAutocompleteInteraction) {
await command.handleAutocompleteInteraction(interaction);
}
}
} catch (error: unknown) { } catch (error: unknown) {
debug(error); debug(error);
// Can't reply with errors for autocomplete queries
if (interaction.isButton()) {
if (interaction.replied || interaction.deferred) { if (interaction.replied || interaction.deferred) {
await interaction.editReply(errorMsg('something went wrong')); await interaction.editReply(errorMsg('something went wrong'));
} else { } else {
await interaction.reply({content: errorMsg(error as Error), ephemeral: true}); await interaction.reply({content: errorMsg(error as Error), ephemeral: true});
} }
} }
}
}); });
const spinner = ora('📡 connecting to Discord...').start(); const spinner = ora('📡 connecting to Discord...').start();

View file

@ -1,5 +1,5 @@
import {SlashCommandBuilder} from '@discordjs/builders'; import {SlashCommandBuilder} from '@discordjs/builders';
import {ButtonInteraction, CommandInteraction} from 'discord.js'; import {AutocompleteInteraction, ButtonInteraction, CommandInteraction} from 'discord.js';
export default interface Command { export default interface Command {
readonly slashCommand: Partial<SlashCommandBuilder> & Pick<SlashCommandBuilder, 'toJSON'>; readonly slashCommand: Partial<SlashCommandBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;
@ -7,4 +7,5 @@ export default interface Command {
readonly requiresVC?: boolean; readonly requiresVC?: boolean;
execute: (interaction: CommandInteraction) => Promise<void>; execute: (interaction: CommandInteraction) => Promise<void>;
handleButtonInteraction?: (interaction: ButtonInteraction) => Promise<void>; handleButtonInteraction?: (interaction: ButtonInteraction) => Promise<void>;
handleAutocompleteInteraction?: (interaction: AutocompleteInteraction) => Promise<void>;
} }

View file

@ -1,4 +1,4 @@
import {CommandInteraction, GuildMember} from 'discord.js'; import {AutocompleteInteraction, CommandInteraction, GuildMember} from 'discord.js';
import {URL} from 'url'; import {URL} from 'url';
import {Except} from 'type-fest'; import {Except} from 'type-fest';
import {SlashCommandBuilder} from '@discordjs/builders'; import {SlashCommandBuilder} from '@discordjs/builders';
@ -12,6 +12,7 @@ import {getMostPopularVoiceChannel, getMemberVoiceChannel} from '../utils/channe
import errorMsg from '../utils/error-msg.js'; import errorMsg from '../utils/error-msg.js';
import GetSongs from '../services/get-songs.js'; import GetSongs from '../services/get-songs.js';
import {prisma} from '../utils/db.js'; import {prisma} from '../utils/db.js';
import getYouTubeSuggestionsFor from '../utils/get-youtube-suggestions-for.js';
@injectable() @injectable()
export default class implements Command { export default class implements Command {
@ -21,7 +22,8 @@ export default class implements Command {
.setDescription('play a song or resume playback') .setDescription('play a song or resume playback')
.addStringOption(option => option .addStringOption(option => option
.setName('query') .setName('query')
.setDescription('YouTube URL, Spotify URL, or search query')) .setDescription('YouTube URL, Spotify URL, or search query')
.setAutocomplete(true))
.addBooleanOption(option => option .addBooleanOption(option => option
.setName('immediate') .setName('immediate')
.setDescription('adds track to the front of the queue')) .setDescription('adds track to the front of the queue'))
@ -191,4 +193,17 @@ export default class implements Command {
await interaction.editReply(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${extraMsg}`); await interaction.editReply(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${extraMsg}`);
} }
} }
public async handleAutocompleteInteraction(interaction: AutocompleteInteraction): Promise<void> {
const query = interaction.options.getString('query')?.trim();
if (!query || query.length === 0) {
return interaction.respond([]);
}
await interaction.respond((await getYouTubeSuggestionsFor(query)).map(s => ({
name: s,
value: s,
})));
}
} }

View file

@ -0,0 +1,15 @@
import got from 'got';
const getYouTubeSuggestionsFor = async (query: string): Promise<string[]> => {
const [_, suggestions] = await got('https://suggestqueries.google.com/complete/search?client=firefox&ds=yt&q=', {
searchParams: {
client: 'firefox',
ds: 'yt',
q: query,
},
}).json<[string, string[]]>();
return suggestions;
};
export default getYouTubeSuggestionsFor;