From 066d27b351eec6268b5d2eec01877bfd92c485ae Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sun, 22 Jan 2023 21:53:50 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20from=20synchron?= =?UTF-8?q?ous=20replies=20to=20asynchronous=20deferred=20replies=20with?= =?UTF-8?q?=20edits=20#64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/disconnect.command.ts | 19 +++++++++++----- src/commands/help.command.ts | 8 +++---- src/commands/next.command.ts | 15 +++++-------- src/commands/pause.command.ts | 9 +++----- src/commands/play.comands.ts | 36 +++++++++++++++++++++--------- src/commands/playlist.command.ts | 12 +++++----- src/commands/previous.command.ts | 15 +++++-------- src/commands/status.command.ts | 24 ++++++++++---------- src/commands/stop.command.ts | 21 ++++++++++------- src/commands/summon.command.ts | 18 ++++++++++----- src/models/generic-try-handler.ts | 9 ++------ 11 files changed, 101 insertions(+), 85 deletions(-) diff --git a/src/commands/disconnect.command.ts b/src/commands/disconnect.command.ts index b0eb637..8f3bdbb 100644 --- a/src/commands/disconnect.command.ts +++ b/src/commands/disconnect.command.ts @@ -4,7 +4,6 @@ import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { DiscordVoiceService } from '../clients/discord/discord.voice.service'; -import { GenericCustomReply } from '../models/generic-try-handler'; @Command({ name: 'disconnect', @@ -17,20 +16,28 @@ export class DisconnectCommand implements DiscordCommand { private readonly discordMessageService: DiscordMessageService, ) {} - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handler(interaction: CommandInteraction): GenericCustomReply { + async handler(interaction: CommandInteraction): Promise { + await interaction.reply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: 'Disconnecting...', + }), + ], + }); + const disconnect = this.discordVoiceService.disconnect(); if (!disconnect.success) { - return disconnect.reply; + await interaction.editReply(disconnect.reply); + return; } - return { + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Disconnected from your channel', }), ], - }; + }); } } diff --git a/src/commands/help.command.ts b/src/commands/help.command.ts index 46827c6..6f04804 100644 --- a/src/commands/help.command.ts +++ b/src/commands/help.command.ts @@ -3,7 +3,6 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; -import { GenericCustomReply } from '../models/generic-try-handler'; @Command({ name: 'help', @@ -13,9 +12,8 @@ import { GenericCustomReply } from '../models/generic-try-handler'; export class HelpCommand implements DiscordCommand { constructor(private readonly discordMessageService: DiscordMessageService) {} - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handler(commandInteraction: CommandInteraction): GenericCustomReply { - return { + async handler(interaction: CommandInteraction): Promise { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Jellyfin Discord Bot', @@ -40,6 +38,6 @@ export class HelpCommand implements DiscordCommand { }, }), ], - }; + }); } } diff --git a/src/commands/next.command.ts b/src/commands/next.command.ts index 04eb4a3..b5b9096 100644 --- a/src/commands/next.command.ts +++ b/src/commands/next.command.ts @@ -1,7 +1,7 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; -import { CommandInteraction, InteractionReplyOptions } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { PlaybackService } from '../playback/playback.service'; @@ -16,26 +16,23 @@ export class SkipTrackCommand implements DiscordCommand { private readonly discordMessageService: DiscordMessageService, ) {} - handler( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interactionCommand: CommandInteraction, - ): InteractionReplyOptions | string { + async handler(interaction: CommandInteraction): Promise { if (!this.playbackService.nextTrack()) { - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildErrorMessage({ title: 'There is no next track', }), ], - }; + }); } - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Skipped to the next track', }), ], - }; + }); } } diff --git a/src/commands/pause.command.ts b/src/commands/pause.command.ts index 1216663..38d858e 100644 --- a/src/commands/pause.command.ts +++ b/src/commands/pause.command.ts @@ -16,18 +16,15 @@ export class PausePlaybackCommand implements DiscordCommand { private readonly discordMessageService: DiscordMessageService, ) {} - handler( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - commandInteraction: CommandInteraction, - ): string | InteractionReplyOptions { + async handler(interaction: CommandInteraction): Promise { const shouldBePaused = this.discordVoiceService.togglePaused(); - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: shouldBePaused ? 'Paused' : 'Unpaused', }), ], - }; + }); } } diff --git a/src/commands/play.comands.ts b/src/commands/play.comands.ts index af451bb..4c6fcc3 100644 --- a/src/commands/play.comands.ts +++ b/src/commands/play.comands.ts @@ -21,6 +21,7 @@ import { TrackRequestDto } from '../models/track-request.dto'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; +import { RemoteImageResult } from '@jellyfin/sdk/lib/generated-client/models'; import { DiscordVoiceService } from '../clients/discord/discord.voice.service'; import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service'; import { @@ -28,7 +29,6 @@ import { searchResultAsJellyfinAudio, } from '../models/jellyfinAudioItems'; import { PlaybackService } from '../playback/playback.service'; -import { RemoteImageResult } from '@jellyfin/sdk/lib/generated-client/models'; import { chooseSuitableRemoteImage } from '../utils/remoteImages/remoteImages'; import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils'; @@ -52,9 +52,10 @@ export class PlayItemCommand async handler( @Payload() dto: TrackRequestDto, - // eslint-disable-next-line @typescript-eslint/no-unused-vars executionContext: TransformedCommandExecutionContext, ): Promise { + await executionContext.interaction.deferReply(); + const items = await this.jellyfinSearchService.search(dto.search); const parsedItems = await Promise.all( items.map( @@ -68,14 +69,15 @@ export class PlayItemCommand ); if (parsedItems.length === 0) { - return { + await executionContext.interaction.followUp({ embeds: [ this.discordMessageService.buildErrorMessage({ title: 'No results for your search query found', description: `I was not able to find any matches for your query \`\`${dto.search}\`\`. Please check that I have access to the desired libraries and that your query is not misspelled`, }), ], - }; + }); + return; } const firstItems = parsedItems.slice(0, 10); @@ -107,7 +109,7 @@ export class PlayItemCommand emoji: item.getEmoji(), })); - return { + await executionContext.interaction.followUp({ embeds: [ this.discordMessageService.buildMessage({ title: 'Jellyfin Search Results', @@ -126,7 +128,7 @@ export class PlayItemCommand ], }, ], - }; + }); } @On(Events.InteractionCreate) @@ -144,6 +146,18 @@ export class PlayItemCommand return; } + await interaction.deferUpdate(); + + await interaction.editReply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: 'Applying your selection to the queue...', + description: `This may take a moment. Please wait`, + }), + ], + components: [], + }); + const guildMember = interaction.member as GuildMember; const tryResult = @@ -156,7 +170,7 @@ export class PlayItemCommand `Unable to process select result because the member was not in a voice channcel`, ); const replyOptions = tryResult.reply as InteractionReplyOptions; - await interaction.update({ + await interaction.editReply({ embeds: replyOptions.embeds, content: undefined, components: [], @@ -183,7 +197,7 @@ export class PlayItemCommand bitrate, remoteImagesOfCurrentAlbum, ); - interaction.update({ + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: item.Name, @@ -212,7 +226,7 @@ export class PlayItemCommand remoteImages, ); }); - interaction.update({ + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: `Added ${album.TotalRecordCount} items from your album`, @@ -247,7 +261,7 @@ export class PlayItemCommand } const bestPlaylistRemoteImage = chooseSuitableRemoteImage(addedRemoteImages); - interaction.update({ + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: `Added ${playlist.TotalRecordCount} items from your playlist`, @@ -267,7 +281,7 @@ export class PlayItemCommand }); break; default: - interaction.update({ + await interaction.editReply({ embeds: [ this.discordMessageService.buildErrorMessage({ title: 'Unable to process your selection', diff --git a/src/commands/playlist.command.ts b/src/commands/playlist.command.ts index de409b7..a0e2b0f 100644 --- a/src/commands/playlist.command.ts +++ b/src/commands/playlist.command.ts @@ -3,7 +3,6 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; -import { GenericCustomReply } from '../models/generic-try-handler'; import { PlaybackService } from '../playback/playback.service'; import { Constants } from '../utils/constants'; import { chooseSuitableRemoteImageFromTrack } from '../utils/remoteImages/remoteImages'; @@ -21,12 +20,11 @@ export class PlaylistCommand implements DiscordCommand { private readonly playbackService: PlaybackService, ) {} - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handler(interaction: CommandInteraction): GenericCustomReply { + async handler(interaction: CommandInteraction): Promise { const playList = this.playbackService.getPlaylist(); if (playList.tracks.length === 0) { - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Your Playlist', @@ -34,7 +32,7 @@ export class PlaylistCommand implements DiscordCommand { 'You do not have any tracks in your playlist.\nUse the play command to add new tracks to your playlist', }), ], - }; + }); } const tracklist = playList.tracks @@ -63,7 +61,7 @@ export class PlaylistCommand implements DiscordCommand { const activeTrack = this.playbackService.getActiveTrack(); const remoteImage = chooseSuitableRemoteImageFromTrack(activeTrack.track); - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Your Playlist', @@ -77,7 +75,7 @@ export class PlaylistCommand implements DiscordCommand { }, }), ], - }; + }); } private getListPoint(isCurrent: boolean, index: number) { diff --git a/src/commands/previous.command.ts b/src/commands/previous.command.ts index 7d89f3a..3d7330f 100644 --- a/src/commands/previous.command.ts +++ b/src/commands/previous.command.ts @@ -1,7 +1,7 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; -import { CommandInteraction, InteractionReplyOptions } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { PlaybackService } from '../playback/playback.service'; @@ -16,26 +16,23 @@ export class PreviousTrackCommand implements DiscordCommand { private readonly discordMessageService: DiscordMessageService, ) {} - handler( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - dcommandInteraction: CommandInteraction, - ): InteractionReplyOptions | string { + async handler(interaction: CommandInteraction): Promise { if (!this.playbackService.previousTrack()) { - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildErrorMessage({ title: 'There is no previous track', }), ], - }; + }); } - return { + await interaction.reply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Went to previous track', }), ], - }; + }); } } diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts index 0b7b752..f94c2cf 100644 --- a/src/commands/status.command.ts +++ b/src/commands/status.command.ts @@ -6,12 +6,7 @@ import { InjectDiscordClient, UsePipes, } from '@discord-nestjs/core'; -import { - Client, - CommandInteraction, - InteractionReplyOptions, - Status, -} from 'discord.js'; +import { Client, CommandInteraction, Status } from 'discord.js'; import { formatDuration, intervalToDuration } from 'date-fns'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; @@ -33,10 +28,15 @@ export class StatusCommand implements DiscordCommand { private readonly jellyfinService: JellyfinService, ) {} - async handler( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - commandInteraction: CommandInteraction, - ): Promise { + async handler(interaction: CommandInteraction): Promise { + await interaction.reply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: 'Retrieving status information...', + }), + ], + }); + const ping = this.client.ws.ping; const status = Status[this.client.ws.status]; @@ -49,7 +49,7 @@ export class StatusCommand implements DiscordCommand { const jellyfinSystemApi = getSystemApi(this.jellyfinService.getApi()); const jellyfinSystemInformation = await jellyfinSystemApi.getSystemInfo(); - return { + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Discord Bot Status', @@ -90,6 +90,6 @@ export class StatusCommand implements DiscordCommand { }, }), ], - }; + }); } } diff --git a/src/commands/stop.command.ts b/src/commands/stop.command.ts index 06def1f..8a17328 100644 --- a/src/commands/stop.command.ts +++ b/src/commands/stop.command.ts @@ -4,7 +4,6 @@ import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; import { CommandInteraction } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { DiscordVoiceService } from '../clients/discord/discord.voice.service'; -import { GenericCustomReply } from '../models/generic-try-handler'; import { PlaybackService } from '../playback/playback.service'; @Command({ @@ -19,22 +18,28 @@ export class StopPlaybackCommand implements DiscordCommand { private readonly discordVoiceService: DiscordVoiceService, ) {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - handler(CommandInteraction: CommandInteraction): GenericCustomReply { - const hasActiveTrack = this.playbackService.hasActiveTrack() - const title = hasActiveTrack ? 'Playback stopped successfully' : 'Playback failed to stop' - const description = hasActiveTrack ? 'In addition, your playlist has been cleared' : 'There is no active track in the queue' + async handler(interaction: CommandInteraction): Promise { + const hasActiveTrack = this.playbackService.hasActiveTrack(); + const title = hasActiveTrack + ? 'Playback stopped successfully' + : 'Playback failed to stop'; + const description = hasActiveTrack + ? 'In addition, your playlist has been cleared' + : 'There is no active track in the queue'; if (hasActiveTrack) { this.playbackService.clear(); this.discordVoiceService.stop(false); } - return { + await interaction.reply({ embeds: [ - this.discordMessageService[hasActiveTrack ? 'buildMessage' : 'buildErrorMessage']({ + this.discordMessageService[ + hasActiveTrack ? 'buildMessage' : 'buildErrorMessage' + ]({ title: title, description: description, }), ], - }; + }); } } diff --git a/src/commands/summon.command.ts b/src/commands/summon.command.ts index 82b210e..08140d2 100644 --- a/src/commands/summon.command.ts +++ b/src/commands/summon.command.ts @@ -5,7 +5,6 @@ import { Logger } from '@nestjs/common'; import { CommandInteraction, GuildMember } from 'discord.js'; import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { DiscordVoiceService } from '../clients/discord/discord.voice.service'; -import { GenericCustomReply } from '../models/generic-try-handler'; @Command({ name: 'summon', @@ -20,7 +19,15 @@ export class SummonCommand implements DiscordCommand { private readonly discordMessageService: DiscordMessageService, ) {} - handler(interaction: CommandInteraction): GenericCustomReply { + async handler(interaction: CommandInteraction): Promise { + await interaction.reply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: 'Joining your voice channel...', + }), + ], + }); + const guildMember = interaction.member as GuildMember; const tryResult = @@ -29,10 +36,11 @@ export class SummonCommand implements DiscordCommand { ); if (!tryResult.success) { - return tryResult.reply; + interaction.editReply(tryResult.reply); + return; } - return { + await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ title: 'Joined your voicehannel', @@ -40,6 +48,6 @@ export class SummonCommand implements DiscordCommand { "I'm ready to play media. Use ``Cast to device`` in Jellyfin or the ``/play`` command to get started.", }), ], - }; + }); } } diff --git a/src/models/generic-try-handler.ts b/src/models/generic-try-handler.ts index 47183c7..78130a9 100644 --- a/src/models/generic-try-handler.ts +++ b/src/models/generic-try-handler.ts @@ -1,11 +1,6 @@ -import { InteractionReplyOptions } from 'discord.js'; +import { InteractionEditReplyOptions, MessagePayload } from 'discord.js'; export interface GenericTryHandler { success: boolean; - reply: GenericCustomReply; + reply: string | MessagePayload | InteractionEditReplyOptions; } - -export type GenericCustomReply = - | string - | InteractionReplyOptions - | Promise;