· 6 years ago · Dec 16, 2019, 11:30 AM
1const ytdl = require('ytdl-core');
2const {YTSearcher} = require('ytsearcher');
3const ytpl = require('ytpl');
4const Discord = require('discord.js');
5const PACKAGE = require('./package.json');
6
7exports.start = (client, options) => {
8try {
9 if (process.version.slice(1).split('.')[0] < 8) console.error(new Error(`[MusicBot] node v8 or higher is needed, please update`));
10 function moduleAvailable(name) {
11 try {
12 require.resolve(name);
13 return true;
14 } catch(e){}
15 return false;
16 };
17 if (moduleAvailable("ffmpeg-binaries")) console.error(new Error("[MUSIC] ffmpeg-binaries was found, this will likely cause problems"));
18 if (!moduleAvailable("ytdl-core") || !moduleAvailable("ytpl") || !moduleAvailable("ytsearcher")) console.error(new Error("[MUSIC] one or more youtube specific modules not found, this module will not work"));
19
20 class Music {
21 constructor(client, options) {
22 // Data Objects
23 this.commands = new Map();
24 this.commandsArray = [];
25 this.aliases = new Map();
26 this.queues = new Map();
27 this.client = client;
28
29 // Play Command options
30 this.play = {
31 enabled: (options.play == undefined ? true : (options.play && typeof options.play.enabled !== 'undefined' ? options.play && options.play.enabled : true)),
32 run: "playFunction",
33 alt: (options && options.play && options.play.alt) || [],
34 help: (options && options.play && options.play.help) || "Queue a song/playlist by URL or name.",
35 name: (options && options.play && options.play.name) || "play",
36 usage: (options && options.play && options.play.usage) || null,
37 exclude: Boolean((options && options.play && options.play.exclude)),
38 masked: "play"
39 };
40
41 // Help Command options
42 this.help = {
43 enabled: (options.help == undefined ? true : (options.help && typeof options.help.enabled !== 'undefined' ? options.help && options.help.enabled : true)),
44 run: "helpFunction",
45 alt: (options && options.help && options.help.alt) || [],
46 help: (options && options.help && options.help.help) || "Help for commands.",
47 name: (options && options.help && options.help.name) || "help",
48 usage: (options && options.help && options.help.usage) || null,
49 exclude: Boolean((options && options.help && options.help.exclude)),
50 masked: "help"
51 };
52
53 // Pause Command options
54 this.pause = {
55 enabled: (options.pause == undefined ? true : (options.pause && typeof options.pause.enabled !== 'undefined' ? options.pause && options.pause.enabled : true)),
56 run: "pauseFunction",
57 alt: (options && options.pause && options.pause.alt) || [],
58 help: (options && options.pause && options.pause.help) || "Pauses playing music.",
59 name: (options && options.pause && options.pause.name) || "pause",
60 usage: (options && options.pause && options.pause.usage) || null,
61 exclude: Boolean((options && options.pause && options.pause.exclude)),
62 masked: "pause"
63 };
64
65 // Resume Command options
66 this.resume = {
67 enabled: (options.resume == undefined ? true : (options.resume && typeof options.resume.enabled !== 'undefined' ? options.resume && options.resume.enabled : true)),
68 run: "resumeFunction",
69 alt: (options && options.resume && options.resume.alt) || [],
70 help: (options && options.resume && options.resume.help) || "Resumes a paused queue.",
71 name: (options && options.resume && options.resume.name) || "resume",
72 usage: (options && options.resume && options.resume.usage) || null,
73 exclude: Boolean((options && options.resume && options.resume.exclude)),
74 masked: "resume"
75 };
76
77 // Leave Command options
78 this.leave = {
79 enabled: (options.leave == undefined ? true : (options.leave && typeof options.leave.enabled !== 'undefined' ? options.leave && options.leave.enabled : true)),
80 run: "leaveFunction",
81 alt: (options && options.leave && options.leave.alt) || [],
82 help: (options && options.leave && options.leave.help) || "Leaves the voice channel.",
83 name: (options && options.leave && options.leave.name) || "leave",
84 usage: (options && options.leave && options.leave.usage) || null,
85 exclude: Boolean((options && options.leave && options.leave.exclude)),
86 masked: "leave"
87 };
88
89 // Queue Command options
90 this.queue = {
91 enabled: (options.queue == undefined ? true : (options.queue && typeof options.queue.enabled !== 'undefined' ? options.queue && options.queue.enabled : true)),
92 run: "queueFunction",
93 alt: (options && options.queue && options.queue.alt) || [],
94 help: (options && options.queue && options.queue.help) || "View the current queue.",
95 name: (options && options.queue && options.queue.name) || "queue",
96 usage: (options && options.queue && options.queue.usage) || null,
97 exclude: Boolean((options && options.queue && options.queue.exclude)),
98 masked: "queue"
99 };
100
101 // Nowplaying Command options
102 this.np = {
103 enabled: (options.np == undefined ? true : (options.np && typeof options.np.enabled !== 'undefined' ? options.np && options.np.enabled : true)),
104 run: "npFunction",
105 alt: (options && options.np && options.np.alt) || [],
106 help: (options && options.np && options.np.help) || "Shows the now playing text.",
107 name: (options && options.np && options.np.name) || "np",
108 usage: (options && options.np && options.np.usage) || null,
109 exclude: Boolean((options && options.np && options.np.exclude)),
110 masked: "np"
111 };
112
113 // Loop Command options
114 this.loop = {
115 enabled: (options.loop == undefined ? true : (options.loop && typeof options.loop.enabled !== 'undefined' ? options.loop && options.loop.enabled : true)),
116 run: "loopFunction",
117 alt: (options && options.loop && options.loop.alt) || [],
118 help: (options && options.loop && options.loop.help) || "Sets the loop state for the queue.",
119 name: (options && options.loop && options.loop.name) || "loop",
120 usage: (options && options.loop && options.loop.usage) || null,
121 exclude: Boolean((options && options.loop && options.loop.exclude)),
122 masked: "loop"
123 };
124
125 // Search Command options
126 this.search = {
127 enabled: (options.search == undefined ? true : (options.search && typeof options.search.enabled !== 'undefined' ? options.search && options.search.enabled : true)),
128 run: "searchFunction",
129 alt: (options && options.search && options.search.alt) || [],
130 help: (options && options.search && options.search.help) || "Searchs for up to 10 videos from YouTube.",
131 name: (options && options.search && options.search.name) || "search",
132 usage: (options && options.search && options.search.usage) || null,
133 exclude: Boolean((options && options.search && options.search.exclude)),
134 masked: "search"
135 };
136
137 // Clear Command options
138 this.clearqueue = {
139 enabled: (options.clearqueue == undefined ? true : (options.clearqueue && typeof options.clearqueue.enabled !== 'undefined' ? options.clearqueue && options.clearqueue.enabled : true)),
140 run: "clearFunction",
141 alt: (options && options.clear && options.clear.alt) || [],
142 help: (options && options.clear && options.clear.help) || "Clears the entire queue.",
143 name: (options && options.clear && options.clear.name) || "clear",
144 usage: (options && options.clear && options.clear.usage) || null,
145 exclude: Boolean((options && options.clearqueue && options.clearqueue.exclude)),
146 masked: "clearqueue"
147 };
148
149 // Volume Command options
150 this.volume = {
151 enabled: (options.volume == undefined ? true : (options.volume && typeof options.volume.enabled !== 'undefined' ? options.volume && options.volume.enabled : true)),
152 run: "volumeFunction",
153 alt: (options && options.volume && options.volume.alt) || [],
154 help: (options && options.volume && options.volume.help) || "Changes the volume output of the bot.",
155 name: (options && options.volume && options.volume.name) || "volume",
156 usage: (options && options.volume && options.volume.usage) || null,
157 exclude: Boolean((options && options.volume && options.volume.exclude)),
158 masked: "volume"
159 };
160
161 this.remove = {
162 enabled: (options.remove == undefined ? true : (options.remove && typeof options.remove.enabled !== 'undefined' ? options.remove && options.remove.enabled : true)),
163 run: "removeFunction",
164 alt: (options && options.remove && options.remove.alt) || [],
165 help: (options && options.remove && options.remove.help) || "Remove a song from the queue by position in the queue.",
166 name: (options && options.remove && options.remove.name) || "remove",
167 usage: (options && options.remove && options.remove.usage) || "{{prefix}}remove [position]",
168 exclude: Boolean((options && options.remove && options.remove.exclude)),
169 masked: "remove"
170 };
171
172 // Skip Command options
173 this.skip = {
174 enabled: (options.skip == undefined ? true : (options.skip && typeof options.skip.enabled !== 'undefined' ? options.skip && options.skip.enabled : true)),
175 run: "skipFunction",
176 alt: (options && options.skip && options.skip.alt) || [],
177 help: (options && options.skip && options.skip.help) || "Skip a song or songs with `skip [number]`",
178 name: (options && options.skip && options.skip.name) || "skip",
179 usage: (options && options.skip && options.skip.usage) || null,
180 exclude: Boolean((options && options.skip && options.skip.exclude)),
181 masked: "skip"
182 };
183 this.shuffle = {
184 enabled: (options.shuffle == undefined ? true : (options.shuffle && typeof options.shuffle.enabled !== 'undefined' ? options.shuffle && options.shuffle.enabled : true)),
185 run: "shuffleFunction",
186 alt: (options && options.shuffle && options.shuffle.alt) || [],
187 help: (options && options.shuffle && options.shuffle.help) || "Shuffle the queue",
188 name: (options && options.shuffle && options.shuffle.name) || "shuffle",
189 usage: (options && options.shuffle && options.shuffle.usage) || null,
190 exclude: Boolean((options && options.shuffle && options.shuffle.exclude)),
191 masked: "shuffle"
192 };
193 this.deleteQueue = {
194 enabled: (options.deleteQueue == undefined ? true : (options.deleteQueue && typeof options.deleteQueue.enabled !== 'undefined' ? options.deleteQueue && options.deleteQueue.enabled : true)),
195 run: "deleteQueueFunction",
196 alt: (options && options.deleteQueue && options.deleteQueue.alt) || [],
197 help: (options && options.deleteQueue && options.deleteQueue.help) || "Delete and re-make an ongoing queue",
198 name: (options && options.deleteQueue && options.deleteQueue.name) || "deletequeue",
199 usage: (options && options.deleteQueue && options.deleteQueue.usage) || null,
200 exclude: Boolean((options && options.deleteQueue && options.deleteQueue.exclude)),
201 masked: "deletequeue"
202 };
203
204 this.embedColor = (options && options.embedColor) || 'GREEN';
205 this.anyoneCanSkip = (options && typeof options.anyoneCanSkip !== 'undefined' ? options && options.anyoneCanSkip : false);
206 this.anyoneCanLeave = (options && typeof options.anyoneCanLeave !== 'undefined' ? options && options.anyoneCanLeave : false);
207 this.djRole = (options && options.djRole) || "DJ";
208 this.anyoneCanPause = (options && typeof options.anyoneCanPause !== 'undefined' ? options && options.anyoneCanPause : false);
209 this.anyoneCanAdjust = (options && typeof options.anyoneCanAdjust !== 'undefined' ? options && options.anyoneCanAdjust : false);
210 this.youtubeKey = (options && options.youtubeKey);
211 this.botPrefix = (options && options.botPrefix) || "!";
212 this.defVolume = (options && options.defVolume) || 50;
213 if (options.maxQueueSize === 0) {
214 this.maxQueueSize = 0;
215 } else {
216 this.maxQueueSize = (options && options.maxQueueSize) || 50;
217 }
218 this.ownerOverMember = (options && typeof options.ownerOverMember !== 'undefined' ? options && options.ownerOverMember : false);
219 this.botAdmins = (options && options.botAdmins) || [];
220 this.ownerID = (options && options.ownerID);
221 this.logging = (options && typeof options.logging !== 'undefined' ? options && options.logging : true);
222 this.requesterName = (options && typeof options.requesterName !== 'undefined' ? options && options.requesterName : true);
223 this.inlineEmbeds = (options && typeof options.inlineEmbeds !== 'undefined' ? options && options.inlineEmbeds : false);
224 this.clearOnLeave = (options && typeof options.clearOnLeave !== 'undefined' ? options && options.clearOnLeave : true);
225 this.messageHelp = (options && typeof options.messageHelp !== 'undefined' ? options && options.messageHelp : false);
226 this.dateLocal = (options && options.dateLocal) || 'en-US';
227 this.bigPicture = (options && typeof options.bigPicture !== 'undefined' ? options && options.bigPicture : false);
228 this.messageNewSong = (options && typeof options.messageNewSong !== 'undefined' ? options && options.messageNewSong : true);
229 this.insertMusic = (options && typeof options.insertMusic !== 'undefined' ? options && options.insertMusic : false);
230 this.defaultPrefix = (options && options.defaultPrefix) || "!";
231 this.channelWhitelist = (options && options.channelWhitelist) || [];
232 this.channelBlacklist = (options && options.channelBlacklist) || [];
233 this.minShuffle = (options && options.shuffle) || 3;
234 this.bitRate = (options && options.bitRate) || "120000";
235 this.userSearching = new Map();
236
237 // Cooldown Settings
238 this.cooldown = {
239 enabled: (options && options.cooldown ? options && options.cooldown.enabled : true),
240 timer: parseInt((options && options.cooldown && options.cooldown.timer) || 10000),
241 exclude: (options && options.cooldown && options.cooldown.exclude) || ["volume","queue","pause","resume","np"]
242 };
243
244 this.musicPresence = options.musicPresence || false;
245 this.clearPresence = options.clearPresence || false;
246 this.nextPresence = (options && options.nextPresence) || null;
247 this.recentTalk = new Set();
248 }
249
250 checkVoice(mem, bot) {
251 return new Promise((resolve, reject) => {
252 if (!mem || !bot) reject("invalid args");
253 if (!mem.voiceChannel) reject("You're not in a voice channel!");
254 if (bot.voiceChannel) {
255 if (bot.voiceChannel.id == mem.voiceChannel.id) resolve(mem.voiceChannel)
256 else reject("You're in a different voice channel!")
257 } else {
258 resolve(mem.voiceChannel);
259 };
260 });
261 };
262
263 async updatePositions(obj, server) {
264 return new Promise((resolve, reject) => {
265 if (!server) reject("stage 0: no server passed for @updatePositions");
266 if (!obj) resolve(this.getQueue(server));
267 if (obj.working == true) reject("The queue is already performing a task!");
268 if (server != "000000") {
269 obj.working = true;
270 this.queues.set(server, obj);
271 }
272 try {
273 var songs = typeof obj == "object" ? Array.from(obj.songs) : [];
274 } catch (e) {
275 console.log("aidjbasiubd");
276 };
277 try {
278 if (!songs || songs.length <= 0 || typeof obj.songs != "object") {
279 if (this.debug) console.log("[MUSICBOT] @updatePositions: songs object was invalid, reseting queue for "+ obj.id);
280 this.queues.set(obj.id, {songs: [], last: obj.last ? obj.last : null, loop: obj.loop ? obj.loop : "none", id: obj.id, volume: this.defVolume, oldSongs: [],working: false, needsRefresh: false})
281 resolve([])
282 }
283 let mm = 0;
284 var newsongs = [];
285 songs.forEach(s => {
286 try {
287 // console.log(s);
288 if (!s) return;
289 if (s.position !== mm) s.position = mm;
290 newsongs.push(s);
291 mm++;
292 } catch (e) {
293 console.log(e);
294 };
295 });
296 } catch (e) {
297 console.log(e);
298 if (server != "000000") {
299 obj.working = false;
300 this.queues.set(server, obj);
301 }
302 reject("stage 1: @updatePositions " + e)
303 };
304 obj.songs = newsongs;
305 obj.last.position = 0;
306 if (server != "000000") {
307 obj.working = false;
308 this.queues.set(server, obj);
309 }
310 setTimeout(() => {
311 resolve(obj);
312 }, 2000)
313 });
314 };
315
316 isAdmin(member) {
317 if (member.roles.find(r => r.name == this.djRole)) return true;
318 if (this.ownerOverMember && member.id === this.botOwner) return true;
319 if (this.botAdmins.includes(member.id)) return true;
320 return member.hasPermission("ADMINISTRATOR");
321 };
322
323 canSkip(member, queue) {
324 if (this.anyoneCanSkip) return true;
325 else if (this.botAdmins.includes(member.id)) return true;
326 else if (this.ownerOverMember && member.id === this.botOwner) return true;
327 else if (queue.last.requester === member.id) return true;
328 else if (this.isAdmin(member)) return true;
329 else return false;
330 };
331
332 canAdjust(member, queue) {
333 if (this.anyoneCanAdjust) return true;
334 else if (this.botAdmins.includes(member.id)) return true;
335 else if (this.ownerOverMember && member.id === this.botOwner) return true;
336 else if (queue.last.requester === member.id) return true;
337 else if (this.isAdmin(member)) return true;
338 else return false;
339 };
340
341 getQueue(server) {
342 if (!this.queues.has(server)) {
343 this.queues.set(server, {songs: [], last: null, loop: "none", id: server,volume: this.defVolume, oldSongs: [],working: false, needsRefresh: false});
344 };
345 return this.queues.get(server);
346 };
347
348 setLast(server, last) {
349 return new Promise((resolve, reject) => {
350 if (this.queues.has(server)) {
351 let q = this.queues.get(server);
352 q.last = last;
353 this.queues.set(server, q);
354 resolve(this.queues.get(server));
355 } else {
356 reject("no server queue");
357 };
358 });
359 };
360
361 emptyQueue(server) {
362 return new Promise((resolve, reject) => {
363 if (!server || typeof server != "string") reject("no server id passed or passed obj was no a string @emptyQueue")
364 this.queues.set(server, {songs: [], last: null, loop: "none", id: server, volume: this.defVolume, oldSongs: [],working: false, needsRefresh: false});
365 resolve(this.queues.get(server));
366 });
367 };
368
369 async updatePresence(queue, client, clear) {
370 return new Promise((resolve, reject) => {
371 if (this.nextPresence !== null) clear = false;
372 if (!queue || !client) reject("invalid arguments");
373 if (queue.songs.length > 0 && queue.last) {
374 client.user.setPresence({
375 game: {
376 name: "? | " + queue.last.title,
377 type: 'PLAYING'
378 }
379 });
380 resolve(client.user.presence);
381 } else {
382 if (clear) {
383 client.user.setPresence({ game: { name: null} });
384 resolve(client.user.presence);
385 } else {
386 if (this.nextPresence !== null) {
387 let props;
388 if (this.nextPresence.status && ["online","dnd","idle","invisible"].includes(this.nextPresence.status)) props.status = this.nextPresence.status;
389 if (this.nextPresence.afk && typeof this.nextPresence.afk == "boolean") props.afk = this.nextPresence.afk;
390 if (this.nextPresence.game && typeof this.nextPresence.game == "string") props.game = {name: this.nextPresence.game}
391 else if (this.nextPresence.game && typeof this.nextPresence.game == "object") props.game = this.nextPresence.game;
392 client.user.setPresence(props).catch((res) => {
393 console.error("[MUSICBOT] Could not update presence\n" + res);
394 client.user.setPresence({ game: { name: null} });
395 resolve(client.user.presence);
396 }).then((res) => {
397 resolve(res);
398 });
399 } else {
400 client.user.setPresence({
401 game: {
402 name: "? | nothing",
403 type: 'PLAYING'
404 }
405 });
406 }
407 resolve(client.user.presence);
408 };
409 };
410 });
411 };
412
413 updatePrefix(server, prefix) {
414 if (typeof prefix == undefined) prefix = this.defaultPrefix;
415 if (typeof this.botPrefix != "object") this.botPrefix = new Map();
416 this.botPrefix.set(server, {prefix: prefix});
417 };
418 };
419
420 var musicbot = new Music(client, options);
421 if (musicbot.insertMusic == true) client.music = musicbot;
422 else exports.bot = musicbot;
423
424 musicbot.searcher = new YTSearcher(musicbot.youtubeKey);
425 musicbot.changeKey = (key) => {
426 return new Promise((resolve, reject) => {
427 if (!key || typeof key !== "string") reject("key must be a string");
428 musicbot.youtubeKey = key;
429 musicbot.searcher.key = key;
430 resolve(musicbot);
431 });
432 };
433
434 client.on("ready", () => {
435 console.log(`------- Music Bot -------\n> Version: ${PACKAGE.version}\n> Extra Logging: ${musicbot.logging}.\n> Node.js Version: ${process.version}\n------- Music Bot -------`);
436 if (musicbot.cooldown.exclude.includes("skip")) console.warn(`[MUSIC] Excluding SKIP CMD from cooldowns can cause issues.`);
437 if (musicbot.cooldown.exclude.includes("remove")) console.warn(`[MUSIC] Excluding REMOVE CMD from cooldowns can cause issues.`);
438 setTimeout(() => { if (musicbot.musicPresence == true && musicbot.client.guilds.length > 1) console.warn(`[MUSIC] MusicPresence is enabled with more than one server!`); }, 2000);
439 });
440
441 client.on("message", (msg) => {
442 if (msg.author.bot || musicbot.channelBlacklist.includes(msg.channel.id)) return;
443 if (musicbot.channelWhitelist.length > 0 && !musicbot.channelWhitelist.includes(msg.channel.id)) return;
444 const message = msg.content.trim();
445 const prefix = typeof musicbot.botPrefix == "object" ? (musicbot.botPrefix.has(msg.guild.id) ? musicbot.botPrefix.get(msg.guild.id).prefix : musicbot.defaultPrefix) : musicbot.botPrefix;
446 const command = message.substring(prefix.length).split(/[ \n]/)[0].trim();
447 const suffix = message.substring(prefix.length + command.length).trim();
448 const args = message.slice(prefix.length + command.length).trim().split(/ +/g);
449
450 if (message.startsWith(prefix) && msg.channel.type == "text") {
451 if (musicbot.commands.has(command)) {
452 let tCmd = musicbot.commands.get(command);
453 if (tCmd.enabled) {
454 if (!musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(tCmd.masked)) {
455 if (musicbot.recentTalk.has(msg.author.id)) {
456 if (musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(tCmd.masked)) return msg.channel.send(musicbot.note("fail", "You must wait to use music commands again."));
457 }
458 musicbot.recentTalk.add(msg.author.id);
459 setTimeout(() => { musicbot.recentTalk.delete(msg.author.id) }, musicbot.cooldown.timer);
460 }
461 return musicbot[tCmd.run](msg, suffix, args);
462 }
463 } else if (musicbot.aliases.has(command)) {
464 let aCmd = musicbot.aliases.get(command);
465 if (aCmd.enabled) {
466 if (!musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(aCmd.masked)) {
467 if (musicbot.recentTalk.has(msg.author.id)) {
468 if (musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(aCmd.masked)) return msg.channel.send(musicbot.note("fail", "You must wait to use music commands again."));
469 }
470 musicbot.recentTalk.add(msg.author.id);
471 setTimeout(() => { musicbot.recentTalk.delete(msg.author.id) }, musicbot.cooldown.timer);
472 }
473 return musicbot[aCmd.run](msg, suffix, args);
474 }
475 };
476 };
477 });
478
479 musicbot.playFunction = (msg, suffix, args, ignore) => {
480 if (msg.member.voiceChannel === undefined) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
481 if (!suffix) return msg.channel.send(musicbot.note('fail', 'No video specified!'));
482 let q = musicbot.getQueue(msg.guild.id);
483
484 let vc = client.voiceConnections.find(val => val.channel.guild.id == msg.member.guild.id)
485 if (vc && vc.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
486 if (q.songs.length >= musicbot.maxQueueSize && musicbot.maxQueueSize !== 0) return msg.channel.send(musicbot.note('fail', 'Maximum queue size reached!'));
487 var searchstring = suffix.trim();
488 if (searchstring.includes("https://youtu.be/") || searchstring.includes("https://www.youtube.com/") && searchstring.includes("&")) searchstring = searchstring.split("&")[0];
489
490
491 if (searchstring.startsWith('http') && searchstring.includes("list=")) {
492 msg.channel.send(musicbot.note("search", `Searching playlist items~`));
493 var playid = searchstring.toString()
494 .split('list=')[1];
495 if (playid.toString()
496 .includes('?')) playid = playid.split('?')[0];
497 if (playid.toString()
498 .includes('&t=')) playid = playid.split('&t=')[0];
499
500 ytpl(playid, {limit: musicbot.maxQueueSize}, function(err, playlist) {
501 if(err) return msg.channel.send(musicbot.note('fail', `Something went wrong fetching that playlist!`));
502 if (playlist.items.length <= 0) return msg.channel.send(musicbot.note('fail', `Couldn't get any videos from that playlist.`));
503 if (playlist.total_items >= musicbot.maxQueueSize && musicbot.maxQueueSize != 0) return msg.channel.send(musicbot.note('fail', `Too many videos to queue. A maximum of ` + musicbot.maxQueueSize + ` is allowed.`));
504 var index = 0;
505 var ran = 0;
506 var queue = musicbot.getQueue(msg.guild.id);
507
508 playlist.items.forEach(async (video) => {
509 ran++;
510 if (queue.songs.length == (musicbot.maxQueueSize + 1) && musicbot.maxQueueSize !== 0 || !video) return;
511 video.url = video.url_simple ? video.url_simple : `https://www.youtube.com/watch?v=` + video.id;
512 musicbot.playFunction(msg, video.url, [], true);
513 index++;
514
515 if (ran >= playlist.items.length) {
516 console.log(queue);
517 if (queue.songs.length >= 1) musicbot.executeQueue(msg, queue);
518 if (index == 0) msg.channel.send(musicbot.note('fail', `Coudln't get any songs from that playlist!`))
519 else if (index == 1) msg.channel.send(musicbot.note('note', `Queued one song.`));
520 else if (index > 1) msg.channel.send(musicbot.note('note', `Queued ${index} songs.`));
521 }
522 });
523 });
524 } else {
525 if (!ignore) msg.channel.send(musicbot.note("search", `\`Searching: ${searchstring}\`~`));
526 new Promise(async (resolve, reject) => {
527 let result = await musicbot.searcher.search(searchstring, { type: 'video' }).catch((err) => {
528 var errorMsg = err.message;
529 if (errorMsg.includes('\"reason\": \"dailyLimitExceeded\",')) {
530 errorMsg = errorMsg.slice(errorMsg.indexOf('Daily Limit Exceeded. '));
531 errorMsg = errorMsg.slice(0, errorMsg.indexOf('\",'));
532 if (!ignore) msg.channel.send(musicbot.note("fail", "**Unable to complete playback:**\n" + errorMsg));
533 return;
534 } else if (errorMsg.includes('\"reason\": \"quotaExceeded\",')) {
535 if (!ignore) msg.channel.send(musicbot.note("fail", "Unable to complete playback! Google API quota exceeded!"));
536 return;
537 } else {
538 if (!ignore) msg.channel.send(musicbot.note("fail", "Unknown error occurred! Playback could not be completed, check the logs for more details."));
539 return console.log(err);
540 }
541 });
542 if (result === undefined) return;
543 resolve(result.first)
544 }).then((res) => {
545 if (!res) return msg.channel.send(musicbot.note("fail", "Something went wrong. Try again!"));
546 res.requester = msg.author.id;
547 if (searchstring.startsWith("https://www.youtube.com/") || searchstring.startsWith("https://youtu.be/")) res.url = searchstring;
548 res.channelURL = `https://www.youtube.com/channel/${res.channelId}`;
549 res.queuedOn = new Date().toLocaleDateString(musicbot.dateLocal, { weekday: 'long', hour: 'numeric' });
550 if (musicbot.requesterName) res.requesterAvatarURL = msg.author.displayAvatarURL;
551 const queue = musicbot.getQueue(msg.guild.id)
552 res.position = queue.songs.length ? queue.songs.length : 0;
553 queue.songs.push(res);
554
555 if (!ignore) {
556 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
557 const embed = new Discord.RichEmbed();
558 try {
559 embed.setAuthor('Adding To Queue', client.user.avatarURL);
560 var songTitle = res.title.replace(/\\/g, '\\\\')
561 .replace(/\`/g, '\\`')
562 .replace(/\*/g, '\\*')
563 .replace(/_/g, '\\_')
564 .replace(/~/g, '\\~')
565 .replace(/`/g, '\\`');
566 embed.setColor(musicbot.embedColor);
567 embed.addField(res.channelTitle, `[${songTitle}](${res.url})`, musicbot.inlineEmbeds);
568 embed.addField("Queued On", res.queuedOn, musicbot.inlineEmbeds);
569 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${res.id}/maxresdefault.jpg`);
570 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${res.id}/maxresdefault.jpg`);
571 const resMem = client.users.get(res.requester);
572 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.get(res.requester).username}`, res.requesterAvatarURL);
573 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${res.requester})\``, res.requesterAvatarURL);
574 msg.channel.send({
575 embed
576 });
577 } catch (e) {
578 console.error(`[${msg.guild.name}] [playCmd] ` + e.stack);
579 };
580 } else {
581 try {
582 var songTitle = res.title.replace(/\\/g, '\\\\')
583 .replace(/\`/g, '\\`')
584 .replace(/\*/g, '\\*')
585 .replace(/_/g, '\\_')
586 .replace(/~/g, '\\~')
587 .replace(/`/g, '\\`');
588 msg.channel.send(`Now Playing: **${songTitle}**\nRequested By: ${client.users.get(res.requester).username}\nQueued On: ${res.queuedOn}`)
589 } catch (e) {
590 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
591 };
592 };
593 };
594 if (queue.songs.length === 1 || !client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
595 }).catch((res) => {
596 console.log(new Error(res));
597 });
598 };
599 };
600
601 musicbot.helpFunction = (msg, suffix, args) => {
602 const prefix = typeof musicbot.botPrefix == "object" ? (musicbot.botPrefix.has(msg.guild.id) ? musicbot.botPrefix.get(msg.guild.id).prefix : musicbot.defaultPrefix) : musicbot.botPrefix;
603 let command = suffix.trim();
604 if (!suffix) {
605 if (msg.channel.permissionsFor(msg.guild.me)
606 .has('EMBED_LINKS')) {
607 const embed = new Discord.RichEmbed();
608 embed.setAuthor("Commands", client.user.avatarURL);
609 embed.setDescription(`Use \`${prefix}${musicbot.help.name} command name\` for help on usage. Anyone with a role named \`${musicbot.djRole}\` can use any command.`);
610 // embed.addField(musicbot.helpCmd, musicbot.helpHelp);
611 const newCmds = Array.from(musicbot.commands);
612 let index = 0;
613 let max = musicbot.commandsArray.length;
614 embed.setColor(musicbot.embedColor);
615 for (var i = 0; i < musicbot.commandsArray.length; i++) {
616 if (!musicbot.commandsArray[i].exclude) embed.addField(musicbot.commandsArray[i].name, musicbot.commandsArray[i].help);
617 index++;
618 if (index == max) {
619 if (musicbot.messageHelp) {
620 let sent = false;
621 msg.author.send({
622 embed
623 })
624 .then(() => {
625 sent = true;
626 });
627 setTimeout(() => {
628 if (!sent) return msg.channel.send({
629 embed
630 });
631 }, 1200);
632 } else {
633 return msg.channel.send({
634 embed
635 });
636 };
637 }
638 };
639 } else {
640 var cmdmsg = `= Music Commands =\nUse ${prefix}${musicbot.help.name} [command] for help on a command. Anyone with a role named \`${musicbot.djRole}\` can use any command.\n`;
641 let index = 0;
642 let max = musicbot.commandsArray.length;
643 for (var i = 0; i < musicbot.commandsArray.length; i++) {
644 if (!musicbot.commandsArray[i].disabled || !musicbot.commandsArray[i].exclude) {
645 cmdmsg = cmdmsg + `\n• ${musicbot.commandsArray[i].name}: ${musicbot.commandsArray[i].help}`;
646 index++;
647 if (index == musicbot.commandsArray.length) {
648 if (musicbot.messageHelp) {
649 let sent = false;
650 msg.author.send(cmdmsg, {
651 code: 'asciidoc'
652 })
653 .then(() => {
654 sent = true;
655 });
656 setTimeout(() => {
657 if (!sent) return msg.channel.send(cmdmsg, {
658 code: 'asciidoc'
659 });
660 }, 500);
661 } else {
662 return msg.channel.send(cmdmsg, {
663 code: 'asciidoc'
664 });
665 };
666 }
667 };
668 };
669 };
670 } else if (musicbot.commands.has(command) || musicbot.aliases.has(command)) {
671 if (msg.channel.permissionsFor(msg.guild.me)
672 .has('EMBED_LINKS')) {
673 const embed = new Discord.RichEmbed();
674 command = musicbot.commands.get(command) || musicbot.aliases.get(command);
675 if (command.exclude) return msg.channel.send(musicbot.note('fail', `${suffix} is not a valid command!`));
676 embed.setAuthor(command.name, msg.client.user.avatarURL);
677 embed.setDescription(command.help);
678 if (command.alt.length > 0) embed.addField(`Aliases`, command.alt.join(", "), musicbot.inlineEmbeds);
679 if (command.usage && typeof command.usage == "string") embed.addField(`Usage`, command.usage.replace(/{{prefix}})/g, prefix), musicbot.inlineEmbeds);
680 embed.setColor(musicbot.embedColor);
681 msg.channel.send({
682 embed
683 });
684 } else {
685 command = musicbot.commands.get(command) || musicbot.aliases.get(command);
686 if (command.exclude) return msg.channel.send(musicbot.note('fail', `${suffix} is not a valid command!`));
687 var cmdhelp = `= ${command.name} =\n`;
688 cmdhelp = cmdhelp + `\n${command.help}`;
689 if (command.usage !== null) cmdhelp = cmdhelp + `\nUsage: ${command.usage.replace(/{{prefix}})/g, prefix)}`;
690 if (command.alt.length > 0) cmdhelp = cmdhelp + `\nAliases: ${command.alt.join(", ")}`;
691 msg.channel.send(cmdhelp, {
692 code: 'asciidoc'
693 });
694 };
695 } else {
696 msg.channel.send(musicbot.note('fail', `${suffix} is not a valid command!`));
697 };
698 };
699
700 musicbot.skipFunction = (msg, suffix, args) => {
701 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
702 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
703 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
704 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
705
706 const queue = musicbot.getQueue(msg.guild.id);
707 if (!musicbot.canSkip(msg.member, queue)) return msg.channel.send(musicbot.note('fail', `You cannot skip this as you didn't queue it.`));
708
709 if (musicbot.queues.get(msg.guild.id).loop == "song") return msg.channel.send(musicbot.note("fail", "Cannot skip while loop is set to single."));
710
711 const dispatcher = voiceConnection.player.dispatcher;
712 if (!dispatcher || dispatcher === null) {
713 if (musicbot.logging) return console.log(new Error(`dispatcher null on skip cmd [${msg.guild.name}] [${msg.author.username}]`));
714 return msg.channel.send(musicbot.note("fail", "Something went wrong running skip."));
715 };
716 if (voiceConnection.paused) dispatcher.end();
717 dispatcher.end();
718 msg.channel.send(musicbot.note("note", "Skipped song."));
719 };
720
721 musicbot.pauseFunction = (msg, suffix, args) => {
722 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
723 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
724 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
725 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
726 if (!musicbot.isAdmin(msg.member) && !musicbot.anyoneCanPause) return msg.channel.send(musicbot.note('fail', 'You cannot pause queues.'));
727
728 const dispatcher = voiceConnection.player.dispatcher;
729 if (dispatcher.paused) return msg.channel.send(musicbot.note(`fail`, `Music already paused!`))
730 else dispatcher.pause();
731 msg.channel.send(musicbot.note('note', 'Playback paused.'));
732 };
733
734 musicbot.resumeFunction = (msg, suffix, args) => {
735 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
736 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
737 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
738 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
739 if (!musicbot.isAdmin(msg.member) && !musicbot.anyoneCanPause) return msg.channel.send(musicbot.note('fail', `You cannot resume queues.`));
740
741 const dispatcher = voiceConnection.player.dispatcher;
742 if (!dispatcher.paused) return msg.channel.send(musicbot.note('fail', `Music already playing.`))
743 else dispatcher.resume();
744 msg.channel.send(musicbot.note('note', 'Playback resumed.'));
745 };
746
747 musicbot.leaveFunction = (msg, suffix) => {
748 if (musicbot.isAdmin(msg.member) || musicbot.anyoneCanLeave === true) {
749 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
750 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
751 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'I\'m not in a voice channel.'));
752 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
753 musicbot.emptyQueue(msg.guild.id).then(() => {
754 if (!voiceConnection.player.dispatcher) return;
755 voiceConnection.player.dispatcher.end();
756 voiceConnection.disconnect();
757 msg.channel.send(musicbot.note('note', 'Successfully left the voice channel.'));
758 }).catch((res) => {
759 console.log("["+msg.guild.id+"] " + res);
760 musicbot.queues.delete(msg.guild.id);
761 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
762 })
763
764 } else {
765 const chance = Math.floor((Math.random() * 100) + 1);
766 if (chance <= 10) return msg.channel.send(musicbot.note('fail', `I'm afraid I can't let you do that, ${msg.author.username}.`))
767 else return msg.channel.send(musicbot.note('fail', 'Sorry, you\'re not allowed to do that.'));
768 }
769 }
770
771 musicbot.npFunction = (msg, suffix, args) => {
772 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
773 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music is being played.'));
774 const queue = musicbot.getQueue(msg.guild.id, true);
775 const dispatcher = voiceConnection.player.dispatcher;
776
777 if (musicbot.queues.get(msg.guild.id).songs.length <= 0) return msg.channel.send(musicbot.note('note', 'Queue empty.'));
778
779 if (msg.channel.permissionsFor(msg.guild.me)
780 .has('EMBED_LINKS')) {
781 const embed = new Discord.RichEmbed();
782 try {
783 embed.setAuthor('Now Playing', client.user.avatarURL);
784 var songTitle = queue.last.title.replace(/\\/g, '\\\\')
785 .replace(/\`/g, '\\`')
786 .replace(/\*/g, '\\*')
787 .replace(/_/g, '\\_')
788 .replace(/~/g, '\\~')
789 .replace(/`/g, '\\`');
790 embed.setColor(musicbot.embedColor);
791 embed.addField(queue.last.channelTitle, `[${songTitle}](${queue.last.url})`, musicbot.inlineEmbeds);
792 embed.addField("Queued On", queue.last.queuedOn, musicbot.inlineEmbeds);
793 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${queue.last.id}/maxresdefault.jpg`);
794 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${queue.last.id}/maxresdefault.jpg`);
795 const resMem = client.users.get(queue.last.requester);
796 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.get(queue.last.requester).username}`, queue.last.requesterAvatarURL);
797 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${queue.last.requester})\``, queue.last.requesterAvatarURL);
798 msg.channel.send({
799 embed
800 });
801 } catch (e) {
802 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
803 };
804 } else {
805 try {
806 var songTitle = queue.last.title.replace(/\\/g, '\\\\')
807 .replace(/\`/g, '\\`')
808 .replace(/\*/g, '\\*')
809 .replace(/_/g, '\\_')
810 .replace(/~/g, '\\~')
811 .replace(/`/g, '\\`');
812 msg.channel.send(`Now Playing: **${songTitle}**\nRequested By: ${client.users.get(queue.last.requester).username}\nQueued On: ${queue.last.queuedOn}`)
813 } catch (e) {
814 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
815 };
816 }
817 };
818
819 musicbot.deleteQueueFunction = async (msg, suffix, args) => {
820 if (!musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", "Only and Admin can do this."));
821 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
822 musicbot.emptyQueue(msg.guild.id).then(() => {
823 if (voiceConnection !== null) {
824 const dispatcher = voiceConnection.player.dispatcher;
825 dispatcher.end()
826 }
827 return msg.channel.send(musicbot.note("note", "The queue should now be emptied."))
828 }).catch(async (res) => {
829 console.log("["+msg.guild.id+"] " + e);
830 // force the queue delete
831 musicbot.queues.delete(msg.guild.id);
832 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
833 if (voiceConnection !== null) {
834 const dispatcher = voiceConnection.player.dispatcher;
835 dispatcher.end()
836 }
837 msg.channel.send(musicbot.note("note", "The queue should now be deleted."))
838 })
839 }
840
841 musicbot.queueFunction = (msg, suffix, args) => {
842 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
843 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
844 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
845 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
846 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note("fail", "Could not find a queue for this server."));
847 else if (musicbot.queues.get(msg.guild.id).songs.length <= 0) return msg.channel.send(musicbot.note("fail", "Queue is empty."));
848 const queue = musicbot.queues.get(msg.guild.id);
849 if (suffix) {
850 let video = queue.songs.find(s => s.position == parseInt(suffix) - 1);
851 if (!video) return msg.channel.send(musicbot.note("fail", "Couldn't find that video."));
852 const embed = new Discord.RichEmbed()
853 .setAuthor('Queued Song', client.user.avatarURL)
854 .setColor(musicbot.embedColor)
855 .addField(video.channelTitle, `[${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}](${video.url})`, musicbot.inlineEmbeds)
856 .addField("Queued On", video.queuedOn, musicbot.inlineEmbeds)
857 .addField("Position", video.position + 1, musicbot.inlineEmbeds);
858 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`);
859 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`);
860 const resMem = client.users.get(video.requester);
861 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.get(video.requester).username}`, video.requesterAvatarURL);
862 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${video.requester})\``, video.requesterAvatarURL);
863 msg.channel.send({embed});
864 } else {
865 if (queue.songs.length > 11) {
866 let pages = [];
867 let page = 1;
868 const newSongs = queue.songs.musicArraySort(10);
869 newSongs.forEach(s => {
870 var i = s.map((video, index) => (
871 `**${video.position + 1}:** __${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}__`
872 )).join('\n\n');
873 if (i !== undefined) pages.push(i)
874 });
875
876 const embed = new Discord.RichEmbed();
877 embed.setAuthor('Queued Songs', client.user.avatarURL);
878 embed.setColor(musicbot.embedColor);
879 embed.setFooter(`Page ${page} of ${pages.length}`);
880 embed.setDescription(pages[page - 1]);
881 msg.channel.send(embed).then(m => {
882 m.react('⏪').then( r => {
883 m.react('⏩')
884 let forwardsFilter = m.createReactionCollector((reaction, user) => reaction.emoji.name === '⏩' && user.id === msg.author.id, { time: 120000 });
885 let backFilter = m.createReactionCollector((reaction, user) => reaction.emoji.name === '⏪' && user.id === msg.author.id, { time: 120000 });
886
887 forwardsFilter.on('collect', r => {
888 if (page === pages.length) return;
889 page++;
890 embed.setDescription(pages[page - 1]);
891 embed.setFooter(`Page ${page} of ${pages.length}`, msg.author.displayAvatarURL);
892 m.edit(embed);
893 })
894 backFilter.on('collect', r => {
895 if (page === 1) return;
896 page--;
897 embed.setDescription(pages[page - 1]);
898 embed.setFooter(`Page ${page} of ${pages.length}`);
899 m.edit(embed);
900 })
901 })
902 })
903 } else {
904 try {
905 var newSongs = musicbot.queues.get(msg.guild.id).songs.map((video, index) => (`**${video.position + 1}:** __${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}__`)).join('\n\n');
906 const embed = new Discord.RichEmbed();
907 embed.setAuthor('Queued Songs', client.user.avatarURL);
908 embed.setColor(musicbot.embedColor);
909 embed.setDescription(newSongs);
910 embed.setFooter(`Page 1 of 1`, msg.author.displayAvatarURL);
911 return msg.channel.send(embed);
912 } catch (e) {
913 console.log("["+msg.guild.id+"] " + e);
914 return msg.channel.send(msicbot.note("fail", "Something went wrong mapping out the queue! Please delete the queue if this persists."));
915 };
916 };
917 };
918 };
919
920 musicbot.searchFunction = (msg, suffix, args) => {
921 if (msg.member.voiceChannel === undefined) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
922 let vc = client.voiceConnections.find(val => val.channel.guild.id == msg.member.guild.id)
923 if (vc && vc.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
924 let us = `${msg.guild.id}-${msg.author.id}`;
925 if (musicbot.userSearching.has(us)) return msg.channel.send(musicbot.note("fail", `You already have a search on-going for \`${musicbot.userSearching.get(us).title}\`.\nYou may type \`cancel\` to cancel it.`));
926
927 if (!suffix) return msg.channel.send(musicbot.note('fail', 'No video specified!'));
928 const queue = musicbot.getQueue(msg.guild.id);
929 if (queue.songs.length >= musicbot.maxQueueSize && musicbot.maxQueueSize !== 0) return msg.channel.send(musicbot.note('fail', 'Maximum queue size reached!'));
930 musicbot.userSearching.set(us, {guild: msg.guild.id, user: msg.author.id, title: suffix})
931 let searchstring = suffix.trim();
932 msg.channel.send(musicbot.note('search', `Searching: \`${searchstring}\``))
933 .then(response => {
934 musicbot.searcher.search(searchstring, {
935 type: 'video'
936 })
937 .then(searchResult => {
938 if (!searchResult.totalResults || searchResult.totalResults === 0) return response.edit(musicbot.note('fail', 'Failed to get search results.'));
939
940 const startTheFun = async (videos, max) => {
941 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
942 const embed = new Discord.RichEmbed();
943 embed.setTitle(`Choose Your Video`);
944 embed.setColor(musicbot.embedColor);
945 var index = 0;
946 videos.forEach(function(video) {
947 index++;
948 embed.addField(`${index} (${video.channelTitle})`, `[${musicbot.note('font', video.title)}](${video.url})`, musicbot.inlineEmbeds);
949 });
950 embed.setFooter(`Search by: ${msg.author.username}`, msg.author.displayAvatarURL);
951 msg.channel.send({
952 embed
953 })
954 .then(firstMsg => {
955 var filter = null;
956 if (max === 0) {
957 filter = m => m.author.id === msg.author.id &&
958 m.content.includes('1') ||
959 m.content.trim() === (`cancel`);
960 } else if (max === 1) {
961 filter = m => m.author.id === msg.author.id &&
962 m.content.includes('1') ||
963 m.content.includes('2') ||
964 m.content.trim() === (`cancel`);
965 } else if (max === 2) {
966 filter = m => m.author.id === msg.author.id &&
967 m.content.includes('1') ||
968 m.content.includes('2') ||
969 m.content.includes('3') ||
970 m.content.trim() === (`cancel`);
971 } else if (max === 3) {
972 filter = m => m.author.id === msg.author.id &&
973 m.content.includes('1') ||
974 m.content.includes('2') ||
975 m.content.includes('3') ||
976 m.content.includes('4') ||
977 m.content.trim() === (`cancel`);
978 } else if (max === 4) {
979 filter = m => m.author.id === msg.author.id &&
980 m.content.includes('1') ||
981 m.content.includes('2') ||
982 m.content.includes('3') ||
983 m.content.includes('4') ||
984 m.content.includes('5') ||
985 m.content.trim() === (`cancel`);
986 } else if (max === 5) {
987 filter = m => m.author.id === msg.author.id &&
988 m.content.includes('1') ||
989 m.content.includes('2') ||
990 m.content.includes('3') ||
991 m.content.includes('4') ||
992 m.content.includes('5') ||
993 m.content.includes('6') ||
994 m.content.trim() === (`cancel`);
995 } else if (max === 6) {
996 filter = m => m.author.id === msg.author.id &&
997 m.content.includes('1') ||
998 m.content.includes('2') ||
999 m.content.includes('3') ||
1000 m.content.includes('4') ||
1001 m.content.includes('5') ||
1002 m.content.includes('6') ||
1003 m.content.includes('7') ||
1004 m.content.trim() === (`cancel`);
1005 } else if (max === 7) {
1006 filter = m => m.author.id === msg.author.id &&
1007 m.content.includes('1') ||
1008 m.content.includes('2') ||
1009 m.content.includes('3') ||
1010 m.content.includes('4') ||
1011 m.content.includes('5') ||
1012 m.content.includes('6') ||
1013 m.content.includes('7') ||
1014 m.content.includes('8') ||
1015 m.content.trim() === (`cancel`);
1016 } else if (max === 8) {
1017 filter = m => m.author.id === msg.author.id &&
1018 m.content.includes('1') ||
1019 m.content.includes('2') ||
1020 m.content.includes('3') ||
1021 m.content.includes('4') ||
1022 m.content.includes('5') ||
1023 m.content.includes('6') ||
1024 m.content.includes('7') ||
1025 m.content.includes('8') ||
1026 m.content.includes('9') ||
1027 m.content.trim() === (`cancel`);
1028 } else if (max === 9) {
1029 filter = m => m.author.id === msg.author.id &&
1030 m.content.includes('1') ||
1031 m.content.includes('2') ||
1032 m.content.includes('3') ||
1033 m.content.includes('4') ||
1034 m.content.includes('5') ||
1035 m.content.includes('6') ||
1036 m.content.includes('7') ||
1037 m.content.includes('8') ||
1038 m.content.includes('9') ||
1039 m.content.includes('10') ||
1040 m.content.trim() === (`cancel`);
1041 }
1042 msg.channel.awaitMessages(filter, {
1043 max: 1,
1044 time: 60000,
1045 errors: ['time']
1046 })
1047 .then(collected => {
1048 const newColl = Array.from(collected);
1049 const mcon = newColl[0][1].content;
1050
1051 if (mcon === "cancel") {
1052 musicbot.userSearching.delete(us);
1053 return firstMsg.edit(musicbot.note('note', 'Searching canceled.'));
1054 };
1055 const song_number = parseInt(mcon) - 1;
1056 if (song_number >= 0) {
1057 musicbot.userSearching.delete(us);
1058 firstMsg.delete();
1059
1060 videos[song_number].requester = msg.author.id;
1061 videos[song_number].position = queue.songs.length ? queue.songs.length : 0;
1062 var embed = new Discord.RichEmbed();
1063 embed.setAuthor('Adding To Queue', client.user.avatarURL);
1064 var songTitle = videos[song_number].title.replace(/\\/g, '\\\\')
1065 .replace(/\`/g, '\\`')
1066 .replace(/\*/g, '\\*')
1067 .replace(/_/g, '\\_')
1068 .replace(/~/g, '\\~')
1069 .replace(/`/g, '\\`');
1070 embed.setColor(musicbot.embedColor);
1071 embed.addField(videos[song_number].channelTitle, `[${songTitle}](${videos[song_number].url})`, musicbot.inlineEmbeds);
1072 embed.addField("Queued On", videos[song_number].queuedOn, musicbot.inlineEmbeds);
1073 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1074 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1075 const resMem = client.users.get(videos[song_number].requester);
1076 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.get(videos[song_number].requester).username}`, videos[song_number].requesterAvatarURL);
1077 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${videos[song_number].requester})\``, videos[song_number].requesterAvatarURL);
1078 msg.channel.send({
1079 embed
1080 }).then(() => {
1081 queue.songs.push(videos[song_number]);
1082 if (queue.songs.length === 1 || !client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
1083 })
1084 .catch(console.log);
1085 };
1086 })
1087 .catch(collected => {
1088 musicbot.userSearching.delete(us);
1089 if (collected.toString().match(/error|Error|TypeError|RangeError|Uncaught/)) return firstMsg.edit(`\`\`\`xl\nSearching canceled. ${collected}\n\`\`\``);
1090 return firstMsg.edit(`\`\`\`xl\nSearching canceled.\n\`\`\``);
1091 });
1092 })
1093 } else {
1094 const vids = videos.map((video, index) => (
1095 `**${index + 1}:** __${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}__`
1096 )).join('\n\n');
1097 msg.channel.send(`\`\`\`\n= Pick Your Video =\n${vids}\n\n= Say Cancel To Cancel =`).then(firstMsg => {
1098 var filter = null;
1099 if (max === 0) {
1100 filter = m => m.author.id === msg.author.id &&
1101 m.content.includes('1') ||
1102 m.content.trim() === (`cancel`);
1103 } else if (max === 1) {
1104 filter = m => m.author.id === msg.author.id &&
1105 m.content.includes('1') ||
1106 m.content.includes('2') ||
1107 m.content.trim() === (`cancel`);
1108 } else if (max === 2) {
1109 filter = m => m.author.id === msg.author.id &&
1110 m.content.includes('1') ||
1111 m.content.includes('2') ||
1112 m.content.includes('3') ||
1113 m.content.trim() === (`cancel`);
1114 } else if (max === 3) {
1115 filter = m => m.author.id === msg.author.id &&
1116 m.content.includes('1') ||
1117 m.content.includes('2') ||
1118 m.content.includes('3') ||
1119 m.content.includes('4') ||
1120 m.content.trim() === (`cancel`);
1121 } else if (max === 4) {
1122 filter = m => m.author.id === msg.author.id &&
1123 m.content.includes('1') ||
1124 m.content.includes('2') ||
1125 m.content.includes('3') ||
1126 m.content.includes('4') ||
1127 m.content.includes('5') ||
1128 m.content.trim() === (`cancel`);
1129 } else if (max === 5) {
1130 filter = m => m.author.id === msg.author.id &&
1131 m.content.includes('1') ||
1132 m.content.includes('2') ||
1133 m.content.includes('3') ||
1134 m.content.includes('4') ||
1135 m.content.includes('5') ||
1136 m.content.includes('6') ||
1137 m.content.trim() === (`cancel`);
1138 } else if (max === 6) {
1139 filter = m => m.author.id === msg.author.id &&
1140 m.content.includes('1') ||
1141 m.content.includes('2') ||
1142 m.content.includes('3') ||
1143 m.content.includes('4') ||
1144 m.content.includes('5') ||
1145 m.content.includes('6') ||
1146 m.content.includes('7') ||
1147 m.content.trim() === (`cancel`);
1148 } else if (max === 7) {
1149 filter = m => m.author.id === msg.author.id &&
1150 m.content.includes('1') ||
1151 m.content.includes('2') ||
1152 m.content.includes('3') ||
1153 m.content.includes('4') ||
1154 m.content.includes('5') ||
1155 m.content.includes('6') ||
1156 m.content.includes('7') ||
1157 m.content.includes('8') ||
1158 m.content.trim() === (`cancel`);
1159 } else if (max === 8) {
1160 filter = m => m.author.id === msg.author.id &&
1161 m.content.includes('1') ||
1162 m.content.includes('2') ||
1163 m.content.includes('3') ||
1164 m.content.includes('4') ||
1165 m.content.includes('5') ||
1166 m.content.includes('6') ||
1167 m.content.includes('7') ||
1168 m.content.includes('8') ||
1169 m.content.includes('9') ||
1170 m.content.trim() === (`cancel`);
1171 } else if (max === 9) {
1172 filter = m => m.author.id === msg.author.id &&
1173 m.content.includes('1') ||
1174 m.content.includes('2') ||
1175 m.content.includes('3') ||
1176 m.content.includes('4') ||
1177 m.content.includes('5') ||
1178 m.content.includes('6') ||
1179 m.content.includes('7') ||
1180 m.content.includes('8') ||
1181 m.content.includes('9') ||
1182 m.content.includes('10') ||
1183 m.content.trim() === (`cancel`);
1184 }
1185 msg.channel.awaitMessages(filter, {
1186 max: 1,
1187 time: 60000,
1188 errors: ['time']
1189 })
1190 .then(collected => {
1191 musicbot.userSearching.delete(us);
1192 const newColl = Array.from(collected);
1193 const mcon = newColl[0][1].content;
1194
1195 if (mcon === "cancel") return firstMsg.edit(musicbot.note('note', 'Searching canceled.'));
1196 const song_number = parseInt(mcon) - 1;
1197 if (song_number >= 0) {
1198 firstMsg.delete();
1199
1200 videos[song_number].requester = msg.author.id;
1201 videos[song_number].position = queue.songs.length ? queue.songs.length : 0;
1202 var embed = new Discord.RichEmbed();
1203 embed.setAuthor('Adding To Queue', client.user.avatarURL);
1204 var songTitle = videos[song_number].title.replace(/\\/g, '\\\\')
1205 .replace(/\`/g, '\\`')
1206 .replace(/\*/g, '\\*')
1207 .replace(/_/g, '\\_')
1208 .replace(/~/g, '\\~')
1209 .replace(/`/g, '\\`');
1210 embed.setColor(musicbot.embedColor);
1211 embed.addField(videos[song_number].channelTitle, `[${songTitle}](${videos[song_number].url})`, musicbot.inlineEmbeds);
1212 embed.addField("Queued On", videos[song_number].queuedOn, musicbot.inlineEmbeds);
1213 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1214 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1215 const resMem = client.users.get(videos[song_number].requester);
1216 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.get(videos[song_number].requester).username}`, videos[song_number].requesterAvatarURL);
1217 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${videos[song_number].requester})\``, videos[song_number].requesterAvatarURL);
1218 msg.channel.send({
1219 embed
1220 }).then(() => {
1221 queue.songs.push(videos[song_number]);
1222 if (queue.songs.length === 1 || !client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
1223 })
1224 .catch(console.log);
1225 };
1226 })
1227 .catch(collected => {
1228 musicbot.userSearching.delete(us);
1229 if (collected.toString()
1230 .match(/error|Error|TypeError|RangeError|Uncaught/)) return firstMsg.edit(`\`\`\`xl\nSearching canceled. ${collected}\n\`\`\``);
1231 return firstMsg.edit(`\`\`\`xl\nSearching canceled.\n\`\`\``);
1232 });
1233 })
1234 }
1235 };
1236
1237 const max = searchResult.totalResults >= 10 ? 9 : searchResult.totalResults - 1;
1238 var videos = [];
1239 for (var i = 0; i < 99; i++) {
1240 var result = searchResult.currentPage[i];
1241 result.requester = msg.author.id;
1242 if (musicbot.requesterName) result.requesterAvatarURL = msg.author.displayAvatarURL;
1243 result.channelURL = `https://www.youtube.com/channel/${result.channelId}`;
1244 result.queuedOn = new Date().toLocaleDateString(musicbot.dateLocal, { weekday: 'long', hour: 'numeric' });
1245 videos.push(result);
1246 if (i === max) {
1247 i = 101;
1248 startTheFun(videos, max);
1249 }
1250 };
1251 });
1252 })
1253 .catch(console.log);
1254 };
1255
1256 musicbot.volumeFunction = (msg, suffix, args) => {
1257 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1258 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1259 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music is being played.'));
1260 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1261 if (!musicbot.canAdjust(msg.member, musicbot.queues.get(msg.guild.id))) return msg.channel.send(musicbot.note('fail', `Only admins or DJ's may change volume.`));
1262 const dispatcher = voiceConnection.player.dispatcher;
1263
1264 if (!suffix || isNaN(suffix)) return msg.channel.send(musicbot.note('fail', 'No volume specified.'));
1265 suffix = parseInt(suffix);
1266 if (suffix > 200 || suffix <= 0) return msg.channel.send(musicbot.note('fail', 'Volume out of range, must be within 1 to 200'));
1267
1268 dispatcher.setVolume((suffix / 100));
1269 musicbot.queues.get(msg.guild.id).volume = suffix;
1270 msg.channel.send(musicbot.note('note', `Volume changed to ${suffix}%.`));
1271 };
1272
1273 musicbot.clearFunction = (msg, suffix, args) => {
1274 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note("fail", "No queue found for this server."));
1275 if (!musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", `Only Admins or people with the ${musicbot.djRole} can clear queues.`));
1276 let vc = client.voiceConnections.find(val => val.channel.guild.id == msg.member.guild.id)
1277 if (vc && vc.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1278 musicbot.emptyQueue(msg.guild.id).then(res => {
1279 msg.channel.send(musicbot.note("note", "Queue cleared."));
1280 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1281 if (voiceConnection !== null) {
1282 const dispatcher = voiceConnection.player.dispatcher;
1283 if (!dispatcher || dispatcher === null) {
1284 if (musicbot.logging) return console.log(new Error(`dispatcher null on skip cmd [${msg.guild.name}] [${msg.author.username}]`));
1285 return msg.channel.send(musicbot.note("fail", "Something went wrong."));
1286 };
1287 if (voiceConnection.paused) dispatcher.end();
1288 dispatcher.end();
1289 }
1290 }).catch(res => {
1291 console.error(new Error(`[clearCmd] [${msg.guild.id}] ${res}`))
1292 return msg.channel.send(musicbot.note("fail", "Something went wrong clearing the queue."));
1293 })
1294 };
1295
1296 musicbot.removeFunction = (msg, suffix, args) => {
1297 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1298 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1299 if (!suffix) return msg.channel.send(musicbot.note("fail", "No video position given."));
1300 let vc = client.voiceConnections.find(val => val.channel.guild.id == msg.member.guild.id)
1301 if (vc && vc.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1302 if (parseInt(suffix) - 1 == 0) return msg.channel.send(musicbot.note("fail", "You cannot clear the currently playing music."));
1303 let test = musicbot.queues.get(msg.guild.id).songs.find(x => x.position == parseInt(suffix) - 1);
1304 if (test) {
1305 if (test.requester !== msg.author.id && !musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", "You cannot remove that item."));
1306 let newq = musicbot.queues.get(msg.guild.id).songs.filter(s => s !== test);
1307 musicbot.updatePositions(musicbot.queues.get(msg.guild.id), msg.guild.id).then(res => {
1308 musicbot.queues.get(msg.guild.id).songs = res;
1309 msg.channel.send(musicbot.note("note", `Removed: \`${test.title.replace(/`/g, "'")}\``));
1310 }).catch(e=> {
1311 console.log(e)
1312 console.log("@ remove function");
1313 })
1314 } else {
1315 msg.channel.send(musicbot.note("fail", "Couldn't find that video or something went wrong."));
1316 }
1317 };
1318
1319 musicbot.loopFunction = (msg, suffix, args) => {
1320 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1321 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1322 let vc = client.voiceConnections.find(val => val.channel.guild.id == msg.member.guild.id)
1323 if (vc && vc.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1324 if (musicbot.queues.get(msg.guild.id).loop == "none" || musicbot.queues.get(msg.guild.id).loop == null) {
1325 musicbot.queues.get(msg.guild.id).loop = "song";
1326 msg.channel.send(musicbot.note('note', 'Looping single enabled! :repeat_one:'));
1327 } else if (musicbot.queues.get(msg.guild.id).loop == "song") {
1328 musicbot.queues.get(msg.guild.id).loop = "queue";
1329 msg.channel.send(musicbot.note('note', 'Looping queue enabled! :repeat:'));
1330 } else if (musicbot.queues.get(msg.guild.id).loop == "queue") {
1331 musicbot.queues.get(msg.guild.id).loop = "none";
1332 msg.channel.send(musicbot.note('note', 'Looping disabled! :arrow_forward:'));
1333 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1334 const dispatcher = voiceConnection.player.dispatcher;
1335 let wasPaused = dispatcher.paused;
1336 if (wasPaused) dispatcher.pause();
1337 let newq = musicbot.queues.get(msg.guild.id).songs.slice(musicbot.queues.get(msg.guild.id).last.position - 1);
1338 if (newq !== musicbot.queues.get(msg.guild.id).songs) musicbot.updatePositions(musicbot.queues.get(msg.guild.id), msg.guild.id).then(res => {
1339 musicbot.queues.get(msg.guild.id).songs = res;
1340 }).catch(e=> {
1341 console.log(e)
1342 console.log("@ loop function");
1343 })
1344 if (wasPaused) dispatcher.resume();
1345 }
1346 };
1347 musicbot.shuffleFunction = (msg, suffix, args) => {
1348 let q = musicbot.getQueue(msg.guild.id);
1349 if (q.working == true) return msg.channel.send(musicbot.note('fail', `This servers queue is already performing a task!`));
1350 if (!msg.member.voiceChannel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1351 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1352 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1353 if (voiceConnection && voiceConnection.channel.id != msg.member.voiceChannel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1354 if (musicbot.queues.get(msg.guild.id).songs.length < musicbot.minShuffle) return msg.channel.send(musicbot.note('fail', `Queue must a minimum of ${musicbot.minShuffle} songs to shuffle!`));
1355 if (musicbot.queues.get(msg.guild.id).loop == "song") return msg.channel.send(musicbot.note("fail", `Cannot shuffle while loop is set to single.`));
1356 const dispatcher = voiceConnection.player.dispatcher;
1357 q.oldSongs = q.songs;
1358 q.songs.musicBotShuffle();
1359 q.needsRefresh = true;
1360 musicbot.updatePositions(q, msg.guild.id).then((res) => {
1361 q.songs = res.songs;
1362 musicbot.queues.set(msg.guild.id, q);
1363 if (voiceConnection.paused) dispatcher.resume();
1364 msg.channel.send(musicbot.note('note', `Queue was shuffled!`));
1365
1366 dispatcher.end();
1367 }).catch((res) => {
1368 message.channel.send(musicbot.note("fail", "Something went wrong shuffling the queue!"))
1369 console.log("@shuffleFunction " + res);
1370 })
1371
1372 // }
1373 };
1374
1375 musicbot.loadCommand = (obj) => {
1376 return new Promise((resolve, reject) => {
1377 let props = {
1378 enabled: obj.enabled,
1379 run: obj.run,
1380 alt: obj.alt,
1381 help: obj.help,
1382 name: obj.name,
1383 exclude: obj.exclude,
1384 masked: obj.masked
1385 };
1386 if (props.enabled == undefined || null) props.enabled = true;
1387 if (obj.alt.length > 0) {
1388 obj.alt.forEach((a) => {
1389 musicbot.aliases.set(a, props);
1390 })
1391 };
1392 musicbot.commands.set(obj.name, props);
1393 musicbot.commandsArray.push(props);
1394 if (musicbot.logging) console.log(`[MUSIC_LOADCMD] Loaded ${obj.name}`);
1395 resolve(musicbot.commands.get(obj.name));
1396 });
1397 }
1398
1399 musicbot.executeQueue = (msg, queue) => {
1400 musicbot.queues.set(queue.id, queue);
1401 if (queue.songs.length == 0) {
1402 msg.channel.send(musicbot.note('note', 'Playback finished~'));
1403 if (musicbot.musicPresence) musicbot.updatePresence(musicbot.queues.get(msg.guild.id), msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1404 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1405 if (voiceConnection !== null) return voiceConnection.disconnect();
1406 };
1407
1408 new Promise((resolve, reject) => {
1409 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1410 if (voiceConnection === null) {
1411 if (msg.member.voiceChannel && msg.member.voiceChannel.joinable) {
1412 msg.member.voiceChannel.join()
1413 .then(connection => {
1414 resolve(connection);
1415 })
1416 .catch((error) => {
1417 console.log(error);
1418 });
1419 } else if (!msg.member.voiceChannel.joinable || msg.member.voiceChannel.full) {
1420 msg.channel.send(musicbot.note('fail', 'I do not have permission to join your voice channel!'))
1421 reject();
1422 } else {
1423 musicbot.emptyQueue(msg.guild.id).then(() => {
1424 reject();
1425 })
1426 }
1427 } else {
1428 resolve(voiceConnection);
1429 }
1430 }).then(connection => {
1431 let video;
1432 if (!queue.last) {
1433 video = queue.songs[0];
1434 } else {
1435 if (queue.loop == "queue") {
1436 video = queue.songs.find(s => s.position == queue.last.position + 1);
1437 if (!video || video && !video.url) video = queue.songs[0];
1438 } else if (queue.loop == "single") {
1439 video = queue.last;
1440 } else {
1441 video = queue.songs.find(s => s.position == queue.last.position);
1442 };
1443 }
1444 if (!video) {
1445 video = queue.songs ? queue.songs[0] : false;
1446 if (!video) {
1447 msg.channel.send(musicbot.note('note', 'Playback finished!'));
1448 musicbot.emptyQueue(msg.guild.id);
1449 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1450 if (voiceConnection !== null) return voiceConnection.disconnect();
1451 }
1452 }
1453
1454 if (musicbot.messageNewSong == true && queue.last && queue.loop !== "song") {
1455 let req = client.users.get(video.requester);
1456 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
1457 const embed = new Discord.RichEmbed()
1458 .setTitle("Now Playing", `${req !== null ? req.displayAvatarURL : null}`)
1459 .setThumbnail(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`)
1460 .setDescription(`[${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}](${video.url}) by [${video.channelTitle}](${video.channelURL})`)
1461 .setColor(musicbot.embedColor)
1462 .setFooter(`Requested by ${req !== null ? req.username : "Unknown User"}`, `${req !== null ? req.displayAvatarURL : null}`);
1463 msg.channel.send({embed});
1464 } else {
1465 msg.channel.send(musicbot.note("note", `\`${video.title.replace(/`/g, "''")}\` by \`${video.channelURL.replace(/`/g, "''")}\``))
1466 }
1467 }
1468
1469 try {
1470 musicbot.setLast(msg.guild.id, video).then(() => {
1471 if (musicbot.musicPresence) musicbot.updatePresence(queue, msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1472 });
1473
1474 let dispatcher = connection.playStream(ytdl(video.url, {
1475 filter: 'audioonly',
1476 quality: 'highestaudio',
1477 highWaterMark: 1<<25
1478 }), {
1479 bitrate: musicbot.bitRate,
1480 volume: (queue.volume / 100),
1481 highWaterMark: 1
1482 })
1483
1484 connection.on('error', (error) => {
1485 console.error(error);
1486 if (msg && msg.channel) msg.channel.send(musicbot.note('fail', `Something went wrong with the connection. Retrying queue...`));
1487 musicbot.executeQueue(msg, queue);
1488 });
1489
1490 dispatcher.on('error', (error) => {
1491 console.error(error);
1492 if (msg && msg.channel) msg.channel.send(musicbot.note('fail', `Something went wrong while playing music. Retrying queue...`));
1493 musicbot.executeQueue(msg, queue);
1494 });
1495
1496
1497 dispatcher.on('debug', (d) => {
1498 console.log(d);
1499 });
1500
1501 dispatcher.on('end', () => {
1502 setTimeout(() => {
1503 if (musicbot.queues.get(queue.id).needsRefresh) {
1504 queue = musicbot.queues.get(queue.id);
1505 queue.needsRefresh = false;
1506 musicbot.queues.set(queue.id, queue)
1507 }
1508 let loop = queue.loop;
1509 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1510 if (voiceConnection !== null && voiceConnection.channel.members.size <= 1){
1511 msg.channel.send(musicbot.note('note', 'No one in the voice channel, leaving...'))
1512 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
1513 if (musicbot.musicPresence) musicbot.updatePresence(musicbot.queues.get(msg.guild.id), msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1514 return voiceConnection.disconnect();
1515 }
1516 if (queue.songs.length > 0) {
1517 if (loop == "none" || loop == null) {
1518 queue.songs.shift();
1519 musicbot.updatePositions(queue, msg ? msg.guild.id : 000000).then(res => {
1520 queue.songs = typeof res.songs == "object" ? Array.from(res.songs) : [];
1521 musicbot.executeQueue(msg, queue);
1522 }).catch(e=> {
1523 console.log(e)
1524 console.log("@ dispatcher function");
1525 })
1526 } else if (loop == "queue" || loop == "song") {
1527 musicbot.executeQueue(msg, queue);
1528 };
1529 } else if (queue.songs.length <= 0) {
1530 if (msg && msg.channel) msg.channel.send(musicbot.note('note', 'Playback finished.'));
1531 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
1532 if (musicbot.musicPresence) musicbot.updatePresence(queue, msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1533 const voiceConnection = client.voiceConnections.find(val => val.channel.guild.id == msg.guild.id);
1534 if (voiceConnection !== null) return voiceConnection.disconnect();
1535 }
1536 }, 1250);
1537 });
1538 } catch (error) {
1539 console.log(error);
1540 }
1541 })
1542 .catch((error) => {
1543 console.log(error);
1544 });
1545 }
1546
1547 musicbot.note = (type, text) => {
1548 if (type === 'wrap') {
1549 let ntext = text
1550 .replace(/`/g, '`' + String.fromCharCode(8203))
1551 .replace(/@/g, '@' + String.fromCharCode(8203))
1552 .replace(client.token, 'REMOVED');
1553 return '```\n' + ntext + '\n```';
1554 } else if (type === 'note') {
1555 return ':musical_note: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1556 } else if (type === 'search') {
1557 return ':mag: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1558 } else if (type === 'fail') {
1559 return ':no_entry_sign: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1560 } else if (type === 'font') {
1561 return text.replace(/`/g, '`' + String.fromCharCode(8203))
1562 .replace(/@/g, '@' + String.fromCharCode(8203))
1563 .replace(/\\/g, '\\\\')
1564 .replace(/\*/g, '\\*')
1565 .replace(/_/g, '\\_')
1566 .replace(/~/g, '\\~')
1567 .replace(/`/g, '\\`');
1568 } else {
1569 console.error(new Error(`${type} was an invalid type`));
1570 }
1571 };
1572
1573 musicbot.loadCommands = async () => {
1574 try {
1575 await musicbot.loadCommand(musicbot.play);
1576 await musicbot.loadCommand(musicbot.remove);
1577 await musicbot.loadCommand(musicbot.help);
1578 await musicbot.loadCommand(musicbot.skip);
1579 await musicbot.loadCommand(musicbot.leave);
1580 await musicbot.loadCommand(musicbot.search);
1581 await musicbot.loadCommand(musicbot.pause);
1582 await musicbot.loadCommand(musicbot.resume);
1583 await musicbot.loadCommand(musicbot.volume);
1584 await musicbot.loadCommand(musicbot.queue);
1585 await musicbot.loadCommand(musicbot.loop);
1586 await musicbot.loadCommand(musicbot.clearqueue);
1587 await musicbot.loadCommand(musicbot.np);
1588 await musicbot.loadCommand(musicbot.shuffle)
1589 await musicbot.loadCommand(musicbot.deleteQueue)
1590 } catch (e) {
1591 console.error(new Error(e));
1592 };
1593 }
1594 musicbot.loadCommands();
1595
1596 Object.defineProperty(Array.prototype, 'musicArraySort', {value: function(n) {
1597 return Array.from(Array(Math.ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
1598 }});
1599 Object.defineProperty(Array.prototype, 'musicBotShuffle', {value: function(){
1600 let input = this;
1601 for (let i = input.length - 1; i >= 0; i--) {
1602 let randomIndex = Math.floor(Math.random() * (i + 1));
1603 let itemAtIndex = input[randomIndex];
1604 input[randomIndex] = input[i];
1605 input[i] = itemAtIndex;
1606 }
1607 return input;
1608 }});
1609
1610 } catch (e) {
1611 console.error(e);
1612 };
1613}