2021-09-16 03:45:55 +00:00
|
|
|
const fetch = require("node-fetch");
|
|
|
|
const ytdl = require("ytdl-core");
|
|
|
|
const Eris = require("eris");
|
2022-04-19 04:49:04 +00:00
|
|
|
const ffprobe = require("node-ffprobe");
|
|
|
|
|
|
|
|
const Command = require("../lib/command.js");
|
|
|
|
const {formatTime, selectionMessage} = require("../lib/utils.js");
|
2021-09-16 03:45:55 +00:00
|
|
|
|
|
|
|
const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/;
|
|
|
|
const REGEX_YOUTUBE_PLAYLIST =
|
|
|
|
/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/;
|
|
|
|
const REGEX_YOUTUBE_PLAYLIST_SHORT = /^PL[a-zA-Z0-9-_]{1,32}$/;
|
|
|
|
const REGEX_SOUNDCLOUD =
|
|
|
|
/^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/;
|
|
|
|
const REGEX_SOUNDCLOUD_PLAYLIST =
|
|
|
|
/^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/(sets\/.+|likes|tracks)$/;
|
|
|
|
const REGEX_FILE =
|
|
|
|
/^(https?:\/\/)?.*\..*\/.+\.(mp3|ogg|flac|wav|webm|mp4|mov|mkv)$/;
|
|
|
|
|
|
|
|
let SOUNDCLOUD_CLIENTID;
|
|
|
|
|
|
|
|
hf.voiceStorage = hf.voiceStorage || new Eris.Collection();
|
2022-04-19 04:49:04 +00:00
|
|
|
const voiceStorage = hf.voiceStorage;
|
2021-09-16 03:45:55 +00:00
|
|
|
|
|
|
|
// https://stackoverflow.com/a/12646864 § "Updating to ES6 / ECMAScript 2015"
|
|
|
|
function shuffleArray(array) {
|
|
|
|
for (let i = array.length - 1; i > 0; i--) {
|
|
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
|
|
[array[i], array[j]] = [array[j], array[i]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getSoundcloudClientID() {
|
|
|
|
if (SOUNDCLOUD_CLIENTID != null) {
|
|
|
|
return SOUNDCLOUD_CLIENTID;
|
|
|
|
}
|
|
|
|
const page = await fetch("https://soundcloud.com").then((res) => res.text());
|
|
|
|
const scripts = page
|
|
|
|
.match(/<script crossorigin src="(.+?)"><\/script>/g)
|
|
|
|
.reverse();
|
|
|
|
for (const script of scripts) {
|
|
|
|
const url = script.match(/src="(.+?)"/)[1];
|
|
|
|
const contents = await fetch(url).then((res) => res.text());
|
|
|
|
if (/,client_id:"(.+?)",/.test(contents)) {
|
|
|
|
const client_id = contents.match(/,client_id:"(.+?)",/)[1];
|
|
|
|
SOUNDCLOUD_CLIENTID = client_id;
|
|
|
|
return SOUNDCLOUD_CLIENTID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2022-04-19 04:49:04 +00:00
|
|
|
|
|
|
|
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 () {
|
2022-04-19 05:00:32 +00:00
|
|
|
if (connection._music_queue.length > 0) {
|
2022-04-19 05:11:52 +00:00
|
|
|
const next = connection._music_queue.splice(0, 1)[0];
|
2022-04-19 04:49:04 +00:00
|
|
|
await enqueue(
|
|
|
|
guild_id,
|
|
|
|
voice_id,
|
|
|
|
text_id,
|
|
|
|
next.url,
|
|
|
|
next.type,
|
|
|
|
next.addedBy
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
await connection.disconnect();
|
2022-04-19 05:03:45 +00:00
|
|
|
if (!connection._music_leave) {
|
2022-04-19 04:49:04 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2022-04-19 04:52:48 +00:00
|
|
|
connection.on("end", connection._music_eventEnd);
|
2022-04-19 04:49:04 +00:00
|
|
|
|
|
|
|
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`,
|
2022-04-20 20:51:56 +00:00
|
|
|
color: 0x00cc00,
|
2022-04-19 04:49:04 +00:00
|
|
|
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: [
|
|
|
|
{
|
2022-04-19 04:52:48 +00:00
|
|
|
title: `:musical_note: Now Playing`,
|
2022-04-20 20:51:56 +00:00
|
|
|
color: 0x0088cc,
|
2022-04-19 04:49:04 +00:00
|
|
|
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(),
|
|
|
|
stream,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function youtubeSearch(msg, str) {
|
2022-04-19 05:16:09 +00:00
|
|
|
const {items} = await fetch(
|
2022-04-19 04:49:04 +00:00
|
|
|
`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,
|
2022-04-19 05:17:44 +00:00
|
|
|
display:
|
|
|
|
`"${item.snippet.title}" from ${item.snippet.channelTitle}`.substring(
|
|
|
|
0,
|
|
|
|
100
|
|
|
|
),
|
2022-04-19 04:49:04 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
try {
|
|
|
|
return await selectionMessage(msg, "Search results:", selection);
|
|
|
|
} catch (out) {
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 20:51:56 +00:00
|
|
|
const NOWPLAYING_BAR_LENGTH = 30;
|
|
|
|
|
2022-04-19 04:49:04 +00:00
|
|
|
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";
|
2022-04-19 04:56:30 +00:00
|
|
|
} else if (msg.attachments.length > 0) {
|
|
|
|
const entries = msg.attachments.filter((attachment) =>
|
|
|
|
REGEX_FILE.test(attachment.url)
|
|
|
|
);
|
|
|
|
if (entries.length > 0) {
|
|
|
|
type = "file";
|
|
|
|
argStr = entries[0].url;
|
|
|
|
}
|
|
|
|
}
|
2022-04-19 04:49:04 +00:00
|
|
|
|
|
|
|
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`,
|
2022-04-20 20:51:56 +00:00
|
|
|
color: 0xcc0088,
|
2022-04-19 04:49:04 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
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`,
|
2022-04-20 20:51:56 +00:00
|
|
|
color: 0xcc0088,
|
2022-04-19 04:49:04 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
} 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();
|
2022-04-19 05:10:13 +00:00
|
|
|
await hf.bot.leaveVoiceChannel(msg.member.voiceState.channelID);
|
2022-04-19 04:49:04 +00:00
|
|
|
return {reaction: "\uD83D\uDC4B"};
|
|
|
|
} else {
|
|
|
|
return "You are not in a voice channel.";
|
|
|
|
}
|
2022-04-20 20:51:56 +00:00
|
|
|
case "np": {
|
|
|
|
if (!voiceStorage.has(msg.guildID))
|
|
|
|
return "The bot is not in a voice channel.";
|
|
|
|
|
|
|
|
const connection = voiceStorage.get(msg.guildID);
|
|
|
|
const nowPlaying = connection._music_nowplaying;
|
|
|
|
if (!nowPlaying || !connection.playing)
|
|
|
|
return "Nothing is currently playing.";
|
|
|
|
|
|
|
|
const position = Date.now() - nowPlaying.start;
|
|
|
|
|
|
|
|
const timeEnd = formatTime(nowPlaying.length);
|
|
|
|
const timePos = formatTime(position);
|
|
|
|
|
|
|
|
const progress = position / length;
|
|
|
|
const barLength = Math.round(progress * NOWPLAYING_BAR_LENGTH);
|
|
|
|
|
|
|
|
const bar = `\`[${"=".repeat(barLength)}${" ".repeat(
|
|
|
|
NOWPLAYING_BAR_LENGTH - barLength
|
|
|
|
)}]\``;
|
|
|
|
const time = `\`${timePos}${" ".repeat(
|
|
|
|
NOWPLAYING_BAR_LENGTH + 2 - timePos.length - timeEnd.length
|
|
|
|
)}${timeEnd}\``;
|
|
|
|
|
|
|
|
return {
|
|
|
|
embed: {
|
|
|
|
title: ":musical_note: Now Playing",
|
|
|
|
color: 0x0088cc,
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: "Title",
|
|
|
|
value: nowPlaying.title
|
|
|
|
? `[${nowPlaying.title}](${nowPlaying.url})`
|
|
|
|
: nowPlaying.url,
|
|
|
|
inline: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Added by",
|
|
|
|
value: `<@${nowPlaying.addedBy}>`,
|
|
|
|
inline: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: bar,
|
|
|
|
value: time,
|
|
|
|
inline: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2022-04-19 04:49:04 +00:00
|
|
|
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);
|