diff --git a/package.json b/package.json index 2e6e0e1..3b28725 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/core": "^9.0.0", "@nestjs/event-emitter": "^1.3.1", "@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", diff --git a/src/commands/command.module.ts b/src/commands/command.module.ts index a6f6066..c014edd 100644 --- a/src/commands/command.module.ts +++ b/src/commands/command.module.ts @@ -3,11 +3,30 @@ import { DiscordModule } from '@discord-nestjs/core'; import { HelpCommand } from './help.command'; import { StatusCommand } from './status.command'; +import { CurrentTrackCommand } from './current.command'; +import { DisconnectCommand } from './disconnect.command'; +import { EnqueueCommand } from './enqueue.command'; +import { PausePlaybackCommand } from './pause.command'; +import { PlayCommand } from './play.command'; +import { SkipTrackCommand } from './skip.command'; +import { StopPlaybackCommand } from './stop.command'; +import { SummonCommand } from './summon.command'; @Module({ imports: [DiscordModule.forFeature()], controllers: [], - providers: [HelpCommand, StatusCommand], + providers: [ + HelpCommand, + StatusCommand, + CurrentTrackCommand, + DisconnectCommand, + EnqueueCommand, + PausePlaybackCommand, + PlayCommand, + SkipTrackCommand, + StopPlaybackCommand, + SummonCommand, + ], exports: [], }) export class CommandModule {} diff --git a/src/commands/current.command.ts b/src/commands/current.command.ts new file mode 100644 index 0000000..c678fad --- /dev/null +++ b/src/commands/current.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@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'; + } +} diff --git a/src/commands/disconnect.command.ts b/src/commands/disconnect.command.ts new file mode 100644 index 0000000..5f596e3 --- /dev/null +++ b/src/commands/disconnect.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'disconnect', + description: 'Join your current voice channel', +}) +@UsePipes(TransformPipe) +export class DisconnectCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/enqueue.command.ts b/src/commands/enqueue.command.ts new file mode 100644 index 0000000..824a548 --- /dev/null +++ b/src/commands/enqueue.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'enqueue', + description: 'Enqueue a track to the current playlist', +}) +@UsePipes(TransformPipe) +export class EnqueueCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/pause.command.ts b/src/commands/pause.command.ts new file mode 100644 index 0000000..c15707c --- /dev/null +++ b/src/commands/pause.command.ts @@ -0,0 +1,25 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'pause', + description: 'Pause or resume the playback of the current track', +}) +@UsePipes(TransformPipe) +export class PausePlaybackCommand + implements DiscordTransformedCommand +{ + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/play.command.ts b/src/commands/play.command.ts new file mode 100644 index 0000000..57219b5 --- /dev/null +++ b/src/commands/play.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'play', + description: 'Immediately play a track', +}) +@UsePipes(TransformPipe) +export class PlayCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/skip.command.ts b/src/commands/skip.command.ts new file mode 100644 index 0000000..a1ae7e3 --- /dev/null +++ b/src/commands/skip.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'skip', + description: 'Skip the current track', +}) +@UsePipes(TransformPipe) +export class SkipTrackCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts index a478e8d..b5c3124 100644 --- a/src/commands/status.command.ts +++ b/src/commands/status.command.ts @@ -8,9 +8,12 @@ import { UsePipes, } from '@discord-nestjs/core'; import { EmbedBuilder } from '@discordjs/builders'; -import { Client, InteractionReplyOptions } from 'discord.js'; +import { Client, InteractionReplyOptions, Status } from 'discord.js'; import { DefaultJellyfinColor } from 'src/types/colors'; +import { formatDuration, intervalToDuration } from 'date-fns'; +import { Constants } from 'src/utils/constants'; + @Command({ name: 'status', description: 'Display the current status for troubleshooting', @@ -27,6 +30,13 @@ export class StatusCommand implements DiscordTransformedCommand { executionContext: TransformedCommandExecutionContext, ): InteractionReplyOptions { const ping = this.client.ws.ping; + const status = Status[this.client.ws.status]; + + const interval = intervalToDuration({ + start: this.client.uptime, + end: 0, + }); + const formattedDuration = formatDuration(interval); return { embeds: [ @@ -34,14 +44,24 @@ export class StatusCommand implements DiscordTransformedCommand { .setTitle('Online and ready') .setColor(DefaultJellyfinColor) .addFields([ + { + name: 'Version', + value: Constants.Metadata.Version, + inline: false, + }, { name: 'Ping', value: `${ping}ms`, inline: true, }, { - name: 'Source code', - value: 'https://github.com/manuel-rw/jellyfin-discord-music-bot', + name: 'Status', + value: `${status}`, + inline: true, + }, + { + name: 'Uptime', + value: `${formattedDuration}`, inline: true, }, ]) diff --git a/src/commands/stop.command.ts b/src/commands/stop.command.ts new file mode 100644 index 0000000..c5d9e42 --- /dev/null +++ b/src/commands/stop.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'stop', + description: 'Stop playback entirely and clear the current playlist', +}) +@UsePipes(TransformPipe) +export class StopPlaybackCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/src/commands/summon.command.ts b/src/commands/summon.command.ts new file mode 100644 index 0000000..2bde645 --- /dev/null +++ b/src/commands/summon.command.ts @@ -0,0 +1,23 @@ +import { TransformPipe } from '@discord-nestjs/common'; + +import { + Command, + DiscordTransformedCommand, + TransformedCommandExecutionContext, + UsePipes, +} from '@discord-nestjs/core'; +import { InteractionReplyOptions } from 'discord.js'; + +@Command({ + name: 'summon', + description: 'Join your current voice channel', +}) +@UsePipes(TransformPipe) +export class SummonCommand implements DiscordTransformedCommand { + handler( + dto: unknown, + executionContext: TransformedCommandExecutionContext, + ): InteractionReplyOptions | string { + return 'nice'; + } +} diff --git a/yarn.lock b/yarn.lock index 306355c..d33eb53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2892,6 +2892,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^2.29.3": + version: 2.29.3 + resolution: "date-fns@npm:2.29.3" + checksum: e01cf5b62af04e05dfff921bb9c9933310ed0e1ae9a81eb8653452e64dc841acf7f6e01e1a5ae5644d0337e9a7f936175fd2cb6819dc122fdd9c5e86c56be484 + languageName: node + linkType: hard + "debug@npm:2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -4446,6 +4453,7 @@ __metadata: "@types/supertest": ^2.0.11 "@typescript-eslint/eslint-plugin": ^5.0.0 "@typescript-eslint/parser": ^5.0.0 + date-fns: ^2.29.3 discord.js: ^14.7.1 eslint: ^8.0.1 eslint-config-prettier: ^8.3.0