2020-01-25 10:02:43 +00:00
|
|
|
const ytdl = require('ytdl-core-discord');
|
|
|
|
const youtubeInfo = require('youtube-info');
|
|
|
|
const getYoutubeId = require('get-youtube-id');
|
2020-03-26 04:31:32 +00:00
|
|
|
const fetch = require('node-fetch');
|
2020-01-25 10:02:43 +00:00
|
|
|
|
|
|
|
module.exports = client => {
|
|
|
|
// Permission level function
|
|
|
|
client.permlevel = message => {
|
|
|
|
let permlvl = 0;
|
|
|
|
|
|
|
|
const permOrder = client.config.permLevels
|
|
|
|
.slice(0)
|
|
|
|
.sort((p, c) => (p.level < c.level ? 1 : -1));
|
|
|
|
|
|
|
|
while (permOrder.length) {
|
|
|
|
const currentLevel = permOrder.shift();
|
|
|
|
if (message.guild && currentLevel.guildOnly) continue;
|
|
|
|
if (currentLevel.check(message)) {
|
|
|
|
permlvl = currentLevel.level;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return permlvl;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Guild settings function
|
|
|
|
client.getSettings = guild => {
|
|
|
|
const defaults = client.config.defaultSettings || {};
|
|
|
|
if (!guild) return defaults;
|
|
|
|
const guildData = client.settings.get(guild) || {};
|
|
|
|
const returnObject = {};
|
|
|
|
Object.keys(defaults).forEach(key => {
|
|
|
|
returnObject[key] = guildData[key] ? guildData[key] : defaults[key];
|
|
|
|
});
|
|
|
|
return returnObject;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Single line await messages
|
|
|
|
client.awaitReply = async (msg, question, limit = 60000) => {
|
|
|
|
const filter = m => m.author.id === msg.author.id;
|
|
|
|
await msg.channel.send(question);
|
|
|
|
try {
|
|
|
|
const collected = await msg.channel.awaitMessages(filter, {
|
|
|
|
max: 1,
|
|
|
|
time: limit,
|
|
|
|
errors: ["time"]
|
|
|
|
});
|
|
|
|
return collected.first().content;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Message clean function
|
|
|
|
client.clean = async (client, text) => {
|
|
|
|
if (text && text.constructor.name == "Promise") text = await text;
|
|
|
|
if (typeof evaled !== "string")
|
|
|
|
text = require("util").inspect(text, { depth: 1 });
|
|
|
|
|
|
|
|
text = text
|
|
|
|
.replace(/`/g, "`" + String.fromCharCode(8203))
|
|
|
|
.replace(/@/g, "@" + String.fromCharCode(8203))
|
|
|
|
.replace(
|
|
|
|
client.token,
|
|
|
|
"NaKzDzgwNDef1Nitl3YmDAy.tHEvdg.r34L.whl7sTok3N.18n4Ryj094p"
|
|
|
|
);
|
|
|
|
|
|
|
|
return text;
|
|
|
|
};
|
|
|
|
|
|
|
|
client.loadCommand = commandName => {
|
|
|
|
try {
|
|
|
|
const props = require(`../commands/${commandName}`);
|
|
|
|
if (props.init) {
|
|
|
|
props.init(client);
|
|
|
|
}
|
|
|
|
client.commands.set(props.help.name, props);
|
|
|
|
props.conf.aliases.forEach(alias => {
|
|
|
|
client.aliases.set(alias, props.help.name);
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
} catch (e) {
|
|
|
|
return `Failed to load command ${commandName}: ${e}`;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
client.unloadCommand = async commandName => {
|
|
|
|
let command;
|
|
|
|
if (client.commands.has(commandName)) {
|
|
|
|
command = client.commands.get(commandName);
|
|
|
|
} else if (client.aliases.has(commandName)) {
|
|
|
|
command = client.commands.get(client.aliases.get(commandName));
|
|
|
|
};
|
|
|
|
if (!command)
|
|
|
|
return `<:error:466995152976871434> The command \`${commandName}\` could not be found.`;
|
|
|
|
|
|
|
|
if (command.shutdown) {
|
|
|
|
await command.shutdown(client);
|
|
|
|
};
|
|
|
|
const mod = require.cache[require.resolve(`../commands/${commandName}`)];
|
|
|
|
delete require.cache[require.resolve(`../commands/${commandName}.js`)];
|
|
|
|
for (let i = 0; i < mod.parent.children.length; i++) {
|
|
|
|
if (mod.parent.children[i] === mod) {
|
|
|
|
mod.parent.children.splice(i, 1);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// MEMBER SEARCH
|
|
|
|
client.searchForMembers = function(guild, query) {
|
|
|
|
if (!query) return;
|
|
|
|
query = query.toLowerCase();
|
|
|
|
|
|
|
|
var a = [];
|
|
|
|
var b;
|
|
|
|
|
|
|
|
try {
|
2020-03-09 01:11:33 +00:00
|
|
|
b = guild.members.cache.find(x => x.displayName.toLowerCase() == query);
|
|
|
|
if (!b) guild.members.cache.find(x => x.user.username.toLowerCase() == query);
|
2020-01-25 10:02:43 +00:00
|
|
|
} catch (err) {};
|
|
|
|
if (b) a.push(b);
|
2020-03-09 01:11:33 +00:00
|
|
|
guild.members.cache.forEach(member => {
|
2020-01-25 10:02:43 +00:00
|
|
|
if (
|
|
|
|
(member.displayName.toLowerCase().startsWith(query) ||
|
2020-03-09 01:11:33 +00:00
|
|
|
member.user.tag.toLowerCase().startsWith(query)) &&
|
2020-01-25 10:02:43 +00:00
|
|
|
member.id != (b && b.id)
|
|
|
|
) {
|
|
|
|
a.push(member);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
return a;
|
|
|
|
};
|
|
|
|
|
2020-03-11 10:46:13 +00:00
|
|
|
// USER OBJECT FROM MENTION
|
|
|
|
client.getUserFromMention = mention => {
|
|
|
|
if (!mention) return;
|
|
|
|
|
|
|
|
if (mention.startsWith('<@') && mention.endsWith('>')) {
|
|
|
|
mention = mention.slice(2, -1);
|
|
|
|
|
|
|
|
if (mention.startsWith('!')) {
|
|
|
|
mention = mention.slice(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return client.users.cache.get(mention);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-09 01:11:33 +00:00
|
|
|
// MUSIC
|
2020-01-25 10:02:43 +00:00
|
|
|
client.music = {guilds: {}};
|
|
|
|
|
|
|
|
client.music.isYoutubeLink = function(input) {
|
|
|
|
return input.startsWith('https://www.youtube.com/') || input.startsWith('http://www.youtube.com/') || input.startsWith('https://youtube.com/') || input.startsWith('http://youtube.com/') || input.startsWith('https://youtu.be/') || input.startsWith('http://youtu.be/') || input.startsWith('http://m.youtube.com/') || input.startsWith('https://m.youtube.com/');
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.search = async function(query)
|
|
|
|
{
|
|
|
|
return new Promise(function(resolve, reject)
|
|
|
|
{
|
2020-03-26 04:31:32 +00:00
|
|
|
try{
|
|
|
|
fetch("https://www.googleapis.com/youtube/v3/search?part=id&type=video&q=" + encodeURIComponent(query) + "&key=" + client.config.ytkey)
|
|
|
|
.then(res => res.json())
|
|
|
|
.then(json => {
|
2020-01-25 10:02:43 +00:00
|
|
|
if(!json.items) { reject(); return; }
|
|
|
|
resolve(json.items[0]);
|
2020-03-26 04:31:32 +00:00
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
client.logger.error("Music search err: ", err);
|
|
|
|
throw err;
|
|
|
|
};
|
2020-01-25 10:02:43 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.getGuild = function(id)
|
|
|
|
{
|
|
|
|
if(client.music.guilds[id]) return client.music.guilds[id];
|
|
|
|
|
|
|
|
return client.music.guilds[id] =
|
|
|
|
{
|
|
|
|
queue: [],
|
|
|
|
playing: false,
|
|
|
|
paused: false,
|
|
|
|
dispatcher: null,
|
|
|
|
skippers: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.getMeta = async function(id)
|
|
|
|
{
|
|
|
|
return new Promise(function(resolve, reject)
|
|
|
|
{
|
|
|
|
youtubeInfo(id, function(err, videoInfo)
|
|
|
|
{
|
|
|
|
if(err) throw err;
|
|
|
|
|
|
|
|
resolve(videoInfo);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.play = async function(message, input, bypassQueue)
|
|
|
|
{
|
2020-03-09 01:11:33 +00:00
|
|
|
let voiceChannel = message.member.voice.channel;
|
2020-01-25 10:02:43 +00:00
|
|
|
if(!voiceChannel) return message.channel.send('<:error:466995152976871434> You need to be in a voice channel to use this command!');
|
|
|
|
|
|
|
|
let permissions = voiceChannel.permissionsFor(client.user);
|
|
|
|
if (!permissions.has('CONNECT')) {
|
|
|
|
return message.channel.send('<:error:466995152976871434> I do not have permission to join your voice channel.');
|
|
|
|
}
|
|
|
|
if (!permissions.has('SPEAK')) {
|
|
|
|
return message.channel.send('<:error:466995152976871434> I do not have permission to join your voice channel.');
|
|
|
|
}
|
2020-03-09 01:11:33 +00:00
|
|
|
if (voiceChannel.joinable != true) {
|
2020-01-25 10:02:43 +00:00
|
|
|
return message.channel.send("<:error:466995152976871434> I do not have permission to join your voice channel.")
|
|
|
|
}
|
|
|
|
|
|
|
|
let id = undefined;
|
|
|
|
|
|
|
|
if(client.music.isYoutubeLink(input))
|
|
|
|
{
|
|
|
|
id = await getYoutubeId(input)
|
|
|
|
} else {
|
|
|
|
let item = await client.music.search(input);
|
|
|
|
if(!item) {
|
|
|
|
return message.channel.send(`<:error:466995152976871434> No results found.`);
|
|
|
|
};
|
|
|
|
id = item.id.videoId;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(client.music.getGuild(message.guild.id).queue.length == 0 || bypassQueue)
|
|
|
|
{
|
|
|
|
let meta = await client.music.getMeta(id);
|
|
|
|
|
|
|
|
if(!bypassQueue) client.music.getGuild(message.guild.id).queue.push({input: input, id: id, requestedBy: message.author, title: meta.title, author: meta.owner, thumbnail: meta.thumbnailUrl, duration: meta.duration});
|
|
|
|
|
|
|
|
let connection = await new Promise((resolve, reject) =>
|
|
|
|
{
|
|
|
|
voiceChannel.join().then((connection) =>
|
|
|
|
{
|
|
|
|
resolve(connection);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function end(silent)
|
|
|
|
{
|
|
|
|
client.music.getGuild(message.guild.id).queue.shift();
|
|
|
|
client.music.getGuild(message.guild.id).dispatcher = null;
|
|
|
|
|
|
|
|
if(client.music.getGuild(message.guild.id).queue.length > 0)
|
|
|
|
{
|
|
|
|
client.music.play(message, client.music.getGuild(message.guild.id).queue[0].input, true);
|
|
|
|
} else {
|
|
|
|
client.music.getGuild(message.guild.id).playing = false;
|
|
|
|
|
|
|
|
if(!silent) {
|
|
|
|
message.channel.send("<:play:467216788187512832> Queue is empty! Disconnecting from the voice channel.");
|
|
|
|
}
|
|
|
|
|
|
|
|
connection.disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.getGuild(message.guild.id).playing = true;
|
|
|
|
|
|
|
|
let song = client.music.getGuild(message.guild.id).queue[0];
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-03-09 01:11:33 +00:00
|
|
|
let dispatcher = client.music.getGuild(message.guild.id).dispatcher = connection.play(await ytdl("https://www.youtube.com/watch?v=" + id, {highWaterMark: 1024 * 1024 * 32}), {type: 'opus'});
|
2020-01-25 10:02:43 +00:00
|
|
|
|
2020-03-09 01:11:33 +00:00
|
|
|
dispatcher.on('finish', (a, b) =>
|
2020-01-25 10:02:43 +00:00
|
|
|
{
|
|
|
|
end(a == "silent");
|
|
|
|
});
|
|
|
|
} catch(err) {
|
|
|
|
message.channel.send('<:error:466995152976871434> Failed to play **' + song.title + '** ' + err);
|
|
|
|
|
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
|
|
|
client.music.getGuild(message.guild.id).skippers = [];
|
|
|
|
message.channel.send(`<:play:467216788187512832> Now playing: **${song.title}**`);
|
|
|
|
} else {
|
|
|
|
let meta = await client.music.getMeta(id);
|
|
|
|
let song = {input: input, id: id, requestedBy: message.author, title: meta.title, author: meta.owner, thumbnail: meta.thumbnailUrl, duration: meta.duration};
|
|
|
|
|
|
|
|
client.music.getGuild(message.guild.id).queue.push(song);
|
|
|
|
|
|
|
|
message.channel.send(`<:success:466995111885144095> Added to queue: **${song.title}**`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 01:11:33 +00:00
|
|
|
// MUSIC - TIMESTAMP
|
2020-01-25 10:02:43 +00:00
|
|
|
client.createTimestamp = function(duration){
|
|
|
|
hrs = ~~(duration / 60 / 60),
|
|
|
|
min = ~~(duration / 60) % 60,
|
|
|
|
sec = ~~(duration - min * 60);
|
|
|
|
|
|
|
|
if(String(hrs).length < 2) {
|
|
|
|
hrs = "0" + String(hrs) + ":";
|
|
|
|
};
|
|
|
|
|
|
|
|
if(String(min).length < 2) {
|
|
|
|
min = "0" + String(min);
|
|
|
|
};
|
|
|
|
|
|
|
|
if(String(sec).length < 2) {
|
|
|
|
sec = "0" + String(sec);
|
|
|
|
};
|
|
|
|
|
|
|
|
if(hrs == "00:") {
|
|
|
|
hrs = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
var time = hrs + min + ":" + sec;
|
|
|
|
return time;
|
|
|
|
};
|
|
|
|
|
2020-03-09 01:11:33 +00:00
|
|
|
//FIND ROLE
|
|
|
|
client.findRole = function(input, message) {
|
|
|
|
var role;
|
|
|
|
role = message.guild.roles.cache.find(r => r.name.toLowerCase() === input.toLowerCase());
|
|
|
|
if(!role) {
|
|
|
|
role = message.guild.roles.cache.get(input.toLowerCase());
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!role) {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
return role;
|
|
|
|
};
|
|
|
|
|
|
|
|
// EMBED COLOUR
|
|
|
|
client.embedColour = function(msg) {
|
|
|
|
if(!msg.guild) {
|
|
|
|
return ["#ff9d68", "#ff97cb", "#d789ff", "#74FFFF"].random();
|
|
|
|
} else {
|
|
|
|
return msg.guild.member(client.user).displayHexColor;
|
|
|
|
};
|
|
|
|
};
|
2020-01-25 10:02:43 +00:00
|
|
|
|
2020-03-22 13:01:54 +00:00
|
|
|
// FIND RANDOM INT BETWEEN TWO INTEGERS
|
|
|
|
client.intBetween = function(min, max){
|
|
|
|
return Math.round((Math.random() * (max - min))+min);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-01-25 10:02:43 +00:00
|
|
|
// <String>.toPropercase() returns a proper-cased string
|
|
|
|
Object.defineProperty(String.prototype, "toProperCase", {
|
|
|
|
value: function() {
|
|
|
|
return this.replace(
|
|
|
|
/([^\W_]+[^\s-]*) */g,
|
|
|
|
txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// <Array>.random() returns a single random element from an array
|
|
|
|
Object.defineProperty(Array.prototype, "random", {
|
|
|
|
value: function() {
|
|
|
|
return this[Math.floor(Math.random() * this.length)];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// `await client.wait(1000);` to "pause" for 1 second.
|
|
|
|
client.wait = require("util").promisify(setTimeout);
|
|
|
|
|
|
|
|
// These 2 process methods will catch exceptions and give *more details* about the error and stack trace.
|
|
|
|
process.on("uncaughtException", err => {
|
|
|
|
const errorMsg = err.stack.replace(new RegExp(`${__dirname}/`, "g"), "./");
|
|
|
|
client.logger.error(`Uncaught Exception: ${errorMsg}`);
|
|
|
|
process.exit(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
process.on("unhandledRejection", err => {
|
2020-03-26 04:31:32 +00:00
|
|
|
client.logger.error(`Unhandled rejection: ${err.stack}`);
|
2020-01-25 10:02:43 +00:00
|
|
|
});
|
|
|
|
};
|