discord-dynamic-music-bot-addon
Version:
Music bot addon for bot
1,048 lines (1,047 loc) • 63.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const discord_js_1 = require("discord.js");
const messages_1 = require("./messages");
const Youtube_1 = require("./Youtube");
const language_1 = require("./language");
const GuildPlayer_1 = require("./GuildPlayer");
const yt_core_discord_1 = require("./yt-core-discord");
const youtubeTester = new RegExp(/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\\_]*)(&(amp;)?[\w\\?=]*)?/g);
const urlTester = new RegExp(/https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,}/g);
const DEFAULT_PLAYER_UPDATE = 10;
const DEFAULT_WAIT_TIME_BETWEEN_TRACKS = 2;
const DEFAULT_SELF_DELETE_TIME = 5;
const DEFAULT_LEAVE_TIME = 20;
const guildPlayer = new WeakMap();
const playerUpdateRate = new WeakMap();
const selfDeleteTime = new WeakMap();
const leaveVoiceChannelAfter = new WeakMap();
const leaveVoiceChannelAfterAllMembersLeft = new WeakMap();
const maxTrackLength = new WeakMap();
const discordClient = new WeakMap();
const youtube = new WeakMap();
const autoQueryDetection = new WeakMap();
const autoPlaylistDetection = new WeakMap();
const waitTimeBetweenTracks = new WeakMap();
const maxItemsInPlayList = new WeakMap();
const maxUserItemsInPlayList = new WeakMap();
const playlistParseWait = new WeakMap();
const multipleParser = new WeakMap();
const playlistParse = new WeakMap();
const votePercentage = new WeakMap();
const coolDown = new WeakMap();
const deleteUserMessage = new WeakMap();
const hardDeleteUserMessage = new WeakMap();
const reactionButtons = new WeakMap();
const destroyed = new WeakMap();
const userCoolDownSet = new WeakMap();
const suggestReplay = new WeakMap();
const PLAYER_VERSION = '2.1.9 build';
class YoutubePlayer {
/**
* Constructor that constructs
* @param {string} string youtube api key
* @param {PlayerLanguage} PlayerLanguage PlayerLanguage
*/
constructor(youtubeApiKey, options) {
if (!options)
options = {};
if (options && typeof options !== 'object')
throw new TypeError('options must be an object!');
if (!youtubeApiKey) {
youtubeApiKey = '';
console.warn('YouTube Api key has not been not provided! Rich embeds are going to contain less information about the video!');
}
else {
try {
const youtubeClass = new Youtube_1.Youtube(youtubeApiKey);
youtube.set(this, youtubeClass);
}
catch (error) {
console.warn('YouTube Api key that has been provided is not valid! Rich embeds are going to contain less information about the video!');
}
}
if (typeof youtubeApiKey !== 'string')
throw new TypeError(`Expected string got ${typeof youtubeApiKey}`);
guildPlayer.set(this, {});
if (options && options.language)
this.defaultLanguage = options.language;
else {
language_1.playerLanguage.set(this, new language_1.Language());
}
this.playerUpdateRate = (options && options.messageUpdateRate !== undefined) ? options.messageUpdateRate : DEFAULT_PLAYER_UPDATE;
this.selfDelete = (options && options.selfDeleteTime !== undefined) ? options.selfDeleteTime : DEFAULT_SELF_DELETE_TIME;
this.waitTimeBetweenTracks = (options && options.waitTimeBetweenTracks !== undefined) ? options.waitTimeBetweenTracks : DEFAULT_WAIT_TIME_BETWEEN_TRACKS;
this.deleteUserMessages = (options && options.deleteUserMessage !== undefined) ? options.deleteUserMessage : true;
this.reactionButtons = (options && options.reactionButtons !== undefined) ? options.reactionButtons : true;
this.parsePlaylistUrl = (options && options.playlistParse !== undefined) ? options.playlistParse : false;
this.maxTrackLength = (options && options.maxTrackLength !== undefined) ? options.maxTrackLength : 60 * 3;
this.maxItemsInPlaylist = (options && options.maxItemsInPlayList !== undefined) ? options.maxItemsInPlayList : 100;
this.maxUsersItemsInPlaylist = (options && options.maxUserItemsInPlayList !== undefined) ? options.maxUserItemsInPlayList : 10;
this.votePercentage = (options && options.votePercentage !== undefined) ? options.votePercentage : 60;
this.playListWaitTime = (options && options.playlistParseWait !== undefined) ? options.playlistParseWait : 2;
this.leaveVoiceChannelAfter = (options && options.leaveVoiceChannelAfter !== undefined) ? options.leaveVoiceChannelAfter : DEFAULT_LEAVE_TIME;
this.leaveVoiceChannelAfterWhenNoPlayersInChannel = (options && options.leaveVoiceChannelAfterAllMembersLeft !== undefined) ? options.leaveVoiceChannelAfterAllMembersLeft : DEFAULT_LEAVE_TIME;
this.userCoolDown = (options && options.coolDown !== undefined) ? options.coolDown : 5;
this.autoQuerySearch = (options && options.autoQueryDetection !== undefined) ? options.autoQueryDetection : true;
this.autoPlaylistDetection = (options && options.autoPlaylistDetection !== undefined) ? options.autoPlaylistDetection : false;
this.multipleVideoParser = (options && options.multipleParser !== undefined) ? options.multipleParser : true;
this.hardDeleteUserMessages = (options && options.hardDeleteUserMessage !== undefined) ? options.hardDeleteUserMessage : false;
this.suggestReplayButtons = (options && options.suggestReplay !== undefined) ? options.suggestReplay : 20;
destroyed.set(this, false);
userCoolDownSet.set(this, new Set());
}
/**
* Should delete user messages?
* @param {boolean} boolean if set to true if possible the messages sent by user are going to be deleted.
*/
set deleteUserMessages(trueFalse) {
if (typeof trueFalse !== 'boolean')
throw new Error(`Expected boolean got ${typeof trueFalse}`);
deleteUserMessage.set(this, trueFalse);
}
/**
* Should parse playlist links.
* @param {boolean} boolean.
*/
set parsePlaylistUrl(trueFalse) {
if (typeof trueFalse !== 'boolean')
throw new Error(`Expected boolean got ${typeof trueFalse}`);
playlistParse.set(this, trueFalse);
}
/**
* Should auto detect playlist link? Requires autoQueryDetection to be enabled.
* @param {boolean} boolean.
*/
set autoPlaylistDetection(trueFalse) {
if (typeof trueFalse !== 'boolean')
throw new Error(`Expected boolean got ${typeof trueFalse}`);
if (trueFalse && !playlistParse.get(this))
throw new Error(`playlistParse has to be enabled in order for this function to work`);
if (trueFalse && !autoQueryDetection.get(this))
throw new Error(`autoQueryDetection has to be enabled in order for this function to work`);
autoPlaylistDetection.set(this, trueFalse);
}
/**
* how much this should wait to parse next playlist time
* @param {number} number seconds
*/
set playListWaitTime(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
playlistParseWait.set(this, seconds);
}
/**
* Should search if none of the player commands where found in message
* @param {boolean} boolean Enables/disables autoQuerySearch
*/
set autoQuerySearch(trueFalse) {
if (typeof trueFalse !== 'boolean')
throw new Error(`Expected boolean got ${typeof trueFalse}`);
autoQueryDetection.set(this, trueFalse);
}
/**
* how much items can be in playlist.
* @param {number} number playlist limit.
*/
set maxItemsInPlaylist(items) {
if (typeof items !== 'number')
throw new Error(`Expected number got ${typeof items}`);
if (items < 1)
throw new Error('max video length cannot be lower than 1 item');
maxItemsInPlayList.set(this, items);
}
/**
* How much track can a user have in playlist
* @param {number} number user playlist limit
*/
set maxUsersItemsInPlaylist(items) {
if (typeof items !== 'number')
throw new Error(`Expected number got ${typeof items}`);
if (items < 1)
throw new Error('max video length cannot be lower than 1 item');
maxUserItemsInPlayList.set(this, items);
}
/**
* User command cool down. How much does bot needs to wait before accepting new command
* @param {number} number time
*/
set userCoolDown(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
if (seconds < 0)
throw new Error('cooldown cannot be lower than 0');
coolDown.set(this, seconds);
}
/**
* Allow multiple videos parsing. This also enables playlist parsing
* @param {boolean} bool time
*/
set multipleVideoParser(bool) {
if (typeof bool !== 'boolean')
throw new Error(`Expected boolean got ${typeof bool}`);
multipleParser.set(this, bool);
}
/**
* Set wait time between tracks
* @param {number} number how much should player wait.
*/
set waitTimeBetweenTracks(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
waitTimeBetweenTracks.set(this, seconds * 1000);
}
/**
* max track length
* @param {number} number max track length
*/
set maxTrackLength(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
if (seconds < 0.0834)
throw new Error('max video length cannot be lower than 5 seconds');
maxTrackLength.set(this, seconds);
}
/**
* Set player edit/update rate
* @param {number} number how fast/slow should player message be updated.
*/
set playerUpdateRate(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
if (seconds < 5)
throw new Error('update rate cannot be lower than 5 seconds');
playerUpdateRate.set(this, seconds * 1000);
}
/**
* When bot runs out of songs how long should wait before disconnecting voice channel
* @param {number} number in seconds. If set to 0 it will leave immediately.
*/
set leaveVoiceChannelAfter(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
leaveVoiceChannelAfter.set(this, seconds * 1000);
}
/**
* When all uses leave voice channel how long should bot wait before destroying player
* @param {number} number in seconds. If set to 0 it will leave immediately .
*/
set leaveVoiceChannelAfterWhenNoPlayersInChannel(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
leaveVoiceChannelAfterAllMembersLeft.set(this, seconds * 1000);
}
/**
* percentage of vote for to be executed
* @param {number} number vote percentage
*/
set votePercentage(percentage) {
if (typeof percentage !== 'number')
throw new Error(`Expected number got ${typeof percentage}`);
if (percentage < 0)
throw new Error(`Number cannot be lower than 0`);
if (percentage > 100)
throw new Error(`Number cannot be higher than 100`);
votePercentage.set(this, percentage / 100);
}
/**
* Should message be garbage collected
* @param {number} seconds if 0 no others numbers are seconds
*/
set selfDelete(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
if (seconds < 0)
throw new Error('Cannot be below 0');
selfDeleteTime.set(this, seconds * 1000);
}
/**
* Delete every message in channels when player is active
* @param {boolean} boolean
*/
set hardDeleteUserMessages(bool) {
if (typeof bool !== 'boolean')
throw new Error(`Expected boolean got ${typeof bool}`);
hardDeleteUserMessage.set(this, bool);
}
/**
* Custom player language pack
* @param {Language} languePack Custom langue pack
*/
set defaultLanguage(playerLang) {
if (typeof playerLang !== 'object')
throw new Error(`Expected object got ${typeof playerLang}`);
language_1.playerLanguage.set(this, new language_1.Language(playerLang));
}
/**
* Create play buttons with that you can control player without use of any other command
* @param {bool} boolean enable/disable
*/
set reactionButtons(bool) {
if (typeof bool !== 'boolean')
throw new Error(`Expected boolean got ${typeof bool}`);
reactionButtons.set(this, bool);
}
/**
* How much before the song should show the replay song button
* reaction button feature has to be enabled in order for this to work
* @param {seconds} seconds if 0 the button is not going to show up
*/
set suggestReplayButtons(seconds) {
if (typeof seconds !== 'number')
throw new Error(`Expected number got ${typeof seconds}`);
if (seconds < 0)
throw new Error('Cannot be below 0');
if (!reactionButtons.get(this))
throw new Error(`reactionButtons feature has to be enabled in order for this feature to work`);
suggestReplay.set(this, seconds * 1000);
}
/**
* @param {Message} Message Discord message
* @param {string} String prefix
* @param {playerLang} PlayerLang object
* @returns {boolean} It's going to return true if command is valid.
*/
onMessagePrefix(message, prefix, playerLang) {
if (!prefix)
throw new Error('Prefix cannot be undefined');
if (typeof prefix !== 'string')
throw new Error('Prefix must be string');
return this.onMessage(message, message.cleanContent.slice(prefix.length).trim(), prefix, playerLang);
}
/**
* Destroys player and makes entire class useless
* If no call back if provided it going to return Promise;
* @param {function} callback call function
*/
async destroy(callback) {
destroyed.set(this, true);
if (!callback) {
return new Promise(resolve => {
this.destroy(() => {
resolve();
});
});
}
const client = discordClient.get(this);
if (!client)
return callback();
const theGuildData = guildPlayer.get(this);
const keys = Object.keys(theGuildData);
for (const key of keys) {
const guild = client.guilds.resolve(key);
if (!guild)
break;
const guildPlayer = getGuildPlayer(this, guild);
const lang = guildPlayer ? guildPlayer.language : language_1.playerLanguage.get(this);
await destroyGuildPlayer(this, guild, lang, true);
}
callback();
}
/**
* @param {Message} Message Discord message
* @param {string} String Discord message without prefix
* @param {string} String Optional just for help command
* @returns {boolean} It's going to return true if command is valid.
*/
onMessage(message, messageContentWithOutPrefix, prefix, playerLang) {
if (!message.guild || message.author.bot || !message.content)
return false;
if (destroyed.get(this))
return false;
stealAndSetClient(this, message.client);
const userCoolDown = userCoolDownSet.get(this);
if (prefix)
language_1.playerLanguage.get(this).setPrefix(prefix);
const guildPlayer = getGuildPlayer(this, message.guild);
let lang;
if (playerLang)
lang = new language_1.Language(playerLang);
else if (guildPlayer)
lang = guildPlayer.language;
else
lang = language_1.playerLanguage.get(this);
const args = messageContentWithOutPrefix.split(' ');
if (!lang.lang.commands.playerCommands.includes(args[0].toLowerCase())) {
return false;
}
const language = lang.getLang();
const commands = language.commands;
const channel = message.channel;
if (!message.guild || !message.guild.me)
return false;
const me = channel.permissionsFor(message.guild.me);
if (!me)
return false;
if (!me.has('SEND_MESSAGES'))
return false;
let checker = messageContentWithOutPrefix.replace(/ {2}/g, ' ');
if (checker.indexOf(' ') === -1)
return false;
const selfDeleteT = selfDeleteTime.get(this);
if (commendChecker(removeFistWord(checker), commands.help)) {
playerHelp(this, message, lang);
delUserMessage(this, message);
return true;
}
if (commendChecker(removeFistWord(checker), commands.version)) {
playerVersion(this, message);
delUserMessage(this, message);
return true;
}
if (guildPlayer && guildPlayer.playerMessage && message.member && guildPlayer.playerMessage.channel !== message.channel) {
const channel = guildPlayer.playerMessage.channel;
const permissions = channel.permissionsFor(message.member);
if (permissions && !permissions.has('VIEW_CHANNEL'))
messages_1.errorInfo(message.channel, language.player.wrongChannelNoAccess.replace(/<CHANNEL>/, channel.toString()), language.error, selfDeleteT);
else if (permissions && !permissions.has('SEND_MESSAGES'))
messages_1.errorInfo(message.channel, language.player.wrongChannelNoPermissions.replace(/<CHANNEL>/, channel.toString()), language.error, selfDeleteT);
else
messages_1.errorInfo(message.channel, language.player.wrongChannel.replace(/<CHANNEL>/, channel.toString()), language.error, selfDeleteT);
return true;
}
if (userCoolDown.has(message.author.id)) {
messages_1.errorInfo(message.channel, language.sendingMessageToQuickly.replace(/<TIME>/g, coolDown.get(this).toString()), language.error, selfDeleteT);
return true;
}
else {
userCoolDown.add(message.author.id);
setTimeout(() => {
userCoolDown.delete(message.author.id);
}, coolDown.get(this) * 1000);
}
if (commendChecker(checker, commands.playerCommands, false)) {
playerHelp(this, message, lang);
return true;
}
if (commendChecker(checker, commands.version, false)) {
playerVersion(this, message);
return true;
}
if (commendChecker(checker, commands.playerCommands)) {
checker = removeFistWord(checker);
}
else
return false;
// just to throw object out of stack so we can return boolean value to the user
setTimeout(() => {
if (!message.member || !message.member.voice)
return;
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) {
return messages_1.errorInfo(message.channel, language.notInVoiceChannel, language.error, selfDeleteTime.get(this));
}
else if (!voiceChannel.joinable) {
return messages_1.errorInfo(message.channel, language.cannotConnect, language.error, selfDeleteTime.get(this));
}
let executed = false;
if (commendChecker(checker, commands.destroy)) {
if (!isSomethingPlaying(this, message, lang))
return;
if (guildPlayer && message.guild && message.guild.voice && message.guild.voice.channel && guildPlayer.canExecute(message.member))
message.guild.voice.channel.leave();
executed = true;
return;
}
else if (commendChecker(checker, commands.search)) {
delUserMessage(this, message);
const spaceIndex = messageContentWithOutPrefix.indexOf(' ', 2);
youtubeLuckSearch(this, message, messageContentWithOutPrefix.slice(spaceIndex).trim(), lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.url)) {
const YTUrls = checker.match(youtubeTester);
if (YTUrls)
addYoutubeToQueue(this, message, YTUrls, undefined, lang);
else
messages_1.errorInfo(message.channel, language.onlyYoutubeLinks, language.error, selfDeleteTime.get(this));
executed = true;
}
else if (commendChecker(checker, commands.next)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandNextTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.previous)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandPreviousTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.pause)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandPauseOrReplyTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.resume)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandPauseOrReplyTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.replay)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandReplayTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.replay)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandReplayTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.loop)) {
if (!isSomethingPlaying(this, message, lang))
return;
commandLoopTrack(this, message, lang);
executed = true;
return;
}
else if (commendChecker(checker, commands.playlist)) {
playlistCommand(this, message, checker, commands, lang);
executed = true;
return;
}
else {
if (autoQueryDetection.get(this)) {
const YTUrls = checker.match(youtubeTester);
const urls = checker.match(urlTester);
const playlistId = /[&?]list=([^&]+)/i.test(checker);
if (YTUrls && urls && playlistId && playlistParse.get(this))
parsePlayList(this, message, urls[0], lang, true);
else if (YTUrls)
addYoutubeToQueue(this, message, YTUrls, undefined, lang);
else if (checker.match(urlTester)) {
messages_1.errorInfo(message.channel, language.error, language.onlyYoutubeLinks);
}
else
youtubeLuckSearch(this, message, checker, lang);
}
else
messages_1.errorInfo(message.channel, lang.incorrectUse(), language.error, selfDeleteT);
}
if (executed)
delUserMessage(this, message);
});
return true;
}
}
exports.YoutubePlayer = YoutubePlayer;
function delUserMessage(youtubePlayer, message) {
if (!message.guild || !message.guild.me)
return;
const canDelete = deleteUserMessage.get(youtubePlayer);
if (!canDelete)
return;
const channel = message.channel;
const permissions = channel.permissionsFor(message.guild.me);
if (permissions && permissions.has('MANAGE_MESSAGES'))
message.delete().catch(err => { message.client.emit('error', err); });
}
function commendChecker(messageContent, aliases, includes = true) {
if (includes)
messageContent = getFirstWord(messageContent);
for (const command of aliases) {
if (messageContent.toLowerCase() === command.toLowerCase()) {
return true;
}
}
return false;
}
function getFirstWord(text) {
const spaceIndex = text.indexOf(' ');
if (spaceIndex !== -1) {
text = text.slice(0, spaceIndex).trim();
}
return text;
}
function removeFistWord(text) {
const spaceIndex = text.indexOf(' ');
if (spaceIndex !== -1) {
text = text.slice(spaceIndex).trim();
}
return text;
}
function isSomethingPlaying(youtubePlayer, message, lang) {
if (!message.guild)
return;
const language = lang.getLang();
const guildData = getGuildPlayer(youtubePlayer, message.guild);
const selfDeleteT = selfDeleteTime.get(youtubePlayer);
if (!guildData) {
messages_1.errorInfo(message.channel, language.player.nothingPlaying, language.error, selfDeleteT);
return false;
}
return true;
}
function playlistCommand(youtubePlayer, message, checker, commands, lang) {
if (!message.guild)
return;
const args = checker.split(' ');
const language = lang.getLang();
const guildPlayer = getOrSetGuildPlayer(youtubePlayer, message, lang);
const selfDelete = selfDeleteTime.get(youtubePlayer);
const shouldBeNumber = parseInt(args[2]);
if (!guildPlayer)
return;
if (commendChecker(args[1], commands.playlistCommands.parse)) {
if (!playlistParse.get(youtubePlayer)) {
return messages_1.errorInfo(message.channel, language.player.featureDisabled, language.error, selfDelete);
}
if (youtubeTester.test(checker)) {
const playlistId = /[&?]list=([^&]+)/i.test(checker);
const urls = checker.match(urlTester);
if (playlistId && urls)
return parsePlayList(youtubePlayer, message, urls[0], lang);
else
return messages_1.errorInfo(message.channel, language.playlistNotFound, language.error, selfDelete);
}
else if (args[2]) {
return messages_1.errorInfo(message.channel, language.onlyYoutubeLinks, language.error, selfDelete);
}
else
return messages_1.errorInfo(message.channel, lang.incorrectUse(), language.error, selfDelete);
}
else if (commendChecker(args[1], commands.playlistCommands.remove)) {
if (isNaN(shouldBeNumber)) {
return messages_1.errorInfo(message.channel, lang.incorrectUse(), language.error, selfDelete);
}
else {
const item = guildPlayer.playlist.find(i => i.index === shouldBeNumber);
if (item) {
if ((item.message && item.message.author === message.author) || canExecute(youtubePlayer, message, lang)) {
guildPlayer.removeItemFromPlaylist(item);
}
else
return messages_1.errorInfo(message.channel, language.missingPermission, language.error, selfDelete);
}
else
return messages_1.errorInfo(message.channel, language.player.playlistUnableToFindItem, language.error, selfDelete);
}
}
else if (commendChecker(args[1], commands.playlistCommands.force)) {
if (canExecute(youtubePlayer, message, lang)) {
if (isNaN(shouldBeNumber)) {
return messages_1.errorInfo(message.channel, lang.incorrectUse(), language.error, selfDelete);
}
else {
let item = guildPlayer.playlist.find(i => i.index === shouldBeNumber);
if (!item)
item = guildPlayer.previous.find(i => i.index === shouldBeNumber);
if (item) {
guildPlayer.setSong(item);
guildPlayer.nextTrack();
return messages_1.info(message.channel, language.player.forceReplay, language.info, selfDeleteTime.get(youtubePlayer));
}
else
return messages_1.errorInfo(message.channel, language.player.playlistUnableToFindItem, language.error, selfDeleteTime.get(youtubePlayer));
}
}
else
return messages_1.errorInfo(message.channel, language.missingPermission, language.error, selfDeleteTime.get(youtubePlayer));
}
else if (commendChecker(args[1], commands.playlistCommands.shuffle)) {
if (canExecute(youtubePlayer, message, lang)) {
if (guildPlayer.shuffle())
return messages_1.info(message.channel, language.player.playlistShuffled, language.info, selfDeleteTime.get(youtubePlayer));
else
return messages_1.errorInfo(message.channel, language.player.playlistNothingToShuffle, language.error, selfDeleteTime.get(youtubePlayer));
}
}
else if (commendChecker(args[1], commands.playlistCommands.sort)) {
if (canExecute(youtubePlayer, message, lang)) {
if (guildPlayer.sort())
return messages_1.info(message.channel, language.player.playlistSorted, language.info, selfDeleteTime.get(youtubePlayer));
else
return messages_1.errorInfo(message.channel, language.player.playlistAlreadySorted, language.error, selfDeleteTime.get(youtubePlayer));
}
}
else {
return messages_1.errorInfo(message.channel, lang.incorrectUse(), language.error);
}
}
function stealAndSetClient(youtubePlayer, client) {
if (!discordClient.get(youtubePlayer)) {
discordClient.set(youtubePlayer, client);
client.on('voiceStateUpdate', guildMember => {
if (!guildMember.guild || !guildMember.guild.voice || !guildMember.guild.voice.channel)
return;
const voiceChannel = guildMember.guild.voice.channel;
if (!voiceChannel) {
client.emit('debug', 'Bot has been disconnected from the voice channel');
const guildPlayer = getGuildPlayer(youtubePlayer, guildMember.guild);
const lang = guildPlayer ? guildPlayer.language : language_1.playerLanguage.get(youtubePlayer);
destroyGuildPlayer(youtubePlayer, guildMember.guild, lang);
}
else {
const theGuildPlayer = getGuildPlayer(youtubePlayer, guildMember.guild);
if (!theGuildPlayer)
return;
const members = !!voiceChannel.members.filter(m => !m.user.bot).size;
if (theGuildPlayer.timeOutPlayerLeaveAllMemberLeft) {
clearTimeout(theGuildPlayer.timeOutPlayerLeaveAllMemberLeft);
theGuildPlayer.timeOutPlayerLeaveAllMemberLeft = undefined;
}
if (!members) {
theGuildPlayer.timeOutPlayerLeaveAllMemberLeft = setTimeout(() => {
if (guildMember.guild && guildMember.guild.voice && guildMember.guild.voice.connection && guildMember.guild.voice.connection.channel)
guildMember.guild.voice.connection.channel.leave();
}, leaveVoiceChannelAfterAllMembersLeft.get(youtubePlayer));
}
}
});
const emojiWatcher = async (theGuildPlayer, guildMembers, messageReaction, message) => {
switch (messageReaction.emoji.name) {
case '⏸️':
if (theGuildPlayer.playerMessage === message && theGuildPlayer.setVote(guildMembers, 'votePauseResume'))
theGuildPlayer.recreateOrRecreatePlayerButtons();
return;
case '▶️':
if (theGuildPlayer.playerMessage === message && theGuildPlayer.setVote(guildMembers, 'votePauseResume'))
theGuildPlayer.recreateOrRecreatePlayerButtons();
return;
case '⏮️':
return theGuildPlayer.playerMessage === message && theGuildPlayer.setVote(guildMembers, 'votePrevious');
case '⏭️':
return theGuildPlayer.playerMessage === message && theGuildPlayer.setVote(guildMembers, 'voteNext');
case '🔁':
if (theGuildPlayer.playerMessage === message && theGuildPlayer.setVote(guildMembers, 'voteReplay'))
theGuildPlayer.recreateOrRecreatePlayerButtons();
return;
case '🔂':
return theGuildPlayer.setVote(guildMembers, 'voteLoop');
case '❎':
if (theGuildPlayer.playerMessage === message && message.guild && message.guild.voice && message.guild.voice.channel && theGuildPlayer.canExecute(guildMembers))
message.guild.voice.channel.leave();
else
theGuildPlayer.removeFromPlayListByMessage(message);
break;
// case '🔀':
// if (theGuildPlayer.setVoteShuffle(guildMembers)) recreateOrRecreatePlayerButtons(theGuildPlayer);
// break;
default:
break;
}
};
const onMessageReactionAddOrRemove = async (messageReaction) => {
const channel = messageReaction.message.channel;
const guild = channel.guild;
if (!reactionButtons.get(youtubePlayer))
return;
if (client.user !== messageReaction.message.author)
return;
const theGuildPlayer = getGuildPlayer(youtubePlayer, guild);
if (!theGuildPlayer)
return;
const guildMembers = theGuildPlayer.getFromVoiceAndMessageReactions(messageReaction);
if (!guildMembers.length)
return;
emojiWatcher(theGuildPlayer, guildMembers, messageReaction, messageReaction.message);
};
client.on('messageReactionAdd', async (messageReaction) => {
if (!messageReaction.message.guild)
return;
const channel = messageReaction.message.channel;
const guild = channel.guild;
if (!guild || !guild.me)
return;
const channelPermissions = channel.permissionsFor(guild.me);
if (channelPermissions && channelPermissions.has('MANAGE_MESSAGES')) {
const theGuildPlayer = getGuildPlayer(youtubePlayer, messageReaction.message.guild);
if (theGuildPlayer) {
const pMsg = theGuildPlayer.playerMessage;
const playlistFiltered = theGuildPlayer.playlist.filter(p => p.message);
const msgs = playlistFiltered.map(u => u.message);
const message = [...msgs, pMsg];
if (!message.includes(messageReaction.message))
return;
}
if (theGuildPlayer && theGuildPlayer.playerMessage) {
await removeUsersThatShouldNotReact(theGuildPlayer, messageReaction);
if (!['⏸️', '▶️', '⏮️', '⏭️', '🔁', '🔂', '❎'].includes(messageReaction.emoji.name)) {
const users = messageReaction.users.cache.map(u => u);
for (const user of users.map(u => u)) {
try {
messageReaction.users.remove(user);
}
catch (_) {
break;
}
}
}
}
onMessageReactionAddOrRemove(messageReaction);
}
});
client.on('messageReactionRemove', async (messageReaction) => {
if (!messageReaction.message || !messageReaction.message.guild)
return;
const channel = messageReaction.message.channel;
const guild = channel.guild;
if (!guild || !guild.me)
return;
const theGuildPlayer = getGuildPlayer(youtubePlayer, messageReaction.message.guild);
if (theGuildPlayer) {
const pMsg = theGuildPlayer.playerMessage;
const playlistFiltered = theGuildPlayer.playlist.filter(p => p.message);
const msgs = playlistFiltered.map(u => u.message);
const message = [...msgs, pMsg];
if (!message.includes(messageReaction.message))
return;
}
const channelPermissions = channel.permissionsFor(guild.me);
if (channelPermissions && channelPermissions.has('MANAGE_MESSAGES'))
onMessageReactionAddOrRemove(messageReaction);
});
client.on('messageDelete', async (message) => {
if (message.author !== client.user)
return;
if (!message.guild)
return;
const guildPlayer = getGuildPlayer(youtubePlayer, message.guild);
if (!guildPlayer)
return;
if (guildPlayer.playerMessage === message) {
guildPlayer.deletePlayerMessage();
await guildPlayer.updatePlayer();
return;
}
if (guildPlayer.removeFromPlayListByMessage(message, true) && guildPlayer.length < 2)
guildPlayer.recreateOrRecreatePlayerButtons();
});
client.on('guildDelete', guild => {
const guildPlayer = getGuildPlayer(youtubePlayer, guild);
const lang = guildPlayer ? guildPlayer.language : language_1.playerLanguage.get(youtubePlayer);
destroyGuildPlayer(youtubePlayer, guild, lang);
});
client.on('channelDelete', channel => {
const guildData = guildPlayer.get(youtubePlayer);
for (const k of Object.entries(guildData)) {
if (k[1].playerChannel === channel) {
const pc = k[1].playerChannel;
const voice = pc.guild.voice;
if (voice && voice.channel)
voice.channel.leave();
return;
}
}
});
client.on('channelUpdate', channel => {
const guildData = guildPlayer.get(youtubePlayer);
for (const k of Object.entries(guildData)) {
if (k[1].playerChannel === channel) {
const me = k[1].playerChannel.guild.me;
if (!me)
continue;
const permissions = k[1].playerChannel.permissionsFor(me);
if (permissions && !permissions.has('SEND_MESSAGES')) {
const voice = k[1].playerChannel.guild.voice;
if (voice && voice.channel)
voice.channel.leave();
return;
}
}
}
});
client.on('message', m => {
const msg = m;
if (!msg.guild)
return;
if (msg.author === msg.client.user)
return;
if (!hardDeleteUserMessage.get(youtubePlayer))
return;
const player = getGuildPlayer(youtubePlayer, msg.guild);
if (!!player && player.playerChannel === msg.channel) {
msg.delete().catch(err => { msg.client.emit('error', err); });
}
});
client.on('voiceStateUpdate', voiceChannel => {
if (!voiceChannel.guild.voice || !voiceChannel.guild.voice.channel) {
const guildPlayer = getGuildPlayer(youtubePlayer, voiceChannel.guild);
if (!guildPlayer)
return;
const language = language_1.playerLanguage.get(youtubePlayer);
destroyGuildPlayer(youtubePlayer, voiceChannel.guild, language);
}
});
}
}
async function removeUsersThatShouldNotReact(guildPlayer, messageReaction) {
const guildMembers = guildPlayer.getFromVoiceAndMessageReactions(messageReaction);
const messageGuildMembersReaction = guildPlayer.getGuildMembersFromReactions(messageReaction);
const membersThatShouldNotReact = messageGuildMembersReaction.filter(g => !guildMembers.includes(g));
for (const member of membersThatShouldNotReact) {
try {
await messageReaction.users.remove(member);
}
catch (err) {
messageReaction.message.client.emit('error', err);
}
}
}
async function addYoutubeToQueue(youtubePlayer, message, urls, playlist = '', lang) {
if (!message.guild || !message.member)
return;
const player = getOrSetGuildPlayer(youtubePlayer, message, lang);
const language = lang.getLang();
if (isPlaylistFull(message, youtubePlayer, lang))
return;
if (!multipleParser.get(youtubePlayer))
urls.length = 1;
const playlistProgress = (index) => {
return language.player.parsingPlaylist.replace(/<URL>/g, `\n<${playlist}>`) + ` ${index}/${urls.length}`;
};
const msgForInfoMsg = playlist ? playlistProgress(0) : language.player.searching.replace(/<URL>/g, `\n<${urls.join('>\n <')}>`);
const infoMessage = await messages_1.info(message.channel, msgForInfoMsg, language.info);
for (let i = 0; i < urls.length; i++) {
const youtubeClass = youtube.get(youtubePlayer);
if ((isPlaylistFull(message, youtubePlayer, lang))) {
player.deletePlayerMessage();
await player.updatePlayer();
break;
}
const title = player.isAlreadyOnPlaylistByUrl(urls[i]);
if (title) {
if (player.currentPlayListItem && player.currentPlayListItem.videoInfo.title === title)
await messages_1.errorInfo(message.channel, `${message.author} ${language.isCurrentlyPlaying}\n${title}`, language.error, selfDeleteTime.get(youtubePlayer));
else
await messages_1.errorInfo(message.channel, `${message.author} ${language.alreadyOnPlaylist}\n${title}`, language.error, selfDeleteTime.get(youtubePlayer));
await infoMessage.delete().catch((e) => { infoMessage.client.emit('error', e); });
player.deletePlayerMessage();
await player.updatePlayer();
break;
}
try {
const videoInfo = await yt_core_discord_1.getYTInfo(urls[i]);
const maxTLength = maxTrackLength.get(youtubePlayer);
if (parseInt(videoInfo.length_seconds) / 60 > maxTLength) {
await messages_1.errorInfo(message.channel, ` ${message.author} ${language.toLongTrack.replace(/<TRACKURL>/g, `<${videoInfo.video_url}>`).replace(/<MAXLENGTH>/g, maxTrackLength.toString())}`, language.error, selfDeleteTime.get(youtubePlayer));
await infoMessage.delete().catch((e) => { infoMessage.client.emit('error', e); });
player.deletePlayerMessage();
await player.updatePlayer();
break;
}
const playlistItem = {
videoInfo,
stream: await yt_core_discord_1.getStream(videoInfo),
videoData: youtubeClass ? await youtubeClass.getVideoInfo(urls[i]) : undefined,
submitted: new Date(Date.now()),
submitter: message.member,
};
if (playlist && infoMessage) {
const m = Array.isArray(infoMessage) ? infoMessage[0] : infoMessage;
const embed = messages_1.Embeds.infoEmbed(playlistProgress(i));
if (messages_1.canEmbed(message.channel))
await m.edit(embed);
else
m.edit(await messages_1.stringifyRichEmbed(embed, message.guild));
}
if (player.push(playlistItem)) {
await startPlayer(youtubePlayer, message, lang);
if (i === urls.length - 1)
await sendQueueVideoInfo(youtubePlayer, message, playlistItem, lang, false, true);
else
await sendQueueVideoInfo(youtubePlayer, message, playlistItem, lang, false, false);
}
if (i !== urls.length - 1)
await wait(playlistParseWait.get(youtubePlayer));
}
catch (error) {
messages_1.errorInfo(message.channel, error.toString(), language.error, selfDeleteTime.get(youtubePlayer));
break;
}
}
if (infoMessage)
infoMessage.delete().catch(() => { });
}
async function youtubeLuckSearch(youtubePlayer, message, query, lang) {
if (!message.member || !message.guild)
return;
const player = getOrSetGuildPlayer(youtubePlayer, message, lang);
const language = player.language.getLang();
const youtubeAPI = youtube.get(youtubePlayer);
if (isPlaylistFull(message, youtubePlayer, lang))
return;
let url = '';
const infoMessage = await messages_1.info(message.channel, language.player.searching.replace(/<URL>/g, `\`${query}\``), language.info);
let playlistItem;
try {
const result = youtubeAPI ? await youtubeAPI.searchOnLuck(query) : await yt_core_discord_1.searchYTVideo(query);
const lengthSeconds = typeof result.length_seconds === 'string' ? parseInt(result.length_seconds) : result.length_seconds;
url = `${result.video_url} `;
const maxTrackLengthMinutes = maxTrackLength.get(youtubePlayer);
if (lengthSeconds > maxTrackLengthMinutes * 60) {
await messages_1.errorInfo(message.channel, `${message.author} ${language.toLongTrack.replace(/<TRACK_URL>/g, `${result.video_url}`).replace(/<MAX_LENGTH>/g, maxTrackLengthMinutes.toString())}`, language.error, selfDeleteTime.get(youtubePlayer));
await infoMessage.delete().catch((e) => { infoMessage.client.emit('error', e); });
player.deletePlayerMessage();
await player.updatePlayer();
return;
}
const title = player.isAlreadyOnPlaylistById(result.video_id);
if (title) {
await messages_1.errorInfo(message.channel, `${message.author} ${language.alreadyOnPlaylist}\n${title}`, language.error, selfDeleteTime.get(youtubePlayer));
await infoMessage.delete().catch((e) => { infoMessage.client.emit('error', e); });
player.deletePlayerMessage();
await player.updatePlayer();
return;
}
const videoInfo = youtubeAPI ? await yt_core_discord_1.getYTInfo(result.video_url) : result;
playlistItem = {
videoInfo,
stream: await yt_core_discord_1.getStream(videoInfo),
videoData: youtubeAPI ? result : undefined,
submitted: new Date(Date.now()),
submitter: message.member,
};
if (!player.push(playlistItem)) {
messages_1.errorInfo(message.channel, `Unable to add to playlist`, language.error, selfDeleteTime.get(youtubePlayer));
return;
}
}
catch (error) {
messages_1.errorInfo(message.channel, `${url}${language.foundVideoUnavailable}`, language.error, selfDeleteTime.get(youtubePlayer));
message.client.emit('error', error);
return;
}
infoMessage.delete().catch((e) => { infoMessage.client.emit('error', e); });
await startPlayer(youtubePlayer, message, lang);
await sendQueueVideoInfo(youtubePlayer, message, playlistItem, lang, true);
}
async function parsePlayList(youtubePlayer, message, playlistUrl, lang, failsafe = false) {
const language = lang.getLang();
if (isPlaylistFull(message, youtubePlayer, lang))
return;
try {
const playlist = await yt_core_discord_1.parsePlaylist(playlistUrl);
const urls = playlist.items.map((i) => i.url_simple);
if (urls.length)
addYoutubeToQueue(youtubePlayer, message, urls, playlistUrl, lang);
else
throw new Error('playlist not found');
}
catch (error) {
const url = playlistUrl.match(youtubeTester);
if (failsafe && url)
addYoutubeToQueue(youtubePlayer, message, url, undefined, lang);
else {
messages_1.errorInfo(message.channel, `${language.playListParseFail}`, language.error, selfDeleteTime.get(youtubePlayer));
}
}
}
function isPlaylistFull(message, youtubePlayer, lang, exceeded = false) {
const language = lang.getLang();
const player = getOrSetGuildPlayer(youtubePlayer, message, lang);
if (!player)
return false;
if (player.length > maxItemsInPlayList.get(youtubePlayer) - 1) {