All files / src/modules/bot/music/commands/play play.command.ts

0% Statements 0/61
0% Branches 0/39
0% Functions 0/4
0% Lines 0/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158                                                                                                                                                                                                                                                                                                                           
import { CommandConfig, CommandPermissions, ValidatedOptions } from "@/common/decorators";
import { CommandConfigGuard, CommandPermissionsGuard } from "@/common/guards";
import { WAIT, isValidURL } from "@/utils/Tools";
import { CurrentTranslate, TranslationFn, localizationMapByKey } from "@necord/localization";
import { Inject, Logger, UseGuards, UseInterceptors } from "@nestjs/common";
import { CommandInteraction, EmbedBuilder, GuildMember, Message, VoiceChannel } from "discord.js";
import { Player, SearchPlatform, SearchResult, SourceLinksRegexes } from "lavalink-client";
import { Ctx, SlashCommandContext, Subcommand } from "necord";
import { MusicCommand } from "../../Music.decorator";
import type { IMusicEmbeds, IMusicService } from "../../interfaces";
import { Music } from "../../types/constants";
import { PlayAutoComplete } from "./play.autocomplete";
import { PlayDTO } from "./play.dto";
 
@MusicCommand()
export class PlayCommand {
	public constructor(
		@Inject(Music.Embeds) private readonly embeds: IMusicEmbeds,
		@Inject(Music.Service) private readonly service: IMusicService,
	) {}
 
	private readonly logger = new Logger(PlayCommand.name);
 
	@Subcommand({
		name: "play",
		description: "Search for a Song or Playlist and play it in a voice channel",
		nameLocalizations: localizationMapByKey("Music.play.name"),
		descriptionLocalizations: localizationMapByKey("Music.play.description"),
	})
	@CommandConfig({ category: "🎵 Music", disable: false })
	@CommandPermissions({
		bot: ["Connect", "EmbedLinks", "DeafenMembers", "Speak"],
		user: ["Connect", "SendMessages"],
		guildOnly: false,
		ownerOnly: false,
	})
	@UseGuards(CommandConfigGuard, CommandPermissionsGuard)
	@UseInterceptors(PlayAutoComplete)
	public async onCommandRun(
		@Ctx() [interaction]: SlashCommandContext,
		@ValidatedOptions() { query, source }: PlayDTO,
		@CurrentTranslate() T: TranslationFn,
	): Promise<Message> {
		let res: SearchResult;
 
		if (!(await this.service.hasVoice(interaction))) {
			return;
		}
 
		let player = await this.service.getPlayer(interaction);
 
		if (!player) {
			player = await this.service.createPlayer(
				interaction,
				(interaction.member as GuildMember).voice.channel as VoiceChannel,
				interaction.channel.id,
			);
		}
		if (!player.connected) {
			player.slash = { isSlash: false };
			player.playerAuthor = interaction.user.id;
			await player.connect();
		}
 
		if (!(await this.service.sameVoice(interaction))) {
			return;
		}
 
		if (isValidURL(query)) {
			res = (await player.search(query, interaction.user.id)) as SearchResult;
		} else {
			res = (await player.search(
				{
					query,
					source: source ? (source as SearchPlatform) : undefined,
				},
				interaction.user,
			)) as SearchResult;
		}
 
		const QueryAddedToQueueMessage = await interaction.reply({
			embeds: [await this.LoadType(res, player, interaction, query)],
		});
		await WAIT(5 * 1000);
		await QueryAddedToQueueMessage.delete();
	}
 
	private async LoadType(
		res: SearchResult,
		player: Player,
		interaction: CommandInteraction,
		query: string,
	): Promise<EmbedBuilder> {
		let Embed: EmbedBuilder;
		const check = await this.service.URLChecker(true, interaction);
		switch (res.loadType) {
			case "error":
				if (!player.queue.current) player.destroy();
				Embed = await this.embeds.LoadType(interaction, "Fail", check);
				break;
			case "empty":
				if (!player.queue.current) player.destroy();
				Embed = await this.embeds.LoadType(interaction, "NoMatches", check);
				break;
			case "search":
			case "track":
				if (!player.connected) {
					player.playerMessage = interaction.id;
					player.playerAuthor = interaction.user.id;
					await player.connect();
				}
				if (!player.playing) {
					await player.queue.add(res.tracks[0]);
					await player.play({ paused: false });
					if (!player.paused && !player.playing) await player.resume();
				} else {
					await player.queue.add(res.tracks[0]);
				}
 
				Embed = await this.embeds.LoadType(interaction, "Success", check, res.tracks[0]);
				break;
			case "playlist":
				Embed = await this.Playlist(interaction, res, player, query);
				break;
		}
 
		return Embed;
	}
 
	private async Playlist(
		interaction: CommandInteraction,
		res: SearchResult,
		player: Player,
		URL: string,
	): Promise<EmbedBuilder> {
		let isValidURL = false;
		for (const regex of Object.values(SourceLinksRegexes)) {
			if (regex.test(URL)) {
				isValidURL = true;
				break;
			}
		}
 
		if (isValidURL) {
			if (!player.playing) {
				await player.queue.add(res.tracks);
				await player.play({ paused: false });
			} else {
				await player.queue.add(res.tracks[0]);
			}
			if (!player.paused && !player.playing) {
				await player.play({ paused: false });
			}
			return await this.embeds.Playlist(interaction, res, await this.service.URLChecker(true, URL));
		}
	}
}