diff --git a/package.json b/package.json index 00c4143..b364c7d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "@nestjs/platform-express": "^9.0.0", "date-fns": "^2.29.3", "discord.js": "^14.7.1", - "jellyfin-apiclient": "^1.10.0", "joi": "^17.7.0", "libsodium-wrappers": "^0.7.10", "reflect-metadata": "^0.1.13", diff --git a/src/clients/discord/discord.voice.service.ts b/src/clients/discord/discord.voice.service.ts index 79ca9bd..9489091 100644 --- a/src/clients/discord/discord.voice.service.ts +++ b/src/clients/discord/discord.voice.service.ts @@ -1,5 +1,7 @@ import { AudioPlayer, + AudioPlayerPausedState, + AudioPlayerStatus, AudioResource, createAudioPlayer, getVoiceConnection, @@ -68,14 +70,67 @@ export class DiscordVoiceService { this.createAndReturnOrGetAudioPlayer().play(resource); } + /** + * Pauses the current audio player + */ pause() { this.createAndReturnOrGetAudioPlayer().pause(); } + /** + * Unpauses the current audio player + */ unpause() { this.createAndReturnOrGetAudioPlayer().unpause(); } + /** + * Check if the current state is paused + * @returns The current pause state as a boolean + */ + isPaused() { + return ( + this.createAndReturnOrGetAudioPlayer().state.status === + AudioPlayerStatus.Paused + ); + } + + /** + * Checks if the current state is paused or not and toggles the states to the opposite. + * @returns The new paused state - true: paused, false: unpaused + */ + togglePaused(): boolean { + if (this.isPaused()) { + this.unpause(); + return true; + } + + this.pause(); + return false; + } + + disconnect(): GenericTryHandler { + if (this.voiceConnection === undefined) { + return { + success: false, + reply: { + embeds: [ + this.discordMessageService.buildErrorMessage({ + title: 'Unable to disconnect from voice channel', + description: 'I am currently not connected to any voice channels', + }), + ], + }, + }; + } + + this.voiceConnection.destroy(); + return { + success: true, + reply: {}, + }; + } + disconnectGracefully() { const connections = getVoiceConnections(); this.logger.debug( diff --git a/src/commands/current.command.ts b/src/commands/current.command.ts index c678fad..e8f93eb 100644 --- a/src/commands/current.command.ts +++ b/src/commands/current.command.ts @@ -1,23 +1,25 @@ import { TransformPipe } from '@discord-nestjs/common'; -import { - Command, - DiscordTransformedCommand, - TransformedCommandExecutionContext, - UsePipes, -} from '@discord-nestjs/core'; -import { InteractionReplyOptions } from 'discord.js'; +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: 'current', description: 'Print the current track information', }) @UsePipes(TransformPipe) -export class CurrentTrackCommand implements DiscordTransformedCommand { - handler( - dto: unknown, - executionContext: TransformedCommandExecutionContext, - ): InteractionReplyOptions | string { - return 'nice'; +export class CurrentTrackCommand implements DiscordCommand { + constructor(private readonly discordMessageService: DiscordMessageService) {} + + handler(interaction: CommandInteraction): GenericCustomReply { + return { + embeds: [ + this.discordMessageService.buildErrorMessage({ + title: 'NOT IMPLEMENTED', + }), + ], + }; } } diff --git a/src/commands/disconnect.command.ts b/src/commands/disconnect.command.ts index 46fcccc..6474d8e 100644 --- a/src/commands/disconnect.command.ts +++ b/src/commands/disconnect.command.ts @@ -1,13 +1,10 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; -import { - CommandInteraction, - EmbedBuilder, - InteractionReplyOptions, -} from 'discord.js'; -import { getVoiceConnection } from '@discordjs/voice'; -import { DefaultJellyfinColor, ErrorJellyfinColor } from '../types/colors'; +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', @@ -15,40 +12,23 @@ import { DefaultJellyfinColor, ErrorJellyfinColor } from '../types/colors'; }) @UsePipes(TransformPipe) export class DisconnectCommand implements DiscordCommand { - handler(interaction: CommandInteraction): InteractionReplyOptions | string { - const connection = getVoiceConnection(interaction.guildId); + constructor( + private readonly discordVoiceService: DiscordVoiceService, + private readonly discordMessageService: DiscordMessageService, + ) {} - if (!connection) { - return { - embeds: [ - new EmbedBuilder() - .setColor(ErrorJellyfinColor) - .setAuthor({ - name: 'Unable to disconnect from voice channel', - iconURL: - 'https://github.com/manuel-rw/jellyfin-discord-music-bot/blob/nestjs-migration/images/icons/alert-circle.png?raw=true', - }) - .setDescription( - 'I am currently not connected to any voice channels', - ) - .toJSON(), - ], - }; - return; + handler(interaction: CommandInteraction): GenericCustomReply { + const disconnect = this.discordVoiceService.disconnect(); + + if (!disconnect.success) { + return disconnect.reply; } - connection.destroy(); - return { embeds: [ - new EmbedBuilder() - .setColor(DefaultJellyfinColor) - .setAuthor({ - name: 'Disconnected from your channel', - iconURL: - 'https://github.com/manuel-rw/jellyfin-discord-music-bot/blob/nestjs-migration/images/icons/circle-check.png?raw=true', - }) - .toJSON(), + this.discordMessageService.buildMessage({ + title: 'Disconnected from your channel', + }), ], }; } diff --git a/src/commands/help.command.ts b/src/commands/help.command.ts index f909a06..4ed6db8 100644 --- a/src/commands/help.command.ts +++ b/src/commands/help.command.ts @@ -1,30 +1,18 @@ import { TransformPipe } from '@discord-nestjs/common'; -import { - Command, - DiscordTransformedCommand, - TransformedCommandExecutionContext, - UsePipes, -} from '@discord-nestjs/core'; +import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; import { EmbedBuilder } from '@discordjs/builders'; -import { InteractionReplyOptions, MessagePayload } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; import { DefaultJellyfinColor } from 'src/types/colors'; +import { GenericCustomReply } from '../models/generic-try-handler'; @Command({ name: 'help', description: 'Get help if you're having problems with this bot', }) @UsePipes(TransformPipe) -export class HelpCommand implements DiscordTransformedCommand { - handler( - dto: unknown, - executionContext: TransformedCommandExecutionContext, - ): - | string - | void - | MessagePayload - | InteractionReplyOptions - | Promise { +export class HelpCommand implements DiscordCommand { + handler(commandInteraction: CommandInteraction): GenericCustomReply { return { embeds: [ new EmbedBuilder() diff --git a/src/commands/summon.command.ts b/src/commands/summon.command.ts index 71b0f4a..3fbb7b4 100644 --- a/src/commands/summon.command.ts +++ b/src/commands/summon.command.ts @@ -1,15 +1,11 @@ import { TransformPipe } from '@discord-nestjs/common'; import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core'; -import { joinVoiceChannel } from '@discordjs/voice'; import { Logger } from '@nestjs/common'; -import { - CommandInteraction, - EmbedBuilder, - GuildMember, - InteractionReplyOptions, -} from 'discord.js'; -import { DefaultJellyfinColor, ErrorJellyfinColor } from '../types/colors'; +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', @@ -19,45 +15,28 @@ import { DefaultJellyfinColor, ErrorJellyfinColor } from '../types/colors'; export class SummonCommand implements DiscordCommand { private readonly logger = new Logger(SummonCommand.name); - handler(interaction: CommandInteraction): InteractionReplyOptions | string { + constructor( + private readonly discordVoiceService: DiscordVoiceService, + private readonly discordMessageService: DiscordMessageService, + ) {} + + handler(interaction: CommandInteraction): GenericCustomReply { const guildMember = interaction.member as GuildMember; - if (guildMember.voice.channel === null) { - return { - embeds: [ - new EmbedBuilder() - .setColor(ErrorJellyfinColor) - .setAuthor({ - name: 'Unable to join your channel', - iconURL: - 'https://github.com/manuel-rw/jellyfin-discord-music-bot/blob/nestjs-migration/images/icons/alert-circle.png?raw=true', - }) - .setDescription( - 'You are in a channel, I am either unabelt to connect to or you aren&apost in a channel yet', - ) - .toJSON(), - ], - }; + const tryResult = + this.discordVoiceService.tryJoinChannelAndEstablishVoiceConnection( + guildMember, + ); + + if (!tryResult.success) { + return tryResult.reply; } - const channel = guildMember.voice.channel; - - joinVoiceChannel({ - channelId: channel.id, - adapterCreator: channel.guild.voiceAdapterCreator, - guildId: channel.guildId, - }); - return { embeds: [ - new EmbedBuilder() - .setColor(DefaultJellyfinColor) - .setAuthor({ - name: 'Joined your voicehannel', - iconURL: - 'https://github.com/manuel-rw/jellyfin-discord-music-bot/blob/nestjs-migration/images/icons/circle-check.png?raw=true&test=a', - }) - .toJSON(), + this.discordMessageService.buildMessage({ + title: 'Joined your voicehannel', + }), ], }; } diff --git a/src/models/generic-try-handler.ts b/src/models/generic-try-handler.ts index a8e5ab4..47183c7 100644 --- a/src/models/generic-try-handler.ts +++ b/src/models/generic-try-handler.ts @@ -2,8 +2,10 @@ import { InteractionReplyOptions } from 'discord.js'; export interface GenericTryHandler { success: boolean; - reply: - | string - | InteractionReplyOptions - | Promise; + reply: GenericCustomReply; } + +export type GenericCustomReply = + | string + | InteractionReplyOptions + | Promise; diff --git a/yarn.lock b/yarn.lock index 06e28ac..8687676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4437,13 +4437,6 @@ __metadata: languageName: node linkType: hard -"jellyfin-apiclient@npm:^1.10.0": - version: 1.10.0 - resolution: "jellyfin-apiclient@npm:1.10.0" - checksum: 494161b3acf2e17db22c50fd13c637890985fa95aced5f4161045ae7a1eb80374625e83f2bd286a5a0f06847e06fab1f4dcc2d93f603bc1b033344adb12a7f7b - languageName: node - linkType: hard - "jellyfin-discord-music-bot@workspace:.": version: 0.0.0-use.local resolution: "jellyfin-discord-music-bot@workspace:." @@ -4472,7 +4465,6 @@ __metadata: eslint: ^8.0.1 eslint-config-prettier: ^8.3.0 eslint-plugin-prettier: ^4.0.0 - jellyfin-apiclient: ^1.10.0 jest: 28.1.3 joi: ^17.7.0 libsodium-wrappers: ^0.7.10