aboutsummaryrefslogtreecommitdiff
path: root/src/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands')
-rw-r--r--src/commands/play.ts47
-rw-r--r--src/commands/queue.ts68
-rw-r--r--src/commands/skip.ts8
-rw-r--r--src/commands/unskip.ts7
4 files changed, 50 insertions, 80 deletions
diff --git a/src/commands/play.ts b/src/commands/play.ts
index 5ef4c1d..d1ab940 100644
--- a/src/commands/play.ts
+++ b/src/commands/play.ts
@@ -10,13 +10,13 @@ import {TYPES} from '../types.js';
import {QueuedSong, STATUS} from '../services/player.js';
import PlayerManager from '../managers/player.js';
import {getMostPopularVoiceChannel, getMemberVoiceChannel} from '../utils/channels.js';
-import errorMsg from '../utils/error-msg.js';
import GetSongs from '../services/get-songs.js';
import {prisma} from '../utils/db.js';
import ThirdParty from '../services/third-party.js';
import getYouTubeAndSpotifySuggestionsFor from '../utils/get-youtube-and-spotify-suggestions-for.js';
import KeyValueCacheProvider from '../services/key-value-cache.js';
import {ONE_HOUR_IN_SECONDS} from '../utils/constants.js';
+import {buildPlayingMessageEmbed} from '../utils/build-embed.js';
@injectable()
export default class implements Command {
@@ -68,31 +68,33 @@ export default class implements Command {
if (!query) {
if (player.status === STATUS.PLAYING) {
- await interaction.reply({content: errorMsg('already playing, give me a song name'), ephemeral: true});
- return;
+ throw new Error('already playing, give me a song name');
}
// Must be resuming play
if (!wasPlayingSong) {
- await interaction.reply({content: errorMsg('nothing to play'), ephemeral: true});
- return;
+ throw new Error('nothing to play');
}
await player.connect(targetVoiceChannel);
await player.play();
- await interaction.reply('the stop-and-go light is now green');
+ await interaction.reply({
+ content: 'the stop-and-go light is now green',
+ embeds: [buildPlayingMessageEmbed(player)],
+ });
+
return;
}
const addToFrontOfQueue = interaction.options.getBoolean('immediate');
const shuffleAdditions = interaction.options.getBoolean('shuffle');
- let newSongs: Array<Except<QueuedSong, 'addedInChannelId'>> = [];
- let extraMsg = '';
-
await interaction.deferReply();
+ let newSongs: Array<Except<QueuedSong, 'addedInChannelId' | 'requestedBy'>> = [];
+ let extraMsg = '';
+
// Test if it's a complete URL
try {
const url = new URL(query);
@@ -111,14 +113,12 @@ export default class implements Command {
// YouTube playlist
newSongs.push(...await this.getSongs.youtubePlaylist(url.searchParams.get('list')!));
} else {
- // Single video
const song = await this.getSongs.youtubeVideo(url.href);
if (song) {
newSongs.push(song);
} else {
- await interaction.editReply(errorMsg('that doesn\'t exist'));
- return;
+ throw new Error('that doesn\'t exist');
}
}
} else if (url.protocol === 'spotify:' || url.host === 'open.spotify.com') {
@@ -149,14 +149,12 @@ export default class implements Command {
if (song) {
newSongs.push(song);
} else {
- await interaction.editReply(errorMsg('that doesn\'t exist'));
- return;
+ throw new Error('that doesn\'t exist');
}
}
if (newSongs.length === 0) {
- await interaction.editReply(errorMsg('no songs found'));
- return;
+ throw new Error('no songs found');
}
if (shuffleAdditions) {
@@ -164,7 +162,7 @@ export default class implements Command {
}
newSongs.forEach(song => {
- player.add({...song, addedInChannelId: interaction.channel?.id}, {immediate: addToFrontOfQueue ?? false});
+ player.add({...song, addedInChannelId: interaction.channel!.id, requestedBy: interaction.member!.user.id}, {immediate: addToFrontOfQueue ?? false});
});
const firstSong = newSongs[0];
@@ -180,6 +178,10 @@ export default class implements Command {
if (wasPlayingSong) {
statusMsg = 'resuming playback';
}
+
+ await interaction.editReply({
+ embeds: [buildPlayingMessageEmbed(player)],
+ });
}
// Build response message
@@ -206,9 +208,18 @@ export default class implements Command {
const query = interaction.options.getString('query')?.trim();
if (!query || query.length === 0) {
- return interaction.respond([]);
+ await interaction.respond([]);
+ return;
}
+ try {
+ // Don't return suggestions for URLs
+ // eslint-disable-next-line no-new
+ new URL(query);
+ await interaction.respond([]);
+ return;
+ } catch {}
+
const suggestions = await this.cache.wrap(
getYouTubeAndSpotifySuggestionsFor,
query,
diff --git a/src/commands/queue.ts b/src/commands/queue.ts
index 9768f58..4d75d42 100644
--- a/src/commands/queue.ts
+++ b/src/commands/queue.ts
@@ -1,80 +1,32 @@
-import {ButtonInteraction, CommandInteraction} from 'discord.js';
+import {CommandInteraction} from 'discord.js';
import {SlashCommandBuilder} from '@discordjs/builders';
import {inject, injectable} from 'inversify';
import {TYPES} from '../types.js';
import PlayerManager from '../managers/player.js';
-import UpdatingQueueEmbedManager from '../managers/updating-queue-embed.js';
-import {BUTTON_IDS} from '../services/updating-queue-embed.js';
import Command from '.';
+import {buildQueueEmbed} from '../utils/build-embed.js';
@injectable()
export default class implements Command {
public readonly slashCommand = new SlashCommandBuilder()
.setName('queue')
- .setDescription('show the current queue');
-
- public readonly handledButtonIds = Object.values(BUTTON_IDS);
+ .setDescription('show the current queue')
+ .addIntegerOption(option => option
+ .setName('page')
+ .setDescription('page of queue to show [default: 1]')
+ .setRequired(false));
private readonly playerManager: PlayerManager;
- private readonly updatingQueueEmbedManager: UpdatingQueueEmbedManager;
- constructor(@inject(TYPES.Managers.Player) playerManager: PlayerManager, @inject(TYPES.Managers.UpdatingQueueEmbed) updatingQueueEmbedManager: UpdatingQueueEmbedManager) {
+ constructor(@inject(TYPES.Managers.Player) playerManager: PlayerManager) {
this.playerManager = playerManager;
- this.updatingQueueEmbedManager = updatingQueueEmbedManager;
}
public async execute(interaction: CommandInteraction) {
- const embed = this.updatingQueueEmbedManager.get(interaction.guild!.id);
-
- await embed.createFromInteraction(interaction);
- }
-
- public async handleButtonInteraction(interaction: ButtonInteraction) {
const player = this.playerManager.get(interaction.guild!.id);
- const embed = this.updatingQueueEmbedManager.get(interaction.guild!.id);
-
- const buttonId = interaction.customId as keyof typeof this.handledButtonIds;
-
- // Not entirely sure why this is necessary.
- // We don't wait for the Promise to resolve here to avoid blocking the
- // main logic. However, we need to wait for the Promise to be resolved before
- // throwing as otherwise a race condition pops up when bot.ts tries updating
- // the interaction.
- const deferedUpdatePromise = interaction.deferUpdate();
-
- try {
- switch (buttonId) {
- case BUTTON_IDS.TRACK_BACK:
- await player.back();
- break;
-
- case BUTTON_IDS.TRACK_FORWARD:
- await player.forward(1);
- break;
-
- case BUTTON_IDS.PAUSE:
- player.pause();
- break;
-
- case BUTTON_IDS.PLAY:
- await player.play();
- break;
-
- case BUTTON_IDS.PAGE_BACK:
- await embed.pageBack();
- break;
-
- case BUTTON_IDS.PAGE_FORWARD:
- await embed.pageForward();
- break;
- default:
- throw new Error('unknown customId');
- }
- } catch (error: unknown) {
- await deferedUpdatePromise;
+ const embed = buildQueueEmbed(player, interaction.options.getInteger('page') ?? 1);
- throw error;
- }
+ await interaction.reply({embeds: [embed]});
}
}
diff --git a/src/commands/skip.ts b/src/commands/skip.ts
index dd24eb2..cfcd605 100644
--- a/src/commands/skip.ts
+++ b/src/commands/skip.ts
@@ -5,6 +5,7 @@ import PlayerManager from '../managers/player.js';
import Command from '.';
import errorMsg from '../utils/error-msg.js';
import {SlashCommandBuilder} from '@discordjs/builders';
+import {buildPlayingMessageEmbed} from '../utils/build-embed.js';
@injectable()
export default class implements Command {
@@ -35,9 +36,12 @@ export default class implements Command {
try {
await player.forward(numToSkip);
- await interaction.reply('keep \'er movin\'');
+ await interaction.reply({
+ content: 'keep \'er movin\'',
+ embeds: player.getCurrent() ? [buildPlayingMessageEmbed(player)] : [],
+ });
} catch (_: unknown) {
- await interaction.reply({content: errorMsg('invalid number of songs to skip'), ephemeral: true});
+ await interaction.reply({content: errorMsg('no song to skip to'), ephemeral: true});
}
}
}
diff --git a/src/commands/unskip.ts b/src/commands/unskip.ts
index 746cdf9..b1947b8 100644
--- a/src/commands/unskip.ts
+++ b/src/commands/unskip.ts
@@ -5,6 +5,7 @@ import PlayerManager from '../managers/player.js';
import errorMsg from '../utils/error-msg.js';
import Command from '.';
import {SlashCommandBuilder} from '@discordjs/builders';
+import {buildPlayingMessageEmbed} from '../utils/build-embed.js';
@injectable()
export default class implements Command {
@@ -25,8 +26,10 @@ export default class implements Command {
try {
await player.back();
-
- await interaction.reply('back \'er up\'');
+ await interaction.reply({
+ content: 'back \'er up\'',
+ embeds: player.getCurrent() ? [buildPlayingMessageEmbed(player)] : [],
+ });
} catch (_: unknown) {
await interaction.reply({
content: errorMsg('no song to go back to'),