aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bot.ts41
-rw-r--r--src/commands/index.ts3
-rw-r--r--src/commands/play.ts19
-rw-r--r--src/utils/get-youtube-suggestions-for.ts15
4 files changed, 61 insertions, 17 deletions
diff --git a/src/bot.ts b/src/bot.ts
index a18e344..c51caf9 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -87,27 +87,40 @@ export default class {
});
this.client.on('interactionCreate', async interaction => {
- if (!interaction.isButton()) {
- return;
- }
+ try {
+ if (interaction.isButton()) {
+ const command = this.commandsByButtonId.get(interaction.customId);
- const command = this.commandsByButtonId.get(interaction.customId);
+ if (!command) {
+ return;
+ }
- if (!command) {
- return;
- }
+ if (command.handleButtonInteraction) {
+ await command.handleButtonInteraction(interaction);
+ }
+ }
- try {
- if (command.handleButtonInteraction) {
- 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) {
debug(error);
- if (interaction.replied || interaction.deferred) {
- await interaction.editReply(errorMsg('something went wrong'));
- } else {
- await interaction.reply({content: errorMsg(error as Error), ephemeral: true});
+ // Can't reply with errors for autocomplete queries
+ if (interaction.isButton()) {
+ if (interaction.replied || interaction.deferred) {
+ await interaction.editReply(errorMsg('something went wrong'));
+ } else {
+ await interaction.reply({content: errorMsg(error as Error), ephemeral: true});
+ }
}
}
});
diff --git a/src/commands/index.ts b/src/commands/index.ts
index b9f5877..1d1646f 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -1,5 +1,5 @@
import {SlashCommandBuilder} from '@discordjs/builders';
-import {ButtonInteraction, CommandInteraction} from 'discord.js';
+import {AutocompleteInteraction, ButtonInteraction, CommandInteraction} from 'discord.js';
export default interface Command {
readonly slashCommand: Partial<SlashCommandBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;
@@ -7,4 +7,5 @@ export default interface Command {
readonly requiresVC?: boolean;
execute: (interaction: CommandInteraction) => Promise<void>;
handleButtonInteraction?: (interaction: ButtonInteraction) => Promise<void>;
+ handleAutocompleteInteraction?: (interaction: AutocompleteInteraction) => Promise<void>;
}
diff --git a/src/commands/play.ts b/src/commands/play.ts
index 619c18f..7e5dee1 100644
--- a/src/commands/play.ts
+++ b/src/commands/play.ts
@@ -1,4 +1,4 @@
-import {CommandInteraction, GuildMember} from 'discord.js';
+import {AutocompleteInteraction, CommandInteraction, GuildMember} from 'discord.js';
import {URL} from 'url';
import {Except} from 'type-fest';
import {SlashCommandBuilder} from '@discordjs/builders';
@@ -12,6 +12,7 @@ import {getMostPopularVoiceChannel, getMemberVoiceChannel} from '../utils/channe
import errorMsg from '../utils/error-msg.js';
import GetSongs from '../services/get-songs.js';
import {prisma} from '../utils/db.js';
+import getYouTubeSuggestionsFor from '../utils/get-youtube-suggestions-for.js';
@injectable()
export default class implements Command {
@@ -21,7 +22,8 @@ export default class implements Command {
.setDescription('play a song or resume playback')
.addStringOption(option => option
.setName('query')
- .setDescription('YouTube URL, Spotify URL, or search query'))
+ .setDescription('YouTube URL, Spotify URL, or search query')
+ .setAutocomplete(true))
.addBooleanOption(option => option
.setName('immediate')
.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}`);
}
}
+
+ 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,
+ })));
+ }
}
diff --git a/src/utils/get-youtube-suggestions-for.ts b/src/utils/get-youtube-suggestions-for.ts
new file mode 100644
index 0000000..11977e4
--- /dev/null
+++ b/src/utils/get-youtube-suggestions-for.ts
@@ -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;