summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author8cy <[email protected]>2020-07-27 19:45:56 -0700
committer8cy <[email protected]>2020-07-27 19:45:56 -0700
commit350d89a259386b73b30a09ca9c04faf647216107 (patch)
tree3dc1f6fee7701e949b822b9628ca4d1424764115
parentimprov: bold wc/ gb msg (diff)
downloaddep-core-test-music.tar.xz
dep-core-test-music.zip
-rw-r--r--server/package.json4
-rw-r--r--server/src/Config.ts3
-rw-r--r--server/src/client/BotClient.ts5
-rw-r--r--server/src/commands/voice/Play.ts52
-rw-r--r--server/src/utils/Play.ts138
-rw-r--r--server/src/utils/Utils.ts12
6 files changed, 211 insertions, 3 deletions
diff --git a/server/package.json b/server/package.json
index e54e63d..1844ce0 100644
--- a/server/package.json
+++ b/server/package.json
@@ -17,9 +17,11 @@
"node-emoji": "^1.10.0",
"node-superfetch": "^0.1.10",
"rss-parser": "^3.9.0",
+ "soundcloud-downloader": "^0.1.3",
"svg-captcha": "^1.4.0",
"svg2img": "^0.7.2",
- "winston": "^3.3.3"
+ "winston": "^3.3.3",
+ "ytdl-core-discord": "^1.2.1"
},
"devDependencies": {
"@types/express": "^4.17.7",
diff --git a/server/src/Config.ts b/server/src/Config.ts
index 81ed605..316b093 100644
--- a/server/src/Config.ts
+++ b/server/src/Config.ts
@@ -23,4 +23,5 @@ export const captchaSettings: object = {
size: 6
};
export const verificationRole: string = "Un-verified";
-export const verificationChannel: string = "verification"; \ No newline at end of file
+export const verificationChannel: string = "verification";
+export const youtubeApiKey: string = ""; \ No newline at end of file
diff --git a/server/src/client/BotClient.ts b/server/src/client/BotClient.ts
index 37c9396..cf8b778 100644
--- a/server/src/client/BotClient.ts
+++ b/server/src/client/BotClient.ts
@@ -14,7 +14,8 @@ declare module 'discord-akairo' {
logger: Logger;
settings: SettingsProvider;
img,
- wait
+ wait,
+ queue
}
}
@@ -39,6 +40,8 @@ export default class BotClient extends AkairoClient {
public wait = require("util").promisify(setTimeout);
+ public queue = new Map();
+
public inhibitorHandler: InhibitorHandler = new InhibitorHandler(this, {
directory: join(__dirname, '..', 'inhibitors')
});
diff --git a/server/src/commands/voice/Play.ts b/server/src/commands/voice/Play.ts
new file mode 100644
index 0000000..9ea335d
--- /dev/null
+++ b/server/src/commands/voice/Play.ts
@@ -0,0 +1,52 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import request from 'node-superfetch';
+
+export default class PlayVoice extends Command {
+ public constructor() {
+ super('play', {
+ aliases: ['play'],
+ category: 'voice',
+ description: {
+ content: 'Gives you the bot\'s IP address.',
+ usage: '[song url/ name]',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'song',
+ type: 'string'
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { song }): Promise<Message> {
+ const { channel } = msg.member.voice;
+
+ const serverQueue = this.client.queue.get(msg.guild.id);
+ if (!channel) return msg.reply('You need to join a voice channel first!'); //error
+ if (serverQueue && channel !== msg.guild.me.voice.channel)
+ return msg.reply(`You must be in the same channel as ${this.client.user}`); //error
+
+ const permissions = channel.permissionsFor(msg.client.user);
+ if (!permissions.has('CONNECT'))
+ return msg.reply('Cannot connect to voice channel, missing permissions.');
+ if (!permissions.has('SPEAK'))
+ return msg.reply('I cannto speak in this voice channel, make sure I have the proper permissions!');
+
+ const search = song;
+ const videoPattern = /^(https?:\/\/)?(www\.)?(m\.)?(youtube\.com|youtu\.?be)\/.+$/gi;
+ const playlistPattern = /^.*(list=)([^#\&\?]*).*/gi;
+ const scRegex = /^https?:\/\/(soundcloud\.com)\/(.*)$/;
+ const url = song;
+ const urlValid = videoPattern.test(song);
+
+ if (!videoPattern.test(song) && playlistPattern.test(song)) {
+ return this.client.commandHandler.commandUtil.
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/utils/Play.ts b/server/src/utils/Play.ts
new file mode 100644
index 0000000..507cc0c
--- /dev/null
+++ b/server/src/utils/Play.ts
@@ -0,0 +1,138 @@
+import { Message } from "discord.js";
+import { AkairoClient } from "discord-akairo";
+import ytdlDiscord from 'ytdl-core-discord';
+import * as scdl from 'soundcloud-downloader';
+import { StreamDispatcher } from "discord.js";
+import { MessageCollector } from "discord.js";
+import Util from '../utils/Utils';
+
+module.exports = {
+ async play(song, client: AkairoClient, msg: Message) {
+ const { PRUNING, SOUNDCLOUD_CLIENT_ID } = require('../Config');
+ const queue = client.queue.get(msg.guild.id);
+
+ if (!song) {
+ queue.channel.leave();
+ client.queue.delete(msg.guild.id);
+ return queue.channel.send('Music queue ended.'); // catch
+ }
+
+ let stream = null;
+
+ try {
+ if (song.url.includes('youtube.com')) {
+ stream = await ytdlDiscord(song.url, { highWaterMark: 1 << 25 });
+ } else if (song.url.includes('soundcloud.com') && SOUNDCLOUD_CLIENT_ID) {
+ stream = await scdl.downloadFormat(song.url, scdl.FORMATS.OPUS, SOUNDCLOUD_CLIENT_ID);
+ }
+ } catch (error) {
+ if (queue) {
+ queue.songs.shift();
+ module.exports.play(queue.songs[0], msg);
+ }
+
+ // error
+ return msg.channel.send(`Error: ${error.message ? error.message : error}`);
+ }
+
+ queue.connection.on('disconnect', () => client.queue.delete(msg.guild.id));
+
+ const type = song.url.includes('youtube.com') ? 'opus' : 'ogg/opus';
+ const dispatcher: StreamDispatcher = queue.connection
+ .play(stream, { type: type })
+ .on('finish', () => {
+ if (collector && !collector.ended) collector.stop();
+
+ if (queue.loop) {
+ let lastSong = queue.songs.shift();
+ queue.songs.push(lastSong);
+ // export
+ } else {
+ queue.songs.shift();
+ // export
+ }
+ })
+ .on('error', (err) => {
+ // error
+ queue.songs.shift();
+ module.exports.play(queue.songs[0], msg);
+ });
+ dispatcher.setVolumeLogarithmic(queue.volume / 100);
+
+ try {
+ var playingMessage = await queue.channel.send(`Started playing: **${song.title}** ${song.url}`);
+ await playingMessage.react("⏭");
+ await playingMessage.react("⏯");
+ await playingMessage.react("🔁");
+ await playingMessage.react("⏹");
+ } catch (error) {
+ // error
+ }
+
+ const filter = (reaction, user) => user.id !== client.user.id;
+ var collector: MessageCollector = playingMessage.createReactionCollector(filter, {
+ time: song.duration > 0 ? song.duration * 1000 : 600000
+ });
+
+ collector.on('collect', (reaction, user) => {
+ if (!queue) return;
+ const member = msg.guild.member(user);
+
+ switch (reaction.emoji.name) {
+ case "⏭":
+ queue.playing = true;
+ reaction.users.remove(user).catch(console.error);
+ if (!Util.canModifyQueue(member)) return;
+ queue.connection.dispatcher.end();
+ queue.textChannel.send(`${user} ⏩ skipped the song`).catch(console.error);
+ collector.stop();
+ break;
+
+ case "⏯":
+ reaction.users.remove(user).catch(console.error);
+ if (!Util.canModifyQueue(member)) return;
+ if (queue.playing) {
+ queue.playing = !queue.playing;
+ queue.connection.dispatcher.pause(true);
+ queue.textChannel.send(`${user} ⏸ paused the music.`).catch(console.error);
+ } else {
+ queue.playing = !queue.playing;
+ queue.connection.dispatcher.resume();
+ queue.textChannel.send(`${user} ▶ resumed the music!`).catch(console.error);
+ }
+ break;
+
+ case "🔁":
+ reaction.users.remove(user).catch(console.error);
+ if (!Util.canModifyQueue(member)) return;
+ queue.loop = !queue.loop;
+ queue.textChannel.send(`Loop is now ${queue.loop ? "**on**" : "**off**"}`).catch(console.error);
+ break;
+
+ case "⏹":
+ reaction.users.remove(user).catch(console.error);
+ if (!Util.canModifyQueue(member)) return;
+ queue.songs = [];
+ queue.textChannel.send(`${user} ⏹ stopped the music!`).catch(console.error);
+ try {
+ queue.connection.dispatcher.end();
+ } catch (error) {
+ console.error(error);
+ queue.connection.disconnect();
+ }
+ collector.stop();
+ break;
+
+ default:
+ reaction.users.remove(user).catch(console.error);
+ break;
+ }
+ });
+
+ collector.on('end', () => {
+ playingMessage.reactions.removeAll(); // catch
+ if (PRUNING && playingMessage && !playingMessage.deleted)
+ playingMessage.delete({ timeout: 3000 }); // catch
+ });
+ }
+} \ No newline at end of file
diff --git a/server/src/utils/Utils.ts b/server/src/utils/Utils.ts
index 454840e..e7972c2 100644
--- a/server/src/utils/Utils.ts
+++ b/server/src/utils/Utils.ts
@@ -2,4 +2,16 @@ export default class Util {
static shorten(text: string, maxLen = 2000) {
return text.length > maxLen ? `${text.substr(0, maxLen - 3)}...` : text;
}
+
+ static canModifyQueue(member) {
+ const { channel } = member.voice;
+ const botChannel = member.guild.me.voice.channel;
+
+ if (channel !== botChannel) {
+ member.send('You need to join the voice channel first!'); // error
+ return false;
+ }
+
+ return false;
+ }
} \ No newline at end of file