mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-25 02:51: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:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
target-branch: "dev"
|
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
|
- package-ecosystem: "docker" # See documentation for possible values
|
||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
target-branch: "dev"
|
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",
|
"name": "jellyfin-discord-music-bot",
|
||||||
"version": "0.0.3",
|
"version": "0.0.4",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "manuel-rw",
|
"author": "manuel-rw",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -38,33 +38,33 @@
|
|||||||
"joi": "^17.7.0",
|
"joi": "^17.7.0",
|
||||||
"libsodium-wrappers": "^0.7.10",
|
"libsodium-wrappers": "^0.7.10",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^4.1.2",
|
||||||
"rxjs": "^7.2.0",
|
"rxjs": "^7.2.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.11.0"
|
"ws": "^8.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^9.0.0",
|
"@nestjs/cli": "^9.1.8",
|
||||||
"@nestjs/schematics": "^9.0.0",
|
"@nestjs/schematics": "^9.0.0",
|
||||||
"@nestjs/testing": "^9.0.0",
|
"@nestjs/testing": "^9.0.0",
|
||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/jest": "28.1.8",
|
"@types/jest": "28.1.8",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.11.18",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
"@typescript-eslint/parser": "^5.52.0",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.32.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "28.1.3",
|
"jest": "28.1.3",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.8.4",
|
||||||
"source-map-support": "^0.5.20",
|
"source-map-support": "^0.5.20",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "28.0.8",
|
"ts-jest": "28.0.8",
|
||||||
"ts-loader": "^9.2.3",
|
"ts-loader": "^9.2.3",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"tsconfig-paths": "4.1.0",
|
"tsconfig-paths": "4.1.2",
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
@ -19,7 +19,7 @@ import { UpdatesModule } from './updates/updates.module';
|
|||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
validationSchema: Joi.object({
|
validationSchema: Joi.object({
|
||||||
DISCORD_CLIENT_TOKEN: Joi.string().required(),
|
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_USERNAME: Joi.string().required(),
|
||||||
JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(),
|
JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(),
|
||||||
UPDATER_DISABLE_NOTIFICATIONS: Joi.boolean(),
|
UPDATER_DISABLE_NOTIFICATIONS: Joi.boolean(),
|
||||||
|
@ -58,6 +58,10 @@ export class JellyinPlaystateService {
|
|||||||
private async onPlaybackPaused(isPaused: boolean) {
|
private async onPlaybackPaused(isPaused: boolean) {
|
||||||
const activeTrack = this.playbackService.getActiveTrack();
|
const activeTrack = this.playbackService.getActiveTrack();
|
||||||
|
|
||||||
|
if (!activeTrack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.playstateApi.reportPlaybackProgress({
|
await this.playstateApi.reportPlaybackProgress({
|
||||||
playbackProgressInfo: {
|
playbackProgressInfo: {
|
||||||
ItemId: activeTrack.track.jellyfinId,
|
ItemId: activeTrack.track.jellyfinId,
|
||||||
@ -70,6 +74,10 @@ export class JellyinPlaystateService {
|
|||||||
private async onPlaybackStopped() {
|
private async onPlaybackStopped() {
|
||||||
const activeTrack = this.playbackService.getActiveTrack();
|
const activeTrack = this.playbackService.getActiveTrack();
|
||||||
|
|
||||||
|
if (!activeTrack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.playstateApi.reportPlaybackStopped({
|
await this.playstateApi.reportPlaybackStopped({
|
||||||
playbackStopInfo: {
|
playbackStopInfo: {
|
||||||
ItemId: activeTrack.track.jellyfinId,
|
ItemId: activeTrack.track.jellyfinId,
|
||||||
|
@ -4,7 +4,6 @@ import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
|||||||
import { CommandInteraction } from 'discord.js';
|
import { CommandInteraction } from 'discord.js';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||||
import { GenericCustomReply } from '../models/generic-try-handler';
|
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'disconnect',
|
name: 'disconnect',
|
||||||
@ -17,20 +16,28 @@ export class DisconnectCommand implements DiscordCommand {
|
|||||||
private readonly discordMessageService: DiscordMessageService,
|
private readonly discordMessageService: DiscordMessageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
handler(interaction: CommandInteraction): GenericCustomReply {
|
await interaction.reply({
|
||||||
|
embeds: [
|
||||||
|
this.discordMessageService.buildMessage({
|
||||||
|
title: 'Disconnecting...',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const disconnect = this.discordVoiceService.disconnect();
|
const disconnect = this.discordVoiceService.disconnect();
|
||||||
|
|
||||||
if (!disconnect.success) {
|
if (!disconnect.success) {
|
||||||
return disconnect.reply;
|
await interaction.editReply(disconnect.reply);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Disconnected from your channel',
|
title: 'Disconnected from your channel',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { TransformPipe } from '@discord-nestjs/common';
|
|||||||
import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
||||||
import { CommandInteraction } from 'discord.js';
|
import { CommandInteraction } from 'discord.js';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { GenericCustomReply } from '../models/generic-try-handler';
|
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'help',
|
name: 'help',
|
||||||
@ -13,9 +12,8 @@ import { GenericCustomReply } from '../models/generic-try-handler';
|
|||||||
export class HelpCommand implements DiscordCommand {
|
export class HelpCommand implements DiscordCommand {
|
||||||
constructor(private readonly discordMessageService: DiscordMessageService) {}
|
constructor(private readonly discordMessageService: DiscordMessageService) {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
handler(commandInteraction: CommandInteraction): GenericCustomReply {
|
await interaction.reply({
|
||||||
return {
|
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Jellyfin Discord Bot',
|
title: 'Jellyfin Discord Bot',
|
||||||
@ -40,6 +38,6 @@ export class HelpCommand implements DiscordCommand {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { TransformPipe } from '@discord-nestjs/common';
|
import { TransformPipe } from '@discord-nestjs/common';
|
||||||
|
|
||||||
import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
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 { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { PlaybackService } from '../playback/playback.service';
|
import { PlaybackService } from '../playback/playback.service';
|
||||||
|
|
||||||
@ -16,26 +16,23 @@ export class SkipTrackCommand implements DiscordCommand {
|
|||||||
private readonly discordMessageService: DiscordMessageService,
|
private readonly discordMessageService: DiscordMessageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handler(
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
interactionCommand: CommandInteraction,
|
|
||||||
): InteractionReplyOptions | string {
|
|
||||||
if (!this.playbackService.nextTrack()) {
|
if (!this.playbackService.nextTrack()) {
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildErrorMessage({
|
this.discordMessageService.buildErrorMessage({
|
||||||
title: 'There is no next track',
|
title: 'There is no next track',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Skipped to the next track',
|
title: 'Skipped to the next track',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,18 +16,15 @@ export class PausePlaybackCommand implements DiscordCommand {
|
|||||||
private readonly discordMessageService: DiscordMessageService,
|
private readonly discordMessageService: DiscordMessageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handler(
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
commandInteraction: CommandInteraction,
|
|
||||||
): string | InteractionReplyOptions {
|
|
||||||
const shouldBePaused = this.discordVoiceService.togglePaused();
|
const shouldBePaused = this.discordVoiceService.togglePaused();
|
||||||
|
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: shouldBePaused ? 'Paused' : 'Unpaused',
|
title: shouldBePaused ? 'Paused' : 'Unpaused',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import { TrackRequestDto } from '../models/track-request.dto';
|
|||||||
|
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
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 { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||||
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
||||||
import {
|
import {
|
||||||
@ -28,9 +29,8 @@ import {
|
|||||||
searchResultAsJellyfinAudio,
|
searchResultAsJellyfinAudio,
|
||||||
} from '../models/jellyfinAudioItems';
|
} from '../models/jellyfinAudioItems';
|
||||||
import { PlaybackService } from '../playback/playback.service';
|
import { PlaybackService } from '../playback/playback.service';
|
||||||
import { RemoteImageResult } from '@jellyfin/sdk/lib/generated-client/models';
|
import { chooseSuitableRemoteImage } from '../utils/remoteImages/remoteImages';
|
||||||
import { chooseSuitableRemoteImage } from '../utils/remoteImages';
|
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'play',
|
name: 'play',
|
||||||
@ -52,9 +52,10 @@ export class PlayItemCommand
|
|||||||
|
|
||||||
async handler(
|
async handler(
|
||||||
@Payload() dto: TrackRequestDto,
|
@Payload() dto: TrackRequestDto,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
executionContext: TransformedCommandExecutionContext<any>,
|
executionContext: TransformedCommandExecutionContext<any>,
|
||||||
): Promise<InteractionReplyOptions | string> {
|
): Promise<InteractionReplyOptions | string> {
|
||||||
|
await executionContext.interaction.deferReply();
|
||||||
|
|
||||||
const items = await this.jellyfinSearchService.search(dto.search);
|
const items = await this.jellyfinSearchService.search(dto.search);
|
||||||
const parsedItems = await Promise.all(
|
const parsedItems = await Promise.all(
|
||||||
items.map(
|
items.map(
|
||||||
@ -68,14 +69,15 @@ export class PlayItemCommand
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (parsedItems.length === 0) {
|
if (parsedItems.length === 0) {
|
||||||
return {
|
await executionContext.interaction.followUp({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildErrorMessage({
|
this.discordMessageService.buildErrorMessage({
|
||||||
title: 'No results for your search query found',
|
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`,
|
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);
|
const firstItems = parsedItems.slice(0, 10);
|
||||||
@ -107,7 +109,7 @@ export class PlayItemCommand
|
|||||||
emoji: item.getEmoji(),
|
emoji: item.getEmoji(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
await executionContext.interaction.followUp({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Jellyfin Search Results',
|
title: 'Jellyfin Search Results',
|
||||||
@ -126,7 +128,7 @@ export class PlayItemCommand
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@On(Events.InteractionCreate)
|
@On(Events.InteractionCreate)
|
||||||
@ -144,6 +146,18 @@ export class PlayItemCommand
|
|||||||
return;
|
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 guildMember = interaction.member as GuildMember;
|
||||||
|
|
||||||
const tryResult =
|
const tryResult =
|
||||||
@ -156,7 +170,7 @@ export class PlayItemCommand
|
|||||||
`Unable to process select result because the member was not in a voice channcel`,
|
`Unable to process select result because the member was not in a voice channcel`,
|
||||||
);
|
);
|
||||||
const replyOptions = tryResult.reply as InteractionReplyOptions;
|
const replyOptions = tryResult.reply as InteractionReplyOptions;
|
||||||
await interaction.update({
|
await interaction.editReply({
|
||||||
embeds: replyOptions.embeds,
|
embeds: replyOptions.embeds,
|
||||||
content: undefined,
|
content: undefined,
|
||||||
components: [],
|
components: [],
|
||||||
@ -183,7 +197,7 @@ export class PlayItemCommand
|
|||||||
bitrate,
|
bitrate,
|
||||||
remoteImagesOfCurrentAlbum,
|
remoteImagesOfCurrentAlbum,
|
||||||
);
|
);
|
||||||
interaction.update({
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: item.Name,
|
title: item.Name,
|
||||||
@ -212,7 +226,7 @@ export class PlayItemCommand
|
|||||||
remoteImages,
|
remoteImages,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
interaction.update({
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: `Added ${album.TotalRecordCount} items from your album`,
|
title: `Added ${album.TotalRecordCount} items from your album`,
|
||||||
@ -247,7 +261,7 @@ export class PlayItemCommand
|
|||||||
}
|
}
|
||||||
const bestPlaylistRemoteImage =
|
const bestPlaylistRemoteImage =
|
||||||
chooseSuitableRemoteImage(addedRemoteImages);
|
chooseSuitableRemoteImage(addedRemoteImages);
|
||||||
interaction.update({
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: `Added ${playlist.TotalRecordCount} items from your playlist`,
|
title: `Added ${playlist.TotalRecordCount} items from your playlist`,
|
||||||
@ -267,7 +281,7 @@ export class PlayItemCommand
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
interaction.update({
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildErrorMessage({
|
this.discordMessageService.buildErrorMessage({
|
||||||
title: 'Unable to process your selection',
|
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 { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
||||||
import { CommandInteraction } from 'discord.js';
|
import { CommandInteraction } from 'discord.js';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { GenericCustomReply } from '../models/generic-try-handler';
|
|
||||||
import { PlaybackService } from '../playback/playback.service';
|
import { PlaybackService } from '../playback/playback.service';
|
||||||
import { Constants } from '../utils/constants';
|
import { Constants } from '../utils/constants';
|
||||||
import { chooseSuitableRemoteImageFromTrack } from '../utils/remoteImages';
|
import { chooseSuitableRemoteImageFromTrack } from '../utils/remoteImages/remoteImages';
|
||||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||||
import { formatMillisecondsAsHumanReadable } from '../utils/timeUtils';
|
import { formatMillisecondsAsHumanReadable } from '../utils/timeUtils';
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
@ -21,12 +20,11 @@ export class PlaylistCommand implements DiscordCommand {
|
|||||||
private readonly playbackService: PlaybackService,
|
private readonly playbackService: PlaybackService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
handler(interaction: CommandInteraction): GenericCustomReply {
|
|
||||||
const playList = this.playbackService.getPlaylist();
|
const playList = this.playbackService.getPlaylist();
|
||||||
|
|
||||||
if (playList.tracks.length === 0) {
|
if (playList.tracks.length === 0) {
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Your Playlist',
|
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',
|
'You do not have any tracks in your playlist.\nUse the play command to add new tracks to your playlist',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const tracklist = playList.tracks
|
const tracklist = playList.tracks
|
||||||
@ -63,7 +61,7 @@ export class PlaylistCommand implements DiscordCommand {
|
|||||||
const activeTrack = this.playbackService.getActiveTrack();
|
const activeTrack = this.playbackService.getActiveTrack();
|
||||||
const remoteImage = chooseSuitableRemoteImageFromTrack(activeTrack.track);
|
const remoteImage = chooseSuitableRemoteImageFromTrack(activeTrack.track);
|
||||||
|
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Your Playlist',
|
title: 'Your Playlist',
|
||||||
@ -77,7 +75,7 @@ export class PlaylistCommand implements DiscordCommand {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getListPoint(isCurrent: boolean, index: number) {
|
private getListPoint(isCurrent: boolean, index: number) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { TransformPipe } from '@discord-nestjs/common';
|
import { TransformPipe } from '@discord-nestjs/common';
|
||||||
|
|
||||||
import { Command, DiscordCommand, UsePipes } from '@discord-nestjs/core';
|
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 { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { PlaybackService } from '../playback/playback.service';
|
import { PlaybackService } from '../playback/playback.service';
|
||||||
|
|
||||||
@ -16,26 +16,23 @@ export class PreviousTrackCommand implements DiscordCommand {
|
|||||||
private readonly discordMessageService: DiscordMessageService,
|
private readonly discordMessageService: DiscordMessageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handler(
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
dcommandInteraction: CommandInteraction,
|
|
||||||
): InteractionReplyOptions | string {
|
|
||||||
if (!this.playbackService.previousTrack()) {
|
if (!this.playbackService.previousTrack()) {
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildErrorMessage({
|
this.discordMessageService.buildErrorMessage({
|
||||||
title: 'There is no previous track',
|
title: 'There is no previous track',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Went to previous track',
|
title: 'Went to previous track',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,7 @@ import {
|
|||||||
InjectDiscordClient,
|
InjectDiscordClient,
|
||||||
UsePipes,
|
UsePipes,
|
||||||
} from '@discord-nestjs/core';
|
} from '@discord-nestjs/core';
|
||||||
import {
|
import { Client, CommandInteraction, Status } from 'discord.js';
|
||||||
Client,
|
|
||||||
CommandInteraction,
|
|
||||||
InteractionReplyOptions,
|
|
||||||
Status,
|
|
||||||
} from 'discord.js';
|
|
||||||
|
|
||||||
import { formatDuration, intervalToDuration } from 'date-fns';
|
import { formatDuration, intervalToDuration } from 'date-fns';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
@ -33,10 +28,15 @@ export class StatusCommand implements DiscordCommand {
|
|||||||
private readonly jellyfinService: JellyfinService,
|
private readonly jellyfinService: JellyfinService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async handler(
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
await interaction.reply({
|
||||||
commandInteraction: CommandInteraction,
|
embeds: [
|
||||||
): Promise<string | InteractionReplyOptions> {
|
this.discordMessageService.buildMessage({
|
||||||
|
title: 'Retrieving status information...',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const ping = this.client.ws.ping;
|
const ping = this.client.ws.ping;
|
||||||
const status = Status[this.client.ws.status];
|
const status = Status[this.client.ws.status];
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ export class StatusCommand implements DiscordCommand {
|
|||||||
const jellyfinSystemApi = getSystemApi(this.jellyfinService.getApi());
|
const jellyfinSystemApi = getSystemApi(this.jellyfinService.getApi());
|
||||||
const jellyfinSystemInformation = await jellyfinSystemApi.getSystemInfo();
|
const jellyfinSystemInformation = await jellyfinSystemApi.getSystemInfo();
|
||||||
|
|
||||||
return {
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Discord Bot Status',
|
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 { CommandInteraction } from 'discord.js';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||||
import { GenericCustomReply } from '../models/generic-try-handler';
|
|
||||||
import { PlaybackService } from '../playback/playback.service';
|
import { PlaybackService } from '../playback/playback.service';
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
@ -19,18 +18,28 @@ export class StopPlaybackCommand implements DiscordCommand {
|
|||||||
private readonly discordVoiceService: DiscordVoiceService,
|
private readonly discordVoiceService: DiscordVoiceService,
|
||||||
) {}
|
) {}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
handler(CommandInteraction: CommandInteraction): GenericCustomReply {
|
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.playbackService.clear();
|
||||||
this.discordVoiceService.stop(false);
|
this.discordVoiceService.stop(false);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
await interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService[
|
||||||
title: 'Playlist cleared',
|
hasActiveTrack ? 'buildMessage' : 'buildErrorMessage'
|
||||||
description:
|
]({
|
||||||
'Playback was stopped and your playlist has been cleared',
|
title: title,
|
||||||
|
description: description,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import { Logger } from '@nestjs/common';
|
|||||||
import { CommandInteraction, GuildMember } from 'discord.js';
|
import { CommandInteraction, GuildMember } from 'discord.js';
|
||||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||||
import { GenericCustomReply } from '../models/generic-try-handler';
|
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
name: 'summon',
|
name: 'summon',
|
||||||
@ -20,7 +19,9 @@ export class SummonCommand implements DiscordCommand {
|
|||||||
private readonly discordMessageService: DiscordMessageService,
|
private readonly discordMessageService: DiscordMessageService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
handler(interaction: CommandInteraction): GenericCustomReply {
|
async handler(interaction: CommandInteraction): Promise<void> {
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
const guildMember = interaction.member as GuildMember;
|
const guildMember = interaction.member as GuildMember;
|
||||||
|
|
||||||
const tryResult =
|
const tryResult =
|
||||||
@ -29,10 +30,11 @@ export class SummonCommand implements DiscordCommand {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!tryResult.success) {
|
if (!tryResult.success) {
|
||||||
return tryResult.reply;
|
await interaction.editReply(tryResult.reply);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
await interaction.editReply({
|
||||||
embeds: [
|
embeds: [
|
||||||
this.discordMessageService.buildMessage({
|
this.discordMessageService.buildMessage({
|
||||||
title: 'Joined your voicehannel',
|
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.",
|
"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';
|
} from '@nestjs/terminus';
|
||||||
import { HealthCheckExecutor } from '@nestjs/terminus/dist/health-check/health-check-executor.service';
|
import { HealthCheckExecutor } from '@nestjs/terminus/dist/health-check/health-check-executor.service';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { useDefaultMockerToken } from '../utils/tests';
|
import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken';
|
||||||
import { HealthController } from './health.controller';
|
import { HealthController } from './health.controller';
|
||||||
import { DiscordHealthIndicator } from './indicators/discord.indicator';
|
import { DiscordHealthIndicator } from './indicators/discord.indicator';
|
||||||
import { JellyfinHealthIndicator } from './indicators/jellyfin.indicator';
|
import { JellyfinHealthIndicator } from './indicators/jellyfin.indicator';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HealthIndicatorResult } from '@nestjs/terminus';
|
import { HealthIndicatorResult } from '@nestjs/terminus';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { JellyfinService } from '../../clients/jellyfin/jellyfin.service';
|
import { JellyfinService } from '../../clients/jellyfin/jellyfin.service';
|
||||||
import { useDefaultMockerToken } from '../../utils/tests';
|
import { useDefaultMockerToken } from '../../utils/tests/defaultMockerToken';
|
||||||
import { JellyfinHealthIndicator } from './jellyfin.indicator';
|
import { JellyfinHealthIndicator } from './jellyfin.indicator';
|
||||||
|
|
||||||
describe('JellyfinHealthIndicator', () => {
|
describe('JellyfinHealthIndicator', () => {
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import { InteractionReplyOptions } from 'discord.js';
|
import { InteractionEditReplyOptions, MessagePayload } from 'discord.js';
|
||||||
|
|
||||||
export interface GenericTryHandler {
|
export interface GenericTryHandler {
|
||||||
success: boolean;
|
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';
|
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||||
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
||||||
import { Track } from '../types/track';
|
import { Track } from '../types/track';
|
||||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
import { trimStringToFixedLength } from '../utils/stringUtils/stringUtils';
|
||||||
|
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { JellyfinSearchService } from '../clients/jellyfin/jellyfin.search.service';
|
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: {
|
Version: {
|
||||||
Major: 0,
|
Major: 0,
|
||||||
Minor: 0,
|
Minor: 0,
|
||||||
Patch: 3,
|
Patch: 4,
|
||||||
All: () =>
|
All: () =>
|
||||||
`${Constants.Metadata.Version.Major}.${Constants.Metadata.Version.Minor}.${Constants.Metadata.Version.Patch}`,
|
`${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,
|
RemoteImageInfo,
|
||||||
RemoteImageResult,
|
RemoteImageResult,
|
||||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||||
import { Track } from '../types/track';
|
import { Track } from '../../types/track';
|
||||||
|
|
||||||
export const chooseSuitableRemoteImage = (
|
export const chooseSuitableRemoteImage = (
|
||||||
remoteImageResult: RemoteImageResult,
|
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');
|
throw new Error('max length must be positive');
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.length > maxLength
|
if (value.length <= maxLength) {
|
||||||
? value.substring(0, maxLength - 3) + '...'
|
return value;
|
||||||
: 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
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@nestjs/cli@npm:^9.0.0":
|
"@nestjs/cli@npm:^9.1.8":
|
||||||
version: 9.1.8
|
version: 9.1.8
|
||||||
resolution: "@nestjs/cli@npm:9.1.8"
|
resolution: "@nestjs/cli@npm:9.1.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1598,7 +1598,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*, @types/node@npm:^18.0.0":
|
"@types/node@npm:*, @types/node@npm:^18.11.18":
|
||||||
version: 18.11.18
|
version: 18.11.18
|
||||||
resolution: "@types/node@npm:18.11.18"
|
resolution: "@types/node@npm:18.11.18"
|
||||||
checksum: 03f17f9480f8d775c8a72da5ea7e9383db5f6d85aa5fefde90dd953a1449bd5e4ffde376f139da4f3744b4c83942166d2a7603969a6f8ea826edfb16e6e3b49d
|
checksum: 03f17f9480f8d775c8a72da5ea7e9383db5f6d85aa5fefde90dd953a1449bd5e4ffde376f139da4f3744b4c83942166d2a7603969a6f8ea826edfb16e6e3b49d
|
||||||
@ -1701,14 +1701,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@npm:^5.0.0":
|
"@typescript-eslint/eslint-plugin@npm:^5.51.0":
|
||||||
version: 5.48.1
|
version: 5.51.0
|
||||||
resolution: "@typescript-eslint/eslint-plugin@npm:5.48.1"
|
resolution: "@typescript-eslint/eslint-plugin@npm:5.51.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager": 5.48.1
|
"@typescript-eslint/scope-manager": 5.51.0
|
||||||
"@typescript-eslint/type-utils": 5.48.1
|
"@typescript-eslint/type-utils": 5.51.0
|
||||||
"@typescript-eslint/utils": 5.48.1
|
"@typescript-eslint/utils": 5.51.0
|
||||||
debug: ^4.3.4
|
debug: ^4.3.4
|
||||||
|
grapheme-splitter: ^1.0.4
|
||||||
ignore: ^5.2.0
|
ignore: ^5.2.0
|
||||||
natural-compare-lite: ^1.4.0
|
natural-compare-lite: ^1.4.0
|
||||||
regexpp: ^3.2.0
|
regexpp: ^3.2.0
|
||||||
@ -1720,43 +1721,53 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: d8d73d123d16fc9b50b500ef21816dcabdffe0d2dcfdb15089dc5a1015d57cbad709de565d1c830f5058c0d7b410069e2554c0b53d1485fe7b237ea8089e58be
|
checksum: 5351d8cec13bd9867ce4aaf7052aa31c9ca867fc89c620fc0fe5718ac2cbc165903275db59974324d98e45df0d33a73a4367d236668772912731031a672cfdcd
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/parser@npm:^5.0.0":
|
"@typescript-eslint/parser@npm:^5.52.0":
|
||||||
version: 5.48.1
|
version: 5.52.0
|
||||||
resolution: "@typescript-eslint/parser@npm:5.48.1"
|
resolution: "@typescript-eslint/parser@npm:5.52.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager": 5.48.1
|
"@typescript-eslint/scope-manager": 5.52.0
|
||||||
"@typescript-eslint/types": 5.48.1
|
"@typescript-eslint/types": 5.52.0
|
||||||
"@typescript-eslint/typescript-estree": 5.48.1
|
"@typescript-eslint/typescript-estree": 5.52.0
|
||||||
debug: ^4.3.4
|
debug: ^4.3.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: c624d24eb209b4ce7f0a6c8116712363f10a9c9a5138f240e254ff265526ee4b0fd73b7b6b4b6a0e7611bd9934c42036350dd27f96ae2fa4efdade1a7ebd0e9e
|
checksum: 1d8ff6e932f9c9db8d24b16ce89fd963f0982c38559e500aa1f8dc5cd66abd02f1659dd1a1361ce550def05331803caa69a69a039b54c94fc0f22919a2305c12
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@npm:5.48.1":
|
"@typescript-eslint/scope-manager@npm:5.51.0":
|
||||||
version: 5.48.1
|
version: 5.51.0
|
||||||
resolution: "@typescript-eslint/scope-manager@npm:5.48.1"
|
resolution: "@typescript-eslint/scope-manager@npm:5.51.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": 5.48.1
|
"@typescript-eslint/types": 5.51.0
|
||||||
"@typescript-eslint/visitor-keys": 5.48.1
|
"@typescript-eslint/visitor-keys": 5.51.0
|
||||||
checksum: f60a7efe917798cccf8652925de6be58b023ded6c6ee44ce74d074f0c2a1927680398a6d73bab33d500c69474ad8c54d63b90fcc6e13256712707d12a60e0a64
|
checksum: b3c9f48b6b7a7ae2ebcad4745ef91e4727776b2cf56d31be6456b1aa063aa649539e20f9fffa83cad9ccaaa9c492f2354a1c15526a2b789e235ec58b3a82d22c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@npm:5.48.1":
|
"@typescript-eslint/scope-manager@npm:5.52.0":
|
||||||
version: 5.48.1
|
version: 5.52.0
|
||||||
resolution: "@typescript-eslint/type-utils@npm:5.48.1"
|
resolution: "@typescript-eslint/scope-manager@npm:5.52.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree": 5.48.1
|
"@typescript-eslint/types": 5.52.0
|
||||||
"@typescript-eslint/utils": 5.48.1
|
"@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
|
debug: ^4.3.4
|
||||||
tsutils: ^3.21.0
|
tsutils: ^3.21.0
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1764,23 +1775,30 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 2739b35caf48c9edbeab82936de58ce0759ab34955ce7eec1786690d6a63146ae0a6c5d9c76034605d9fe200c87a73ede0772c6244c5df6e66df992d9ebbab72
|
checksum: ab9747b0c629cfaaab903eed8ce1e39d34d69a402ce5faf2f1fff2bbb461bdbe034044b1368ba67ba8e5c1c512172e07d83c8a563635d8de811bf148d95c7dec
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/types@npm:5.48.1":
|
"@typescript-eslint/types@npm:5.51.0":
|
||||||
version: 5.48.1
|
version: 5.51.0
|
||||||
resolution: "@typescript-eslint/types@npm:5.48.1"
|
resolution: "@typescript-eslint/types@npm:5.51.0"
|
||||||
checksum: 8437986e9d86d792b23327517ae2f9861ec55992d5a9cd55991e525409b6244169436cd708f3987ab7c579e45e59b6eab5a9d3583f7729219e25691164293094
|
checksum: b31021a0866f41ba5d71b6c4c7e20cc9b99d49c93bb7db63b55b2e51542fb75b4e27662ee86350da3c1318029e278a5a807facaf4cb5aeea724be8b0e021e836
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@npm:5.48.1":
|
"@typescript-eslint/types@npm:5.52.0":
|
||||||
version: 5.48.1
|
version: 5.52.0
|
||||||
resolution: "@typescript-eslint/typescript-estree@npm:5.48.1"
|
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:
|
dependencies:
|
||||||
"@typescript-eslint/types": 5.48.1
|
"@typescript-eslint/types": 5.51.0
|
||||||
"@typescript-eslint/visitor-keys": 5.48.1
|
"@typescript-eslint/visitor-keys": 5.51.0
|
||||||
debug: ^4.3.4
|
debug: ^4.3.4
|
||||||
globby: ^11.1.0
|
globby: ^11.1.0
|
||||||
is-glob: ^4.0.3
|
is-glob: ^4.0.3
|
||||||
@ -1789,35 +1807,63 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 2b26e5848ef131e1bb99ed54d8c0efa8279cf8e8f7d8b72de00c2ca6cf2799d96c20f5bbbcf26e14e81b7b9d1035ba509bff30f2d852c174815879e8f14c27ed
|
checksum: aec23e5cab48ee72fefa6d1ac266639ebabf6cebec1e0207ad47011d3a48186ac9a632c8e34c3bac896155f54895a497230c11d789fd81263b08eb267d7113ce
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/utils@npm:5.48.1":
|
"@typescript-eslint/typescript-estree@npm:5.52.0":
|
||||||
version: 5.48.1
|
version: 5.52.0
|
||||||
resolution: "@typescript-eslint/utils@npm:5.48.1"
|
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:
|
dependencies:
|
||||||
"@types/json-schema": ^7.0.9
|
"@types/json-schema": ^7.0.9
|
||||||
"@types/semver": ^7.3.12
|
"@types/semver": ^7.3.12
|
||||||
"@typescript-eslint/scope-manager": 5.48.1
|
"@typescript-eslint/scope-manager": 5.51.0
|
||||||
"@typescript-eslint/types": 5.48.1
|
"@typescript-eslint/types": 5.51.0
|
||||||
"@typescript-eslint/typescript-estree": 5.48.1
|
"@typescript-eslint/typescript-estree": 5.51.0
|
||||||
eslint-scope: ^5.1.1
|
eslint-scope: ^5.1.1
|
||||||
eslint-utils: ^3.0.0
|
eslint-utils: ^3.0.0
|
||||||
semver: ^7.3.7
|
semver: ^7.3.7
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
checksum: 2d112cbb6a920f147c6c3322e404ca3c56c1170e1ede3bcbf16fb779960dc24cdba688b1f2d06acd242859fc1dbc8702da5f8fa8bbf53e7081e41d80bec4c236
|
checksum: c6e28c942fbac5500f0e8ed67ef304b484ba296486e55306f78fb090dc9d5bb1f25a0bedc065e14680041eadce5e95fa10aab618cb0c316599ec987e6ea72442
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@npm:5.48.1":
|
"@typescript-eslint/visitor-keys@npm:5.51.0":
|
||||||
version: 5.48.1
|
version: 5.51.0
|
||||||
resolution: "@typescript-eslint/visitor-keys@npm:5.48.1"
|
resolution: "@typescript-eslint/visitor-keys@npm:5.51.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": 5.48.1
|
"@typescript-eslint/types": 5.51.0
|
||||||
eslint-visitor-keys: ^3.3.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
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -3317,7 +3363,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"eslint-config-prettier@npm:^8.3.0":
|
"eslint-config-prettier@npm:^8.6.0":
|
||||||
version: 8.6.0
|
version: 8.6.0
|
||||||
resolution: "eslint-config-prettier@npm:8.6.0"
|
resolution: "eslint-config-prettier@npm:8.6.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3388,9 +3434,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"eslint@npm:^8.0.1":
|
"eslint@npm:^8.32.0":
|
||||||
version: 8.31.0
|
version: 8.32.0
|
||||||
resolution: "eslint@npm:8.31.0"
|
resolution: "eslint@npm:8.32.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc": ^1.4.1
|
"@eslint/eslintrc": ^1.4.1
|
||||||
"@humanwhocodes/config-array": ^0.11.8
|
"@humanwhocodes/config-array": ^0.11.8
|
||||||
@ -3433,7 +3479,7 @@ __metadata:
|
|||||||
text-table: ^0.2.0
|
text-table: ^0.2.0
|
||||||
bin:
|
bin:
|
||||||
eslint: bin/eslint.js
|
eslint: bin/eslint.js
|
||||||
checksum: 5e5688bb864edc6b12d165849994812eefa67fb3fc44bb26f53659b63edcd8bcc68389d27cc6cc9e5b79ee22f24b6f311fa3ed047bddcafdec7d84c1b5561e4f
|
checksum: 23c8fb3c57291eecd9c1448faf603226a8f885022a2cd96e303459bf72e39b7f54987c6fb948f0f9eecaf7085600e6eb0663482a35ea83da12e9f9141a22b91e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -4551,7 +4597,7 @@ __metadata:
|
|||||||
"@discordjs/opus": ^0.9.0
|
"@discordjs/opus": ^0.9.0
|
||||||
"@discordjs/voice": ^0.14.0
|
"@discordjs/voice": ^0.14.0
|
||||||
"@jellyfin/sdk": ^0.7.0
|
"@jellyfin/sdk": ^0.7.0
|
||||||
"@nestjs/cli": ^9.0.0
|
"@nestjs/cli": ^9.1.8
|
||||||
"@nestjs/common": ^9.0.0
|
"@nestjs/common": ^9.0.0
|
||||||
"@nestjs/config": ^2.2.0
|
"@nestjs/config": ^2.2.0
|
||||||
"@nestjs/core": ^9.0.0
|
"@nestjs/core": ^9.0.0
|
||||||
@ -4564,28 +4610,28 @@ __metadata:
|
|||||||
"@types/cron": ^2.0.0
|
"@types/cron": ^2.0.0
|
||||||
"@types/express": ^4.17.13
|
"@types/express": ^4.17.13
|
||||||
"@types/jest": 28.1.8
|
"@types/jest": 28.1.8
|
||||||
"@types/node": ^18.0.0
|
"@types/node": ^18.11.18
|
||||||
"@types/supertest": ^2.0.11
|
"@types/supertest": ^2.0.11
|
||||||
"@typescript-eslint/eslint-plugin": ^5.0.0
|
"@typescript-eslint/eslint-plugin": ^5.51.0
|
||||||
"@typescript-eslint/parser": ^5.0.0
|
"@typescript-eslint/parser": ^5.52.0
|
||||||
date-fns: ^2.29.3
|
date-fns: ^2.29.3
|
||||||
discord.js: ^14.7.1
|
discord.js: ^14.7.1
|
||||||
eslint: ^8.0.1
|
eslint: ^8.32.0
|
||||||
eslint-config-prettier: ^8.3.0
|
eslint-config-prettier: ^8.6.0
|
||||||
eslint-plugin-prettier: ^4.0.0
|
eslint-plugin-prettier: ^4.0.0
|
||||||
jest: 28.1.3
|
jest: 28.1.3
|
||||||
joi: ^17.7.0
|
joi: ^17.7.0
|
||||||
libsodium-wrappers: ^0.7.10
|
libsodium-wrappers: ^0.7.10
|
||||||
prettier: ^2.3.2
|
prettier: ^2.8.4
|
||||||
reflect-metadata: ^0.1.13
|
reflect-metadata: ^0.1.13
|
||||||
rimraf: ^3.0.2
|
rimraf: ^4.1.2
|
||||||
rxjs: ^7.2.0
|
rxjs: ^7.2.0
|
||||||
source-map-support: ^0.5.20
|
source-map-support: ^0.5.20
|
||||||
supertest: ^6.1.3
|
supertest: ^6.1.3
|
||||||
ts-jest: 28.0.8
|
ts-jest: 28.0.8
|
||||||
ts-loader: ^9.2.3
|
ts-loader: ^9.2.3
|
||||||
ts-node: ^10.0.0
|
ts-node: ^10.0.0
|
||||||
tsconfig-paths: 4.1.0
|
tsconfig-paths: 4.1.2
|
||||||
typescript: ^4.7.4
|
typescript: ^4.7.4
|
||||||
uuid: ^9.0.0
|
uuid: ^9.0.0
|
||||||
ws: ^8.11.0
|
ws: ^8.11.0
|
||||||
@ -6082,12 +6128,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"prettier@npm:^2.3.2":
|
"prettier@npm:^2.8.4":
|
||||||
version: 2.8.2
|
version: 2.8.4
|
||||||
resolution: "prettier@npm:2.8.2"
|
resolution: "prettier@npm:2.8.4"
|
||||||
bin:
|
bin:
|
||||||
prettier: bin-prettier.js
|
prettier: bin-prettier.js
|
||||||
checksum: 740c56c2128d587d656ea1dde9bc9c3503dfc94db4f3ac387259215eeb2e216680bdad9d18a0c9feecc6b42cfa188d6fa777df4c36c1d00cedd4199074fbfbd2
|
checksum: c173064bf3df57b6d93d19aa98753b9b9dd7657212e33b41ada8e2e9f9884066bb9ca0b4005b89b3ab137efffdf8fbe0b462785aba20364798ff4303aadda57e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -6408,6 +6454,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"run-async@npm:^2.4.0":
|
||||||
version: 2.4.1
|
version: 2.4.1
|
||||||
resolution: "run-async@npm:2.4.1"
|
resolution: "run-async@npm:2.4.1"
|
||||||
@ -7145,17 +7200,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"tsconfig-paths@npm:4.1.1":
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
resolution: "tsconfig-paths@npm:4.1.1"
|
resolution: "tsconfig-paths@npm:4.1.1"
|
||||||
@ -7167,7 +7211,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tsconfig-paths@npm:^4.0.0":
|
"tsconfig-paths@npm:4.1.2, tsconfig-paths@npm:^4.0.0":
|
||||||
version: 4.1.2
|
version: 4.1.2
|
||||||
resolution: "tsconfig-paths@npm:4.1.2"
|
resolution: "tsconfig-paths@npm:4.1.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user