mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-24 18:41:57 +01:00
🔀 Version 0.0.4
This commit is contained in:
commit
e665ebec21
21
.github/dependabot.yml
vendored
21
.github/dependabot.yml
vendored
@ -10,8 +10,29 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "⬆️" # prefix with gitmoji
|
||||
include: "scope" # list updated dependencies in message
|
||||
assignees:
|
||||
- "manuel-rw"
|
||||
- package-ecosystem: "docker" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "🚀" # prefix with gitmoji
|
||||
include: "scope" # list updated dependencies in message
|
||||
assignees:
|
||||
- "manuel-rw"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: /.github
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "👷" # prefix with gitmoji
|
||||
include: "scope" # list updated dependencies in message
|
||||
assignees:
|
||||
- "manuel-rw"
|
||||
|
||||
|
852
package-lock.json
generated
852
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jellyfin-discord-music-bot",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"description": "",
|
||||
"author": "manuel-rw",
|
||||
"private": true,
|
||||
@ -38,33 +38,33 @@
|
||||
"joi": "^17.7.0",
|
||||
"libsodium-wrappers": "^0.7.10",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "^4.1.2",
|
||||
"rxjs": "^7.2.0",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
"@nestjs/cli": "^9.1.8",
|
||||
"@nestjs/schematics": "^9.0.0",
|
||||
"@nestjs/testing": "^9.0.0",
|
||||
"@types/cron": "^2.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "28.1.8",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
"eslint": "^8.32.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "28.1.3",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier": "^2.8.4",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "28.0.8",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"tsconfig-paths": "4.1.2",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"jest": {
|
||||
|
@ -19,7 +19,7 @@ import { UpdatesModule } from './updates/updates.module';
|
||||
ConfigModule.forRoot({
|
||||
validationSchema: Joi.object({
|
||||
DISCORD_CLIENT_TOKEN: Joi.string().required(),
|
||||
JELLYFIN_SERVER_ADDRESS: Joi.string().required(),
|
||||
JELLYFIN_SERVER_ADDRESS: Joi.string().uri().required(),
|
||||
JELLYFIN_AUTHENTICATION_USERNAME: Joi.string().required(),
|
||||
JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(),
|
||||
UPDATER_DISABLE_NOTIFICATIONS: Joi.boolean(),
|
||||
|
@ -58,6 +58,10 @@ export class JellyinPlaystateService {
|
||||
private async onPlaybackPaused(isPaused: boolean) {
|
||||
const activeTrack = this.playbackService.getActiveTrack();
|
||||
|
||||
if (!activeTrack) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.playstateApi.reportPlaybackProgress({
|
||||
playbackProgressInfo: {
|
||||
ItemId: activeTrack.track.jellyfinId,
|
||||
@ -69,6 +73,10 @@ export class JellyinPlaystateService {
|
||||
@OnEvent('playback.state.stop')
|
||||
private async onPlaybackStopped() {
|
||||
const activeTrack = this.playbackService.getActiveTrack();
|
||||
|
||||
if (!activeTrack) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
|
@ -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<void> {
|
||||
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',
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<void> {
|
||||
await interaction.reply({
|
||||
embeds: [
|
||||
this.discordMessageService.buildMessage({
|
||||
title: 'Jellyfin Discord Bot',
|
||||
@ -40,6 +38,6 @@ export class HelpCommand implements DiscordCommand {
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<void> {
|
||||
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',
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<void> {
|
||||
const shouldBePaused = this.discordVoiceService.togglePaused();
|
||||
|
||||
return {
|
||||
await interaction.reply({
|
||||
embeds: [
|
||||
this.discordMessageService.buildMessage({
|
||||
title: shouldBePaused ? 'Paused' : 'Unpaused',
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,9 +29,8 @@ 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';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
||||
import { chooseSuitableRemoteImage } from '../utils/remoteImages/remoteImages';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||
|
||||
@Command({
|
||||
name: 'play',
|
||||
@ -52,9 +52,10 @@ export class PlayItemCommand
|
||||
|
||||
async handler(
|
||||
@Payload() dto: TrackRequestDto,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
executionContext: TransformedCommandExecutionContext<any>,
|
||||
): Promise<InteractionReplyOptions | string> {
|
||||
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',
|
||||
|
@ -3,11 +3,10 @@ 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';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
||||
import { chooseSuitableRemoteImageFromTrack } from '../utils/remoteImages/remoteImages';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||
import { formatMillisecondsAsHumanReadable } from '../utils/timeUtils';
|
||||
|
||||
@Command({
|
||||
@ -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<void> {
|
||||
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) {
|
||||
|
@ -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<void> {
|
||||
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',
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<string | InteractionReplyOptions> {
|
||||
async handler(interaction: CommandInteraction): Promise<void> {
|
||||
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 {
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,18 +18,28 @@ export class StopPlaybackCommand implements DiscordCommand {
|
||||
private readonly discordVoiceService: DiscordVoiceService,
|
||||
) {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
handler(CommandInteraction: CommandInteraction): GenericCustomReply {
|
||||
this.playbackService.clear();
|
||||
this.discordVoiceService.stop(false);
|
||||
async handler(interaction: CommandInteraction): Promise<void> {
|
||||
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.buildMessage({
|
||||
title: 'Playlist cleared',
|
||||
description:
|
||||
'Playback was stopped and your playlist has been cleared',
|
||||
this.discordMessageService[
|
||||
hasActiveTrack ? 'buildMessage' : 'buildErrorMessage'
|
||||
]({
|
||||
title: title,
|
||||
description: description,
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,9 @@ export class SummonCommand implements DiscordCommand {
|
||||
private readonly discordMessageService: DiscordMessageService,
|
||||
) {}
|
||||
|
||||
handler(interaction: CommandInteraction): GenericCustomReply {
|
||||
async handler(interaction: CommandInteraction): Promise<void> {
|
||||
await interaction.deferReply();
|
||||
|
||||
const guildMember = interaction.member as GuildMember;
|
||||
|
||||
const tryResult =
|
||||
@ -29,10 +30,11 @@ export class SummonCommand implements DiscordCommand {
|
||||
);
|
||||
|
||||
if (!tryResult.success) {
|
||||
return tryResult.reply;
|
||||
await interaction.editReply(tryResult.reply);
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
this.discordMessageService.buildMessage({
|
||||
title: 'Joined your voicehannel',
|
||||
@ -40,6 +42,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.",
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
} from '@nestjs/terminus';
|
||||
import { HealthCheckExecutor } from '@nestjs/terminus/dist/health-check/health-check-executor.service';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { useDefaultMockerToken } from '../utils/tests';
|
||||
import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken';
|
||||
import { HealthController } from './health.controller';
|
||||
import { DiscordHealthIndicator } from './indicators/discord.indicator';
|
||||
import { JellyfinHealthIndicator } from './indicators/jellyfin.indicator';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { HealthIndicatorResult } from '@nestjs/terminus';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { JellyfinService } from '../../clients/jellyfin/jellyfin.service';
|
||||
import { useDefaultMockerToken } from '../../utils/tests';
|
||||
import { useDefaultMockerToken } from '../../utils/tests/defaultMockerToken';
|
||||
import { JellyfinHealthIndicator } from './jellyfin.indicator';
|
||||
|
||||
describe('JellyfinHealthIndicator', () => {
|
||||
|
@ -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<string | InteractionReplyOptions>;
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
||||
import { Track } from '../types/track';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { JellyfinSearchService } from '../clients/jellyfin/jellyfin.search.service';
|
||||
|
110
src/updates/updates.service.spec.ts
Normal file
110
src/updates/updates.service.spec.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import axios from 'axios';
|
||||
import { Client, GuildMember } from 'discord.js';
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { GithubRelease } from '../models/github-release';
|
||||
import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken';
|
||||
import { UpdatesService } from './updates.service';
|
||||
|
||||
// mock axios: https://stackoverflow.com/questions/51275434/type-of-axios-mock-using-jest-typescript/55351900#55351900
|
||||
jest.mock('axios');
|
||||
const mockedAxios = axios as jest.MockedFunction<typeof axios>;
|
||||
|
||||
describe('UpdatesService', () => {
|
||||
const OLD_ENV = process.env;
|
||||
|
||||
let updatesService: UpdatesService;
|
||||
let discordClient: Client;
|
||||
let discordMessageService: DiscordMessageService;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetModules();
|
||||
process.env = { ...OLD_ENV };
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
providers: [UpdatesService],
|
||||
})
|
||||
.useMocker((token) => {
|
||||
if (token === DiscordMessageService) {
|
||||
return {
|
||||
client: jest.fn().mockReturnValue({}),
|
||||
buildMessage: jest.fn(),
|
||||
buildErrorMessage: jest.fn(),
|
||||
} as DiscordMessageService;
|
||||
}
|
||||
|
||||
if (token === Client || token == '__inject_discord_client__') {
|
||||
return {
|
||||
guilds: {
|
||||
cache: [
|
||||
{
|
||||
fetchOwner: () =>
|
||||
({
|
||||
send: jest.fn(),
|
||||
user: { tag: 'test' },
|
||||
} as unknown as GuildMember),
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return useDefaultMockerToken(token);
|
||||
})
|
||||
.compile();
|
||||
|
||||
updatesService = moduleRef.get<UpdatesService>(UpdatesService);
|
||||
discordClient = moduleRef.get<Client>('__inject_discord_client__');
|
||||
discordMessageService = moduleRef.get<DiscordMessageService>(
|
||||
DiscordMessageService,
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = OLD_ENV;
|
||||
});
|
||||
|
||||
it('handleCronShouldNotNotifyWhenDisabledViaEnvironmentVariable', async () => {
|
||||
process.env.UPDATER_DISABLE_NOTIFICATIONS = 'true';
|
||||
mockedAxios.mockResolvedValue({
|
||||
data: {
|
||||
html_url: 'https://github.com',
|
||||
name: 'testing release',
|
||||
tag_name: '0.0.6',
|
||||
published_at: '2023-01-09T22:11:25Z',
|
||||
} as GithubRelease,
|
||||
status: 200,
|
||||
statusText: 'Ok',
|
||||
headers: {},
|
||||
config: {},
|
||||
});
|
||||
|
||||
await updatesService.handleCron();
|
||||
|
||||
expect(mockedAxios).not.toHaveBeenCalled();
|
||||
expect(discordMessageService.buildMessage).not.toHaveBeenCalled();
|
||||
expect(discordMessageService.buildErrorMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handleCronShouldNotifyWhenNewRelease', async () => {
|
||||
process.env.UPDATER_DISABLE_NOTIFICATIONS = 'false';
|
||||
|
||||
mockedAxios.mockResolvedValue({
|
||||
data: {
|
||||
html_url: 'https://github.com',
|
||||
name: 'testing release',
|
||||
tag_name: '0.0.6',
|
||||
published_at: '2023-01-09T22:11:25Z',
|
||||
} as GithubRelease,
|
||||
status: 200,
|
||||
statusText: 'Ok',
|
||||
headers: {},
|
||||
config: {},
|
||||
});
|
||||
|
||||
await updatesService.handleCron();
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalled();
|
||||
expect(discordMessageService.buildMessage).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -3,7 +3,7 @@ export const Constants = {
|
||||
Version: {
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
Patch: 3,
|
||||
Patch: 4,
|
||||
All: () =>
|
||||
`${Constants.Metadata.Version.Major}.${Constants.Metadata.Version.Minor}.${Constants.Metadata.Version.Patch}`,
|
||||
},
|
||||
|
29
src/utils/remoteImages/remoteImages.spec.ts
Normal file
29
src/utils/remoteImages/remoteImages.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { chooseSuitableRemoteImageFromTrack } from './remoteImages';
|
||||
|
||||
describe('remoteImages', () => {
|
||||
it('chooseSuitableRemoteImageFromTrack', () => {
|
||||
const remoteImage = chooseSuitableRemoteImageFromTrack({
|
||||
name: 'Testing Music',
|
||||
durationInMilliseconds: 6969,
|
||||
jellyfinId: '7384783',
|
||||
remoteImages: {
|
||||
Images: [
|
||||
{
|
||||
Type: ImageType.Primary,
|
||||
Url: 'nice picture.png',
|
||||
},
|
||||
{
|
||||
Type: ImageType.Screenshot,
|
||||
Url: 'not nice picture',
|
||||
},
|
||||
],
|
||||
},
|
||||
streamUrl: 'http://jellyfin/example-stream',
|
||||
});
|
||||
|
||||
expect(remoteImage).not.toBeNull();
|
||||
expect(remoteImage.Type).toBe(ImageType.Primary);
|
||||
expect(remoteImage.Url).toBe('nice picture.png');
|
||||
});
|
||||
});
|
@ -3,7 +3,7 @@ import {
|
||||
RemoteImageInfo,
|
||||
RemoteImageResult,
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { Track } from '../types/track';
|
||||
import { Track } from '../../types/track';
|
||||
|
||||
export const chooseSuitableRemoteImage = (
|
||||
remoteImageResult: RemoteImageResult,
|
23
src/utils/stringUtils/stringUtils.spec.ts
Normal file
23
src/utils/stringUtils/stringUtils.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { trimStringToFixedLength } from './stringUtils';
|
||||
|
||||
describe('stringUtils', () => {
|
||||
it('trimStringToFixedLengthShouldNotTrim', () => {
|
||||
const trimmedString = trimStringToFixedLength('test', 20);
|
||||
|
||||
expect(trimmedString).toBe('test');
|
||||
});
|
||||
|
||||
it('trimStringToFixedLengthShouldThrowError', () => {
|
||||
const action = () => {
|
||||
trimStringToFixedLength('testing value', 0);
|
||||
};
|
||||
|
||||
expect(action).toThrow(Error);
|
||||
});
|
||||
|
||||
it('trimStringToFixedLengthShouldTrimWhenLengthExceeded', () => {
|
||||
const trimmedString = trimStringToFixedLength('hello world', 5);
|
||||
|
||||
expect(trimmedString).toBe('he...');
|
||||
});
|
||||
});
|
@ -3,7 +3,11 @@ export const trimStringToFixedLength = (value: string, maxLength: number) => {
|
||||
throw new Error('max length must be positive');
|
||||
}
|
||||
|
||||
return value.length > maxLength
|
||||
? value.substring(0, maxLength - 3) + '...'
|
||||
: value;
|
||||
if (value.length <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const upperBound = maxLength - 3;
|
||||
|
||||
return value.substring(0, upperBound) + '...';
|
||||
};
|
17
src/utils/tests/defaultMockerToken.spec.ts
Normal file
17
src/utils/tests/defaultMockerToken.spec.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { useDefaultMockerToken } from './defaultMockerToken';
|
||||
|
||||
describe('defaultMockerToken', () => {
|
||||
it('useDefaultMockerTokenShouldbeNull', () => {
|
||||
const mockerToken = useDefaultMockerToken('test');
|
||||
|
||||
expect(mockerToken).toBeNull();
|
||||
});
|
||||
|
||||
it('useDefaultMockerTokenShouldReturnNull', () => {
|
||||
const mockerToken = useDefaultMockerToken(() => ({
|
||||
test: () => jest.fn(),
|
||||
}));
|
||||
|
||||
expect(mockerToken).not.toBeNull();
|
||||
});
|
||||
});
|
204
yarn.lock
204
yarn.lock
@ -978,7 +978,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@nestjs/cli@npm:^9.0.0":
|
||||
"@nestjs/cli@npm:^9.1.8":
|
||||
version: 9.1.8
|
||||
resolution: "@nestjs/cli@npm:9.1.8"
|
||||
dependencies:
|
||||
@ -1598,7 +1598,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*, @types/node@npm:^18.0.0":
|
||||
"@types/node@npm:*, @types/node@npm:^18.11.18":
|
||||
version: 18.11.18
|
||||
resolution: "@types/node@npm:18.11.18"
|
||||
checksum: 03f17f9480f8d775c8a72da5ea7e9383db5f6d85aa5fefde90dd953a1449bd5e4ffde376f139da4f3744b4c83942166d2a7603969a6f8ea826edfb16e6e3b49d
|
||||
@ -1701,14 +1701,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^5.0.0":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:5.48.1"
|
||||
"@typescript-eslint/eslint-plugin@npm:^5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:5.51.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": 5.48.1
|
||||
"@typescript-eslint/type-utils": 5.48.1
|
||||
"@typescript-eslint/utils": 5.48.1
|
||||
"@typescript-eslint/scope-manager": 5.51.0
|
||||
"@typescript-eslint/type-utils": 5.51.0
|
||||
"@typescript-eslint/utils": 5.51.0
|
||||
debug: ^4.3.4
|
||||
grapheme-splitter: ^1.0.4
|
||||
ignore: ^5.2.0
|
||||
natural-compare-lite: ^1.4.0
|
||||
regexpp: ^3.2.0
|
||||
@ -1720,43 +1721,53 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: d8d73d123d16fc9b50b500ef21816dcabdffe0d2dcfdb15089dc5a1015d57cbad709de565d1c830f5058c0d7b410069e2554c0b53d1485fe7b237ea8089e58be
|
||||
checksum: 5351d8cec13bd9867ce4aaf7052aa31c9ca867fc89c620fc0fe5718ac2cbc165903275db59974324d98e45df0d33a73a4367d236668772912731031a672cfdcd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^5.0.0":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/parser@npm:5.48.1"
|
||||
"@typescript-eslint/parser@npm:^5.52.0":
|
||||
version: 5.52.0
|
||||
resolution: "@typescript-eslint/parser@npm:5.52.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": 5.48.1
|
||||
"@typescript-eslint/types": 5.48.1
|
||||
"@typescript-eslint/typescript-estree": 5.48.1
|
||||
"@typescript-eslint/scope-manager": 5.52.0
|
||||
"@typescript-eslint/types": 5.52.0
|
||||
"@typescript-eslint/typescript-estree": 5.52.0
|
||||
debug: ^4.3.4
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: c624d24eb209b4ce7f0a6c8116712363f10a9c9a5138f240e254ff265526ee4b0fd73b7b6b4b6a0e7611bd9934c42036350dd27f96ae2fa4efdade1a7ebd0e9e
|
||||
checksum: 1d8ff6e932f9c9db8d24b16ce89fd963f0982c38559e500aa1f8dc5cd66abd02f1659dd1a1361ce550def05331803caa69a69a039b54c94fc0f22919a2305c12
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:5.48.1"
|
||||
"@typescript-eslint/scope-manager@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:5.51.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.48.1
|
||||
"@typescript-eslint/visitor-keys": 5.48.1
|
||||
checksum: f60a7efe917798cccf8652925de6be58b023ded6c6ee44ce74d074f0c2a1927680398a6d73bab33d500c69474ad8c54d63b90fcc6e13256712707d12a60e0a64
|
||||
"@typescript-eslint/types": 5.51.0
|
||||
"@typescript-eslint/visitor-keys": 5.51.0
|
||||
checksum: b3c9f48b6b7a7ae2ebcad4745ef91e4727776b2cf56d31be6456b1aa063aa649539e20f9fffa83cad9ccaaa9c492f2354a1c15526a2b789e235ec58b3a82d22c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:5.48.1"
|
||||
"@typescript-eslint/scope-manager@npm:5.52.0":
|
||||
version: 5.52.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:5.52.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": 5.48.1
|
||||
"@typescript-eslint/utils": 5.48.1
|
||||
"@typescript-eslint/types": 5.52.0
|
||||
"@typescript-eslint/visitor-keys": 5.52.0
|
||||
checksum: 9a03fe30f8e90a5106c482478f213eefdd09f2f74e24d9dc59b453885466a758fe6d1cd24d706aed6188fb03c84b16ca6491cf20da6b16b8fc53cad8b8c327f2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:5.51.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": 5.51.0
|
||||
"@typescript-eslint/utils": 5.51.0
|
||||
debug: ^4.3.4
|
||||
tsutils: ^3.21.0
|
||||
peerDependencies:
|
||||
@ -1764,23 +1775,30 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 2739b35caf48c9edbeab82936de58ce0759ab34955ce7eec1786690d6a63146ae0a6c5d9c76034605d9fe200c87a73ede0772c6244c5df6e66df992d9ebbab72
|
||||
checksum: ab9747b0c629cfaaab903eed8ce1e39d34d69a402ce5faf2f1fff2bbb461bdbe034044b1368ba67ba8e5c1c512172e07d83c8a563635d8de811bf148d95c7dec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/types@npm:5.48.1"
|
||||
checksum: 8437986e9d86d792b23327517ae2f9861ec55992d5a9cd55991e525409b6244169436cd708f3987ab7c579e45e59b6eab5a9d3583f7729219e25691164293094
|
||||
"@typescript-eslint/types@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/types@npm:5.51.0"
|
||||
checksum: b31021a0866f41ba5d71b6c4c7e20cc9b99d49c93bb7db63b55b2e51542fb75b4e27662ee86350da3c1318029e278a5a807facaf4cb5aeea724be8b0e021e836
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:5.48.1"
|
||||
"@typescript-eslint/types@npm:5.52.0":
|
||||
version: 5.52.0
|
||||
resolution: "@typescript-eslint/types@npm:5.52.0"
|
||||
checksum: 018940d61aebf7cf3f7de1b9957446e2ea01f08fe950bef4788c716a3a88f7c42765fe7d80152b0d0428fcd4bd3ace2dfa8c459ba1c59d9a84e951642180f869
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:5.51.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.48.1
|
||||
"@typescript-eslint/visitor-keys": 5.48.1
|
||||
"@typescript-eslint/types": 5.51.0
|
||||
"@typescript-eslint/visitor-keys": 5.51.0
|
||||
debug: ^4.3.4
|
||||
globby: ^11.1.0
|
||||
is-glob: ^4.0.3
|
||||
@ -1789,35 +1807,63 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 2b26e5848ef131e1bb99ed54d8c0efa8279cf8e8f7d8b72de00c2ca6cf2799d96c20f5bbbcf26e14e81b7b9d1035ba509bff30f2d852c174815879e8f14c27ed
|
||||
checksum: aec23e5cab48ee72fefa6d1ac266639ebabf6cebec1e0207ad47011d3a48186ac9a632c8e34c3bac896155f54895a497230c11d789fd81263b08eb267d7113ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/utils@npm:5.48.1"
|
||||
"@typescript-eslint/typescript-estree@npm:5.52.0":
|
||||
version: 5.52.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:5.52.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.52.0
|
||||
"@typescript-eslint/visitor-keys": 5.52.0
|
||||
debug: ^4.3.4
|
||||
globby: ^11.1.0
|
||||
is-glob: ^4.0.3
|
||||
semver: ^7.3.7
|
||||
tsutils: ^3.21.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 67d396907fee3d6894e26411a5098a37f07e5d50343189e6361ff7db91c74a7ffe2abd630d11f14c2bda1f4af13edf52b80b11cbccb55b44079c7cec14c9e108
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/utils@npm:5.51.0"
|
||||
dependencies:
|
||||
"@types/json-schema": ^7.0.9
|
||||
"@types/semver": ^7.3.12
|
||||
"@typescript-eslint/scope-manager": 5.48.1
|
||||
"@typescript-eslint/types": 5.48.1
|
||||
"@typescript-eslint/typescript-estree": 5.48.1
|
||||
"@typescript-eslint/scope-manager": 5.51.0
|
||||
"@typescript-eslint/types": 5.51.0
|
||||
"@typescript-eslint/typescript-estree": 5.51.0
|
||||
eslint-scope: ^5.1.1
|
||||
eslint-utils: ^3.0.0
|
||||
semver: ^7.3.7
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
checksum: 2d112cbb6a920f147c6c3322e404ca3c56c1170e1ede3bcbf16fb779960dc24cdba688b1f2d06acd242859fc1dbc8702da5f8fa8bbf53e7081e41d80bec4c236
|
||||
checksum: c6e28c942fbac5500f0e8ed67ef304b484ba296486e55306f78fb090dc9d5bb1f25a0bedc065e14680041eadce5e95fa10aab618cb0c316599ec987e6ea72442
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:5.48.1":
|
||||
version: 5.48.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:5.48.1"
|
||||
"@typescript-eslint/visitor-keys@npm:5.51.0":
|
||||
version: 5.51.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:5.51.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.48.1
|
||||
"@typescript-eslint/types": 5.51.0
|
||||
eslint-visitor-keys: ^3.3.0
|
||||
checksum: 2bda10cf4e6bc48b0d463767617e48a832d708b9434665dff6ed101f7d33e0d592f02af17a2259bde1bd17e666246448ae78d0fe006148cb93d897fff9b1d134
|
||||
checksum: b49710f3c6b3b62a846a163afffd81be5eb2b1f44e25bec51ff3c9f4c3b579d74aa4cbd3753b4fc09ea3dbc64a7062f9c658c08d22bb2740a599cb703d876220
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:5.52.0":
|
||||
version: 5.52.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:5.52.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 5.52.0
|
||||
eslint-visitor-keys: ^3.3.0
|
||||
checksum: 33b44f0cd35b7b47f34e89d52e47b8d8200f55af306b22db4de104d79f65907458ea022e548f50d966e32fea150432ac9c1ae65b3001b0ad2ac8a17c0211f370
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3317,7 +3363,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-config-prettier@npm:^8.3.0":
|
||||
"eslint-config-prettier@npm:^8.6.0":
|
||||
version: 8.6.0
|
||||
resolution: "eslint-config-prettier@npm:8.6.0"
|
||||
peerDependencies:
|
||||
@ -3388,9 +3434,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:^8.0.1":
|
||||
version: 8.31.0
|
||||
resolution: "eslint@npm:8.31.0"
|
||||
"eslint@npm:^8.32.0":
|
||||
version: 8.32.0
|
||||
resolution: "eslint@npm:8.32.0"
|
||||
dependencies:
|
||||
"@eslint/eslintrc": ^1.4.1
|
||||
"@humanwhocodes/config-array": ^0.11.8
|
||||
@ -3433,7 +3479,7 @@ __metadata:
|
||||
text-table: ^0.2.0
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 5e5688bb864edc6b12d165849994812eefa67fb3fc44bb26f53659b63edcd8bcc68389d27cc6cc9e5b79ee22f24b6f311fa3ed047bddcafdec7d84c1b5561e4f
|
||||
checksum: 23c8fb3c57291eecd9c1448faf603226a8f885022a2cd96e303459bf72e39b7f54987c6fb948f0f9eecaf7085600e6eb0663482a35ea83da12e9f9141a22b91e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -4551,7 +4597,7 @@ __metadata:
|
||||
"@discordjs/opus": ^0.9.0
|
||||
"@discordjs/voice": ^0.14.0
|
||||
"@jellyfin/sdk": ^0.7.0
|
||||
"@nestjs/cli": ^9.0.0
|
||||
"@nestjs/cli": ^9.1.8
|
||||
"@nestjs/common": ^9.0.0
|
||||
"@nestjs/config": ^2.2.0
|
||||
"@nestjs/core": ^9.0.0
|
||||
@ -4564,28 +4610,28 @@ __metadata:
|
||||
"@types/cron": ^2.0.0
|
||||
"@types/express": ^4.17.13
|
||||
"@types/jest": 28.1.8
|
||||
"@types/node": ^18.0.0
|
||||
"@types/node": ^18.11.18
|
||||
"@types/supertest": ^2.0.11
|
||||
"@typescript-eslint/eslint-plugin": ^5.0.0
|
||||
"@typescript-eslint/parser": ^5.0.0
|
||||
"@typescript-eslint/eslint-plugin": ^5.51.0
|
||||
"@typescript-eslint/parser": ^5.52.0
|
||||
date-fns: ^2.29.3
|
||||
discord.js: ^14.7.1
|
||||
eslint: ^8.0.1
|
||||
eslint-config-prettier: ^8.3.0
|
||||
eslint: ^8.32.0
|
||||
eslint-config-prettier: ^8.6.0
|
||||
eslint-plugin-prettier: ^4.0.0
|
||||
jest: 28.1.3
|
||||
joi: ^17.7.0
|
||||
libsodium-wrappers: ^0.7.10
|
||||
prettier: ^2.3.2
|
||||
prettier: ^2.8.4
|
||||
reflect-metadata: ^0.1.13
|
||||
rimraf: ^3.0.2
|
||||
rimraf: ^4.1.2
|
||||
rxjs: ^7.2.0
|
||||
source-map-support: ^0.5.20
|
||||
supertest: ^6.1.3
|
||||
ts-jest: 28.0.8
|
||||
ts-loader: ^9.2.3
|
||||
ts-node: ^10.0.0
|
||||
tsconfig-paths: 4.1.0
|
||||
tsconfig-paths: 4.1.2
|
||||
typescript: ^4.7.4
|
||||
uuid: ^9.0.0
|
||||
ws: ^8.11.0
|
||||
@ -6082,12 +6128,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^2.3.2":
|
||||
version: 2.8.2
|
||||
resolution: "prettier@npm:2.8.2"
|
||||
"prettier@npm:^2.8.4":
|
||||
version: 2.8.4
|
||||
resolution: "prettier@npm:2.8.4"
|
||||
bin:
|
||||
prettier: bin-prettier.js
|
||||
checksum: 740c56c2128d587d656ea1dde9bc9c3503dfc94db4f3ac387259215eeb2e216680bdad9d18a0c9feecc6b42cfa188d6fa777df4c36c1d00cedd4199074fbfbd2
|
||||
checksum: c173064bf3df57b6d93d19aa98753b9b9dd7657212e33b41ada8e2e9f9884066bb9ca0b4005b89b3ab137efffdf8fbe0b462785aba20364798ff4303aadda57e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -6408,6 +6454,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rimraf@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "rimraf@npm:4.1.2"
|
||||
bin:
|
||||
rimraf: dist/cjs/src/bin.js
|
||||
checksum: 480b8147fd9bcbef3ac118f88a7b1169c3872977a3411a0c84df838bfc30e175a394c0db6f9619fc8b8a886a18c6d779d5e74f380a0075ecc710afaf81b3f50c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"run-async@npm:^2.4.0":
|
||||
version: 2.4.1
|
||||
resolution: "run-async@npm:2.4.1"
|
||||
@ -7145,17 +7200,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfig-paths@npm:4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "tsconfig-paths@npm:4.1.0"
|
||||
dependencies:
|
||||
json5: ^2.2.1
|
||||
minimist: ^1.2.6
|
||||
strip-bom: ^3.0.0
|
||||
checksum: e4b101f81b2abd95499d8145e0aa73144e857c2c359191058486cef101b7accae22a69114e5d5814a13d5ab3b0bae70dd0c85bcdb7e829bbe1bfda5c9067c9b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfig-paths@npm:4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "tsconfig-paths@npm:4.1.1"
|
||||
@ -7167,7 +7211,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfig-paths@npm:^4.0.0":
|
||||
"tsconfig-paths@npm:4.1.2, tsconfig-paths@npm:^4.0.0":
|
||||
version: 4.1.2
|
||||
resolution: "tsconfig-paths@npm:4.1.2"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user