· 5 years ago · Jul 03, 2020, 10:02 PM
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 console.log(member);
343 if (!this.queues.has(server)) {
344 this.queues.set(server,
345 {songs: [],
346 last: null,
347 loop: "none",
348 id: server,volume: this.defVolume,
349 oldSongs: [],
350 working: false,
351 needsRefresh: false,
352 connection: member.join()
353 .then(connection => console.log('Connected!'))
354 .catch(console.error)});
355 };
356 return this.queues.get(server);
357 };
358
359 setLast(server, last) {
360 return new Promise((resolve, reject) => {
361 if (this.queues.has(server)) {
362 let q = this.queues.get(server);
363 q.last = last;
364 this.queues.set(server, q);
365 resolve(this.queues.get(server));
366 } else {
367 reject("no server queue");
368 };
369 });
370 };
371
372 emptyQueue(server) {
373 return new Promise((resolve, reject) => {
374 if (!server || typeof server != "string") reject("no server id passed or passed obj was no a string @emptyQueue")
375 this.queues.set(server, {songs: [], last: null, loop: "none", id: server, volume: this.defVolume, oldSongs: [],working: false, needsRefresh: false});
376 resolve(this.queues.get(server));
377 });
378 };
379
380 async updatePresence(queue, client, clear) {
381 return new Promise((resolve, reject) => {
382 if (this.nextPresence !== null) clear = false;
383 if (!queue || !client) reject("invalid arguments");
384 if (queue.songs.length > 0 && queue.last) {
385 client.user.setPresence({
386 game: {
387 name: "? | " + queue.last.title,
388 type: 'LISTENING'
389 }
390 });
391 resolve(client.user.presence);
392 } else {
393 if (clear) {
394 client.user.setPresence({ game: { name: null} });
395 resolve(client.user.presence);
396 } else {
397 if (this.nextPresence !== null) {
398 let props;
399 if (this.nextPresence.status && ["online","dnd","idle","invisible"].includes(this.nextPresence.status)) props.status = this.nextPresence.status;
400 if (this.nextPresence.afk && typeof this.nextPresence.afk == "boolean") props.afk = this.nextPresence.afk;
401 if (this.nextPresence.game && typeof this.nextPresence.game == "string") props.game = {name: this.nextPresence.game}
402 else if (this.nextPresence.game && typeof this.nextPresence.game == "object") props.game = this.nextPresence.game;
403 client.user.setPresence(props).catch((res) => {
404 console.error("[MUSICBOT] Could not update presence\n" + res);
405 client.user.setPresence({ game: { name: null} });
406 resolve(client.user.presence);
407 }).then((res) => {
408 resolve(res);
409 });
410 } else {
411 client.user.setPresence({
412 game: {
413 name: "? | nothing",
414 type: 'LISTENING'
415 }
416 });
417 }
418 resolve(client.user.presence);
419 };
420 };
421 });
422 };
423
424 updatePrefix(server, prefix) {
425 if (typeof prefix == undefined) prefix = this.defaultPrefix;
426 if (typeof this.botPrefix != "object") this.botPrefix = new Map();
427 this.botPrefix.set(server, {prefix: prefix});
428 };
429 };
430
431 var musicbot = new Music(client, options);
432 if (musicbot.insertMusic == true) client.music = musicbot;
433 else exports.bot = musicbot;
434
435 musicbot.searcher = new YTSearcher(musicbot.youtubeKey);
436 musicbot.changeKey = (key) => {
437 return new Promise((resolve, reject) => {
438 if (!key || typeof key !== "string") reject("key must be a string");
439 musicbot.youtubeKey = key;
440 musicbot.searcher.key = key;
441 resolve(musicbot);
442 });
443 };
444
445 client.on("ready", () => {
446 console.log(`------- Music Bot -------\n> Version: ${PACKAGE.version}\n> Extra Logging: ${musicbot.logging}.\n> Node.js Version: ${process.version}\n------- Music Bot -------`);
447 if (musicbot.cooldown.exclude.includes("skip")) console.warn(`[MUSIC] Excluding SKIP CMD from cooldowns can cause issues.`);
448 if (musicbot.cooldown.exclude.includes("remove")) console.warn(`[MUSIC] Excluding REMOVE CMD from cooldowns can cause issues.`);
449 setTimeout(() => { if (musicbot.musicPresence == true && musicbot.client.guilds.length > 1) console.warn(`[MUSIC] MusicPresence is enabled with more than one server!`); }, 2000);
450 });
451
452 client.on("message", (msg) => {
453 if (msg.author.bot || musicbot.channelBlacklist.includes(msg.channel.id)) return;
454 if (musicbot.channelWhitelist.length > 0 && !musicbot.channelWhitelist.includes(msg.channel.id)) return;
455 const message = msg.content.trim();
456 const prefix = typeof musicbot.botPrefix == "object" ? (musicbot.botPrefix.has(msg.guild.id) ? musicbot.botPrefix.get(msg.guild.id).prefix : musicbot.defaultPrefix) : musicbot.botPrefix;
457 const command = message.substring(prefix.length).split(/[ \n]/)[0].trim();
458 const suffix = message.substring(prefix.length + command.length).trim();
459 const args = message.slice(prefix.length + command.length).trim().split(/ +/g);
460
461 if (message.startsWith(prefix) && msg.channel.type == "text") {
462 if (musicbot.commands.has(command)) {
463 let tCmd = musicbot.commands.get(command);
464 if (tCmd.enabled) {
465 if (!musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(tCmd.masked)) {
466 if (musicbot.recentTalk.has(msg.author.id)) {
467 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."));
468 }
469 musicbot.recentTalk.add(msg.author.id);
470 setTimeout(() => { musicbot.recentTalk.delete(msg.author.id) }, musicbot.cooldown.timer);
471 }
472 return musicbot[tCmd.run](msg, suffix, args);
473 }
474 } else if (musicbot.aliases.has(command)) {
475 let aCmd = musicbot.aliases.get(command);
476 if (aCmd.enabled) {
477 if (!musicbot.cooldown.enabled == true && !musicbot.cooldown.exclude.includes(aCmd.masked)) {
478 if (musicbot.recentTalk.has(msg.author.id)) {
479 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."));
480 }
481 musicbot.recentTalk.add(msg.author.id);
482 setTimeout(() => { musicbot.recentTalk.delete(msg.author.id) }, musicbot.cooldown.timer);
483 }
484 return musicbot[aCmd.run](msg, suffix, args);
485 }
486 };
487 };
488 });
489
490 musicbot.playFunction = (msg, suffix, args, ignore) => {
491 if (msg.member.voice.channel === undefined) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
492 if (!suffix) return msg.channel.send(musicbot.note('fail', 'No video specified!'));
493 let q = musicbot.getQueue(msg.guild.id);
494
495 let vc = client.voice.connections.find(val => val.channel.guild.id == msg.member.guild.id)
496 if (vc && vc.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
497 if (q.songs.length >= musicbot.maxQueueSize && musicbot.maxQueueSize !== 0) return msg.channel.send(musicbot.note('fail', 'Maximum queue size reached!'));
498 var searchstring = suffix.trim();
499 if (searchstring.includes("https://youtu.be/") || searchstring.includes("https://www.youtube.com/") && searchstring.includes("&")) searchstring = searchstring.split("&")[0];
500
501
502 if (searchstring.startsWith('http') && searchstring.includes("list=")) {
503 msg.channel.send(musicbot.note("search", `Searching playlist items~`));
504 var playid = searchstring.toString()
505 .split('list=')[1];
506 if (playid.toString()
507 .includes('?')) playid = playid.split('?')[0];
508 if (playid.toString()
509 .includes('&t=')) playid = playid.split('&t=')[0];
510
511 ytpl(playid, {limit: musicbot.maxQueueSize}, function(err, playlist) {
512 if(err) return msg.channel.send(musicbot.note('fail', `Something went wrong fetching that playlist!`));
513 if (playlist.items.length <= 0) return msg.channel.send(musicbot.note('fail', `Couldn't get any videos from that playlist.`));
514 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.`));
515 var index = 0;
516 var ran = 0;
517 var queue = musicbot.getQueue(msg.guild.id);
518
519 playlist.items.forEach(async (video) => {
520 ran++;
521 if (queue.songs.length == (musicbot.maxQueueSize + 1) && musicbot.maxQueueSize !== 0 || !video) return;
522 video.url = video.url_simple ? video.url_simple : `https://www.youtube.com/watch?v=` + video.id;
523 musicbot.playFunction(msg, video.url, [], true);
524 index++;
525
526 if (ran >= playlist.items.length) {
527 console.log(queue);
528 if (queue.songs.length >= 1) musicbot.executeQueue(msg, queue);
529 if (index == 0) msg.channel.send(musicbot.note('fail', `Coudln't get any songs from that playlist!`))
530 else if (index == 1) msg.channel.send(musicbot.note('note', `Queued one song.`));
531 else if (index > 1) msg.channel.send(musicbot.note('note', `Queued ${index} songs.`));
532 }
533 });
534 });
535 } else {
536 if (!ignore) msg.channel.send(musicbot.note("search", `\`Searching: ${searchstring}\`~`));
537 new Promise(async (resolve, reject) => {
538 let result = await musicbot.searcher.search(searchstring, { type: 'video' }).catch((err) => {
539 var errorMsg = err.message;
540 if (errorMsg.includes('\"reason\": \"dailyLimitExceeded\",')) {
541 errorMsg = errorMsg.slice(errorMsg.indexOf('Daily Limit Exceeded. '));
542 errorMsg = errorMsg.slice(0, errorMsg.indexOf('\",'));
543 if (!ignore) msg.channel.send(musicbot.note("fail", "**Unable to complete playback:**\n" + errorMsg));
544 return;
545 } else if (errorMsg.includes('\"reason\": \"quotaExceeded\",')) {
546 if (!ignore) msg.channel.send(musicbot.note("fail", "Unable to complete playback! Google API quota exceeded!"));
547 return;
548 } else {
549 if (!ignore) msg.channel.send(musicbot.note("fail", "Unknown error occurred! Playback could not be completed, check the logs for more details."));
550 return console.log(err);
551 }
552 });
553 if (result === undefined) return;
554 resolve(result.first)
555 }).then((res) => {
556 if (!res) return msg.channel.send(musicbot.note("fail", "Something went wrong. Try again!"));
557 res.requester = msg.author.id;
558 if (searchstring.startsWith("https://www.youtube.com/") || searchstring.startsWith("https://youtu.be/")) res.url = searchstring;
559 res.channelURL = `https://www.youtube.com/channel/${res.channelId}`;
560 res.queuedOn = new Date().toLocaleDateString(musicbot.dateLocal, { weekday: 'long', hour: 'numeric' });
561 if (musicbot.requesterName) res.requesterAvatarURL = msg.author.displayAvatarURL;
562 const queue = musicbot.getQueue(msg.guild.id)
563 res.position = queue.songs.length ? queue.songs.length : 0;
564 queue.songs.push(res);
565
566 if (!ignore) {
567 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
568 const embed = new Discord.MessageEmbed();
569 try {
570 embed.setAuthor('Adding To Queue', client.user.avatarURL);
571 var songTitle = res.title.replace(/\\/g, '\\\\')
572 .replace(/\`/g, '\\`')
573 .replace(/\*/g, '\\*')
574 .replace(/_/g, '\\_')
575 .replace(/~/g, '\\~')
576 .replace(/`/g, '\\`');
577 embed.setColor(musicbot.embedColor);
578 embed.addField(res.channelTitle, `[${songTitle}](${res.url})`, musicbot.inlineEmbeds);
579 embed.addField("Queued On", res.queuedOn, musicbot.inlineEmbeds);
580 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${res.id}/maxresdefault.jpg`);
581 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${res.id}/maxresdefault.jpg`);
582 const resMem = client.users.cache.get(res.requester);
583 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.cache.get(res.requester).username}`, res.requesterAvatarURL);
584 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${res.requester})\``, res.requesterAvatarURL);
585 msg.channel.send({
586 embed
587 });
588 } catch (e) {
589 console.error(`[${msg.guild.name}] [playCmd] ` + e.stack);
590 };
591 } else {
592 try {
593 var songTitle = res.title.replace(/\\/g, '\\\\')
594 .replace(/\`/g, '\\`')
595 .replace(/\*/g, '\\*')
596 .replace(/_/g, '\\_')
597 .replace(/~/g, '\\~')
598 .replace(/`/g, '\\`');
599 msg.channel.send(`Now Playing: **${songTitle}**\nRequested By: ${client.users.cache.get(res.requester).username}\nQueued On: ${res.queuedOn}`)
600 } catch (e) {
601 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
602 };
603 };
604 };
605 if (queue.songs.length === 1 || !client.voice.connections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
606 }).catch((res) => {
607 console.log(new Error(res));
608 });
609 };
610 };
611
612 musicbot.helpFunction = (msg, suffix, args) => {
613 const prefix = typeof musicbot.botPrefix == "object" ? (musicbot.botPrefix.has(msg.guild.id) ? musicbot.botPrefix.get(msg.guild.id).prefix : musicbot.defaultPrefix) : musicbot.botPrefix;
614 let command = suffix.trim();
615 if (!suffix) {
616 if (msg.channel.permissionsFor(msg.guild.me)
617 .has('EMBED_LINKS')) {
618 const embed = new Discord.MessageEmbed();
619 embed.setAuthor("Commands", client.user.avatarURL);
620 embed.setDescription(`Use \`${prefix}${musicbot.help.name} command name\` for help on usage. Anyone with a role named \`${musicbot.djRole}\` can use any command.`);
621 // embed.addField(musicbot.helpCmd, musicbot.helpHelp);
622 const newCmds = Array.from(musicbot.commands);
623 let index = 0;
624 let max = musicbot.commandsArray.length;
625 embed.setColor(musicbot.embedColor);
626 for (var i = 0; i < musicbot.commandsArray.length; i++) {
627 if (!musicbot.commandsArray[i].exclude) embed.addField(musicbot.commandsArray[i].name, musicbot.commandsArray[i].help);
628 index++;
629 if (index == max) {
630 if (musicbot.messageHelp) {
631 let sent = false;
632 msg.author.send({
633 embed
634 })
635 .then(() => {
636 sent = true;
637 });
638 setTimeout(() => {
639 if (!sent) return msg.channel.send({
640 embed
641 });
642 }, 1200);
643 } else {
644 return msg.channel.send({
645 embed
646 });
647 };
648 }
649 };
650 } else {
651 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`;
652 let index = 0;
653 let max = musicbot.commandsArray.length;
654 for (var i = 0; i < musicbot.commandsArray.length; i++) {
655 if (!musicbot.commandsArray[i].disabled || !musicbot.commandsArray[i].exclude) {
656 cmdmsg = cmdmsg + `\n• ${musicbot.commandsArray[i].name}: ${musicbot.commandsArray[i].help}`;
657 index++;
658 if (index == musicbot.commandsArray.length) {
659 if (musicbot.messageHelp) {
660 let sent = false;
661 msg.author.send(cmdmsg, {
662 code: 'asciidoc'
663 })
664 .then(() => {
665 sent = true;
666 });
667 setTimeout(() => {
668 if (!sent) return msg.channel.send(cmdmsg, {
669 code: 'asciidoc'
670 });
671 }, 500);
672 } else {
673 return msg.channel.send(cmdmsg, {
674 code: 'asciidoc'
675 });
676 };
677 }
678 };
679 };
680 };
681 } else if (musicbot.commands.has(command) || musicbot.aliases.has(command)) {
682 if (msg.channel.permissionsFor(msg.guild.me)
683 .has('EMBED_LINKS')) {
684 const embed = new Discord.MessageEmbed();
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 embed.setAuthor(command.name, msg.client.user.avatarURL);
688 embed.setDescription(command.help);
689 if (command.alt.length > 0) embed.addField(`Aliases`, command.alt.join(", "), musicbot.inlineEmbeds);
690 if (command.usage && typeof command.usage == "string") embed.addField(`Usage`, command.usage.replace(/{{prefix}})/g, prefix), musicbot.inlineEmbeds);
691 embed.setColor(musicbot.embedColor);
692 msg.channel.send({
693 embed
694 });
695 } else {
696 command = musicbot.commands.get(command) || musicbot.aliases.get(command);
697 if (command.exclude) return msg.channel.send(musicbot.note('fail', `${suffix} is not a valid command!`));
698 var cmdhelp = `= ${command.name} =\n`;
699 cmdhelp = cmdhelp + `\n${command.help}`;
700 if (command.usage !== null) cmdhelp = cmdhelp + `\nUsage: ${command.usage.replace(/{{prefix}})/g, prefix)}`;
701 if (command.alt.length > 0) cmdhelp = cmdhelp + `\nAliases: ${command.alt.join(", ")}`;
702 msg.channel.send(cmdhelp, {
703 code: 'asciidoc'
704 });
705 };
706 } else {
707 msg.channel.send(musicbot.note('fail', `${suffix} is not a valid command!`));
708 };
709 };
710
711 musicbot.skipFunction = (msg, suffix, args) => {
712 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
713 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
714 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
715 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
716
717 const queue = musicbot.getQueue(msg.guild.id);
718 if (!musicbot.canSkip(msg.member, queue)) return msg.channel.send(musicbot.note('fail', `You cannot skip this as you didn't queue it.`));
719
720 if (musicbot.queues.get(msg.guild.id).loop == "song") return msg.channel.send(musicbot.note("fail", "Cannot skip while loop is set to single."));
721
722 const dispatcher = voiceConnection.player.dispatcher;
723 if (!dispatcher || dispatcher === null) {
724 if (musicbot.logging) return console.log(new Error(`dispatcher null on skip cmd [${msg.guild.name}] [${msg.author.username}]`));
725 return msg.channel.send(musicbot.note("fail", "Something went wrong running skip."));
726 };
727 if (voiceConnection.paused) dispatcher.end();
728 dispatcher.end();
729 msg.channel.send(musicbot.note("note", "Skipped song."));
730 };
731
732 musicbot.pauseFunction = (msg, suffix, args) => {
733 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
734 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
735 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
736 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
737 if (!musicbot.isAdmin(msg.member) && !musicbot.anyoneCanPause) return msg.channel.send(musicbot.note('fail', 'You cannot pause queues.'));
738
739 const dispatcher = voiceConnection.player.dispatcher;
740 if (dispatcher.paused) return msg.channel.send(musicbot.note(`fail`, `Music already paused!`))
741 else dispatcher.pause();
742 msg.channel.send(musicbot.note('note', 'Playback paused.'));
743 };
744
745 musicbot.resumeFunction = (msg, suffix, args) => {
746 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
747 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
748 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
749 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
750 if (!musicbot.isAdmin(msg.member) && !musicbot.anyoneCanPause) return msg.channel.send(musicbot.note('fail', `You cannot resume queues.`));
751
752 const dispatcher = voiceConnection.player.dispatcher;
753 if (!dispatcher.paused) return msg.channel.send(musicbot.note('fail', `Music already playing.`))
754 else dispatcher.resume();
755 msg.channel.send(musicbot.note('note', 'Playback resumed.'));
756 };
757
758 musicbot.leaveFunction = (msg, suffix) => {
759 if (musicbot.isAdmin(msg.member) || musicbot.anyoneCanLeave === true) {
760 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
761 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
762 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'I\'m not in a voice channel.'));
763 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
764 musicbot.emptyQueue(msg.guild.id).then(() => {
765 if (!voiceConnection.player.dispatcher) return;
766 voiceConnection.player.dispatcher.end();
767 voiceConnection.disconnect();
768 msg.channel.send(musicbot.note('note', 'Successfully left the voice channel.'));
769 }).catch((res) => {
770 console.log("["+msg.guild.id+"] " + res);
771 musicbot.queues.delete(msg.guild.id);
772 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
773 })
774
775 } else {
776 const chance = Math.floor((Math.random() * 100) + 1);
777 if (chance <= 10) return msg.channel.send(musicbot.note('fail', `I'm afraid I can't let you do that, ${msg.author.username}.`))
778 else return msg.channel.send(musicbot.note('fail', 'Sorry, you\'re not allowed to do that.'));
779 }
780 }
781
782 musicbot.npFunction = (msg, suffix, args) => {
783 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
784 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music is being played.'));
785 const queue = musicbot.getQueue(msg.guild.id, true);
786 const dispatcher = voiceConnection.player.dispatcher;
787
788 if (musicbot.queues.get(msg.guild.id).songs.length <= 0) return msg.channel.send(musicbot.note('note', 'Queue empty.'));
789
790 if (msg.channel.permissionsFor(msg.guild.me)
791 .has('EMBED_LINKS')) {
792 const embed = new Discord.MessageEmbed();
793 try {
794 embed.setAuthor('Now Playing', client.user.avatarURL);
795 var songTitle = queue.last.title.replace(/\\/g, '\\\\')
796 .replace(/\`/g, '\\`')
797 .replace(/\*/g, '\\*')
798 .replace(/_/g, '\\_')
799 .replace(/~/g, '\\~')
800 .replace(/`/g, '\\`');
801 embed.setColor(musicbot.embedColor);
802 embed.addField(queue.last.channelTitle, `[${songTitle}](${queue.last.url})`, musicbot.inlineEmbeds);
803 embed.addField("Queued On", queue.last.queuedOn, musicbot.inlineEmbeds);
804 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${queue.last.id}/maxresdefault.jpg`);
805 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${queue.last.id}/maxresdefault.jpg`);
806 const resMem = client.users.cache.get(queue.last.requester);
807 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.cache.get(queue.last.requester).username}`, queue.last.requesterAvatarURL);
808 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${queue.last.requester})\``, queue.last.requesterAvatarURL);
809 msg.channel.send({
810 embed
811 });
812 } catch (e) {
813 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
814 };
815 } else {
816 try {
817 var songTitle = queue.last.title.replace(/\\/g, '\\\\')
818 .replace(/\`/g, '\\`')
819 .replace(/\*/g, '\\*')
820 .replace(/_/g, '\\_')
821 .replace(/~/g, '\\~')
822 .replace(/`/g, '\\`');
823 msg.channel.send(`Now Playing: **${songTitle}**\nRequested By: ${client.users.cache.get(queue.last.requester).username}\nQueued On: ${queue.last.queuedOn}`)
824 } catch (e) {
825 console.error(`[${msg.guild.name}] [npCmd] ` + e.stack);
826 };
827 }
828 };
829
830 musicbot.deleteQueueFunction = async (msg, suffix, args) => {
831 if (!musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", "Only and Admin can do this."));
832 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
833 musicbot.emptyQueue(msg.guild.id).then(() => {
834 if (voiceConnection !== null) {
835 const dispatcher = voiceConnection.player.dispatcher;
836 dispatcher.end()
837 }
838 return msg.channel.send(musicbot.note("note", "The queue should now be emptied."))
839 }).catch(async (res) => {
840 console.log("["+msg.guild.id+"] " + e);
841 // force the queue delete
842 musicbot.queues.delete(msg.guild.id);
843 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
844 if (voiceConnection !== null) {
845 const dispatcher = voiceConnection.player.dispatcher;
846 dispatcher.end()
847 }
848 msg.channel.send(musicbot.note("note", "The queue should now be deleted."))
849 })
850 }
851
852 musicbot.queueFunction = (msg, suffix, args) => {
853 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
854 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
855 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music being played.'));
856 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
857 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note("fail", "Could not find a queue for this server."));
858 else if (musicbot.queues.get(msg.guild.id).songs.length <= 0) return msg.channel.send(musicbot.note("fail", "Queue is empty."));
859 const queue = musicbot.queues.get(msg.guild.id);
860 if (suffix) {
861 let video = queue.songs.find(s => s.position == parseInt(suffix) - 1);
862 if (!video) return msg.channel.send(musicbot.note("fail", "Couldn't find that video."));
863 const embed = new Discord.MessageEmbed()
864 .setAuthor('Queued Song', client.user.avatarURL)
865 .setColor(musicbot.embedColor)
866 .addField(video.channelTitle, `[${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}](${video.url})`, musicbot.inlineEmbeds)
867 .addField("Queued On", video.queuedOn, musicbot.inlineEmbeds)
868 .addField("Position", video.position + 1, musicbot.inlineEmbeds);
869 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`);
870 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`);
871 const resMem = client.users.cache.get(video.requester);
872 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.cache.get(video.requester).username}`, video.requesterAvatarURL);
873 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${video.requester})\``, video.requesterAvatarURL);
874 msg.channel.send({embed});
875 } else {
876 if (queue.songs.length > 11) {
877 let pages = [];
878 let page = 1;
879 const newSongs = queue.songs.musicArraySort(10);
880 newSongs.forEach(s => {
881 var i = s.map((video, index) => (
882 `**${video.position + 1}:** __${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}__`
883 )).join('\n\n');
884 if (i !== undefined) pages.push(i)
885 });
886
887 const embed = new Discord.MessageEmbed();
888 embed.setAuthor('Queued Songs', client.user.avatarURL);
889 embed.setColor(musicbot.embedColor);
890 embed.setFooter(`Page ${page} of ${pages.length}`);
891 embed.setDescription(pages[page - 1]);
892 msg.channel.send(embed).then(m => {
893 m.react('⏪').then( r => {
894 m.react('⏩')
895 let forwardsFilter = m.createReactionCollector((reaction, user) => reaction.emoji.name === '⏩' && user.id === msg.author.id, { time: 120000 });
896 let backFilter = m.createReactionCollector((reaction, user) => reaction.emoji.name === '⏪' && user.id === msg.author.id, { time: 120000 });
897
898 forwardsFilter.on('collect', r => {
899 if (page === pages.length) return;
900 page++;
901 embed.setDescription(pages[page - 1]);
902 embed.setFooter(`Page ${page} of ${pages.length}`, msg.author.displayAvatarURL);
903 m.edit(embed);
904 })
905 backFilter.on('collect', r => {
906 if (page === 1) return;
907 page--;
908 embed.setDescription(pages[page - 1]);
909 embed.setFooter(`Page ${page} of ${pages.length}`);
910 m.edit(embed);
911 })
912 })
913 })
914 } else {
915 try {
916 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');
917 const embed = new Discord.MessageEmbed();
918 embed.setAuthor('Queued Songs', client.user.avatarURL);
919 embed.setColor(musicbot.embedColor);
920 embed.setDescription(newSongs);
921 embed.setFooter(`Page 1 of 1`, msg.author.displayAvatarURL);
922 return msg.channel.send(embed);
923 } catch (e) {
924 console.log("["+msg.guild.id+"] " + e);
925 return msg.channel.send(msicbot.note("fail", "Something went wrong mapping out the queue! Please delete the queue if this persists."));
926 };
927 };
928 };
929 };
930
931 musicbot.searchFunction = (msg, suffix, args) => {
932 if (msg.member.voice.channel === undefined) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
933 let vc = client.voice.connections.find(val => val.channel.guild.id == msg.member.guild.id)
934 if (vc && vc.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
935 let us = `${msg.guild.id}-${msg.author.id}`;
936 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.`));
937
938 if (!suffix) return msg.channel.send(musicbot.note('fail', 'No video specified!'));
939 const queue = musicbot.getQueue(msg.guild.id);
940 if (queue.songs.length >= musicbot.maxQueueSize && musicbot.maxQueueSize !== 0) return msg.channel.send(musicbot.note('fail', 'Maximum queue size reached!'));
941 musicbot.userSearching.set(us, {guild: msg.guild.id, user: msg.author.id, title: suffix})
942 let searchstring = suffix.trim();
943 msg.channel.send(musicbot.note('search', `Searching: \`${searchstring}\``))
944 .then(response => {
945 musicbot.searcher.search(searchstring, {
946 type: 'video'
947 })
948 .then(searchResult => {
949 if (!searchResult.totalResults || searchResult.totalResults === 0) return response.edit(musicbot.note('fail', 'Failed to get search results.'));
950
951 const startTheFun = async (videos, max) => {
952 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
953 const embed = new Discord.MessageEmbed();
954 embed.setTitle(`Choose Your Video`);
955 embed.setColor(musicbot.embedColor);
956 var index = 0;
957 videos.forEach(function(video) {
958 index++;
959 embed.addField(`${index} (${video.channelTitle})`, `[${musicbot.note('font', video.title)}](${video.url})`, musicbot.inlineEmbeds);
960 });
961 embed.setFooter(`Search by: ${msg.author.username}`, msg.author.displayAvatarURL);
962 msg.channel.send({
963 embed
964 })
965 .then(firstMsg => {
966 var filter = null;
967 if (max === 0) {
968 filter = m => m.author.id === msg.author.id &&
969 m.content.includes('1') ||
970 m.content.trim() === (`cancel`);
971 } else if (max === 1) {
972 filter = m => m.author.id === msg.author.id &&
973 m.content.includes('1') ||
974 m.content.includes('2') ||
975 m.content.trim() === (`cancel`);
976 } else if (max === 2) {
977 filter = m => m.author.id === msg.author.id &&
978 m.content.includes('1') ||
979 m.content.includes('2') ||
980 m.content.includes('3') ||
981 m.content.trim() === (`cancel`);
982 } else if (max === 3) {
983 filter = m => m.author.id === msg.author.id &&
984 m.content.includes('1') ||
985 m.content.includes('2') ||
986 m.content.includes('3') ||
987 m.content.includes('4') ||
988 m.content.trim() === (`cancel`);
989 } else if (max === 4) {
990 filter = m => m.author.id === msg.author.id &&
991 m.content.includes('1') ||
992 m.content.includes('2') ||
993 m.content.includes('3') ||
994 m.content.includes('4') ||
995 m.content.includes('5') ||
996 m.content.trim() === (`cancel`);
997 } else if (max === 5) {
998 filter = m => m.author.id === msg.author.id &&
999 m.content.includes('1') ||
1000 m.content.includes('2') ||
1001 m.content.includes('3') ||
1002 m.content.includes('4') ||
1003 m.content.includes('5') ||
1004 m.content.includes('6') ||
1005 m.content.trim() === (`cancel`);
1006 } else if (max === 6) {
1007 filter = m => m.author.id === msg.author.id &&
1008 m.content.includes('1') ||
1009 m.content.includes('2') ||
1010 m.content.includes('3') ||
1011 m.content.includes('4') ||
1012 m.content.includes('5') ||
1013 m.content.includes('6') ||
1014 m.content.includes('7') ||
1015 m.content.trim() === (`cancel`);
1016 } else if (max === 7) {
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.trim() === (`cancel`);
1027 } else if (max === 8) {
1028 filter = m => m.author.id === msg.author.id &&
1029 m.content.includes('1') ||
1030 m.content.includes('2') ||
1031 m.content.includes('3') ||
1032 m.content.includes('4') ||
1033 m.content.includes('5') ||
1034 m.content.includes('6') ||
1035 m.content.includes('7') ||
1036 m.content.includes('8') ||
1037 m.content.includes('9') ||
1038 m.content.trim() === (`cancel`);
1039 } else if (max === 9) {
1040 filter = m => m.author.id === msg.author.id &&
1041 m.content.includes('1') ||
1042 m.content.includes('2') ||
1043 m.content.includes('3') ||
1044 m.content.includes('4') ||
1045 m.content.includes('5') ||
1046 m.content.includes('6') ||
1047 m.content.includes('7') ||
1048 m.content.includes('8') ||
1049 m.content.includes('9') ||
1050 m.content.includes('10') ||
1051 m.content.trim() === (`cancel`);
1052 }
1053 msg.channel.awaitMessages(filter, {
1054 max: 1,
1055 time: 60000,
1056 errors: ['time']
1057 })
1058 .then(collected => {
1059 const newColl = Array.from(collected);
1060 const mcon = newColl[0][1].content;
1061
1062 if (mcon === "cancel") {
1063 musicbot.userSearching.delete(us);
1064 return firstMsg.edit(musicbot.note('note', 'Searching canceled.'));
1065 };
1066 const song_number = parseInt(mcon) - 1;
1067 if (song_number >= 0) {
1068 musicbot.userSearching.delete(us);
1069 firstMsg.delete();
1070
1071 videos[song_number].requester = msg.author.id;
1072 videos[song_number].position = queue.songs.length ? queue.songs.length : 0;
1073 var embed = new Discord.MessageEmbed();
1074 embed.setAuthor('Adding To Queue', client.user.avatarURL);
1075 var songTitle = videos[song_number].title.replace(/\\/g, '\\\\')
1076 .replace(/\`/g, '\\`')
1077 .replace(/\*/g, '\\*')
1078 .replace(/_/g, '\\_')
1079 .replace(/~/g, '\\~')
1080 .replace(/`/g, '\\`');
1081 embed.setColor(musicbot.embedColor);
1082 embed.addField(videos[song_number].channelTitle, `[${songTitle}](${videos[song_number].url})`, musicbot.inlineEmbeds);
1083 embed.addField("Queued On", videos[song_number].queuedOn, musicbot.inlineEmbeds);
1084 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1085 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1086 const resMem = client.users.cache.get(videos[song_number].requester);
1087 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.cache.get(videos[song_number].requester).username}`, videos[song_number].requesterAvatarURL);
1088 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${videos[song_number].requester})\``, videos[song_number].requesterAvatarURL);
1089 msg.channel.send({
1090 embed
1091 }).then(() => {
1092 queue.songs.push(videos[song_number]);
1093 if (queue.songs.length === 1 || !client.voice.connections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
1094 })
1095 .catch(console.log);
1096 };
1097 })
1098 .catch(collected => {
1099 musicbot.userSearching.delete(us);
1100 if (collected.toString().match(/error|Error|TypeError|RangeError|Uncaught/)) return firstMsg.edit(`\`\`\`xl\nSearching canceled. ${collected}\n\`\`\``);
1101 return firstMsg.edit(`\`\`\`xl\nSearching canceled.\n\`\`\``);
1102 });
1103 })
1104 } else {
1105 const vids = videos.map((video, index) => (
1106 `**${index + 1}:** __${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}__`
1107 )).join('\n\n');
1108 msg.channel.send(`\`\`\`\n= Pick Your Video =\n${vids}\n\n= Say Cancel To Cancel =`).then(firstMsg => {
1109 var filter = null;
1110 if (max === 0) {
1111 filter = m => m.author.id === msg.author.id &&
1112 m.content.includes('1') ||
1113 m.content.trim() === (`cancel`);
1114 } else if (max === 1) {
1115 filter = m => m.author.id === msg.author.id &&
1116 m.content.includes('1') ||
1117 m.content.includes('2') ||
1118 m.content.trim() === (`cancel`);
1119 } else if (max === 2) {
1120 filter = m => m.author.id === msg.author.id &&
1121 m.content.includes('1') ||
1122 m.content.includes('2') ||
1123 m.content.includes('3') ||
1124 m.content.trim() === (`cancel`);
1125 } else if (max === 3) {
1126 filter = m => m.author.id === msg.author.id &&
1127 m.content.includes('1') ||
1128 m.content.includes('2') ||
1129 m.content.includes('3') ||
1130 m.content.includes('4') ||
1131 m.content.trim() === (`cancel`);
1132 } else if (max === 4) {
1133 filter = m => m.author.id === msg.author.id &&
1134 m.content.includes('1') ||
1135 m.content.includes('2') ||
1136 m.content.includes('3') ||
1137 m.content.includes('4') ||
1138 m.content.includes('5') ||
1139 m.content.trim() === (`cancel`);
1140 } else if (max === 5) {
1141 filter = m => m.author.id === msg.author.id &&
1142 m.content.includes('1') ||
1143 m.content.includes('2') ||
1144 m.content.includes('3') ||
1145 m.content.includes('4') ||
1146 m.content.includes('5') ||
1147 m.content.includes('6') ||
1148 m.content.trim() === (`cancel`);
1149 } else if (max === 6) {
1150 filter = m => m.author.id === msg.author.id &&
1151 m.content.includes('1') ||
1152 m.content.includes('2') ||
1153 m.content.includes('3') ||
1154 m.content.includes('4') ||
1155 m.content.includes('5') ||
1156 m.content.includes('6') ||
1157 m.content.includes('7') ||
1158 m.content.trim() === (`cancel`);
1159 } else if (max === 7) {
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.trim() === (`cancel`);
1170 } else if (max === 8) {
1171 filter = m => m.author.id === msg.author.id &&
1172 m.content.includes('1') ||
1173 m.content.includes('2') ||
1174 m.content.includes('3') ||
1175 m.content.includes('4') ||
1176 m.content.includes('5') ||
1177 m.content.includes('6') ||
1178 m.content.includes('7') ||
1179 m.content.includes('8') ||
1180 m.content.includes('9') ||
1181 m.content.trim() === (`cancel`);
1182 } else if (max === 9) {
1183 filter = m => m.author.id === msg.author.id &&
1184 m.content.includes('1') ||
1185 m.content.includes('2') ||
1186 m.content.includes('3') ||
1187 m.content.includes('4') ||
1188 m.content.includes('5') ||
1189 m.content.includes('6') ||
1190 m.content.includes('7') ||
1191 m.content.includes('8') ||
1192 m.content.includes('9') ||
1193 m.content.includes('10') ||
1194 m.content.trim() === (`cancel`);
1195 }
1196 msg.channel.awaitMessages(filter, {
1197 max: 1,
1198 time: 60000,
1199 errors: ['time']
1200 })
1201 .then(collected => {
1202 musicbot.userSearching.delete(us);
1203 const newColl = Array.from(collected);
1204 const mcon = newColl[0][1].content;
1205
1206 if (mcon === "cancel") return firstMsg.edit(musicbot.note('note', 'Searching canceled.'));
1207 const song_number = parseInt(mcon) - 1;
1208 if (song_number >= 0) {
1209 firstMsg.delete();
1210
1211 videos[song_number].requester = msg.author.id;
1212 videos[song_number].position = queue.songs.length ? queue.songs.length : 0;
1213 var embed = new Discord.MessageEmbed();
1214 embed.setAuthor('Adding To Queue', client.user.avatarURL);
1215 var songTitle = videos[song_number].title.replace(/\\/g, '\\\\')
1216 .replace(/\`/g, '\\`')
1217 .replace(/\*/g, '\\*')
1218 .replace(/_/g, '\\_')
1219 .replace(/~/g, '\\~')
1220 .replace(/`/g, '\\`');
1221 embed.setColor(musicbot.embedColor);
1222 embed.addField(videos[song_number].channelTitle, `[${songTitle}](${videos[song_number].url})`, musicbot.inlineEmbeds);
1223 embed.addField("Queued On", videos[song_number].queuedOn, musicbot.inlineEmbeds);
1224 if (!musicbot.bigPicture) embed.setThumbnail(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1225 if (musicbot.bigPicture) embed.setImage(`https://img.youtube.com/vi/${videos[song_number].id}/maxresdefault.jpg`);
1226 const resMem = client.users.cache.get(videos[song_number].requester);
1227 if (musicbot.requesterName && resMem) embed.setFooter(`Requested by ${client.users.cache.get(videos[song_number].requester).username}`, videos[song_number].requesterAvatarURL);
1228 if (musicbot.requesterName && !resMem) embed.setFooter(`Requested by \`UnknownUser (ID: ${videos[song_number].requester})\``, videos[song_number].requesterAvatarURL);
1229 msg.channel.send({
1230 embed
1231 }).then(() => {
1232 queue.songs.push(videos[song_number]);
1233 if (queue.songs.length === 1 || !client.voice.connections.find(val => val.channel.guild.id == msg.guild.id)) musicbot.executeQueue(msg, queue);
1234 })
1235 .catch(console.log);
1236 };
1237 })
1238 .catch(collected => {
1239 musicbot.userSearching.delete(us);
1240 if (collected.toString()
1241 .match(/error|Error|TypeError|RangeError|Uncaught/)) return firstMsg.edit(`\`\`\`xl\nSearching canceled. ${collected}\n\`\`\``);
1242 return firstMsg.edit(`\`\`\`xl\nSearching canceled.\n\`\`\``);
1243 });
1244 })
1245 }
1246 };
1247
1248 const max = searchResult.totalResults >= 10 ? 9 : searchResult.totalResults - 1;
1249 var videos = [];
1250 for (var i = 0; i < 99; i++) {
1251 var result = searchResult.currentPage[i];
1252 result.requester = msg.author.id;
1253 if (musicbot.requesterName) result.requesterAvatarURL = msg.author.displayAvatarURL;
1254 result.channelURL = `https://www.youtube.com/channel/${result.channelId}`;
1255 result.queuedOn = new Date().toLocaleDateString(musicbot.dateLocal, { weekday: 'long', hour: 'numeric' });
1256 videos.push(result);
1257 if (i === max) {
1258 i = 101;
1259 startTheFun(videos, max);
1260 }
1261 };
1262 });
1263 })
1264 .catch(console.log);
1265 };
1266
1267 musicbot.volumeFunction = (msg, suffix, args) => {
1268 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1269 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1270 if (voiceConnection === null) return msg.channel.send(musicbot.note('fail', 'No music is being played.'));
1271 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1272 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.`));
1273 const dispatcher = voiceConnection.player.dispatcher;
1274
1275 if (!suffix || isNaN(suffix)) return msg.channel.send(musicbot.note('fail', 'No volume specified.'));
1276 suffix = parseInt(suffix);
1277 if (suffix > 200 || suffix <= 0) return msg.channel.send(musicbot.note('fail', 'Volume out of range, must be within 1 to 200'));
1278
1279 dispatcher.setVolume((suffix / 100));
1280 musicbot.queues.get(msg.guild.id).volume = suffix;
1281 msg.channel.send(musicbot.note('note', `Volume changed to ${suffix}%.`));
1282 };
1283
1284 musicbot.clearFunction = (msg, suffix, args) => {
1285 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note("fail", "No queue found for this server."));
1286 if (!musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", `Only Admins or people with the ${musicbot.djRole} can clear queues.`));
1287 let vc = client.voice.connections.find(val => val.channel.guild.id == msg.member.guild.id)
1288 if (vc && vc.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1289 musicbot.emptyQueue(msg.guild.id).then(res => {
1290 msg.channel.send(musicbot.note("note", "Queue cleared."));
1291 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1292 if (voiceConnection !== null) {
1293 const dispatcher = voiceConnection.player.dispatcher;
1294 if (!dispatcher || dispatcher === null) {
1295 if (musicbot.logging) return console.log(new Error(`dispatcher null on skip cmd [${msg.guild.name}] [${msg.author.username}]`));
1296 return msg.channel.send(musicbot.note("fail", "Something went wrong."));
1297 };
1298 if (voiceConnection.paused) dispatcher.end();
1299 dispatcher.end();
1300 }
1301 }).catch(res => {
1302 console.error(new Error(`[clearCmd] [${msg.guild.id}] ${res}`))
1303 return msg.channel.send(musicbot.note("fail", "Something went wrong clearing the queue."));
1304 })
1305 };
1306
1307 musicbot.removeFunction = (msg, suffix, args) => {
1308 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1309 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1310 if (!suffix) return msg.channel.send(musicbot.note("fail", "No video position given."));
1311 let vc = client.voice.connections.find(val => val.channel.guild.id == msg.member.guild.id)
1312 if (vc && vc.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1313 if (parseInt(suffix) - 1 == 0) return msg.channel.send(musicbot.note("fail", "You cannot clear the currently playing music."));
1314 let test = musicbot.queues.get(msg.guild.id).songs.find(x => x.position == parseInt(suffix) - 1);
1315 if (test) {
1316 if (test.requester !== msg.author.id && !musicbot.isAdmin(msg.member)) return msg.channel.send(musicbot.note("fail", "You cannot remove that item."));
1317 let newq = musicbot.queues.get(msg.guild.id).songs.filter(s => s !== test);
1318 musicbot.updatePositions(musicbot.queues.get(msg.guild.id), msg.guild.id).then(res => {
1319 musicbot.queues.get(msg.guild.id).songs = res;
1320 msg.channel.send(musicbot.note("note", `Removed: \`${test.title.replace(/`/g, "'")}\``));
1321 }).catch(e=> {
1322 console.log(e)
1323 console.log("@ remove function");
1324 })
1325 } else {
1326 msg.channel.send(musicbot.note("fail", "Couldn't find that video or something went wrong."));
1327 }
1328 };
1329
1330 musicbot.loopFunction = (msg, suffix, args) => {
1331 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1332 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1333 let vc = client.voice.connections.find(val => val.channel.guild.id == msg.member.guild.id)
1334 if (vc && vc.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1335 if (musicbot.queues.get(msg.guild.id).loop == "none" || musicbot.queues.get(msg.guild.id).loop == null) {
1336 musicbot.queues.get(msg.guild.id).loop = "song";
1337 msg.channel.send(musicbot.note('note', 'Looping single enabled! :repeat_one:'));
1338 } else if (musicbot.queues.get(msg.guild.id).loop == "song") {
1339 musicbot.queues.get(msg.guild.id).loop = "queue";
1340 msg.channel.send(musicbot.note('note', 'Looping queue enabled! :repeat:'));
1341 } else if (musicbot.queues.get(msg.guild.id).loop == "queue") {
1342 musicbot.queues.get(msg.guild.id).loop = "none";
1343 msg.channel.send(musicbot.note('note', 'Looping disabled! :arrow_forward:'));
1344 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1345 const dispatcher = voiceConnection.player.dispatcher;
1346 let wasPaused = dispatcher.paused;
1347 if (wasPaused) dispatcher.pause();
1348 let newq = musicbot.queues.get(msg.guild.id).songs.slice(musicbot.queues.get(msg.guild.id).last.position - 1);
1349 if (newq !== musicbot.queues.get(msg.guild.id).songs) musicbot.updatePositions(musicbot.queues.get(msg.guild.id), msg.guild.id).then(res => {
1350 musicbot.queues.get(msg.guild.id).songs = res;
1351 }).catch(e=> {
1352 console.log(e)
1353 console.log("@ loop function");
1354 })
1355 if (wasPaused) dispatcher.resume();
1356 }
1357 };
1358 musicbot.shuffleFunction = (msg, suffix, args) => {
1359 let q = musicbot.getQueue(msg.guild.id);
1360 if (q.working == true) return msg.channel.send(musicbot.note('fail', `This servers queue is already performing a task!`));
1361 if (!msg.member.voice.channel) return msg.channel.send(musicbot.note('fail', `You're not in a voice channel.`));
1362 if (!musicbot.queues.has(msg.guild.id)) return msg.channel.send(musicbot.note('fail', `No queue for this server found!`));
1363 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1364 if (voiceConnection && voiceConnection.channel.id != msg.member.voice.channel.id) return msg.channel.send(musicbot.note('fail', `You must be in the same voice channel as me.`));
1365 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!`));
1366 if (musicbot.queues.get(msg.guild.id).loop == "song") return msg.channel.send(musicbot.note("fail", `Cannot shuffle while loop is set to single.`));
1367 const dispatcher = voiceConnection.player.dispatcher;
1368 q.oldSongs = q.songs;
1369 q.songs.musicBotShuffle();
1370 q.needsRefresh = true;
1371 musicbot.updatePositions(q, msg.guild.id).then((res) => {
1372 q.songs = res.songs;
1373 musicbot.queues.set(msg.guild.id, q);
1374 if (voiceConnection.paused) dispatcher.resume();
1375 msg.channel.send(musicbot.note('note', `Queue was shuffled!`));
1376
1377 dispatcher.end();
1378 }).catch((res) => {
1379 message.channel.send(musicbot.note("fail", "Something went wrong shuffling the queue!"))
1380 console.log("@shuffleFunction " + res);
1381 })
1382
1383 // }
1384 };
1385
1386 musicbot.loadCommand = (obj) => {
1387 return new Promise((resolve, reject) => {
1388 let props = {
1389 enabled: obj.enabled,
1390 run: obj.run,
1391 alt: obj.alt,
1392 help: obj.help,
1393 name: obj.name,
1394 exclude: obj.exclude,
1395 masked: obj.masked
1396 };
1397 if (props.enabled == undefined || null) props.enabled = true;
1398 if (obj.alt.length > 0) {
1399 obj.alt.forEach((a) => {
1400 musicbot.aliases.set(a, props);
1401 })
1402 };
1403 musicbot.commands.set(obj.name, props);
1404 musicbot.commandsArray.push(props);
1405 if (musicbot.logging) console.log(`[MUSIC_LOADCMD] Loaded ${obj.name}`);
1406 resolve(musicbot.commands.get(obj.name));
1407 });
1408 }
1409
1410 musicbot.executeQueue = (msg, queue) => {
1411 musicbot.queues.set(queue.id, queue);
1412 if (queue.songs.length == 0) {
1413 msg.channel.send(musicbot.note('note', 'Playback finished~'));
1414 if (musicbot.musicPresence) musicbot.updatePresence(musicbot.queues.get(msg.guild.id), msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1415 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1416 if (voiceConnection !== null) return voiceConnection.disconnect();
1417 };
1418
1419 new Promise((resolve, reject) => {
1420 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1421 if (voiceConnection === null) {
1422 if (msg.member.voice.channel && msg.member.voice.channel.joinable) {
1423 msg.member.voice.channel.join()
1424 .then(connection => {
1425 resolve(connection);
1426 })
1427 .catch((error) => {
1428 console.log(error);
1429 });
1430 } else if (!msg.member.voice.channel.joinable || msg.member.voice.channel.full) {
1431 msg.channel.send(musicbot.note('fail', 'I do not have permission to join your voice channel!'))
1432 reject();
1433 } else {
1434 musicbot.emptyQueue(msg.guild.id).then(() => {
1435 reject();
1436 })
1437 }
1438 } else {
1439 resolve(voiceConnection);
1440 }
1441 }).then(connection => {
1442 let video;
1443 if (!queue.last) {
1444 video = queue.songs[0];
1445 } else {
1446 if (queue.loop == "queue") {
1447 video = queue.songs.find(s => s.position == queue.last.position + 1);
1448 if (!video || video && !video.url) video = queue.songs[0];
1449 } else if (queue.loop == "single") {
1450 video = queue.last;
1451 } else {
1452 video = queue.songs.find(s => s.position == queue.last.position);
1453 };
1454 }
1455 if (!video) {
1456 video = queue.songs ? queue.songs[0] : false;
1457 if (!video) {
1458 msg.channel.send(musicbot.note('note', 'Playback finished!'));
1459 musicbot.emptyQueue(msg.guild.id);
1460 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1461 if (voiceConnection !== null) return voiceConnection.disconnect();
1462 }
1463 }
1464
1465 if (musicbot.messageNewSong == true && queue.last && queue.loop !== "song") {
1466 let req = client.users.cache.get(video.requester);
1467 if (msg.channel.permissionsFor(msg.guild.me).has('EMBED_LINKS')) {
1468 const embed = new Discord.MessageEmbed()
1469 .setTitle("Now Playing", `${req !== null ? req.displayAvatarURL : null}`)
1470 .setThumbnail(`https://img.youtube.com/vi/${video.id}/maxresdefault.jpg`)
1471 .setDescription(`[${video.title.replace(/\\/g, '\\\\').replace(/\`/g, '\\`').replace(/\*/g, '\\*').replace(/_/g, '\\_').replace(/~/g, '\\~').replace(/`/g, '\\`')}](${video.url}) by [${video.channelTitle}](${video.channelURL})`)
1472 .setColor(musicbot.embedColor)
1473 .setFooter(`Requested by ${req !== null ? req.username : "Unknown User"}`, `${req !== null ? req.displayAvatarURL : null}`);
1474 msg.channel.send({embed});
1475 } else {
1476 msg.channel.send(musicbot.note("note", `\`${video.title.replace(/`/g, "''")}\` by \`${video.channelURL.replace(/`/g, "''")}\``))
1477 }
1478 }
1479
1480 try {
1481 musicbot.setLast(msg.guild.id, video).then(() => {
1482 if (musicbot.musicPresence) musicbot.updatePresence(queue, msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1483 });
1484 console.log(musicbot.getQueue(msg.guild.id));
1485 let dispatcher = queue.connection.play(ytdl(video.url, {
1486 filter: 'audioonly',
1487 quality: 'highestaudio',
1488 highWaterMark: 1<<25
1489 }), {
1490 bitrate: musicbot.bitRate,
1491 volume: (queue.volume / 100),
1492 highWaterMark: 1
1493 })
1494
1495 connection.on('error', (error) => {
1496 console.error(error);
1497 if (msg && msg.channel) msg.channel.send(musicbot.note('fail', `Something went wrong with the connection. Retrying queue...`));
1498 musicbot.executeQueue(msg, queue);
1499 });
1500
1501 dispatcher.on('error', (error) => {
1502 console.error(error);
1503 if (msg && msg.channel) msg.channel.send(musicbot.note('fail', `Something went wrong while playing music. Retrying queue...`));
1504 musicbot.executeQueue(msg, queue);
1505 });
1506
1507
1508 dispatcher.on('debug', (d) => {
1509 console.log(d);
1510 });
1511
1512 dispatcher.on('end', () => {
1513 setTimeout(() => {
1514 if (musicbot.queues.get(queue.id).needsRefresh) {
1515 queue = musicbot.queues.get(queue.id);
1516 queue.needsRefresh = false;
1517 musicbot.queues.set(queue.id, queue)
1518 }
1519 let loop = queue.loop;
1520 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1521 if (voiceConnection !== null && voiceConnection.channel.members.size <= 1){
1522 msg.channel.send(musicbot.note('note', 'No one in the voice channel, leaving...'))
1523 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
1524 if (musicbot.musicPresence) musicbot.updatePresence(musicbot.queues.get(msg.guild.id), msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1525 return voiceConnection.disconnect();
1526 }
1527 if (queue.songs.length > 0) {
1528 if (loop == "none" || loop == null) {
1529 queue.songs.shift();
1530 musicbot.updatePositions(queue, msg ? msg.guild.id : 000000).then(res => {
1531 queue.songs = typeof res.songs == "object" ? Array.from(res.songs) : [];
1532 musicbot.executeQueue(msg, queue);
1533 }).catch(e=> {
1534 console.log(e)
1535 console.log("@ dispatcher function");
1536 })
1537 } else if (loop == "queue" || loop == "song") {
1538 musicbot.executeQueue(msg, queue);
1539 };
1540 } else if (queue.songs.length <= 0) {
1541 if (msg && msg.channel) msg.channel.send(musicbot.note('note', 'Playback finished.'));
1542 musicbot.queues.set(msg.guild.id, {songs: [], last: null, loop: "none", id: msg.guild.id, volume: musicbot.defVolume, oldSongs: [],working: false, needsRefresh: false});
1543 if (musicbot.musicPresence) musicbot.updatePresence(queue, msg.client, musicbot.clearPresence).catch((res) => { console.warn(`[MUSIC] Problem updating MusicPresence`); });
1544 const voiceConnection = client.voice.connections.find(val => val.channel.guild.id == msg.guild.id);
1545 if (voiceConnection !== null) return voiceConnection.disconnect();
1546 }
1547 }, 1250);
1548 });
1549 } catch (error) {
1550 console.log(error);
1551 }
1552 })
1553 .catch((error) => {
1554 console.log(error);
1555 });
1556 }
1557
1558 musicbot.note = (type, text) => {
1559 if (type === 'wrap') {
1560 let ntext = text
1561 .replace(/`/g, '`' + String.fromCharCode(8203))
1562 .replace(/@/g, '@' + String.fromCharCode(8203))
1563 .replace(client.token, 'REMOVED');
1564 return '```\n' + ntext + '\n```';
1565 } else if (type === 'note') {
1566 return ':musical_note: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1567 } else if (type === 'search') {
1568 return ':mag: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1569 } else if (type === 'fail') {
1570 return ':no_entry_sign: | ' + text.replace(/`/g, '`' + String.fromCharCode(8203));
1571 } else if (type === 'font') {
1572 return text.replace(/`/g, '`' + String.fromCharCode(8203))
1573 .replace(/@/g, '@' + String.fromCharCode(8203))
1574 .replace(/\\/g, '\\\\')
1575 .replace(/\*/g, '\\*')
1576 .replace(/_/g, '\\_')
1577 .replace(/~/g, '\\~')
1578 .replace(/`/g, '\\`');
1579 } else {
1580 console.error(new Error(`${type} was an invalid type`));
1581 }
1582 };
1583
1584 musicbot.loadCommands = async () => {
1585 try {
1586 await musicbot.loadCommand(musicbot.play);
1587 await musicbot.loadCommand(musicbot.remove);
1588 await musicbot.loadCommand(musicbot.help);
1589 await musicbot.loadCommand(musicbot.skip);
1590 await musicbot.loadCommand(musicbot.leave);
1591 await musicbot.loadCommand(musicbot.search);
1592 await musicbot.loadCommand(musicbot.pause);
1593 await musicbot.loadCommand(musicbot.resume);
1594 await musicbot.loadCommand(musicbot.volume);
1595 await musicbot.loadCommand(musicbot.queue);
1596 await musicbot.loadCommand(musicbot.loop);
1597 await musicbot.loadCommand(musicbot.clearqueue);
1598 await musicbot.loadCommand(musicbot.np);
1599 await musicbot.loadCommand(musicbot.shuffle)
1600 await musicbot.loadCommand(musicbot.deleteQueue)
1601 } catch (e) {
1602 console.error(new Error(e));
1603 };
1604 }
1605 musicbot.loadCommands();
1606
1607 Object.defineProperty(Array.prototype, 'musicArraySort', {value: function(n) {
1608 return Array.from(Array(Math.ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
1609 }});
1610 Object.defineProperty(Array.prototype, 'musicBotShuffle', {value: function(){
1611 let input = this;
1612 for (let i = input.length - 1; i >= 0; i--) {
1613 let randomIndex = Math.floor(Math.random() * (i + 1));
1614 let itemAtIndex = input[randomIndex];
1615 input[randomIndex] = input[i];
1616 input[i] = itemAtIndex;
1617 }
1618 return input;
1619 }});
1620
1621 } catch (e) {
1622 console.error(e);
1623 };
1624}