diff --git a/clientfactory.js b/clientfactory.js index 5b6394c..9f5dc90 100644 --- a/clientfactory.js +++ b/clientfactory.js @@ -9,6 +9,7 @@ const EnmapLevel = require('enmap-level'); const klaw = require('klaw'); const path = require('path'); const dbBackend = require('./util/db.js'); +require('./modules/Prototypes.js'); class PokeBlob extends Discord.Client { diff --git a/commands/eval.js b/commands/eval.js index c387b00..7e6519e 100644 --- a/commands/eval.js +++ b/commands/eval.js @@ -1,40 +1,67 @@ -// The EVAL command will execute **ANY** arbitrary javascript code given to it. -// THIS IS PERMISSION LEVEL 10 FOR A REASON! It's perm level 10 because eval -// can be used to do **anything** on your machine, from stealing information to -// purging the hard drive. DO NOT LET ANYONE ELSE USE THIS - - -// However it's, like, super ultra useful for troubleshooting and doing stuff -// you don't want to put in a command. const Command = require('../base/Command.js'); +const Stopwatch = require('../modules/Stopwatch.js'); +const { inspect } = require('util'); +const { post } = require('snekfetch'); class Eval extends Command { constructor(client) { super(client, { name: 'eval', description: 'Evaluates arbitrary Javascript.', - category:'System', - usage: 'eval ', + category: 'System', + usage: '', + extended: 'This is an extremely dangerous command, use with caution and never eval stuff strangers tell you.', aliases: ['ev'], permLevel: 'Bot Owner' }); } async run(message, args, level) { // eslint-disable-line no-unused-vars + const stopwatch = new Stopwatch(); + let syncTime, asyncTime; + const { client } = message; const code = args.join(' '); - if (!code) { - message.response(undefined, 'You must supply a string.'); - return; - } + const token = client.token.split('').join('[^]{0,2}'); + const rev = client.token.split('').reverse().join('[^]{0,2}'); + const filter = new RegExp(`${token}|${rev}`, 'g'); try { - const asyncCode = `(async() => ${code} )()`; - const evaled = await eval(asyncCode); - const clean = await this.client.clean(this.client, evaled); - message.channel.send(`\`\`\`js\n${clean}\n\`\`\``); - } catch (err) { - message.channel.send(`\`ERROR\` \`\`\`xl\n${await this.client.clean(this.client, err)}\n\`\`\``); + let result = eval(code); + syncTime = stopwatch.friendlyDuration; + if (result instanceof Promise || (Boolean(result) && typeof result.then === 'function' && typeof result.catch === 'function')) { + stopwatch.restart(); + result = await result; + asyncTime = stopwatch.friendlyDuration; + } + result = inspect(result, { depth: 0, maxArrayLength: null }); + result = result.replace(filter, '[TOKEN]'); + result = this.clean(result); + const type = typeof(result); + if (result.length < 1950) { + stopwatch.stop(); + const time = this.formatTime(syncTime, asyncTime); + message.evalBlock('js', result, type, time); + } else { + try { + const { body } = await post('https://www.hastebin.com/documents').send(result); + message.channel.send(`Output was to long so it was uploaded to hastebin https://www.hastebin.com/${body.key}.js `); + } catch (error) { + message.channel.send(`I tried to upload the output to hastebin but encountered this error ${error.name}:${error.message}`); + } + } + } catch (error) { + message.channel.send(`The following error occured \`\`\`js\n${error.stack}\`\`\``); } } + + formatTime(syncTime, asyncTime) { + return asyncTime ? `⏱ ${asyncTime}<${syncTime}>` : `⏱ ${syncTime}`; + } + + clean(text) { + return text + .replace(/`/g, '`' + String.fromCharCode(8203)) + .replace(/@/g, '@' + String.fromCharCode(8203)); + } } module.exports = Eval; diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js new file mode 100644 index 0000000..54be214 --- /dev/null +++ b/events/guildMemberAdd.js @@ -0,0 +1,21 @@ +module.exports = class { + constructor(client) { + this.client = client; + } + + async run(member) { + if (!member || !member.id || !member.guild) return; + const guild = member.guild; + + if (!guild.id === '408709336861507584') return; + + const checkGuild = this.client.guilds.get('272885620769161216'); + const checkMember = checkGuild.members.get(member.id); + const role = checkGuild.roles.find('name', 'PokéBlob Master'); + if (!checkMember.has(role.id)) { + return; + } else { + member.addRole('name', 'PokéBlob Master'); + } + } +}; diff --git a/modules/Prototypes.js b/modules/Prototypes.js new file mode 100644 index 0000000..d9250c7 --- /dev/null +++ b/modules/Prototypes.js @@ -0,0 +1,36 @@ +const { Message, Channel, MessageEmbed, TextChannel, DMChannel, User } = require('discord.js'); + +String.prototype.toProperCase = function() { + return this.replace(/([^\W_]+[^\s-]*) */g, function(txt) {return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); +}; + +String.prototype.toPlural = function() { + return this.replace(/((?:\D|^)1 .+?)s/g, '$1'); +}; + +Array.prototype.random = function() { + return this[Math.floor(Math.random() * this.length)]; +}; + + +Message.prototype.buildEmbed = function() { + return this.channel.buildEmbed(); +}; + +Message.prototype.evalBlock = function(lang, expression, type, time) { + return this.channel.send(`**Output:**\n\`\`\`${lang}\n${expression}\n\`\`\`**Type:**\n\`\`\`${type}\`\`\`\n${time}`); +}; + +Message.prototype.codeBlock = function(lang, expression) { + return `\`\`\`${lang}\n${expression}\`\`\``; +}; + +Channel.prototype.buildEmbed = function() { + return Object.defineProperty(new MessageEmbed(), 'sendToChannel', { value: this }); +}; + +MessageEmbed.prototype.send = function(content) { + if (!this.sendToChannel || !(this.sendToChannel instanceof TextChannel || this.sendToChannel instanceof User || this.sendToChannel instanceof DMChannel)) return Promise.reject('Embed not created in a channel'); + return this.sendToChannel.send(content || '', { embed: this }); +}; + diff --git a/modules/Stopwatch.js b/modules/Stopwatch.js new file mode 100644 index 0000000..6df9e0d --- /dev/null +++ b/modules/Stopwatch.js @@ -0,0 +1,55 @@ +const { performance } = require('perf_hooks'); + +class Stopwatch { + constructor(digits = 2) { + this.digits = digits; + this._start = performance.now(); + this._end = null; + } + + get duration() { + return this._end ? this._end - this._start : performance.now() - this._start; + } + + get friendlyDuration() { + const time = this.duration; + if (time >= 1000) return `${(time / 1000).toFixed(this.digits)}s`; + if (time >= 1) return `${time.toFixed(this.digits)}ms`; + return `${(time * 1000).toFixed(this.digits)}μs`; + } + + get running() { + return Boolean(!this._end); + } + + restart() { + this._start = performance.now(); + this._end = null; + return this; + } + + reset() { + this._start = performance.now(); + this._end = this._start; + return this; + } + + start() { + if (!this.running) { + this._start = performance.now() - this.duration; + this._end = null; + } + return this; + } + + stop() { + if (this.running) this._end = performance.now(); + return this; + } + + toString() { + return this.friendlyDuration; + } +} + +module.exports = Stopwatch; diff --git a/modules/functions.js b/modules/functions.js index a9c6775..ba1d304 100644 --- a/modules/functions.js +++ b/modules/functions.js @@ -72,25 +72,6 @@ module.exports = (client) => { return text; }; - - /* MISCELANEOUS NON-CRITICAL FUNCTIONS */ - - // EXTENDING NATIVE TYPES IS BAD PRACTICE. Why? Because if JavaScript adds this - // later, this conflicts with native code. Also, if some other lib you use does - // this, a conflict also occurs. KNOWING THIS however, the following 2 methods - // are, we feel, very useful in code. - - // .toPropercase() returns a proper-cased string such as: - // "Mary had a little lamb".toProperCase() returns "Mary Had A Little Lamb" - String.prototype.toProperCase = function() { - return this.replace(/([^\W_]+[^\s-]*) */g, function(txt) {return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); - }; - // .random() returns a single random element from an array - // [1, 2, 3, 4, 5].random() can return 1, 2, 3, 4 or 5. - Array.prototype.random = function() { - return this[Math.floor(Math.random() * this.length)]; - }; - // `await client.wait(1000);` to "pause" for 1 second. client.wait = require('util').promisify(setTimeout);