diff --git a/README.md b/README.md index 7261aa2..8ffdfc3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -# Discord.js Music Bot Addon [![npm package](https://nodei.co/npm/discord.js-musicbot-addon.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/discord.js-musicbot-addon/) [![Discord Server](https://discordapp.com/api/guilds/360519133219127297/embed.png)](https://discord.gg/cADwxKs) [![Downlaods](https://img.shields.io/npm/dt/discord.js-musicbot-addon.svg?maxAge=3600)](https://www.npmjs.com/package/discord.js-musicbot-addon) [![Version](https://img.shields.io/npm/v/discord.js-musicbot-addon.svg?maxAge=3600)](https://www.npmjs.com/package/discord.js-musicbot-addon) *** @@ -31,11 +30,11 @@ __Pre-installation:__ It is recommended to have the stable over dev branch. 2. `ffmpeg installed` and in your PATH. -Allows the bot to join voice as well as speak. +Allows the bot to join voice as well as speak. * Download the ffmpeg package for your system. * Extract it to the proper place. -* Set it to your PATH environment variables. - +* Set it to your PATH environment variables. + 3. `npm install node-opus` or `npm install opusscript` Required for voice. Discord _prefers_ node-opus, but it is your choice. @@ -43,7 +42,7 @@ __Installation:__ * `npm install discord.js-musicbot-addon` __Common installation issues:__ -__Issue:__ FFMPEG was not found on your system. +__Issue:__ FFMPEG was not found on your system. __Fix:__ Make sure ffmpeg is installed correctly and set in your PATH variable. __Issue:__ Couldn't find an Opus engine. @@ -61,6 +60,10 @@ __Fix:__ This one is a little more complicated. If that doesn't fix your issue; 1. Download and install the [Windows 8.1 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-8-1-sdk) +__Issue:__ `ffluent-ffmpeg` errors. +1. Download and install ffmpeg correctly for your OS. +2. Make sure it's in your PATH/exported. + *** # Examples & Options *** @@ -80,30 +83,43 @@ const music = new Music(, ``` __Most options are optional and thus not needed.__ -The options you can pass in music(client, options) is as followed: -* prefix: Prefix to set for commands. -* global: true/false. If set to true, will use global queues, false will use server. (default false) -* maxQueueSize: Max size of queues. -* anyoneCanSkip: Whether or not anyone can skip, true/false. Defaults false. -* anyoneCanAdjust: Whether or not anyone can set volume. Defaults false. -* clearInvoker: Whether or not to clear the command message. -* volume: Default volume. Anywhere from 1 to 200, default is 50. -* helpCmd: Name of the help command. -* playCmd: Sets the play command name. -* skipCmd: Sets the skip command name. -* queueCmd: Sets the queue command name. -* pauseCmd: Sets the name for the pause command. -* resumeCmd: Sets the name for the resume command. -* volumeCmd: Sets the name for the volume command. -* leaveCmd: Sets the name for the leave command. -* clearCmd: Sets the name for the clearqueue command. -* enableQueueStat: Disables or enables queue status (useful to prevent errors sometimes, defaults true). -* ownerOverMember: Makes it so you bypass restrictions from the bot. -* botOwner: ID of your account, __required__ if using ownerOverMember. -* logging: Disable/enable some extra, none need logging. Defaults to true. Useful but not needed. -* __youtubeKey:__ This is __REQUIRED__. Something [like this article](https://elfsight.com/help/how-to-get-youtube-api-key/) should help with that, or google how to get a YouTube API3 key. - -An example of a few custom commands would be: +The options you can pass in `music(client, {options})` and their types is as followed: +* youtubeKey: *Required*, string, a YouTube API3 key. +* botPrefix: String, the prefix of the bot. Defaults to "!". +* global: Boolean, whether to use one global queue or server specific ones. Defaults false. +* maxQueueSize: Number, max queue size allowed. Defaults 20. +* defVolume: Number, the default volume of music. 1 - 200, defaults 50. +* anyoneCanSkip: Boolean, whether or not anyone can skip. Defaults false. +* clearInvoker: Boolean, whether to delete command messages. Defaults false. +* helpCmd: String, name of the help command. +* playCmd: String, name of the play command. +* skipCmd: String, name of the skip command. +* queueCmd: String, name of the queue command. +* pauseCmd: String, name of the pause command. +* resumeCmd: String, name of the resume command. +* volumeCmd: String, name of the volume command. +* leaveCmd: String, name of the leave command. +* clearCmd: String, name of the clear command. +* loopCmd: String, name of the loop command. +* enableQueueStat: Boolean, whether to enable the queue status, old fix for an error that probably won't occur. +* anyoneCanAdjust: Boolean, whether anyone can adjust volume. Defaults false. +* ownerOverMember: Boolean, whether the owner over-rides CanAdjust and CanSkip. Defaults false. +* botOwner: String, the ID of the Discord user to be seen as the owner. Required if using ownerOverMember. +* logging: Boolean, some extra none needed logging (such as caught errors that didn't crash the bot, etc). Defaults false. +* enableAliveMessage: Boolean, enables the bot to log a message in the console every x milliseconds. +* aliveMessage: String, the message to be logged.\*_note_ +* aliveMessageTime: Number, time in _**milliseconds**_ the bot logs the message. Defaults to 600000 (5 minutes). + +\* defaut for aliveMessage looks like: +``` +---------------------------------- +'BotUsername' online since 'lastReadyTime'! +---------------------------------- +``` + +For the youtube API3 key, something [like this article](https://elfsight.com/help/how-to-get-youtube-api-key/) should help with that, or google how to get a YouTube API3 key from the Google console. + +An example of a few custom options would be: ```javascript const music = new Music(client, { prefix: ">", @@ -113,6 +129,7 @@ const music = new Music(client, { playCmd: 'music', leaveCmd: 'begone', ownerOverMember: true, + enableClear: false, botOwner: '1234567890', youtubeKey: 'some-key_here' }); @@ -123,18 +140,32 @@ Again if you have any issues, feel free to open one on the repo, or join my [Dis *** # Changelog *** +# 1.6.1 +* Fixed some typos and minor errors. + +## 1.6 +* Removed asynchronous functions since some people don't like using the latest and greatest. +* Added enabling/disabling commands. +* Added looping. +* "Class" update. +* Fixed some errors/bugs from 1.5. +* Added alive message function && (heh) options. +* Removed enabling/disabling commands until further notice. +* Added all additions from pull requests to the main npm module. +* Updated examples. + ## 1.5.1 * Added playlist support (thanks Rodabaugh for reminding me). * Fixed `clearqueue` crashing if queue is empty. * Fixed `skip` crashing if the queue is empty. * Now requires `ffmpeg` installed over `ffmpeg-binaries`. - + ## 1.4.0 * Added wrapping. * Added owner over member options. * Fixed errors. -* Reworked the the playing music method. -* Fixed (probably) UnknownSpawn errors. +* Reworked the playing music method. +* Fixed `UnknownSpawn` errors. * Bot now requires your own YouTube Data API3 key for searching. * Music is less likely to cut out now. * Volume works again (again). diff --git a/examples/advancedMusicBot.js b/examples/advancedMusicBot.js index 9a0f4c3..3296787 100644 --- a/examples/advancedMusicBot.js +++ b/examples/advancedMusicBot.js @@ -33,7 +33,7 @@ const music = new Music(client, { prefix: prefix, // Prefix for the commands. global: true, // Non-server-specific queues. maxQueueSize: 25, // Maximum queue size of 25. - clearInvoker: true, // If permissions applicable, allow the bot to delete the messages that invoke it (start with prefix). + clearInvoker: true, // If permissions applicable, allow the bot to delete the messages that invoke it. helpCmd: 'mhelp', //Sets the name for the help command. playCmd: 'music', //Sets the name for the 'play' command. volumeCmd: 'adjust', //Sets the name for the 'volume' command. diff --git a/examples/basicMusicBot.js b/examples/basicMusicBot.js index ae9ca4b..5c98c84 100644 --- a/examples/basicMusicBot.js +++ b/examples/basicMusicBot.js @@ -7,5 +7,7 @@ client.on('ready', () => { console.log(`[Start] ${new Date()}`); }); -const music = new Music(client); +const music = new Music(client, { + youtubeKey: "some-key_here" +}); client.login(token); diff --git a/examples/settings.json b/examples/settings.json index 58ceaef..8007355 100644 --- a/examples/settings.json +++ b/examples/settings.json @@ -1,4 +1,4 @@ { "token": "supper secret bot token here", - "sprefix": "!" + "prefix": "!" } diff --git a/index.js b/index.js index 4c9e1c0..356252f 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ /** - * Original code nexu-dev, https://github.com/nexu-dev/discord.js-music + * Original code nexu-dev, https://github.com/nexu-dev/discord.js-client * Tweeked by Demise. */ @@ -9,191 +9,266 @@ const stream = require('youtube-audio-stream'); const search = require('youtube-search'); const ypi = require('youtube-playlist-info'); +// const {Client} = require('discord.js'); const Discord = require('discord.js'); const EventEmitter = require('events'); class Emitter extends EventEmitter {} const emitter = new Emitter(); -/** - * Takes a discord.js client and turns it into a music bot. - * Thanks to 'derekmartinez18' for helping. - * - * @param {Client} client - The discord.js client. - * @param {object} options - (All Are Optional) Options to configure the music bot. Acceptable options are: - * prefix: The prefix to use for the commands (default '!'). - * global: Whether to use a global queue instead of a server-specific queue (default false). - * maxQueueSize: The maximum queue size (default 20). - * anyoneCanSkip: Allow anybody to skip the song. - * anyoneCanAdjust: Allow anyone to adjust volume. - * clearInvoker: Clear the command message. - * volume: The default volume of the player. - * helpCmd: Name of the help command (defualt: musichelp). - * playCmd: Sets the play command name. - * skipCmd: Sets the skip command name. - * queueCmd: Sets the queue command name. - * pauseCmd: Sets the name for the pause command. - * resumeCmd: Sets the name for the resume command. - * volumeCmd: Sets the name for the volume command. - * leaveCmd: Sets the name for the leave command. - * clearCmd: Sets the name for the clear command. - * enableQueueStat: Disables or enables queue status (useful to prevent errors sometimes, defaults true). - */ + /** + * Takes a discord.js client and turns it into a client bot. + * Thanks to Rodabaugh for helping with some tweaks and ideas. + * + * @param {Client} client - The discord.js client. + * @param {object} options - Options to configure the client bot. Acceptable options are: + * prefix: The prefix to use for the commands (default '!'). + * global: Whether to use a global queue instead of a server-specific queue (default false). + * maxQueueSize: The maximum queue size (default 20). + * anyoneCanSkip: Allow anybody to skip the song. + * anyoneCanAdjust: Allow anyone to adjust volume. + * clearInvoker: Clear the command message. + * volume: The default volume of the player. + * helpCmd: Name of the help command (defualt: clienthelp). + * playCmd: Sets the play command name. + * skipCmd: Sets the skip command name. + * queueCmd: Sets the queue command name. + * pauseCmd: Sets the name for the pause command. + * resumeCmd: Sets the name for the resume command. + * volumeCmd: Sets the name for the volume command. + * leaveCmd: Sets the name for the leave command. + * clearCmd: Sets the name for the clear command. + * enableQueueStat: Disables or enables queue status (useful to prevent errors sometimes, defaults true). + */ + //note that I'm too lazy to update those ^, refer to the readme.md instead. + module.exports = function (client, options) { // Get all options. - const YOUTUBE_KEY = (options && options.youtubeKey); - const PREFIX = (options && options.prefix) || '!'; - const GLOBAL = (options && options.global) || false; - const MAX_QUEUE_SIZE = parseInt((options && options.maxQueueSize) || 20); - const DEFAULT_VOLUME = parseInt((options && options.volume) || 50); - const ALLOW_ALL_SKIP = (options && options.anyoneCanSkip) || false; - const CLEAR_INVOKER = (options && options.clearInvoker) || false; - const HELP_CMD = (options && options.helpCmd) || 'musichelp'; - const PLAY_CMD = (options && options.playCmd) || 'play'; - const SKIP_CMD = (options && options.skipCmd) || 'skip'; - const QUEUE_CMD = (options && options.queueCmd) || 'queue'; - const PAUSE_CMD = (options && options.pauseCmd) || 'pause'; - const RESUME_CMD = (options && options.resumeCmd) || 'resume'; - const VOLUME_CMD = (options && options.volumeCmd) || 'volume'; - const LEAVE_CMD = (options && options.leaveCmd) || 'leave'; - const CLEAR_CMD = (options && options.clearCmd) || 'clearqueue'; - const ENABLE_Q_STAT = (options && options.enableQueueStat) || true; - const ALLOW_ALL_VOL = (options && options.anyoneCanAdjust) || false; - const OWNER_OVER = (options && options.ownerOverMember) || false; - const BOT_OWNER_ID = (options && options.botOwner) || null; - const LOGGING = (options && options.logging) || true; + class Music { + constructor(client, options) { + this.youtubeKey = (options && options.youtubeKey); + this.botPrefix = (options && options.prefix) || '!'; + this.global = (options && options.global) || false; + this.maxQueueSize = parseInt((options && options.maxQueueSize) || 20); + this.defVolume = parseInt((options && options.volume) || 50); + this.anyoneCanSkip = (options && options.anyoneCanSkip) || false; + this.clearInvoker = (options && options.clearInvoker) || false; + this.helpCmd = (options && options.helpCmd) || 'musichelp'; + this.playCmd = (options && options.playCmd) || 'play'; + this.skipCmd = (options && options.skipCmd) || 'skip'; + this.queueCmd = (options && options.queueCmd) || 'queue'; + this.pauseCmd = (options && options.pauseCmd) || 'pause'; + this.resumeCmd = (options && options.resumeCmd) || 'resume'; + this.volumeCmd = (options && options.volumeCmd) || 'volume'; + this.leaveCmd = (options && options.leaveCmd) || 'leave'; + this.clearCmd = (options && options.clearCmd) || 'clearqueue'; + this.loopCmd = (options && options.loopCmd) || 'loop'; + this.enableQueueStat = (options && options.enableQueueStat) || true; + this.anyoneCanAdjust = (options && options.anyoneCanAdjust) || false; + this.ownerOverMember = (options && options.ownerOverMember) || false; + this.botOwner = (options && options.botOwner) || null; + this.logging = (options && options.logging) || false; + this.enableAliveMessage = (options && options.enableAliveMessage) || false; + this.aliveMessage = (options && options.aliveMessage) || ""; + this.aliveMessageTime = (options && options.aliveMessageTime) || 600000; + this.loop = "false"; + } + } + + var musicbot = new Music(client, options); //Init errors. - if (process.version.slice(1).split('.')[0] < 8) throw new Error('Node 8.0.0 or higher was not found, please update Node.js.'); - if (!YOUTUBE_KEY) { - console.log(new Error(`youtubeKey is required but missing`)); - process.exit(1); - }; - if (YOUTUBE_KEY && typeof YOUTUBE_KEY !== 'string') { - console.log(new TypeError(`youtubeKey must be a string`)); - process.exit(1); - }; + function checkErrors() { + if (process.version.slice(1).split('.')[0] < 8) console.log(new Error('Node 8.0.0 or higher was not found, 8+ is recommended. You may still use your version however.')); + if (!musicbot.youtubeKey) { + console.log(new Error(`youtubeKey is required but missing`)); + process.exit(1); + }; + if (musicbot.youtubeKey && typeof musicbot.youtubeKey !== 'string') { + console.log(new TypeError(`youtubeKey must be a string`)); + process.exit(1); + }; - //Owner errors. - if (typeof OWNER_OVER !== 'boolean') { - console.log(new TypeError(`ownerOverMember must be a boolean`)); - process.exit(1); - }; - if (OWNER_OVER && typeof BOT_OWNER_ID !== 'string') { - console.log(new TypeError(`botOwner must be a string`)); - process.exit(1); - }; + //Owner errors. + if (typeof musicbot.ownerOverMember !== 'boolean') { + console.log(new TypeError(`ownerOverMember must be a boolean`)); + process.exit(1); + }; + if (musicbot.ownerOverMember && typeof musicbot.botOwner !== 'string') { + console.log(new TypeError(`botOwner must be a string`)); + process.exit(1); + }; - //PREFIX errors. - if (typeof PREFIX !== 'string') { - console.log(new TypeError(`prefix must be a string`)); - process.exit(1); - }; - if (PREFIX.length < 1 || PREFIX.length > 10) { - console.log(new RangeError(`prefix length must be between 1 and 10`)); - process.exit(1); - }; + //musicbot.botPrefix errors. + if (typeof musicbot.botPrefix !== 'string') { + console.log(new TypeError(`prefix must be a string`)); + process.exit(1); + }; + if (musicbot.botPrefix.length < 1 || musicbot.botPrefix.length > 10) { + console.log(new RangeError(`prefix length must be between 1 and 10`)); + process.exit(1); + }; - //GLOBAL errors. - if (typeof GLOBAL !== 'boolean') { - console.log(new TypeError(`global must be a boolean`)); - process.exit(1); - }; + //musicbot.global errors. + if (typeof musicbot.global !== 'boolean') { + console.log(new TypeError(`global must be a boolean`)); + process.exit(1); + }; - //MAX_QUEUE_SIZE errors. - if (typeof MAX_QUEUE_SIZE !== 'number') { - console.log(new TypeError(`maxQueueSize must be a number`)); - process.exit(1); - }; - if (!Number.isInteger(MAX_QUEUE_SIZE) || MAX_QUEUE_SIZE < 1) { - console.log(new TypeError(`maxQueueSize must be an integer more than 0`)); - process.exit(1); - }; + //musicbot.maxQueueSize errors. + if (typeof musicbot.maxQueueSize !== 'number') { + console.log(new TypeError(`maxQueueSize must be a number`)); + process.exit(1); + }; + if (!Number.isInteger(musicbot.maxQueueSize) || musicbot.maxQueueSize < 1) { + console.log(new TypeError(`maxQueueSize must be an integer more than 0`)); + process.exit(1); + }; - //DEFAULT_VOLUME errors. - if (typeof DEFAULT_VOLUME !== 'number') { - console.log(new TypeError(`defaultVolume must be a number`)); - process.exit(1); - }; - if (!Number.isInteger(DEFAULT_VOLUME) || DEFAULT_VOLUME < 1 || DEFAULT_VOLUME > 200) { - console.log(new TypeError(`defaultVolume must be an integer between 1 and 200`)); - process.exit(1); - }; + //DEFAULT_VOLUME errors. + if (typeof musicbot.defVolume !== 'number') { + console.log(new TypeError(`defaultVolume must be a number`)); + process.exit(1); + }; + if (!Number.isInteger(musicbot.defVolume) || musicbot.defVolume < 1 || musicbot.defVolume > 200) { + console.log(new TypeError(`defaultVolume must be an integer between 1 and 200`)); + process.exit(1); + }; - //ALLOW_ALL_SKIP errors. - if (typeof ALLOW_ALL_SKIP !== 'boolean') { - console.log(new TypeError(`anyoneCanSkip must be a boolean`)); - process.exit(1); - }; + //musicbot.anyoneCanSkip errors. + if (typeof musicbot.anyoneCanSkip !== 'boolean') { + console.log(new TypeError(`anyoneCanSkip must be a boolean`)); + process.exit(1); + }; - //CLEAR_INVOKER errors. - if (typeof CLEAR_INVOKER !== 'boolean') { - console.log(new TypeError(`clearInvoker must be a boolean`)); - process.exit(1); - }; + //CLEAR_INVOKER errors. + if (typeof musicbot.clearInvoker !== 'boolean') { + console.log(new TypeError(`clearInvoker must be a boolean`)); + process.exit(1); + }; - //Command name errors. - if (typeof HELP_CMD !== 'string') { - console.log(new TypeError(`helpCmd must be a string`)); - process.exit(1); - }; - if (typeof PLAY_CMD !== 'string') { - console.log(new TypeError(`playCmd must be a string`)); - process.exit(1); - }; - if (typeof SKIP_CMD !== 'string') { - console.log(new TypeError(`skipCmd must be a string`)); - process.exit(1); - }; - if (typeof QUEUE_CMD !== 'string') { - console.log(new TypeError(`queueCmd must be a string`)); - process.exit(1); - }; - if (typeof PAUSE_CMD !== 'string') { - console.log(new TypeError(`pauseCmd must be a string`)); - process.exit(1); - }; - if (typeof RESUME_CMD !== 'string') { - console.log(new TypeError(`resumeCmd must be a string`)); - process.exit(1); - }; - if (typeof VOLUME_CMD !== 'string') { - console.log(new TypeError(`volumeCmd must be a string`)); - process.exit(1); - }; - if (typeof LEAVE_CMD !== 'string') { - console.log(new TypeError(`leaveCmd must be a string`)); - process.exit(1); - }; - if (typeof CLEAR_CMD !== 'string') { - console.log(new TypeError(`clearCmd must be a string`)); - process.exit(1); - }; + //aliveMessage erros. + if (typeof musicbot.enableAliveMessage !== 'boolean') { + console.log(new TypeError(`enableAliveMessage must be a boolean`)); + process.exit(1); + } + if (typeof musicbot.aliveMessage !== 'string') { + console.log(new TypeError(`aliveMessage must be a string`)); + process.exit(1); + } + if (typeof musicbot.aliveMessageTime !== 'number') { + console.log(new TypeError(`aliveMessageTime must be a number`)); + process.exit(1); + } - //ENABLE_Q_STAT errors. - if (typeof ENABLE_Q_STAT !== 'boolean') { - console.log(new TypeError(`enableQueueStat must be a boolean`)); - process.exit(1); - }; + //Command name errors. + if (typeof musicbot.helpCmd !== 'string') { + console.log(new TypeError(`helpCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableHelp !== 'boolean') { + // console.log(new TypeError(`enableHelp must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.playCmd !== 'string') { + console.log(new TypeError(`playCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enablePlay !== 'boolean') { + // console.log(new TypeError(`enablePlay must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.skipCmd !== 'string') { + console.log(new TypeError(`skipCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableSkip !== 'boolean') { + // console.log(new TypeError(`enableSkip must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.queueCmd !== 'string') { + console.log(new TypeError(`queueCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableQueue !== 'boolean') { + // console.log(new TypeError(`enableQueue must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.pauseCmd !== 'string') { + console.log(new TypeError(`pauseCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enablePause !== 'boolean') { + // console.log(new TypeError(`enablePause must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.resumeCmd !== 'string') { + console.log(new TypeError(`resumeCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableResume !== 'boolean') { + // console.log(new TypeError(`enableResume must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.volumeCmd !== 'string') { + console.log(new TypeError(`volumeCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableVolume !== 'boolean') { + // console.log(new TypeError(`enableVolume must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.leaveCmd !== 'string') { + console.log(new TypeError(`leaveCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableLeave !== 'boolean') { + // console.log(new TypeError(`enableLeave must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.clearCmd !== 'string') { + console.log(new TypeError(`clearCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableClear !== 'boolean') { + // console.log(new TypeError(`enableClear must be a boolean`)); + // process.exit(1); + // }; + if (typeof musicbot.loopCmd !== 'string') { + console.log(new TypeError(`loopCmd must be a string`)); + process.exit(1); + }; + // if (typeof musicbot.enableLoop !== 'boolean') { + // console.log(new TypeError(`enableLoop must be a boolean`)); + // process.exit(1); + // }; + + //musicbot.enableQueueStat errors. + if (typeof musicbot.enableQueueStat !== 'boolean') { + console.log(new TypeError(`enableQueueStat must be a boolean`)); + process.exit(1); + }; - //ALLOW_ALL_VOL errors. - if (typeof ALLOW_ALL_VOL !== 'boolean') { - console.log(new TypeError(`anyoneCanAdjust must be a boolean`)); - process.exit(1); - }; + //musicbot.anyoneCanAdjust errors. + if (typeof musicbot.anyoneCanAdjust !== 'boolean') { + console.log(new TypeError(`anyoneCanAdjust must be a boolean`)); + process.exit(1); + }; - if (typeof LOGGING !== 'boolean') { - console.log(new TypeError(`logging must be a boolean`)); - process.exit(1); - } + if (typeof musicbot.logging !== 'boolean') { + console.log(new TypeError(`logging must be a boolean`)); + process.exit(1); + } - //Misc. - if (GLOBAL && MAX_QUEUE_SIZE < 50) console.warn(`global queues are enabled while maxQueueSize is below 50! Recommended to use a higher size.`); + //Misc. + if (musicbot.global && musicbot.maxQueueSize < 50) console.warn(`global queues are enabled while maxQueueSize is below 50! Recommended to use a higher size.`); + }; + checkErrors(); //Set the YouTube API key. const opts = { maxResults: 1, - key: YOUTUBE_KEY + key: musicbot.youtubeKey }; // Create an object of queues. @@ -204,38 +279,53 @@ module.exports = function (client, options) { const message = msg.content.trim(); // Check if the message is a command. - if (message.toLowerCase().startsWith(PREFIX.toLowerCase())) { - // Get the command and suffix. - const command = message.substring(PREFIX.length).split(/[ \n]/)[0].toLowerCase().trim(); - const suffix = message.substring(PREFIX.length + command.length).trim(); + if (message.toLowerCase().startsWith(musicbot.botPrefix.toLowerCase())) { + // Get the command, suffix and bot. + const command = message.substring(musicbot.botPrefix.length).split(/[ \n]/)[0].toLowerCase().trim(); + const suffix = message.substring(musicbot.botPrefix.length + command.length).trim(); // Process the commands. switch (command) { - case HELP_CMD: - return musichelp(msg, suffix); - case PLAY_CMD: + case musicbot.helpCmd: + return musicbothelp(msg, suffix); + case musicbot.playCmd: return play(msg, suffix); - case SKIP_CMD: + case musicbot.skipCmd: return skip(msg, suffix); - case QUEUE_CMD: + case musicbot.queueCmd: return queue(msg, suffix); - case PAUSE_CMD: + case musicbot.pauseCmd: return pause(msg, suffix); - case RESUME_CMD: + case musicbot.resumeCmd: return resume(msg, suffix); - case VOLUME_CMD: + case musicbot.volumeCmd: return volume(msg, suffix); - case LEAVE_CMD: + case musicbot.leaveCmd: return leave(msg, suffix); - case CLEAR_CMD: + case musicbot.clearCmd: return clearqueue(msg, suffix); + case musicbot.loopCmd: + return loop(msg, suffix); } - if (CLEAR_INVOKER) { + if (musicbot.clearInvoker) { msg.delete(); } } }); + /** + * Live message function. + */ + if (musicbot.enableAliveMessage) { + setInterval(function liveMessage() { + if (musicbot.aliveMessage.length < 2) { + musicbot.aliveMessage = "----------------------------------\n"+client.user.username+" online since "+client.readyAt+"!"+"\n----------------------------------"; + } + + console.log(musicbot.aliveMessage); + }, musicbot.aliveMessageTime); + }; + /** * Checks if a user is an admin. * @@ -243,7 +333,7 @@ module.exports = function (client, options) { * @returns {boolean} - */ function isAdmin(member) { - if (OWNER_OVER && member.id === BOT_OWNER_ID) return member.hasPermission("ADMINISTRATOR"); + if (musicbot.ownerOverMember && member.id === musicbot.botOwner) return member.hasPermission("ADMINISTRATOR"); return member.hasPermission("ADMINISTRATOR"); } @@ -255,8 +345,8 @@ module.exports = function (client, options) { * @returns {boolean} - If the user can skip */ function canSkip(member, queue) { - if (OWNER_OVER && member.id === BOT_OWNER_ID) return true; - if (ALLOW_ALL_SKIP) return true; + if (musicbot.ownerOverMember && member.id === musicbot.botOwner) return true; + if (musicbot.anyoneCanSkip) return true; else if (queue[0].requester === member.id) return true; else if (isAdmin(member)) return true; else return false; @@ -270,7 +360,7 @@ module.exports = function (client, options) { * @returns {boolean} - If the user can adjust */ function canAdjust(member, queue) { - if (ALLOW_ALL_VOL) return true; + if (musicbot.anyoneCanAdjust) return true; else if (queue[0].requester === member.id) return true; else if (isAdmin(member)) return true; else return false; @@ -284,7 +374,7 @@ module.exports = function (client, options) { */ function getQueue(server) { // Check if global queues are enabled. - if (GLOBAL) server = '_'; // Change to global queue. + if (musicbot.global) server = '_'; // Change to global queue. // Return the queue. if (!queues[server]) queues[server] = []; @@ -298,76 +388,83 @@ module.exports = function (client, options) { * @param {string} suffix - Command suffix. * @returns {} - The response edit. */ - function musichelp(msg, suffix) { - if (!suffix) { - const embed = new Discord.RichEmbed() - .setAuthor("Commands", msg.author.displayAvatarURL) - .setDescription(`Commands with a * require Admin perms. Use \`${PREFIX}musichelp command\` for help on usage.`) - .addField(HELP_CMD, `Displays this text.`) - .addField(PLAY_CMD, `Queue a song by url or search.`) - .addField(SKIP_CMD, `Skip a song or mutli songs.`) - .addField(QUEUE_CMD, `Shows the current queue`) - .addField(PAUSE_CMD, `* Pauses the queue.`) - .addField(RESUME_CMD, `* Resume the queue.`) - .addField(VOLUME_CMD, `* Adjusts the volume of the bot.`) - .addField(LEAVE_CMD, `Leave and clear the queue`) - .addField(CLEAR_CMD, `Clears the current queue.`) - .setColor(0x27e33d) + function musicbothelp(msg, suffix) { + if (!suffix || suffix.includes('help')) { + console.log(musicbot); + const embed = new Discord.RichEmbed(); + embed.setAuthor("Commands", msg.author.displayAvatarURL) + embed.setDescription(`Commands with a * require Admin perms. Use \`${musicbot.botPrefix}musicbothelp command\` for help on usage.`) + embed.addField(musicbot.helpCmd, `Displays this text.`) + embed.addField(musicbot.playCmd, `Queue a song by url or search.`) + embed.addField(musicbot.skipCmd, `Skip a song or mutli songs.`) + embed.addField(musicbot.queueCmd, `Shows the current queue`) + embed.addField(musicbot.pauseCmd, `* Pauses the queue.`) + embed.addField(musicbot.resumeCmd, `* Resume the queue.`) + embed.addField(musicbot.volumeCmd, `* Adjusts the volume of the bot.`) + embed.addField(musicbot.leaveCmd, `Leave and clear the queue`) + embed.addField(musicbot.clearCmd, `Clears the current queue.`) + embed.setColor(0x27e33d) msg.channel.send({embed}); } else { - if (suffix.includes(PLAY_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${PLAY_CMD}`, client.user.avatarURL) - .setDescription(`Addes a song to the queue.\n**__Usage:__** ${PREFIX}${PLAY_CMD} URL|search for something.`) - .setColor(0x27e33d) + if (suffix.includes(musicbot.playCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.playCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Addes a song to the queue.\n**__Usage:__** ${musicbot.botPrefix}${musicbot.playCmd} Video URL | Playlist URL | search for something.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(SKIP_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${SKIP_CMD}`, client.user.avatarURL) - .setDescription(`Skips the playing song or mutli songs. You must be the person that queued the song to skip it, or admin.\n**__Usage:__** ${PREFIX}${SKIP_CMD} [numer of songs]`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.skipCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.skipCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Skips the playing song or mutli songs. You must be the person that queued the song to skip it, or admin.\n**__Usage:__** ${musicbot.botPrefix}${musicbot.skipCmd} [numer of songs]`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(QUEUE_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${QUEUE_CMD}`, client.user.avatarURL) - .setDescription(`Displays the current queue.`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.queueCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.queueCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Displays the current queue.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(PAUSE_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${PAUSE_CMD}`, client.user.avatarURL) - .setDescription(`Pauses the current queue.`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.pauseCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.pauseCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Pauses the current queue.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(RESUME_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${RESUME_CMD}`, client.user.avatarURL) - .setDescription(`Resumes the current queue if paused.`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.resumeCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.resumeCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Resumes the current queue if paused.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(VOLUME_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${VOLUME_CMD}`, client.user.avatarURL) - .setDescription(`Adjusts the streams volume. Must be admin.\n**__Usage:__** ${PREFIX}${VOLUME_CMD} <1 to 200>`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.volumeCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.volumeCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Adjusts the streams volume. Must be admin.\n**__Usage:__** ${musicbot.botPrefix}${musicbot.volumeCmd} <1 to 200>`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(LEAVE_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${LEAVE_CMD}`, client.user.avatarURL) - .setDescription(`Leaves the voice channel and clears the queue.`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.leaveCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.leaveCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Leaves the voice channel and clears the queue.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else if (suffix.includes(CLEAR_CMD)) { - const embed = new Discord.RichEmbed() - .setAuthor(`${PREFIX}${CLEAR_CMD}`, client.user.avatarURL) - .setDescription(`Clears the current queue playing.`) - .setColor(0x27e33d) + } else if (suffix.includes(musicbot.clearCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.clearCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Clears the current queue playing.`); + embed.setColor(0x27e33d); msg.channel.send({embed}); - } else { - msg.channel.send(note('fail', `${suffix} is not a vlaid command!`)); - }; + } else if (suffix.includes(musicbot.loopCmd)) { + const embed = new Discord.RichEmbed(); + embed.setAuthor(`${musicbot.botPrefix}${musicbot.loopCmd}`, musicbot.user.avatarURL); + embed.setDescription(`Enables/disables looping of the currently being played song.`); + embed.setColor(0x27e33d); + msg.channel.send({embed}); + } else { + msg.channel.send(note('fail', `${suffix} is not a vlaid command!`)); }; }; +}; /** * The command for adding a song to the queue. @@ -376,7 +473,7 @@ module.exports = function (client, options) { * @param {string} suffix - Command suffix. * @returns {} - The response edit. */ - async function play(msg, suffix) { + function play(msg, suffix) { // Make sure the user is in a voice channel. if (msg.member.voiceChannel === undefined) return msg.channel.send(note('fail', 'You\'re not in a voice channel~')); @@ -387,7 +484,7 @@ module.exports = function (client, options) { const queue = getQueue(msg.guild.id); // Check if the queue has reached its maximum size. - if (queue.length >= MAX_QUEUE_SIZE) { + if (queue.length >= musicbot.maxQueueSize) { return msg.channel.send(note('fail', 'Maximum queue size reached!')); } @@ -400,14 +497,14 @@ module.exports = function (client, options) { const playid = searchstring.toString().split('playlist?list=')[1]; //Get info on the playlist. - ypi.playlistInfo(YOUTUBE_KEY, playid, function(playlistItems) { + ypi.playlistInfo(musicbot.youtubeKey, playid, function(playlistItems) { const newItems = Array.from(playlistItems); var skippedVideos = new Array(); var queuedVids = new Array(); for (var i = 0; i < newItems.length; i++) { var results = newItems[i]; - if (queue.length > MAX_QUEUE_SIZE) { + if (queue.length > musicbot.maxQueueSize) { skippedVideos.push(results.title); } else { results.link = `https://www.youtube.com/watch?v=` + newItems[i].resourceId.videoId; @@ -428,7 +525,7 @@ module.exports = function (client, options) { if (svids != ""){ msg.channel.send(note('wrap', `Queued:\n${qvids}\nSkipped: (Max Queue)\n${svids}`), {split: true}); } else { - msg.channel.send(note('wrap', `Queued:\n${qvids}`), {split: true}); + msg.channel.send(note('wrap', `Queued:\n${qvids}`), {split: true}); }; }; setTimeout(endrun, 5000); @@ -438,7 +535,7 @@ module.exports = function (client, options) { } else { search(searchstring, opts, function(err, results) { if (err) { - if (LOGGING) console.log(err); + if (musicbot.logging) console.log(err); const nerr = err.toString().split(':'); return response.edit(note('fail', `error occoured!\`\`\`\n${nerr[0]}: ${nerr[1]}\n\`\`\``)); }; @@ -464,7 +561,7 @@ module.exports = function (client, options) { * @param {string} suffix - Command suffix. * @returns {} - The response message. */ - function skip(msg, suffix) { + function skip(msg, suffix) { // Get the voice connection. const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id); if (voiceConnection === null) return msg.channel.send(note('fail', 'No music being played.')); @@ -487,9 +584,18 @@ module.exports = function (client, options) { queue.splice(0, toSkip - 1); // Resume and stop playing. - const dispatcher = voiceConnection.player.dispatcher; - if (voiceConnection.paused) dispatcher.resume(); - dispatcher.end(); + try { + const dispatcher = voiceConnection.player.dispatcher; + if (!dispatcher || dispatcher === null) { + if (musicbot.logging) console.log(`-----------------------------------\ndispatcher fail (testing alert)\n-----------------------------------`); + return; + }; + if (voiceConnection.paused) dispatcher.resume(); + dispatcher.end(); + } catch (e) { + if (musicbot.logging) console.log(new Error(`Play command error from userID ${msg.author.id} in guildID ${msg.guild.id}\n${e}`)); + return msg.channel.send(note('fail', 'An error occoured, sorry!')); + }; msg.channel.send(note('note', 'Skipped ' + toSkip + '!')); } @@ -508,20 +614,44 @@ module.exports = function (client, options) { const text = queue.map((video, index) => ( (index + 1) + ': ' + video.title )).join('\n'); + if (text.length > 1900) { + const newText = text.substr(0, 1899); + const otherText = text.substr(1900, text.length); + if (otherText.length > 1900) { + msg.channel.send(note('wrap', 'Queue ('+ queueStatus +'):\n' + "Past character limit...")); + } else { + if (musicbot.enableQueueStat) { + //Get the status of the queue. + let queueStatus = 'Stopped'; + const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id); + if (voiceConnection !== null) { + const dispatcher = voiceConnection.player.dispatcher; + queueStatus = dispatcher.paused ? 'Paused' : 'Playing'; + } + + // Send the queue and status. + msg.channel.send(note('wrap', 'Queue ('+ queueStatus +'):\n' + newText)); + msg.channel.send(note('wrap', 'Queue (2) ('+ queueStatus +'):\n' + otherText)); + } else { + msg.channel.send(note('wrap', 'Queue:\n' + newText)); + msg.channel.send(note('wrap', 'Queue (2):\n' + otherText)); + } + }; + } else { + if (musicbot.enableQueueStat) { + //Get the status of the queue. + let queueStatus = 'Stopped'; + const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id); + if (voiceConnection !== null) { + const dispatcher = voiceConnection.player.dispatcher; + queueStatus = dispatcher.paused ? 'Paused' : 'Playing'; + } - if (ENABLE_Q_STAT) { - //Get the status of the queue. - let queueStatus = 'Stopped'; - const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id); - if (voiceConnection !== null) { - const dispatcher = voiceConnection.player.dispatcher; - queueStatus = dispatcher.paused ? 'Paused' : 'Playing'; + // Send the queue and status. + msg.channel.send(note('wrap', 'Queue ('+ queueStatus +'):\n' + text)); + } else { + msg.channel.send(note('wrap', 'Queue:\n' + text)); } - - // Send the queue and status. - msg.channel.send(note('wrap', 'Queue ('+ queueStatus +'):\n' + text)); - } else { - msg.channel.send(note('wrap', 'Queue:\n' + text)); } } @@ -564,6 +694,7 @@ module.exports = function (client, options) { // End the stream and disconnect. voiceConnection.player.dispatcher.end(); voiceConnection.disconnect(); + msg.channel.send(note('note', 'Left voice channel.')); } else { msg.channel.send(note('fail', 'You don\'t have permission to use that command!')); } @@ -576,7 +707,7 @@ module.exports = function (client, options) { * @param {string} suffix - Command suffix. */ function clearqueue(msg, suffix) { - if (canSkip(msg.member)) { + if (isAdmin(msg.member)) { const queue = getQueue(msg.guild.id); const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id); if (voiceConnection === null) return msg.channel.send(note('fail', 'I\'m not in any channel!.')); @@ -587,7 +718,7 @@ module.exports = function (client, options) { voiceConnection.player.dispatcher.end(); voiceConnection.disconnect(); } else { - msg.channel.send(note('fail', 'You don\'t have permission to use that command! Only admins may!')); + msg.channel.send(note('fail', `You don't have permission to use that command! Only admins may!`)); } } @@ -641,6 +772,23 @@ module.exports = function (client, options) { dispatcher.setVolume((suffix/100)); } + /** + * Looping command/option. + * + * @param {Message} msg - Original message. + * @param {object} queue - The song queue for this server. + * @param {string} suffix - Command suffix. + */ + function loop(msg, suffix) { + if (musicbot.loop === "true") { + musicbot.loop = "false"; + msg.channel.send(note('note', 'Looping disabled! :arrow_forward:')); + } else if (musicbot.loop === "false") { + musicbot.loop = "true"; + msg.channel.send(note('note', 'Looping enabled! :repeat_one:')); + } + }; + /** * Executes the next song in the queue. * @@ -686,7 +834,7 @@ module.exports = function (client, options) { // Play the video. msg.channel.send(note('note', 'Now Playing: ' + video.title)).then(() => { - let dispatcher = connection.playStream(stream(video.link), {seek: 0, volume: (DEFAULT_VOLUME/100)}); + let dispatcher = connection.playStream(stream(video.link), {seek: 0, volume: (musicbot.defVolume/100)}); connection.on('error', (error) => { // Skip to the next song. @@ -705,11 +853,15 @@ module.exports = function (client, options) { dispatcher.on('end', () => { // Wait a second. setTimeout(() => { - if (queue.length > 0) { - // Remove the song from the queue. - queue.shift(); - // Play the next song in the queue. + if (musicbot.loop === "true") { executeQueue(msg, queue); + } else { + if (queue.length > 0) { + // Remove the song from the queue. + queue.shift(); + // Play the next song in the queue. + executeQueue(msg, queue); + } } }, 1000); }); @@ -735,7 +887,7 @@ module.exports = function (client, options) { } else if (type === 'fail') { return ':no_entry_sign: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203)); } else { - const harp = new Error(`${type} was an invalid type`); + const harp = new Error(`${type} was an invalid type; note function`); console.log(harp); } }; diff --git a/package.json b/package.json index 02ad6ad..1b149b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord.js-musicbot-addon", - "version": "1.5.1", + "version": "1.6.1", "description": "A music bot addon for an already made Discord.js bot to run with or simply run by itself.", "main": "index.js", "scripts": {