From 405051211d60afa8a7fc4fc3fa5ffb456242973d Mon Sep 17 00:00:00 2001 From: Alejandro Sior Date: Fri, 19 Feb 2021 11:43:18 +0100 Subject: initial commit --- src/CommandLoader.js | 12 ++ src/Events.js | 57 ++++++++ src/Hastebin.js | 6 + src/RegexFramework.js | 31 +++++ src/Time.js | 5 + src/Troffman.js | 35 +++++ src/TroffmanDatabase.js | 25 ++++ src/Wait.js | 7 + src/cmd/debug.js | 5 + src/cmd/info.js | 19 +++ src/cmd/sudo.js | 139 +++++++++++++++++++ src/cmd/troff.js | 59 ++++++++ src/cmd/voice.js | 356 ++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 756 insertions(+) create mode 100644 src/CommandLoader.js create mode 100644 src/Events.js create mode 100644 src/Hastebin.js create mode 100644 src/RegexFramework.js create mode 100644 src/Time.js create mode 100644 src/Troffman.js create mode 100644 src/TroffmanDatabase.js create mode 100644 src/Wait.js create mode 100644 src/cmd/debug.js create mode 100644 src/cmd/info.js create mode 100644 src/cmd/sudo.js create mode 100644 src/cmd/troff.js create mode 100644 src/cmd/voice.js (limited to 'src') diff --git a/src/CommandLoader.js b/src/CommandLoader.js new file mode 100644 index 0000000..d38250b --- /dev/null +++ b/src/CommandLoader.js @@ -0,0 +1,12 @@ +const fs = require('fs'); + +module.exports = function CommandLoader(bot) { + fs.readdirSync('./src/cmd/').forEach(file => { + if (file.endsWith('.js')) { + let plugin = require(`./cmd/${file}`); + if (plugin.loadModule) { + plugin.loadModule(bot); + } + } + }); +}; diff --git a/src/Events.js b/src/Events.js new file mode 100644 index 0000000..d3ea15f --- /dev/null +++ b/src/Events.js @@ -0,0 +1,57 @@ +const { PlayerManager } = require('eris-lavalink'); +const { Op } = require('sequelize'); + +module.exports = function Events(bot) { + let r; + + bot.on('ready', async () => { + Logger.info(`Successfully connected as user ${bot.user.username}#${bot.user.discriminator}`); + r = new RegExp(`^(?:<@!?${bot.user.id}> +|-)\\b`); + + bot.editStatus('online', { + name: `watching LaTeX.`, + type: 3, + }); + }); + + bot.on('error', (err, id) => { + Logger.error(`Error encountered on shard ${id}`); + Logger.error(err); + }); + + bot.on('messageCreate', async (msg) => { + if (!r) { + Logger.error('Some real shit hapened : message matching regex hasn\'t been defined for some reason.'); + process.exit(); + } + + let content = msg.content.replace(r, ''); + + if (content === msg.content) return; + if (msg.author.bot) return; + if (msg.author === bot.user) return; + if (msg.channel.type !== 0) return; + + /* + let important = msg.channel.guild.members.filter(m => m.permission.has('administrator')).map(m => m.id); + + important.push(msg.author.id); + + let user = await bot.db.Auser.findOne({ + where: { + userId: important, + }, + }); + let guild = await bot.db.Aguild.findOne({ where: { guildId: msg.channel.guild.id } }); + + if ((bot.config.sudoers && bot.config.sudoers.indexOf(msg.author.id) <= -1) && !user && !guild) return; */ + + Logger.debug(`Command '${msg.content}' issued`); + + let trimmedContent = content.trim(); + let result = bot.handler.apply(trimmedContent, msg); + if (Array.isArray(result)) { + bot.createMessage(msg.channel.id, `Missing permissions : ${result.join(', ')}`).catch(Logger.error); + } + }); +}; diff --git a/src/Hastebin.js b/src/Hastebin.js new file mode 100644 index 0000000..1d7bae0 --- /dev/null +++ b/src/Hastebin.js @@ -0,0 +1,6 @@ +const superagent = require('superagent'); + +module.exports = async function hastebin(text) { + let res = await superagent.post('https://hasteb.in/documents').send(text); + return JSON.parse(res.text).key; +}; \ No newline at end of file diff --git a/src/RegexFramework.js b/src/RegexFramework.js new file mode 100644 index 0000000..fb915aa --- /dev/null +++ b/src/RegexFramework.js @@ -0,0 +1,31 @@ +const CommandHandler = require('dbot-regex-handler'); + + +module.exports = class RegexFramework extends CommandHandler { + endpoint(r, perms, cb) { + super.endpoint(r, cb); + this.commands[this.commands.length - 1].p = perms; + } + + apply(str, context) { + for (let i = 0; i < this.commands.length; i++) { + let match = str.match(this.commands[i].regex); + if (match) { + let missingPerms = []; + this.commands[i].p.forEach(name => { + if (!context.member.permission.has(name)) { + missingPerms.push(name); + } + }); + if (missingPerms.length === 0) { + this.commands[i].callback(match, context); + return true; + } + else { + return missingPerms; + } + } + } + return false; + } +}; diff --git a/src/Time.js b/src/Time.js new file mode 100644 index 0000000..1bbd7eb --- /dev/null +++ b/src/Time.js @@ -0,0 +1,5 @@ +module.exports.msToMinutes = function msToMinutes(ms) { + let d = new Date(); + d.setTime(ms); + return `${d.getMinutes()}min ${d.getSeconds()}s`; +}; \ No newline at end of file diff --git a/src/Troffman.js b/src/Troffman.js new file mode 100644 index 0000000..2b8860c --- /dev/null +++ b/src/Troffman.js @@ -0,0 +1,35 @@ +const Eris = require('eris'); +const toml = require('toml'); +const fs = require('fs'); + +const RegexFramework = require('./RegexFramework'); +const Events = require('./Events'); +const CommandLoader = require('./CommandLoader'); +const TroffmanDatabase = require('./TroffmanDatabase'); + +module.exports = class Troffman extends Eris { + constructor(configPath, options) { + if (!fs.existsSync(configPath)) { + fs.copyFileSync("./config.def.toml", configPath); + process.exit(0); + } + let c = toml.parse(fs.readFileSync(configPath)); + super(c.token, options); + this.config = c; + if (!this.config.sudoers) { + this.config.sudoers = ['']; + } + else if (!Array.isArray(this.config.sudoers)) { + this.config.sudoers = ['']; + } + else if (this.config.sudoers.length <= 0) { + this.config.sudoers = ['']; + } + this._ds = Array.from(this.config.sudoers); + this.db = new TroffmanDatabase(c.database); + this.handler = new RegexFramework(); + Events(this); + CommandLoader(this); + this.db.sync(); + } +}; diff --git a/src/TroffmanDatabase.js b/src/TroffmanDatabase.js new file mode 100644 index 0000000..58e7608 --- /dev/null +++ b/src/TroffmanDatabase.js @@ -0,0 +1,25 @@ +const Sequelize = require('sequelize'); + +module.exports = class TroffmanDatabase extends Sequelize { + constructor(path) { + super('database', 'user', 'password', { + host: 'localhost', + dialect: 'sqlite', + logging: false, + storage: path, + }); + + this.Auser = this.define('ausers', { + userId: { + type: Sequelize.STRING, + unique: true, + }, + }); + this.Aguild = this.define('aguilds', { + guildId: { + type: Sequelize.STRING, + unique: true, + }, + }); + } +}; diff --git a/src/Wait.js b/src/Wait.js new file mode 100644 index 0000000..0f0bdfd --- /dev/null +++ b/src/Wait.js @@ -0,0 +1,7 @@ +module.exports = function wait(ms) { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, ms); + }); +}; diff --git a/src/cmd/debug.js b/src/cmd/debug.js new file mode 100644 index 0000000..ecb475c --- /dev/null +++ b/src/cmd/debug.js @@ -0,0 +1,5 @@ +module.exports.loadModule = function loadModule(bot) { + bot.handler.endpoint('^ping$', [], (match, message) => { + bot.createMessage(message.channel.id, 'Pong').catch(Logger.error); + }); +}; diff --git a/src/cmd/info.js b/src/cmd/info.js new file mode 100644 index 0000000..899dac6 --- /dev/null +++ b/src/cmd/info.js @@ -0,0 +1,19 @@ +const exec = require('child_process').exec; + +module.exports.loadModule = function loadModule(bot) { + bot.handler.endpoint('^v(?:ersion)?$', [], (match, message) => { + exec('git rev-list --count HEAD', (error, stdout) => { + if (error) { + bot.createMessage(message.channel.id, 'An error has occured').catch(Logger.error); + return; + } + exec('git log -1 --pretty=%B', (error2, stdout2) => { + let msg = `Commit number ${stdout}`; + if (!error2) { + msg += `\n\`\`\`\n${stdout2}\`\`\``; + } + bot.createMessage(message.channel.id, msg).catch(Logger.error); + }); + }); + }); +}; diff --git a/src/cmd/sudo.js b/src/cmd/sudo.js new file mode 100644 index 0000000..cc590bf --- /dev/null +++ b/src/cmd/sudo.js @@ -0,0 +1,139 @@ +const { inspect } = require('util'); +const vm = require('vm'); + +module.exports.loadModule = function loadModule(bot) { + bot.handler.endpoint('^guilds$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + bot.createMessage(message.channel.id, `\`${bot.guilds.size}\``).catch(Logger.error); + }); + bot.handler.endpoint('^is-sudoer$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + bot.createMessage(message.channel.id, 'true').catch(Logger.error); + }); + bot.handler.endpoint('^sudoers$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + bot.createMessage(message.channel.id, `\`\`\`\n[ ${bot.config.sudoers.join(',\n ')} ]\n\`\`\``).catch(Logger.error); + }); + bot.handler.endpoint('^addsudo (.+)$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + bot.config.sudoers.push(match[1]); + bot.createMessage(message.channel.id, `${match[1]} temporarily added to the sudoers list`).catch(Logger.error); + }); + bot.handler.endpoint('^resetsudo$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + bot.config.sudoers = Array.from(bot._ds); + bot.createMessage(message.channel.id, `Sudoers list reset`).catch(Logger.error); + }); + bot.handler.endpoint('^removesudo (.+)$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + let i = bot.config.sudoers.indexOf(match[1]); + if (i >= 0) { + bot.config.sudoers.splice(i, 1); + bot.createMessage(message.channel.id, `${match[1]} removed from sudoers list`).catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, `${match[1]} was not sudoer`).catch(Logger.error); + } + }); + bot.handler.endpoint('^e ?```(?:.*\n)?(.*)\n?```$', [], (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + try { + let evaled = vm.runInNewContext(match[1], { + ctx: { + bot: bot, + message: message, + match: match, + }, + require: require, + }); + + if (typeof evaled !== 'string') { + evaled = inspect(evaled); + } + evaled = evaled.replace(bot.config.token, 'ツ'); + bot.createMessage(message.channel.id, `\`\`\`${evaled}\`\`\``).catch(Logger.error); + } + catch (e) { + bot.createMessage(message.channel.id, `Error:\n\`\`\`\n${e}\`\`\``).catch(Logger.error); + } + }); + bot.handler.endpoint('^aguilds$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + let aguilds = await bot.db.Aguild.findAll({ attributes: ['guildId'] }); + let buff = '```\nAuthorized guilds:\n\n'; + aguilds.forEach(g => { + buff += `- ${g.guildId}\n`; + }); + buff += '```'; + bot.createMessage(message.channel.id, buff).catch(Logger.error); + }); + bot.handler.endpoint('^ausers$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + let ausers = await bot.db.Auser.findAll({ attributes: ['userId'] }); + let buff = '```\nAuthorized users:\n\n'; + ausers.forEach(u => { + buff += `- ${u.userId}\n`; + }); + buff += '```'; + bot.createMessage(message.channel.id, buff).catch(Logger.error); + }); + bot.handler.endpoint('^aguild ([0-9]+)$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + if (!match[1]) return; + try { + await bot.db.Aguild.create({ + guildId: match[1], + }); + bot.createMessage(message.channel.id, `Guild \`${match[1]}\` successfully authorized!`).catch(Logger.error); + } + catch (e) { + bot.createMessage(message.channel.id, 'Guild was already authorized.').catch(Logger.error); + } + }); + bot.handler.endpoint('^auser ([0-9]+)$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + if (!match[1]) return; + try { + await bot.db.Auser.create({ + userId: match[1], + }); + bot.createMessage(message.channel.id, `User \`${match[1]}\` successfully authorized!`).catch(Logger.error); + } + catch (e) { + bot.createMessage(message.channel.id, 'User was already authorized.').catch(Logger.error); + } + }); + bot.handler.endpoint('^rguild ([0-9]+)$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + if (!match[1]) return; + await bot.db.Aguild.destroy({ + where: { + guildId: match[1], + } + }); + bot.createMessage(message.channel.id, `Guild \`${match[1]}\` successfully removed!`).catch(Logger.error); + }); + bot.handler.endpoint('^ruser ([0-9]+)$', [], async (match, message) => { + if (!bot.config.sudoers) return; + if (bot.config.sudoers.indexOf(message.author.id) <= -1) return; + if (!match[1]) return; + await bot.db.Auser.destroy({ + where: { + userId: match[1], + } + }); + bot.createMessage(message.channel.id, `User \`${match[1]}\` successfully removed!`).catch(Logger.error); + }); +}; diff --git a/src/cmd/troff.js b/src/cmd/troff.js new file mode 100644 index 0000000..91090a5 --- /dev/null +++ b/src/cmd/troff.js @@ -0,0 +1,59 @@ +const spawn = require('child_process').spawn; +const fs = require('fs'); +const { ConsoleTransportOptions } = require('winston/lib/winston/transports'); + +/* +\\"".fp - R cmunrm +\\"".fp - I cmunti +\\"".fp - B cmunrb +\\"".fp - BI cmunbi +*/ + +const prelude = ` +.fp - CMEX CMEX10 +.fp - CMSY CMSY10 +.fp - CMMI CMMI10 +.fp - CB CB +.fspecial R CMEX CMSY +.fspecial I CMMI + +.EQ +define <=> %{vcenter roman " \\\\N'arrowdblboth' "}% +define appr %{vcenter roman "≈"}% +define intd %{vcenter roman "\\\\N'integraldisplay'"}% +delim $$ +.EN + +.LP +.ps +20 +\\m[white] +`; + +module.exports.loadModule = function loadModule(bot) { + bot.handler.endpoint(/^tr(?:off)?!?(?: |\n)([\s\S]*)$/m, [], (match, message) => { + ma = match[1].replace(/^\.sy\s.*$/gm, ''); + ma = ma.replace(/^sh\s.*$/gm, ''); + ma = ma.replace(/^\.?co\s.*$/gm, ''); + ma = ma.replace(/^\\X.*$/gm, ''); + if (ma !== match[1]) console.log('had to sanitize'); + const subprocess0 = spawn('pic'); + console.log(ma); + subprocess0.stdin.write(prelude + '\n' + ma + '\n'); + subprocess0.stdin.end(); + const subprocess1 = spawn('eqn'); + subprocess0.stdout.pipe(subprocess1.stdin); + const subprocess2 = spawn('roff', ['-ms']); + subprocess1.stdout.pipe(subprocess2.stdin); + const subprocess3 = spawn('pdf', ['-p', '10000x10000']); + subprocess2.stdout.pipe(subprocess3.stdin); + const subprocess4 = spawn('convert', ['-', '-trim', 'png:-']); + subprocess3.stdout.pipe(subprocess4.stdin); + let ch = Buffer.from(""); + subprocess4.stdout.on('data', (data) => { + ch = Buffer.concat([ch, data]); + }); + subprocess4.stdout.on('end', () => { + message.channel.createMessage('', {name: 'troff.png', file: ch}); + }); + }); +}; diff --git a/src/cmd/voice.js b/src/cmd/voice.js new file mode 100644 index 0000000..7f12603 --- /dev/null +++ b/src/cmd/voice.js @@ -0,0 +1,356 @@ +const superagent = require('superagent'); +const wait = require('../Wait'); +const time = require('../Time'); +const hastebin = require('../Hastebin'); + +const querystring = require('querystring'); +const chunk = require('chunk-text'); + +async function resolveTracks(node, search) { + let result; + try { + result = await superagent.get(`http://${node.host}:${node.port}/loadtracks?identifier=${search}`) + .set('Authorization', node.password) + .set('Accept', 'application/json'); + } catch (err) { + throw err; + } + + if (!result) { + throw 'Unable play that video.'; + } + + return result.body; // array of tracks resolved from lavalink +} + +function getLavalinkPlayer(channel, bot) { + if (!channel || !channel.guild) { + return Promise.reject('Not a guild channel.'); + } + + let player = bot.voiceConnections.get(channel.guild.id); + if (player) { + return Promise.resolve(player); + } + + let options = {}; + if (channel.guild.region) { + options.region = channel.guild.region; + } + + return bot.joinVoiceChannel(channel.id, options); +} + +function getRandomColor() { + var letters = '0123456789ABCDEF'; + var color = ''; + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return parseInt(color); +} + +async function crawl(player, bot, message) { + if (bot.voices[message.channel.guild.id].queue.length === 0) { + bot.voices[message.channel.guild.id].crawling = false; + delete bot.voices[message.channel.guild.id].current; + bot.leaveVoiceChannel(player.channelId); + return; + }; + + bot.voices[message.channel.guild.id].crawling = true; + let next = bot.voices[message.channel.guild.id].queue.shift(); + bot.voices[message.channel.guild.id].current = next; + player.play(next.track); + + player.removeAllListeners('disconnect'); + player.once('disconnect', (err) => { + if (err) { + Logger.error(err); + } + bot.voices[message.channel.guild.id].crawling = false; + delete bot.voices[message.channel.guild.id].current; //Attempt to fix disconnect bug, implement `come' command to recrawl is still to do. Refer to previous commit for previous implementation + bot.leaveVoiceChannel(player.channelId); + }); + + player.removeAllListeners('error'); + player.once('error', (err) => { + Logger.error(`Player error: ${err}`); + // I do not know how it behaves after there's an error so I just put these in comment for now + // bot.voices[message.channel.guild.id].crawling = false; + // delete bot.voices[message.channel.guild.id].current; + // bot.leaveVoiceChannel(player.channelId); + + // Just in case, continue crawling + crawl(player, bot, message); + }); + + player.removeAllListeners('end'); + player.once('end', async (data) => { + // REPLACED reason is emitted when playing without stopping, I ignore these to prevent skip loops + if (data.reason && data.reason === 'REPLACED') return; + await wait(750); + crawl(player, bot, message); + }); +} + +module.exports.loadModule = function loadModule(bot) { + bot.handler.endpoint('^(?:add|meme|play|p|a)([;:-][0-9,-]+)? (.+)$', [], async (match, message) => { + let ones = []; + // The following messy loop parses playlist selection. + // A hacky limitation has been used here to prevent abuse. A new system HAS to be used. + if (match[1]) { + let param = match[1].substring(1); + let selectors = param.split(','); + selectors.forEach(s => { + let r = s.split('-'); + if (r.length === 2) { + let indS = parseInt(r[0]); + let indE = parseInt(r[1]); + if (indE <= indS) return; + if (indE - indS > 100) return; + for (let i = indS; i <= indE; i++) { + if (ones.indexOf(i) <= -1) { + ones.push(i); + } + } + } + else if (r.length === 1) { + let ind = parseInt(r[0]); + if (ones.indexOf(ind) <= -1) { + ones.push(ind); + } + } + }); + } + + if (!bot.voices[message.channel.guild.id]) bot.voices[message.channel.guild.id] = { queue: [] }; + + let m = match[2]; + + if (!m.match(/^') { + m = m.slice(1); + m = m.slice(0, -1); + } + + let t = await resolveTracks(bot._main, `${m}`); + if (t.loadType === 'TRACK_LOADED' || t.loadType === 'SEARCH_RESULT') { + let tr = t.tracks[0]; + if (tr.info.isStream) { + bot.createMessage(message.channel.id, `I do not yet support streams, I'm sorry :(`).catch(Logger.error); + return; + } + tr.adder = `${message.author.username}#${message.author.discriminator}`; + bot.voices[message.channel.guild.id].queue.push(tr); + let ign = !match[1] ? '' : ' Playlist selectors parameters have been ignored as it is a sole song.'; + bot.createMessage(message.channel.id, `\`${tr.info.title}\` has been added by \`${tr.adder}\`.${ign}`).catch(Logger.error); + } + else if (t.loadType === 'PLAYLIST_LOADED') { + let co = 0; + for (let i = 0; i < t.tracks.length; i++) { + if (ones.indexOf(i + 1) > -1 || ones.length === 0) { + let tr = t.tracks[i]; + tr.adder = `${message.author.username}#${message.author.discriminator}`; + bot.voices[message.channel.guild.id].queue.push(tr); + co++; + } + } + bot.createMessage(message.channel.id, `${co} songs have been added to the queue by \`${message.author.username}#${message.author.discriminator}\``).catch(Logger.error); + } + else if (t.loadType === 'NO_MATCHES' || t.loadType === 'LOAD_FAILED') { + bot.createMessage(message.channel.id, `No results, sorry.`).catch(Logger.error); + return; + } + + if (!message.member.voiceState.channelID) return; + let channel = message.channel.guild.channels.find(m => m.id === message.member.voiceState.channelID); + if (!bot.voices[message.channel.guild.id].crawling) { + let player = await getLavalinkPlayer(channel, bot); + player.switchChannel(message.member.voiceState.channelID, true); + bot.createMessage(message.channel.id, 'Joined voice channel.').catch(Logger.error); // TEMPORARY + crawl(player, bot, message); + } + }); + + bot.handler.endpoint('^skip$', [], async (match, message) => { + let player = bot.voiceConnections.get(message.channel.guild.id); + if (player) { + player.stop(); + bot.createMessage(message.channel.id, 'Current song skipped').catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, 'The bot wasn\'t playing.').catch(Logger.error); + } + }); + + bot.handler.endpoint('^clear$', [], async (match, message) => { + bot.voices[message.channel.guild.id].queue = []; + bot.createMessage(message.channel.id, 'The current queue has been cleared!').catch(Logger.error); + }); + + bot.handler.endpoint('^(?:stop|quit|halt)!?$', [], async (match, message) => { + let player = bot.voiceConnections.get(message.channel.guild.id); + bot.voices[message.channel.guild.id].queue = []; + if (player) { + player.stop(); + bot.createMessage(message.channel.id, 'Successfully stopped!').catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, 'The bot wasn\'t playing but the queue still has been cleared.').catch(Logger.error); + } + }); + + bot.handler.endpoint('^(?:come|resume)!?$', [], async (match, message) => { + if (!message.member.voiceState.channelID) return; + let channel = message.channel.guild.channels.find(m => m.id === message.member.voiceState.channelID); + let player = bot.voiceConnections.get(message.channel.guild.id); + if (player) { + player.switchChannel(message.member.voiceState.channelID, true); + if (player.paused) { + player.resume(); + bot.createMessage(message.channel.id, 'Resumed the song.').catch(Logger.error); + } + } + else { + if (!message.member.voiceState.channelID) return; + if (!bot.voices[message.channel.guild.id]) bot.voices[message.channel.guild.id] = { queue: [] }; + if (bot.voices[message.channel.guild.id].queue.length >= 1) { + let channel = message.channel.guild.channels.find(m => m.id === message.member.voiceState.channelID); + //if (!bot.voices[message.channel.guild.id].crawling) { + let player = await getLavalinkPlayer(channel, bot); + player.switchChannel(message.member.voiceState.channelID, true); + bot.createMessage(message.channel.id, 'Joined voice channel.').catch(Logger.error); // TEMPORARY + crawl(player, bot, message); + //} + } + else { + bot.createMessage(message.channel.id, 'The bot wasn\'t in any voice channel.').catch(Logger.error); + } + } + }); + + bot.handler.endpoint('^pause!?$', [], async (match, message) => { + let player = bot.voiceConnections.get(message.channel.guild.id); + if (player) { + player.pause(); + bot.createMessage(message.channel.id, 'Song paused.').catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, 'The bot isn\'t in any voice channel.').catch(Logger.error); + } + }); + + bot.handler.endpoint('^skip-to!? ([0-9]+)$', [], async (match, message) => { + let player = bot.voiceConnections.get(message.channel.guild.id); + if (!bot.voices[message.channel.guild.id]) bot.voices[message.channel.guild.id] = { queue: [] }; + let arg = parseInt(match[1]); + if (arg <= 0) { + bot.createMessage(message.channel.id, `The argument should be greater than 0 (the queue is one-indexed), but 0 was provided.`).catch(Logger.error); + return; + } + if (arg > bot.voices[message.channel.guild.id].queue.length) { + bot.createMessage(message.channel.id, `Can't skip that far, the queue is currently only ${bot.voices[message.channel.guild.id].queue.length} titles long :')`).catch(Logger.error); + return; + } + bot.voices[message.channel.guild.id].queue = bot.voices[message.channel.guild.id].queue.splice(arg - 1); + if (player) { + player.stop(); + bot.createMessage(message.channel.id, `Current song and ${arg - 1} following where skipped.`).catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, `The bot wasn\'t playing but the ${arg - 1} next songs were successfully removed.`).catch(Logger.error); + } + }); + + bot.handler.endpoint('^remove-to!? ([0-9]+)$', [], async (match, message) => { + if (!bot.voices[message.channel.guild.id]) bot.voices[message.channel.guild.id] = { queue: [] }; + let arg = parseInt(match[1]); + if (arg <= 0) { + bot.createMessage(message.channel.id, `The argument should be greater than 0 (the queue is one-indexed), but 0 was provided.`).catch(Logger.error); + return; + } + if (arg > bot.voices[message.channel.guild.id].queue.length) { + bot.createMessage(message.channel.id, `Can't remove that far, the queue is currently only ${bot.voices[message.channel.guild.id].queue.length} titles long :')`).catch(Logger.error); + return; + } + bot.voices[message.channel.guild.id].queue = bot.voices[message.channel.guild.id].queue.splice(arg); + bot.createMessage(message.channel.id, `${arg} next songs in the queue were removed.`).catch(Logger.error); + }); + + bot.handler.endpoint('^remove!? ([0-9]+)$', [], async (match, message) => { + if (!bot.voices[message.channel.guild.id]) bot.voices[message.channel.guild.id] = { queue: [] }; + let arg = parseInt(match[1]); + if (arg <= 0) { + bot.createMessage(message.channel.id, `The argument should be greater than 0 (the queue is one-indexed), but 0 was provided.`).catch(Logger.error); + return; + } + if (arg > bot.voices[message.channel.guild.id].queue.length) { + bot.createMessage(message.channel.id, `Can't remove that far, the queue is currently only ${bot.voices[message.channel.guild.id].queue.length} titles long :')`).catch(Logger.error); + return; + } + bot.voices[message.channel.guild.id].queue.splice(arg - 1, 1); + bot.createMessage(message.channel.id, `The ${arg}e song in the queue was removed.`).catch(Logger.error); + }); + + bot.handler.endpoint('^now\\??$', [], async (match, message) => { + let player = bot.voiceConnections.get(message.channel.guild.id); + if (player && bot.voices[message.channel.guild.id].current) { + bot.createMessage(message.channel.id, `Now playing \`${bot.voices[message.channel.guild.id].current.info.title}\` at position ${time.msToMinutes(player.state.position)} added by \`${bot.voices[message.channel.guild.id].current.adder}\`. Total duration: ${time.msToMinutes(bot.voices[message.channel.guild.id].current.info.length)}`).catch(Logger.error); + } + else { + bot.createMessage(message.channel.id, 'The bot is not playing anything ¯\\_(ツ)_/¯').catch(Logger.error); + } + }); + + bot.handler.endpoint('^(?:queue|playlist|q|list)\\??$', [], async (match, message) => { + if (!bot.voices[message.channel.guild.id].queue || bot.voices[message.channel.guild.id].queue.length === 0) { + bot.createMessage(message.channel.id, 'The queue is empty!').catch(Logger.error); + return; + } + let buff = ""; + let count = 1; + bot.voices[message.channel.guild.id].queue.forEach(t => { + buff += `${count}. ${t.info.title}, added by \`${t.adder}\` and lasts ${time.msToMinutes(t.info.length)}\n`; + count++; + }); + bot.createMessage(message.channel.id, buff).catch(Logger.error); + }); + + bot.handler.endpoint('^(?:lyrics|text)\\??(?: (.+))?$', [], async (match, message) => { + let s = match[1] ? match[1] : null; + if (!s && (bot.voices[message.channel.guild.id] && bot.voices[message.channel.guild.id].current)) s = bot.voices[message.channel.guild.id].current.info.title; + if (!s) { + bot.createMessage(message.channel.id, 'No request mentionned and no song currently playing ¯\\_(ツ)_/¯').catch(Logger.error); + return; + } + let result; + try { + result = await superagent.get(`https://lyrics.tsu.sh/v1/?q=${s}`); + } + catch (e) { + return; + } + let res = JSON.parse(result.text); + let chunks = chunk(res.content, 2048); + if (chunks.length <= 4) { + let col = getRandomColor(); + for (let i = 0; i < chunks.length; i++) { + let data = { + embed: { + description: chunks[i], + color: col, + } + } + if (i === 0) data.embed.title = res.song.full_title; + await bot.createMessage(message.channel.id, data).catch(Logger.error); + } + } + else { + let key = await hastebin(res.content); + bot.createMessage(message.channel.id, `https://hasteb.in/${key}.txt`).catch(Logger.error); + } + }); +}; -- cgit v1.2.3