early music rewrite. now playing, queue, queue removal and skip locking not impl yet
This commit is contained in:
		
							parent
							
								
									39c34a2ef1
								
							
						
					
					
						commit
						f30101e296
					
				
					 1 changed files with 487 additions and 0 deletions
				
			
		| 
						 | 
					@ -1,6 +1,10 @@
 | 
				
			||||||
const fetch = require("node-fetch");
 | 
					const fetch = require("node-fetch");
 | 
				
			||||||
const ytdl = require("ytdl-core");
 | 
					const ytdl = require("ytdl-core");
 | 
				
			||||||
const Eris = require("eris");
 | 
					const Eris = require("eris");
 | 
				
			||||||
 | 
					const ffprobe = require("node-ffprobe");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Command = require("../lib/command.js");
 | 
				
			||||||
 | 
					const {formatTime, selectionMessage} = require("../lib/utils.js");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
 | 
					const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
 | 
				
			||||||
const REGEX_YOUTUBE_PLAYLIST =
 | 
					const REGEX_YOUTUBE_PLAYLIST =
 | 
				
			||||||
| 
						 | 
					@ -16,6 +20,7 @@ const REGEX_FILE =
 | 
				
			||||||
let SOUNDCLOUD_CLIENTID;
 | 
					let SOUNDCLOUD_CLIENTID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hf.voiceStorage = hf.voiceStorage || new Eris.Collection();
 | 
					hf.voiceStorage = hf.voiceStorage || new Eris.Collection();
 | 
				
			||||||
 | 
					const voiceStorage = hf.voiceStorage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://stackoverflow.com/a/12646864 § "Updating to ES6 / ECMAScript 2015"
 | 
					// https://stackoverflow.com/a/12646864 § "Updating to ES6 / ECMAScript 2015"
 | 
				
			||||||
function shuffleArray(array) {
 | 
					function shuffleArray(array) {
 | 
				
			||||||
| 
						 | 
					@ -44,3 +49,485 @@ async function getSoundcloudClientID() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return null;
 | 
					  return null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function processPlaylist(url, type, shuffle = false) {
 | 
				
			||||||
 | 
					  let playlist;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (type === "yt") {
 | 
				
			||||||
 | 
					    const playlistId =
 | 
				
			||||||
 | 
					      url.match(REGEX_YOUTUBE_PLAYLIST)?.[4] ??
 | 
				
			||||||
 | 
					      url.match(REGEX_YOUTUBE_PLAYLIST_SHORT)?.[0];
 | 
				
			||||||
 | 
					    if (!playlistId) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const baseUrl = `https://www.googleapis.com/youtube/v3/playlistItems?key=${hf.apikeys.google}&part=snippet&playlistId=${playlistId}&maxResults=50`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const data = await fetch(baseUrl).then((res) => res.json());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    playlist = data.items;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let pageToken = data.nextPageToken;
 | 
				
			||||||
 | 
					    while (pageToken != null) {
 | 
				
			||||||
 | 
					      const pageData = await fetch(baseUrl + "&pageToken=" + pageToken).then(
 | 
				
			||||||
 | 
					        (res) => res.json()
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      if (pageData.nextPageToken) pageToken = pageData.nextPageToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      playlist = [...playlist, ...pageData.items];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (type === "sc") {
 | 
				
			||||||
 | 
					    const clientId = await getSoundcloudClientID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (url.indexOf("/likes") > -1) {
 | 
				
			||||||
 | 
					      const userInfo = await fetch(
 | 
				
			||||||
 | 
					        `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
 | 
				
			||||||
 | 
					      ).then((res) => res.json());
 | 
				
			||||||
 | 
					      const likesUrl =
 | 
				
			||||||
 | 
					        userInfo.uri.replace("api.", "api-v2.") +
 | 
				
			||||||
 | 
					        "/likes?limit=500&client_id=" +
 | 
				
			||||||
 | 
					        clientId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let currentLikes = await fetch(likesUrl).then((res) => res.json());
 | 
				
			||||||
 | 
					      playlist = currentLikes.collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while (currentLikes.next_href != null) {
 | 
				
			||||||
 | 
					        currentLikes = await fetch(
 | 
				
			||||||
 | 
					          currentLikes.next_href + "&client_id=" + clientId
 | 
				
			||||||
 | 
					        ).then((res) => res.json());
 | 
				
			||||||
 | 
					        playlist = [...playlist, ...currentLikes.collection];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      playlist = await fetch(
 | 
				
			||||||
 | 
					        `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${clientId}&limit=500`
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					        .then((res) => res.json())
 | 
				
			||||||
 | 
					        .then((obj) => obj.tracks);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (shuffle === true && playlist != null) {
 | 
				
			||||||
 | 
					    shuffleArray(playlist);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return playlist;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function createVoiceConnection(guild_id, voice_id, text_id) {
 | 
				
			||||||
 | 
					  const connection = await hf.bot.joinVoiceChannel(voice_id);
 | 
				
			||||||
 | 
					  connection._music_text_id = text_id;
 | 
				
			||||||
 | 
					  connection._music_queue = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  connection._music_eventEnd = async function () {
 | 
				
			||||||
 | 
					    if (connection._music.queue.length > 0) {
 | 
				
			||||||
 | 
					      const next = connection._music_queue.splice(0, 1);
 | 
				
			||||||
 | 
					      await enqueue(
 | 
				
			||||||
 | 
					        guild_id,
 | 
				
			||||||
 | 
					        voice_id,
 | 
				
			||||||
 | 
					        text_id,
 | 
				
			||||||
 | 
					        next.url,
 | 
				
			||||||
 | 
					        next.type,
 | 
				
			||||||
 | 
					        next.addedBy
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      await connection.disconnect();
 | 
				
			||||||
 | 
					      if (connection._music_leave === false) {
 | 
				
			||||||
 | 
					        await hf.bot.guilds
 | 
				
			||||||
 | 
					          .get(guild_id)
 | 
				
			||||||
 | 
					          .channels.get(text_id)
 | 
				
			||||||
 | 
					          .createMessage(
 | 
				
			||||||
 | 
					            ":musical_note: Queue is empty, leaving voice channel."
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      connection.off("end", connection._music_eventEnd);
 | 
				
			||||||
 | 
					      voiceStorage.delete(guild_id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  voiceStorage.set(guild_id, connection);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return connection;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function enqueue(
 | 
				
			||||||
 | 
					  guild_id,
 | 
				
			||||||
 | 
					  voice_id,
 | 
				
			||||||
 | 
					  text_id,
 | 
				
			||||||
 | 
					  url,
 | 
				
			||||||
 | 
					  type,
 | 
				
			||||||
 | 
					  addedBy,
 | 
				
			||||||
 | 
					  suppress = false
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const connection =
 | 
				
			||||||
 | 
					    voiceStorage.get(guild_id) ??
 | 
				
			||||||
 | 
					    (await createVoiceConnection(guild_id, voice_id, text_id));
 | 
				
			||||||
 | 
					  const textChannel = hf.bot.guilds.get(guild_id).channels.get(text_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let title,
 | 
				
			||||||
 | 
					    length,
 | 
				
			||||||
 | 
					    thumbnail,
 | 
				
			||||||
 | 
					    media,
 | 
				
			||||||
 | 
					    stream = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (type == "yt") {
 | 
				
			||||||
 | 
					    let info;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      info = await ytdl.getInfo(url, {});
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      await textChannel.createMessage(
 | 
				
			||||||
 | 
					        `:warning: Failed to get metadata: \`\`\`\n${err}\n\`\`\``
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    title = info?.videoDetails?.title;
 | 
				
			||||||
 | 
					    length = info?.videoDetails?.lengthSeconds * 1000;
 | 
				
			||||||
 | 
					    thumbnail = info?.videoDetails?.thumbnails?.[
 | 
				
			||||||
 | 
					      info.videoDetails.thumbnails.length - 1
 | 
				
			||||||
 | 
					    ].url
 | 
				
			||||||
 | 
					      .replace("vi_webp", "vi")
 | 
				
			||||||
 | 
					      .replace(".webp", ".jpg");
 | 
				
			||||||
 | 
					    media = ytdl(url, {
 | 
				
			||||||
 | 
					      quality: "highestaudio",
 | 
				
			||||||
 | 
					      filter: "audioonly",
 | 
				
			||||||
 | 
					      highWaterMark: 1 << 25,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } else if (type == "sc") {
 | 
				
			||||||
 | 
					    url = url.replace(/^sc:/, "https://soundcloud.com/");
 | 
				
			||||||
 | 
					    const client_id = await getSoundcloudClientID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const info = await fetch(
 | 
				
			||||||
 | 
					      `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${client_id}`
 | 
				
			||||||
 | 
					    ).then((res) => res.json());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const formatUrl = info.media.transcodings.filter(
 | 
				
			||||||
 | 
					      (obj) => !obj.snipped && obj.format.protocol == "progressive"
 | 
				
			||||||
 | 
					    )[0].url;
 | 
				
			||||||
 | 
					    const streamUrl = await fetch(`${formatUrl}?client_id=${client_id}`)
 | 
				
			||||||
 | 
					      .then((res) => res.json())
 | 
				
			||||||
 | 
					      .then((obj) => obj.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    title = info.title;
 | 
				
			||||||
 | 
					    length = info.duration;
 | 
				
			||||||
 | 
					    thumbnail = info.artwork_url;
 | 
				
			||||||
 | 
					    media = streamUrl;
 | 
				
			||||||
 | 
					  } else if (type == "file") {
 | 
				
			||||||
 | 
					    title = url;
 | 
				
			||||||
 | 
					    let info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      info = await ffprobe(url).then((obj) => obj.format);
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      textChannel.createMessage(
 | 
				
			||||||
 | 
					        `:warning: Failed to get metadata: \`\`\`\n${err}\n\`\`\``
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stream = !info.duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (info.tags) {
 | 
				
			||||||
 | 
					      title = `${
 | 
				
			||||||
 | 
					        info.tags.artist ?? info.tags.album_artist ?? "<unknown artist>"
 | 
				
			||||||
 | 
					      } - ${info.tags.title ?? "<no title>"}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    length = info.duration ? Math.floor(info.duration) * 1000 : 0;
 | 
				
			||||||
 | 
					    media = url;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (connection.playing) {
 | 
				
			||||||
 | 
					    connection._music_queue.push({
 | 
				
			||||||
 | 
					      url,
 | 
				
			||||||
 | 
					      type,
 | 
				
			||||||
 | 
					      title,
 | 
				
			||||||
 | 
					      length,
 | 
				
			||||||
 | 
					      addedBy,
 | 
				
			||||||
 | 
					      stream,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    if (suppress === false) {
 | 
				
			||||||
 | 
					      textChannel.createMessage({
 | 
				
			||||||
 | 
					        embeds: [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            title: `<:ms_tick:503341995348066313> Added to queue`,
 | 
				
			||||||
 | 
					            color: 0x3fdcee,
 | 
				
			||||||
 | 
					            fields: [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                name: "Title",
 | 
				
			||||||
 | 
					                value: title && title != url ? `[${title}](${url})` : url,
 | 
				
			||||||
 | 
					                inline: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                name: "Length",
 | 
				
			||||||
 | 
					                value: stream
 | 
				
			||||||
 | 
					                  ? "<continuous>"
 | 
				
			||||||
 | 
					                  : length
 | 
				
			||||||
 | 
					                  ? formatTime(length)
 | 
				
			||||||
 | 
					                  : "<unknown>",
 | 
				
			||||||
 | 
					                inline: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                name: "Added by",
 | 
				
			||||||
 | 
					                value: `<@${addedBy}>`,
 | 
				
			||||||
 | 
					                inline: true,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            thumbnail: {
 | 
				
			||||||
 | 
					              url: thumbnail,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (!media) {
 | 
				
			||||||
 | 
					      textChannel.createMessage(
 | 
				
			||||||
 | 
					        `:warning: No usable media was found for \`${url}\`. May possibly be due to region restrictions or paywalls.`
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await connection.play(media, {inlineVolume: true, voiceDataTimeout: -1});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    textChannel.createMessage({
 | 
				
			||||||
 | 
					      embeds: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          title: `:musical_note Now Playing`,
 | 
				
			||||||
 | 
					          color: 0x18e3c8,
 | 
				
			||||||
 | 
					          fields: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              name: "Title",
 | 
				
			||||||
 | 
					              value: title ? `[${title}](${url})` : url,
 | 
				
			||||||
 | 
					              inline: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              name: "Length",
 | 
				
			||||||
 | 
					              value: stream
 | 
				
			||||||
 | 
					                ? "<continuous>"
 | 
				
			||||||
 | 
					                : length
 | 
				
			||||||
 | 
					                ? formatTime(length)
 | 
				
			||||||
 | 
					                : "<unknown>",
 | 
				
			||||||
 | 
					              inline: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              name: "Added by",
 | 
				
			||||||
 | 
					              value: `<@${addedBy}>`,
 | 
				
			||||||
 | 
					              inline: true,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          thumbnail: {
 | 
				
			||||||
 | 
					            url: thumbnail,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connection._music_nowplaying = {
 | 
				
			||||||
 | 
					      title,
 | 
				
			||||||
 | 
					      addedBy,
 | 
				
			||||||
 | 
					      thumbnail,
 | 
				
			||||||
 | 
					      length,
 | 
				
			||||||
 | 
					      start: Date.now(),
 | 
				
			||||||
 | 
					      end: Date.now() + length,
 | 
				
			||||||
 | 
					      stream,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function youtubeSearch(msg, str) {
 | 
				
			||||||
 | 
					  const items = await fetch(
 | 
				
			||||||
 | 
					    `https://www.googleapis.com/youtube/v3/search?key=${
 | 
				
			||||||
 | 
					      hf.apikeys.google
 | 
				
			||||||
 | 
					    }&maxResults=5&part=snippet&type=video&q=${encodeURIComponent(str)}`
 | 
				
			||||||
 | 
					  ).then((res) => res.json());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const selection = items.map((item) => ({
 | 
				
			||||||
 | 
					    value: "https://youtu.be/" + item.id.videoId,
 | 
				
			||||||
 | 
					    key: item.id.videoId,
 | 
				
			||||||
 | 
					    display: `"${item.snippet.title}" from ${item.snippet.channelTitle}`,
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    return await selectionMessage(msg, "Search results:", selection);
 | 
				
			||||||
 | 
					  } catch (out) {
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const command = new Command("music");
 | 
				
			||||||
 | 
					command.addAlias("m");
 | 
				
			||||||
 | 
					command.category = "misc";
 | 
				
			||||||
 | 
					command.helpText = "Music";
 | 
				
			||||||
 | 
					command.usage = "[search term]";
 | 
				
			||||||
 | 
					command.callback = async function (msg, line) {
 | 
				
			||||||
 | 
					  if (!msg.guildID) return "This command can only be used in guilds.";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [subcommand, ...args] = line.split(" ");
 | 
				
			||||||
 | 
					  let argStr = args.join(" ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (subcommand) {
 | 
				
			||||||
 | 
					    case "play":
 | 
				
			||||||
 | 
					    case "p":
 | 
				
			||||||
 | 
					      if (msg.member?.voiceState?.channelID) {
 | 
				
			||||||
 | 
					        if (voiceStorage.has(msg.guildID)) {
 | 
				
			||||||
 | 
					          const conn = voiceStorage.get(msg.guildID);
 | 
				
			||||||
 | 
					          if (conn.channelID != msg.member.voiceState.channelID) {
 | 
				
			||||||
 | 
					            return "You are in a different voice channel than the bot.";
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let shuffle = false;
 | 
				
			||||||
 | 
					        if (argStr.startsWith("--shuffle ")) {
 | 
				
			||||||
 | 
					          shuffle = true;
 | 
				
			||||||
 | 
					          argStr = argStr.replace(/^--shuffle /, "");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let type;
 | 
				
			||||||
 | 
					        let playlist = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          REGEX_YOUTUBE_PLAYLIST.test(argStr) ||
 | 
				
			||||||
 | 
					          REGEX_YOUTUBE_PLAYLIST_SHORT.test(argStr)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          type = "yt";
 | 
				
			||||||
 | 
					          playlist = true;
 | 
				
			||||||
 | 
					        } else if (REGEX_SOUNDCLOUD_PLAYLIST.test(argStr)) {
 | 
				
			||||||
 | 
					          type = "sc";
 | 
				
			||||||
 | 
					          playlist = true;
 | 
				
			||||||
 | 
					        } else if (REGEX_YOUTUBE.test(argStr)) {
 | 
				
			||||||
 | 
					          type = "yt";
 | 
				
			||||||
 | 
					        } else if (REGEX_SOUNDCLOUD.test(argStr)) {
 | 
				
			||||||
 | 
					          type = "sc";
 | 
				
			||||||
 | 
					        } else if (REGEX_FILE.test(argStr)) {
 | 
				
			||||||
 | 
					          type = "file";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (type != null) {
 | 
				
			||||||
 | 
					          if (playlist) {
 | 
				
			||||||
 | 
					            const playlist = await processPlaylist(argStr, type, shuffle);
 | 
				
			||||||
 | 
					            const statusMessage = msg.channel.createMessage({
 | 
				
			||||||
 | 
					              embeds: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  title:
 | 
				
			||||||
 | 
					                    "<a:loading:493087964918972426> Processing playlist...",
 | 
				
			||||||
 | 
					                  description: `${playlist.length} tracks`,
 | 
				
			||||||
 | 
					                  color: 0xff8037,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            for (const track of playlist) {
 | 
				
			||||||
 | 
					              let url;
 | 
				
			||||||
 | 
					              if (type == "yt") {
 | 
				
			||||||
 | 
					                url = "https://youtu.be/" + track.snippet.resourceId.videoId;
 | 
				
			||||||
 | 
					              } else if (type == "sc") {
 | 
				
			||||||
 | 
					                url = track.track
 | 
				
			||||||
 | 
					                  ? track.track.permalink_url
 | 
				
			||||||
 | 
					                  : track.permalink_url;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              await enqueue(
 | 
				
			||||||
 | 
					                msg.guildID,
 | 
				
			||||||
 | 
					                msg.member.voiceState.channelID,
 | 
				
			||||||
 | 
					                msg.channel.id,
 | 
				
			||||||
 | 
					                url,
 | 
				
			||||||
 | 
					                type,
 | 
				
			||||||
 | 
					                msg.author.id,
 | 
				
			||||||
 | 
					                true
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            await statusMessage.edit({
 | 
				
			||||||
 | 
					              embeds: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  title: "<:ms_tick:503341995348066313> Done processing",
 | 
				
			||||||
 | 
					                  description: `${playlist.length} tracks`,
 | 
				
			||||||
 | 
					                  color: 0xff8037,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            await enqueue(
 | 
				
			||||||
 | 
					              msg.guildID,
 | 
				
			||||||
 | 
					              msg.member.voiceState.channelID,
 | 
				
			||||||
 | 
					              msg.channel.id,
 | 
				
			||||||
 | 
					              argStr,
 | 
				
			||||||
 | 
					              type,
 | 
				
			||||||
 | 
					              msg.author.id
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          const url = await youtubeSearch(msg, argStr);
 | 
				
			||||||
 | 
					          if (url != "Canceled" && url != "Request timed out") {
 | 
				
			||||||
 | 
					            await enqueue(
 | 
				
			||||||
 | 
					              msg.guildID,
 | 
				
			||||||
 | 
					              msg.member.voiceState.channelID,
 | 
				
			||||||
 | 
					              msg.channel.id,
 | 
				
			||||||
 | 
					              url,
 | 
				
			||||||
 | 
					              "yt",
 | 
				
			||||||
 | 
					              msg.author.id
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            return url;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return "You are not in a voice channel.";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case "skip":
 | 
				
			||||||
 | 
					    case "s":
 | 
				
			||||||
 | 
					      if (msg.member?.voiceState?.channelID) {
 | 
				
			||||||
 | 
					        const connection = voiceStorage.get(msg.guildID);
 | 
				
			||||||
 | 
					        if (voiceStorage.has(msg.guildID)) {
 | 
				
			||||||
 | 
					          if (connection.channelID != msg.member.voiceState.channelID) {
 | 
				
			||||||
 | 
					            return "You are in a different voice channel than the bot.";
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: skip lock checks
 | 
				
			||||||
 | 
					        await connection.stopPlaying();
 | 
				
			||||||
 | 
					        return {reaction: "\u23ed"};
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return "You are not in a voice channel.";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    case "leave":
 | 
				
			||||||
 | 
					    case "l":
 | 
				
			||||||
 | 
					    case "stop":
 | 
				
			||||||
 | 
					      if (msg.member?.voiceState?.channelID) {
 | 
				
			||||||
 | 
					        const connection = voiceStorage.get(msg.guildID);
 | 
				
			||||||
 | 
					        if (voiceStorage.has(msg.guildID)) {
 | 
				
			||||||
 | 
					          if (connection.channelID != msg.member.voiceState.channelID) {
 | 
				
			||||||
 | 
					            return "You are in a different voice channel than the bot.";
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: skip lock checks
 | 
				
			||||||
 | 
					        connection._music_queue = [];
 | 
				
			||||||
 | 
					        connection._music_leave = true;
 | 
				
			||||||
 | 
					        await connection.stopPlaying();
 | 
				
			||||||
 | 
					        await hf.bot.leaveVoiceChannel();
 | 
				
			||||||
 | 
					        return {reaction: "\uD83D\uDC4B"};
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return "You are not in a voice channel.";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    case "np":
 | 
				
			||||||
 | 
					    case "queue":
 | 
				
			||||||
 | 
					    case "q":
 | 
				
			||||||
 | 
					    case "remove":
 | 
				
			||||||
 | 
					    case "qr":
 | 
				
			||||||
 | 
					    case "lock":
 | 
				
			||||||
 | 
					    case "unlock":
 | 
				
			||||||
 | 
					      return "TODO";
 | 
				
			||||||
 | 
					    case "help":
 | 
				
			||||||
 | 
					    case "h":
 | 
				
			||||||
 | 
					      return `**__Music Subcommands__**
 | 
				
			||||||
 | 
					\u2022 \`play/p [url|search string]\` - Play or add to queue. Supports: YouTube, SoundCloud, mp3/flac/ogg/wav/webm/mp4/mov/mkv/mod/s3m/it/xm, streams
 | 
				
			||||||
 | 
					\u2022 \`queue/q (page)\` - Lists the current queue.
 | 
				
			||||||
 | 
					\u2022 \`leave/l/stop\` - Leaves the voice channel.
 | 
				
			||||||
 | 
					\u2022 \`np\` - Shows whats currently playing.
 | 
				
			||||||
 | 
					\u2022 \`skip/s\` - Skips whats currently playing.
 | 
				
			||||||
 | 
					\u2022 \`remove/qr\` - Remove an item from queue.
 | 
				
			||||||
 | 
					\u2022 \`lock\` - Lock skipping and queue. (manage messages)
 | 
				
			||||||
 | 
					\u2022 \`unlock\` - Unlock skipping and queue. (manage messages)
 | 
				
			||||||
 | 
					\u2022 \`help/h\` - This text.`;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return `Invalid/missing subcommand. See \`${hf.config.prefix}music help\` for all subcommands.`;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return null; // shutting up eslint
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					hf.registerCommand(command);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue