Compare commits

...

No commits in common. "5bedb80d6b775b5854a288aaeb6562f43aecabb5" and "202aacc4b04cbed555803286a5e9e5797ac07f5a" have entirely different histories.

83 changed files with 4993 additions and 2 deletions

58
.eslintrc.json Normal file
View File

@ -0,0 +1,58 @@
{
"parserOptions": {
"ecmaVersion": 2017
},
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"no-console": "off",
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"warn",
"double"
],
"semi": [
"warn",
"always"
],
"keyword-spacing": [
"error", {
"before": true,
"after": true
}
],
"space-before-blocks": [
"error", {
"functions":"always",
"keywords": "always",
"classes": "always"
}
],
"space-before-function-paren": [
"error", {
"anonymous": "never",
"named": "never",
"asyncArrow": "always"
}
],
"prefer-const": [
"error", {
"destructuring": "any",
"ignoreReadBeforeAssign": false
}
]
}
}

62
.gitignore vendored Normal file
View File

@ -0,0 +1,62 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
config.json
config.js
data/
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env

View File

@ -1,3 +1,27 @@
# cardboardbox
---
description: Official documentation of the Cardboard Box
---
# Welcome
## Information
**Cardboard Box** is a [Discord](https://discordapp.com) bot for my [**Discord server**](https://discord.gg/4zJ8xqV) powered by [Discord.js](https://discord.js.org). It comes packaged with many utilities and fun stuff, and even some special moderation functions.
**This bot is not suitable for usage outside of my Discord server!** This repository is primarily for issue tracking and global management between my own devices. If you really, _really_ want to run it yourself, read the sections below.
### Adding the bot to your guild
Don't want to run the bot yourself? Simply use [this link](https://discordapp.com/oauth2/authorize/?permissions=8&scope=bot&client_id=618576806177538079) to invite it to your own guild.
## Credits
* [SharpBot, a selfbot for Discord](https://github.com/RayzrDev/SharpBot) was actually my source for most of these cool commands! Huge kudos to [RayzrDev](https://github.com/RayzrDev) for making a cool selfbot.
* This was initially an edit to [GuideBot, the boilerplate example bot in Discord.js](https://github.com/AnIdiotsGuide/guidebot). It has some minor edits to the base code, but most of the basic stuff came from this very example bot.
## Join me
If you need a fun server to join, regardless of whether or not you're new to Discord, join now! If you've never used Discord before, don't fret! It only takes a few minutes to sign up and get started.
## [https://discord.gg/4zJ8xqV](https://discord.gg/4zJ8xqV)
Discord bot powered by Discord.js.

8
SUMMARY.md Normal file
View File

@ -0,0 +1,8 @@
# Table of contents
* [Welcome](README.md)
## Bot Usage
* [Host the bot yourself](bot-usage/host-the-bot-yourself.md)

View File

@ -0,0 +1,38 @@
---
description: Make the Box your own copy!
---
# Host the bot yourself
If you're really wanting to use this bot yourself, let's go over what you need and how to install it.
### Requirements
* `git` command line. Install by clicking your operating system's name in this list:
* [Windows](https://git-scm.com/download/win)
* [Linux](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Macintosh](https://git-scm.com/download/mac)
* `node` version [8.0.0 or higher](https://nodejs.org).
* Your **Discord bot token.** Read the first section of [this page](https://anidiots.guide/getting-started/the-long-version.html) on how to snag it.
* `Python`. Install it from [here](https://www.python.org/downloads).
### Installation
1. Head on over to your terminal \(preferrably **Command Prompt**\) and clone the repository.
* If you want to clone to a specific folder, you have two options:
* **`cd` into your desired folder.** For example, if you wanted to clone the repository into Documents, open the terminal normally and run `cd C:/Users/<user>/Documents`.
* Open the terminal in a specific folder by doing **Shift-Rightclick =&gt; Open &lt;Powershell/command terminal&gt; here**.
* After doing either of those steps, clone the repository by running `git clone https://github.com/rhearmas/cardboardbot.git` through your terminal.
2. When `git` has finished, `cd` into the newly-created folder for the repository.
3. Run `npm install`. This installs all the prerequisites for the bot through **Node package manager**, which is bundled with `node`.
* If you get any errors about python or msibuild.exe or binding, read the requirements section again and make sure you've installed **everything**.
4. Run `node setup.js` to get a configuration file added.
### Running the bot
Open your terminal and run `node index.js` inside the folder. Don't forget to `cd` into it if the folder isn't in places like your Desktop.
### Getting it to your server
Generate an OAuth link for your bot. Use [this delicious link](https://finitereality.github.io/permissions-calculator/?v=0) to get your link. It even has a calculator for permissions!

View File

@ -0,0 +1,122 @@
const Discord = require("discord.js");
exports.run = async (client, message, args) => {
message.delete();
const output = await message.channel.send("Generating the landing. Reading arguments...");
guild = message.guild;
let chnl = guild.channels.find(c => c.name === 'landing');
if(args[0]) {
chnl = args[0].substring("<#".length);
chnl = chnl.substring(chnl.length-1,0);
chnl = guild.channels.find(c => c.id === chnl);
output.edit(`Landing channel set to <#${chnl}>.`);
} else {
output.edit(`Arguments provided are invalid or channel was not found; set landing channel to ${chnl}.`);
}
async function clearLanding() {
let fetched;
fetched = await chnl.fetchMessages();
await output.edit(`Clearing messages in ${chnl}.`);
chnl.bulkDelete(fetched);
await output.edit(`Messages cleared in ${chnl}.`);
}
clearLanding();
const intro = new Discord.RichEmbed()
.setTitle("Introduction")
.setColor(0x2f3136)
.setDescription(`Welcome to **${guild.name}**! This is a Discord server that I personally run for all my shenanigans. This is the channel where I explain rules and stuff and other stuff.`)
.addField("IMPORTANT DISCLAIMER:", "This list does not constitute the full list of rules. With that in mind, try to use common sense and good judgement for any action you're about to take at all times.")
.addField("Before we go further...","I don't care if you use profanity here, since Discord only allows users who are at least 13 years of age. If you have a problem with this, then this isn't the discord server for you.")
.setThumbnail(guild.iconURL);
const textrules = new Discord.RichEmbed()
.setTitle("Text Channel Rules")
.setColor(0x2f3136)
.addField("Use common sense.","This means that you shouldn't be a jerk to others.")
.addField("No raiding.","Nobody here endorses raiding. Raiding this server or trying to rally other users to help you raid this server or another server is not allowed.")
.addField("Do not attempt to destroy the server.","This includes vandalizing non-spam channels with spam or playing excessively loud music.")
.addField("No discrimination.","This includes disrespecting people because of personal factors such as gender, sexual orientation, mental disabilities, race, age, location of residence, or anything else of the like.")
.addField("Threats towards others are not allowed.","Do not attempt to cause others to feel endangered in this server. We want to provide a safe space for everyone here, so don't ruin it.")
.addField("Abide by Discord's global platform rules.","This means that you should take the time to read the [Discord Terms of Service](https://discordapp.com/terms) and [Community Guidelines](https://discordapp.com/guidelines).")
.addField("Don't complain about your punishments.","They were given for a valid reason. ModMail exists just in case a staff member falsely punishes you.")
.addField("Loopholes aren't a thing, so don't go looking for them.","Don't be a wise-ass and just follow the rules. Don't test your boundaries, being here is a priviledge, not a right.")
.addField("Advertising outside of the advertisement channel is not permitted.","This includes third-party services (that are not related to this Discord) or anything you or others have created, regardless of permission granted. Advertising other Discord servers -- or advertising **anything** in your playing status or through unsolicited DMs -- is strictly forbidden.")
.addField("Don't cause beef with other members.","Don't send hate to other people if you see them as a bad person. Tolerate everyone here, even if you despise them, and don't engage in arguments.")
.addField("Do not humiliate other people.","Mentioning previous drama or other situations any person was involved in is included.")
.addField("No excuses for punishment.","The rule nullification system exists for a valid reason. Staff don't care until someone complains. Don't use this system as a way to attempt to circumvent a punishment.")
.addField("Absolutely no NSFW content.","Shocking or graphic content, animal cruelty, and gore are not allowed whatsoever outside of the designated NSFW channels. Breaking this rule will result in a permanent ban on sight.");
const voicerules = new Discord.RichEmbed()
.setTitle("Voice Channel Rules")
.setColor(0x2f3136)
.addField("Most text rules apply in the voice chat.","Don't go crazy because of the channels being different.")
.addField("No loud audio.","Playing loud and/or annoying audio through your microphone or music bots is not allowed.")
.addField("Be responsible.","Everyone in the server holds the right to report users in the voice channels to have them muted as needed.")
.addField("Use common sense.","You can't escape this rule. Please don't ignore it.")
.addField("Limit the usage of voice changers.","Don't fake what you sound like. We prevent discrimination, you're fine.");
const punishmentsystem = new Discord.RichEmbed()
.setTitle("Punishment System")
.setColor(0x2f3136)
.setDescription("Break a rule and you will get warned or muted. Three warns from the moderation bot results in a kick, then a ban after going through the same process. Extreme offenders get worse punishments. If you have a higher-tier role such as Annoying, you may lose it until it's deemed necessary we give it back.")
.addBlankField()
.addField("IMPORTANT:","Staff hold a right to punish anyone how they see fit. Staff here are pretty lenient, just be a well rounded individual and respect others. The staff team can see any kind of deleted images because of the readily-available logging system they have at their disposal.");
const nullify = new Discord.RichEmbed()
.setTitle("Rule Nullification System")
.setColor(0x2f3136)
.setDescription("This is a system about how to actually be punished for breaking rules, and how to avoid it if what you want to do is somehow \"important\". This is **especially important** for staff members.")
.addField("The System","When a user breaks a rule, as long as everybody within the constituting area is okay with that rule being broken, then **most** rules *(Embed keyword, most)* broken are nullified until a complaining or reporting individual, or group of individuals, is present.")
.addField("HOWEVER...","This does NOT mean that you can break the rules first and ask questions later. Use this additional information at your own discretion, but if you misuse it or abuse it, the punishment is your own fault.\n_ _\nThere are also a few exceptions to this system. Those exceptions are as follows:\n• Abide by [Discord's ToS](https://discordapp.com/terms) and [Community Guidelines](https://discordapp.com/terms)\n• No discrimination\n• No NSFW outside of designated channels\n_ _\n**__Staff members still hold the right to punish you, even if nobody complains about something!__**\nBe wary of what you do, as all actions have consequences.")
const confirmation = new Discord.RichEmbed()
.setTitle("User confirmation and autoconfirmation")
.setColor(0x2f3136)
.addField("Overview","User confirmation allows for full functionality in the majority of channels.")
.addField("What's so special about being a confirmed user?","Being a confirmed user shows that you're special. You get special permissions in text channels. These permissions include:\n• Image posting in regular channels\n• Adding reactions to messages\n• Ability to use external emotes\n• Unlocked access to posting in self-advertisement channels")
.addField("How to be confirmed?","There are two methods to being a confirmed user:\n• Manual confirmation by receiving the **confirmed** role\n• Achieving level **25**")
.addField("Method 1: Manual confirmation","Manually confirmed users are able to 100% bypass the autoconfirmation process, and immediately receive the abilities a confirmed user has. The **board of commissioners** have the ability to grant any user the `confirmed` role.")
.addField("Method 2: Autoconfirmation","Being autoconfirmed means that you're active enough to receive special permissions. This role is called `mayhaps`.\nYou will be granted this role __automatically__ once you reach level **25**.")
const links = new Discord.RichEmbed()
.setTitle("Important Links")
.setColor(0x2f3136)
.addField("Discord Server Invite Link","Use [this invite](https://discord.gg/4zJ8xqV) to invite others.")
.addField("Member Survey","After being here for a while, feel free to fill out [this form](https://goo.gl/forms/Gwa62lcu4zC7B8992).")
.addField("Carrd","Rhearmas has an official website that shows a lot of the stuff about her. [Click here](https://rhearmas.carrd.co) to take a look!");
async function sendLandingEmbeds() {
await output.edit("Sending intro... *(Embed 1/7)*");
await chnl.send({embed: intro});
await output.edit("Sending text rules... *(Embed 2/7)*");
await chnl.send({embed: textrules});
await output.edit("Sending voice rules... *(Embed 3/7)*");
await chnl.send({embed: voicerules});
await output.edit("Sending punishment system... *(Embed 4/7)*");
await chnl.send({embed: punishmentsystem});
await output.edit("Sending punishment nullification system... *(Embed 5/7)*");
await chnl.send({embed: nullify});
await output.edit("Sending confirmation system... *(Embed 6/7)*");
await chnl.send({embed: confirmation});
await output.edit("Sending punishment nullification system... *(Embed 7/7)*");
await chnl.send({embed: links});
await chnl.send("*If you can't copy the invite link, here's the raw URL: https://discord.gg/4zJ8xqV*");
await output.edit(`Landing has finished building in ${chnl}. This message will be removed in 5 seconds.`).then(output => {
output.delete(5000)
})
.catch(() => console.error("Error while deleting message."));
}
sendLandingEmbeds();
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "buildlanding",
category: "Channel Building",
description: "Builds the landing by purging the whole channel and sending embeds. Builds in first channel named **landing** if no arguments are provided.",
usage: "buildlanding channel"
};

View File

@ -0,0 +1,88 @@
const Discord = require("discord.js");
exports.run = async (client, message, args) => {
message.delete();
const output = await message.channel.send("Generating the staff information. Reading arguments...");
guild = message.guild;
let chnl = message.channel;
if(args[0]) {
chnl = args[0].substring("<#".length);
chnl = chnl.substring(chnl.length-1,0);
chnl = guild.channels.find(c => c.id === chnl);
output.edit(`Staff info channel set to <#${chnl}>.`);
} else {
output.edit(`Arguments provided are invalid or channel was not found; set staff info channel to the current channel, ${chnl}.`);
}
async function clearChannel() {
let fetched;
fetched = await chnl.fetchMessages();
await output.edit(`Clearing messages in ${chnl}.`);
chnl.bulkDelete(fetched);
await output.edit(`Messages cleared in ${chnl}.`);
}
clearChannel();
const intro = new Discord.RichEmbed()
.setTitle("Introduction")
.setColor(0x00AE86)
.setDescription(`Welcome to our staff team. We're great to have you. Part of your duties include keeping the server standing on two feet. But that doesn't mean your day is over yet. When taking part of our team, these guidelines must be met or exceeded, otherwise your position will be terminated. Please remember that this position is **voluntary**, and will most likely receive no compensation for your work.`);
const rules = new Discord.RichEmbed()
.setTitle("Staff Rules")
.setColor(0x8E8E38)
.addField("Follow all this server's rules in #info.","Just because you're higher in the member list hierchy, doesn't mean you suddenly have a one-way ticket to total entitlement to break the rules. Set the good example for the nonadmins.")
.addField("Don't be a hypocrite.","Hypocrisy is so, *so* incredibly common, and without properly addressing this, absolute chaos may ensue. With that said, remember that you need to always give yourself a taste of your own medicine before giving it to others.")
.addField("Giveaways must require permission from a Head Admin or above.","Yeah, giving stuff away is fun, but don't abuse it.")
.addField("Don't ping people unless it's justified.","Tying to this main server's rules, don't ping someone unless you're actively in conversation with them. Only ping higher-ups if someone's doing something wrong or you need help on what to do in a situation.")
.addField("Don't abuse your power.","You aren't Stalin or Hitler or anything. Don't pull a fast one and go on murder sprees in-game or mass ban waves.")
.addField("Don't do stuff to certain people because you \"don't like them\".","We all have our distaste for someone, but a community consensus for which bans are justified and which ones aren't. Warning people for stupid reasons isn't the right thing to do.")
.addField("Changing parts of the server without prior permission is not allowed.","This isn't your server, it's the community's. Be righteous with what you do. You will need evidence to prove that you were permitted to do such actions.")
.addField("Breaking Discord Terms of Service results in immediate action.","The Terms of Service are an important part to Discord. If you are exhibited to have assumed any behavior, you will be suspended from your priviledges.")
.addField("Your rank doesn't let you ruin lower users' lives.","Do not use your rank as a way to get other people's attention or make them uncomfortable, **especially when trying to groom them like a predator**.")
.addField("Please remember that the rule nullification system exists.","This system determines whether someone is warned or not. This also means that the nonadmins need to take on responsibility and talk to one of the staff members if something comes up.");
const botinfo = new Discord.RichEmbed()
.setTitle("Management Information")
.setColor(0x388E8E)
.setDescription("We use @GalacticTest#2913 and @Carl-bot#1536 for management. Here's what you use them for.")
.addField("GalacticBot","**PREFIX: - (DASH)** | Moderation. Useful commands:\n`-warn user reason` | Example: `;warn rhearmas#9001 spamming`\n`-mute user [time]; [reason]` | Example: `-mute rhearmas#9001 5m spamming`\n`-kick user reason` | Example: `;kick rhearmas#9001 flooding channels`\n`-ban user reason` | Example: `-ban rhearmas#9001 abusing staff rank` | BANS WITH THIS ARE PERMANENT\n`-tempban user time; reason` | Example: `-tempban rhearmas#9001 1d; mass pinging` | **USE THIS BEFORE ANYTHING ELSE**")
.addField("Carl-Bot","**PREFIX: c/** | Fun, utilities, reminders. Useful commands:\n`c/remindme time reason`\n`c/reminder user`\n**Use `c/help commandname` to see more syntax information.**");
const importantInfo = new Discord.RichEmbed()
.setTitle("Super Important Information")
.setColor(0xC5C1AA)
.setDescription("As with the nonadmins, we hold the right to punish you as we see fit. Don't test your boundaries, and for the love of god, **do not use `@everyone` without any context**. People who don't tolerate pings may either leave the discord or will mute it, effectively missing out on events.")
.addField("Don't be afraid to ask questions!","Other staff members are always ready to help if you hit a snag and need someone higher up in the user hierarchy to handle something, or if that one button doesn't work or that lever doesn't go down all the way.");
await output.edit("Sending beginning... *(1/6)*");
await chnl.send("\`<BEGIN_TAPE>\`");
await output.edit("Sending introduction... *(2/6)*");
await chnl.send({embed: intro});
await output.edit("Sending rules... *(3/6)*");
await chnl.send({embed: rules});
await output.edit("Sending bot usage... *(4/6)*");
await chnl.send({embed: botinfo});
await output.edit("Sending important information... *(5/6)*");
await chnl.send({embed: importantInfo});
await output.edit("Sending ending... *(6/6)*");
await chnl.send("\`<END_TAPE>\`");
await output.edit(`Staff information has finished building in ${chnl}. This message will be removed in 5 seconds.`).then(output => {
output.delete(5000)
})
.catch(() => console.error("Error while deleting message."));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "staffinfo",
category: "Channel Building",
description: "Builds the staff rules channel in the mentioned channel. Builds in current if no arguments are provided.",
usage: "staffinfo channel"
};

69
commands/Fun/8ball.js Normal file
View File

@ -0,0 +1,69 @@
const responses = [
'Ask again later.',
'Soon.',
'Yes.',
'Absolutely!',
'Never.',
'When you are ready.',
'Hopefully.',
'Hopefully not.',
'Oh my, why would you even ask that?',
'What kind of a question is that?',
'Over my dead body!',
'Haha, funny joke.',
'It is certain.',
'It is decidedly so.',
'Without a doubt.',
'Yes - definitely.',
'You may rely on it.',
'As I see it, yes.',
'Most likely.',
'Outlook good.',
'Signs point to yes.',
'Reply hazy, try again.',
'Better not tell you now.',
'Cannot predict now.',
'Concentrate and ask again.',
'Don\'t count on it.',
'My reply is no.',
'My sources say no.',
'Outlook not so good.',
'Very doubtful.'
];
function randomItem(array) {
return array[Math.floor(Math.random() * array.length)];
}
exports.run = async (client, message, args, level) => {
message.delete();
if (!args[0]) {
message.delete();
return (await message.reply("text")).delete(5000);
}
let response = randomItem(responses);
const query = args.join(' ');
if (query.indexOf('ipodtouch0218') > -1 || query.indexOf('233360087979130882') > -1) {
response = 'HAH';
}
message.channel.send(`**${message.author.username}** asks: "${query}"\n**:8ball::** ${response}`);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "8ball",
category: "Fun",
description: "Ask the magic eight ball for wisdom.",
usage: "8ball <query>"
};

58
commands/Fun/binary.js Normal file
View File

@ -0,0 +1,58 @@
exports.methods = {
encode: input => {
return input.toString().split('')
.map(c => c.charCodeAt(0).toString(2));
},
decode: input => {
let _input = typeof input === 'string' ? input.split(' ') : input;
return _input.map(c => parseInt(c, 2))
.map(c => String.fromCharCode(c))
.join('');
}
};
exports.run = async (client, message, args, level) => {
if (args.length < 2) {
message.delete();
return (await message.channel.send(`Hey ${message.author}, do \`${client.settings.get("default").prefix}help binary\` to see how to use this.`)).delete(5000);
}
let input = args.slice(1).join(' ');
if (args[0].match(/^enc(ode(Text)?)?$/i)) {
message.channel.send(this.methods.encode(input).join(' '));
} else if (args[0].match(/^dec(ode(Text)?)?$/i)) {
message.channel.send(this.methods.decode(input));
} else if (args[0].match(/^decToBin$/i)) {
if (isNaN(input)) {
message.delete();
return (await message.reply('your input must be a number.')).delete(5000);
}
message.channel.send(parseInt(input).toString(2));
} else if (args[0].match(/^binToDec$/i)) {
if (isNaN(input)) {
message.delete();
return (await message.reply('your input must be a number.')).delete(5000);
}
message.channel.send(parseInt(input, 2));
} else {
message.delete();
return (await message.reply(`the sub command you entered, \`${args[0]}\`, is something I'm unfamiliar with.`)).delete(5000);
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "binary",
category: "Fun",
description: "Convert text to binary, or vice versa.",
usage: "binary <encodeText|decodeText|decToBin|binToDec> <input>"
};

24
commands/Fun/clap.js Normal file
View File

@ -0,0 +1,24 @@
const randomizeCase = word => word.split('').map(c => Math.random() > 0.5 ? c.toUpperCase() : c.toLowerCase()).join('');
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.channel.send("Please provide some text to clapify")).delete(5000);
}
message.delete();
message.channel.send(args.map(randomizeCase).join(':clap:'));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "clap",
category: "Fun",
description: "Clap clap clap! Clapifies your text.",
usage: "clap <text>"
};

49
commands/Fun/fanceh.js Normal file
View File

@ -0,0 +1,49 @@
const mapping = {
' ': ' ',
'0': ':zero:',
'1': ':one:',
'2': ':two:',
'3': ':three:',
'4': ':four:',
'5': ':five:',
'6': ':six:',
'7': ':seven:',
'8': ':eight:',
'9': ':nine:',
'!': ':grey_exclamation:',
'?': ':grey_question:',
'#': ':hash:',
'*': ':asterisk:'
};
'abcdefghijklmnopqrstuvwxyz'.split('').forEach(c => {
mapping[c] = mapping[c.toUpperCase()] = ` :regional_indicator_${c}:`;
});
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.reply("Please specify something to ask of the magic 8-ball!")).delete(5000);
}
message.delete();
message.channel.send(
args.join(' ')
.split('')
.map(c => mapping[c] || c)
.join('')
);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "fanceh",
category: "Fun",
description: "Renders text in big emoji letters.",
usage: "fanceh <text>"
};

32
commands/Fun/fliptext.js Normal file
View File

@ -0,0 +1,32 @@
const mapping = '¡"#$%⅋,)(*+\'-˙/0ƖᄅƐㄣϛ9ㄥ86:;<=>?@∀qƆpƎℲפHIſʞ˥WNOԀQɹS┴∩ΛMX⅄Z[/]^_`ɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz{|}~';
const OFFSET = '!'.charCodeAt(0);
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
message.reply("you didn't specify any text for me to flip!").delete(5000);
}
message.delete();
message.channel.send(
args.join(' ').split('')
.map(c => c.charCodeAt(0) - OFFSET)
.map(c => mapping[c] || ' ')
.reverse().join('')
);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["flip"],
permLevel: "User"
};
exports.help = {
name: "fliptext",
category: "Fun",
description: "Flips text upside down!",
usage: "fliptext <text>"
};

43
commands/Fun/image.js Normal file
View File

@ -0,0 +1,43 @@
const IMAGE_NAME = /\.(jpe?g|png|gif|webp)$/i;
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.reply("please provide an image URL to send.")
message.delete();
return;
}
message.delete();
const url = args[0];
let name;
if (!IMAGE_NAME.test(url)) {
name = 'image.png';
}
try {
let msg = await message.channel.send({
file: {
name,
attachment: url
}
});
} catch (ignore) {
return msg.edit('Failed to send image.');
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "image",
category: "Utiltiy",
description: "Sends an image from a URL.",
usage: "image <url>"
};

View File

@ -0,0 +1,50 @@
const got = require('got');
const fs = require('fs');
const path = require('path');
exports.run = async (client, message, args, level) => {
let count = parseInt(args[0]) || 100;
let attachments = [];
message.channel.fetchMessages({ limit: Math.min(count, 100), before: message.id }).then(messages => {
messages.map(m => {
m.attachments.map(attachment => {
if (attachment.height) {
attachments.push(attachment.url);
}
});
});
let dir = __dirname + '/../../imagedumper_output';
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
for (let i = 0; i < attachments.length; i++) download(attachments[i]);
if (attachments.length === 0) {
message.reply("I couldn\'t find any images here.");
message.delete();
return;
}
message.channel.send(`:white_check_mark: ${attachments.length} images scraped and saved to the "out" folder in this bot's folder.`).then(m => { m.delete(10000); });
message.delete();
}).catch(message.error);
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "imagedumper",
category: "Fun",
description: "Grabs all images from the specified amount of messages (max 100).",
usage: "imagedumper <amount>"
};
function download(url) {
let file = fs.createWriteStream(`${__dirname}/../../imagedumper_output/attachment_${path.basename(url)}`);
got.stream(url).pipe(file);
}

22
commands/Fun/initial.js Normal file
View File

@ -0,0 +1,22 @@
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.delete();
return (await message.reply("you must input some text to be transformed.")).delete(5000);
}
message.delete();
message.channel.send(args.map(arg => arg[0].toUpperCase() + arg.slice(1).toLowerCase()).join(' '));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "initial",
category: "Fun",
description: "Transforms the text you insert into Initial Caps.",
usage: "initial <text>"
};

46
commands/Fun/jumbo.js Normal file
View File

@ -0,0 +1,46 @@
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.reply("you didn't provide an emoji to enlarge.")).delete(5000);
}
if (args[0].charCodeAt(0) >= 55296) {
message.delete();
return (await message.reply("I can't enlarge Discord's built-in emoji.")).delete(5000);
}
const match = args[0].match(/<:[a-zA-Z0-9_-]+:(\d{18})>/);
if (!match || !match[1]) {
message.delete();
return (await message.reply("please provide a valid emoji.")).delete(5000);
}
const emoji = client.emojis.get(match[1]);
if (!emoji) {
message.delete();
return (await message.reply("I couldn't identify that emoji.")).delete(5000);
}
message.delete();
message.channel.send({
files: [
emoji.url
]
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "jumbo",
category: "Fun",
description: "Enlarges an emoji.",
usage: "jumbo <emoji>"
};

100
commands/Fun/leet.js Normal file
View File

@ -0,0 +1,100 @@
let _inverseReplacementsCached = null;
const getInverseReplacements = replacements => {
if (_inverseReplacementsCached) {
return _inverseReplacementsCached;
}
const inverseReplacements = new Map();
Object.keys(replacements)
.map(letter => {
replacements[letter].forEach(replacement => {
inverseReplacements.set(new RegExp(global.utils.quoteRegex(replacement), 'gi'), letter);
});
});
_inverseReplacementsCached = inverseReplacements;
return inverseReplacements;
};
exports.run = async (client, message, args, level) => {
const parsedArgs = client.parseArgs(args, ['e', 't']);
if (parsedArgs.leftover.length < 1) {
message.delete();
return (await message.reply("please provide some text to convert.")).delete(5000);
}
let parsed;
if (parsedArgs.options.e) {
const extendedLeetReplacements = {
'a': ['4', '@', '/-\\', 'Д'],
'b': ['ß'],
'c': ['¢', '©'],
'e': ['3', '€'],
'f': ['ph', 'ƒ'],
'g': ['6'],
'i': ['1', '!'],
'l': ['7'],
'n': ['И', 'ท'],
'q': ['Ø'],
'r': ['®', 'Я'],
's': ['5', '$', '§'],
't': ['†'],
'u': ['|_|', 'µ', 'บ'],
'v': ['\\/'],
'w': ['\\/\\/', 'VV', 'Ш', 'พ'],
'x': ['Ж', '×'],
'y': ['¥']
};
const inverseReplacements = getInverseReplacements(extendedLeetReplacements);
if (parsedArgs.options.t) {
parsed = parsedArgs.leftover.join(' ');
for (let [replacement, origValue] of inverseReplacements) {
parsed = parsed.replace(replacement, origValue);
}
} else {
parsed = parsedArgs.leftover
.join(' ')
.replace(/[a-z]/gi, str => {
let selection = client.randomSelection(extendedLeetReplacements[str.toLowerCase()] || [str]);
selection = client.quoteRegex(selection);
return selection;
});
}
} else {
const simpleLeetReplacements = '4BCD3F6H1JKLMN0PQR57';
if (parsedArgs.options.t) {
parsed = parsedArgs.leftover.join(' ').replace(/[a-z0-9]/g, function (a) {
let foundInReplacements = simpleLeetReplacements.indexOf(a);
if (foundInReplacements === -1) {
return a;
}
return String.fromCharCode(97 + foundInReplacements);
});
} else {
parsed = parsedArgs.leftover.join(' ').replace(/[a-z]/g, function f(a) {
return simpleLeetReplacements[parseInt(a, 36) - 10] || a.replace(/[a-t]/gi, f);
}).toLowerCase();
}
}
message.delete();
message.channel.send(parsed);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "leet",
category: "Fun",
description: "Talk like a true gamer.",
usage: "leet <text>"
};

160
commands/Fun/react.js vendored Normal file
View File

@ -0,0 +1,160 @@
const discordEmoji = require('discord-emoji');
const emoji = {};
Object.values(discordEmoji).forEach(value => {
Object.keys(value).forEach(key => {
emoji[key] = value[key];
});
});
const mappings = {
'a': [':regional_indicator_a:', ':a:'],
'b': [':regional_indicator_b:', ':b:'],
'c': [':regional_indicator_c:'],
'd': [':regional_indicator_d:'],
'e': [':regional_indicator_e:'],
'f': [':regional_indicator_f:'],
'g': [':regional_indicator_g:', ':compression:'],
'h': [':regional_indicator_h:'],
'i': [':regional_indicator_i:', ':information_source:'],
'j': [':regional_indicator_j:'],
'k': [':regional_indicator_k:'],
'l': [':regional_indicator_l:'],
'm': [':regional_indicator_m:', ':m:'],
'n': [':regional_indicator_n:'],
'o': [':regional_indicator_o:', ':o2:', ':o:'],
'p': [':regional_indicator_p:', ':parking:'],
'q': [':regional_indicator_q:'],
'r': [':regional_indicator_r:'],
's': [':regional_indicator_s:'],
't': [':regional_indicator_t:', ':cross:'],
'u': [':regional_indicator_u:'],
'v': [':regional_indicator_v:'],
'w': [':regional_indicator_w:'],
'x': [':regional_indicator_x:', ':heavy_multiplication_x:', ':x:', ':negative_squared_cross_mark:'],
'y': [':regional_indicator_y:'],
'z': [':regional_indicator_z:'],
'0': [':zero:'],
'1': [':one:'],
'2': [':two:'],
'3': [':three:'],
'4': [':four:'],
'5': [':five:'],
'6': [':six:'],
'7': [':seven:'],
'8': [':eight:'],
'9': [':nine:'],
'!': [':exclamation:', ':grey_exclamation:'],
'?': [':question:', ':grey_question:'],
'*': [':asterisk:', ':eight_spoked_asterisk:'],
'#': [':hash:'],
'$': [':heavy_dollar_sign:']
};
function clone(object) {
const newObject = {};
Object.keys(object).forEach(key => {
if (object[key] instanceof Array) {
newObject[key] = new Array(...object[key]);
} else {
newObject[key] = object[key];
}
});
return newObject;
}
function emojiToUnicode(input) {
if (/^:regional_indicator_[a-z]:$/.test(input)) {
return String.fromCharCode(55356) + String.fromCharCode(56806 + input.substr(20, 1).charCodeAt(0) - 97);
}
return emoji[input.slice(1, -1)];
}
function react(message, remaining, allowedMappings) {
if (remaining.length < 1) {
// We're out of stuff
return;
}
const char = remaining.shift().toLowerCase();
if (!char) {
return;
}
if (!allowedMappings[char]) {
// Not a usable char
return;
}
const next = allowedMappings[char].shift();
if (!next) {
// We have no more mappings available
return;
}
message.react(emojiToUnicode(next)).then(() => {
react(message, remaining, allowedMappings);
});
}
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
await message.reply("you should actually try to tell me what I should react with...")
message.delete();
client.user.lastMessage.delete(5000);
}
const fetchOptions = { limit: 1 };
if (args[1]) {
if (!/\d{18}/.test(args[1])) {
await message.reply(`${args[1]} isn't a valid message ID!`);
message.delete();
client.user.lastMessage.delete(5000);
}
fetchOptions.around = args[1];
} else {
fetchOptions.before = message.id;
}
message.channel.fetchMessages(fetchOptions).then(messages => {
if (messages.length < 1) {
return message.reply("I couldn't find the specified message.");
}
const target = messages.first();
const allowedMappings = clone(mappings);
// Remove current reactions from allowed emojis
target.reactions.forEach(reaction => {
const emoji = reaction.toString();
for (const key in allowedMappings) {
const index = allowedMappings[key].indexOf(emoji);
if (index > -1) {
allowedMappings[key].splice(index, 1);
}
}
});
message.delete();
react(target, args[0].split(''), allowedMappings);
}).catch(message.error);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "react",
category: "Fun",
description: "Reacts to the last sent message (or another message) with a given text; your response cannot contain spaces.",
usage: "react <text> [message ID]"
};

22
commands/Fun/reverse.js Normal file
View File

@ -0,0 +1,22 @@
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.reply("text")).delete(5000);
}
message.delete();
message.channel.send(args.join(' ').split('').reverse().join(''));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "reverse",
category: "Fun",
description: "Reverses the text you insert.",
usage: "reverse <text>"
};

53
commands/Fun/roll.js Normal file
View File

@ -0,0 +1,53 @@
const Roll = require('roll');
const roller = new Roll();
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.delete();
return (await message.reply("you must specify in dice notation (XdY).")).delete(5000);
}
let reason = '';
let footer = '';
footer += `:game_die: **${args[0]}**`;
if (args.length > 1) {
reason = args.splice(1).join(' ');
footer += ` | Reason: ${reason}`;
}
let results = roller.roll(args[0]);
message.delete();
let embed = client.embed(
`Total: ${results.result}`,
`${[].concat.apply([], results.rolled).join(', ').substr(0, 1800)}`,
[
{
name: '\u200b',
value: footer
}
],
{
author: `Rolled by ${message.author.tag}`,
authorURL: message.author.avatarURL
}
);
message.channel.send({ embed });
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "roll",
category: "Fun",
description: "Rolls X dice with Y sides. Supports standard dice notation.",
usage: "roll <XdY> [reason]"
};

60
commands/Fun/shoot.js Normal file
View File

@ -0,0 +1,60 @@
const responses = [
'is on a killing spree!',
'just shot someone!',
'murdered an innocent! Grab the sherrif!',
'got a bullseye.',
'wrangled a person!',
'made someone get got!',
'brought a gun to a knife fight.',
'earned someone a closed-casket funeral.',
'shot a weeb!',
'pumped up some kicks!'
];
const critResponses = [
'GOT A HEADSHOT!!',
'HIT A 360 NOSCOPE!!!!',
'EXPLODED A CORPSE WITH A BULLET!!',
'HAS LUCK ON THEIR SIDE!!',
'SENT SOMEONE FLYING HOME!!!!'
];
function randomItem(array) {
return array[Math.floor(Math.random() * array.length)];
}
exports.run = async (client, message, args, level) => {
if (message.mentions.users.size < 1) {
message.delete();
return (await message.reply("gotta mention those people you wanna shoot, ya know.")).delete(5000);
}
let response = randomItem(responses);
let crit = Math.floor(Math.random() * 10);
// console.log(`crit is ${crit}`)
if(crit === 1) {
response = randomItem(critResponses);
}
let output = message.mentions.users.map(m => `**${m}** ${crit === 1 ? ':skull:' : ''}:boom::gun: **${message.author}**`).join('\n');
message.delete();
message.channel.send({
embed: client.embed(`${crit === 1 ? message.author.username.toUpperCase() : message.author.username} ${response}`, output)
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "shoot",
category: "Fun",
description: "Shoots yer friendz!",
usage: "shoot <mention>"
};

41
commands/Fun/shorturl.js Normal file
View File

@ -0,0 +1,41 @@
const got = require('got');
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
await message.reply("please provide a url to shorten!");
client.user.lastMessage.delete(5000);
message.delete();
}
const url = args.join(' ');
message.delete();
const res = await got(`http://v.gd/create.php?format=simple&url=${encodeURIComponent(url)}`);
message.channel.send({
embed: client.embed('', '', [
{
name: 'Link',
value: url
},
{
name: 'Short URL',
value: res.body
}
])
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ['shorten','vgd'],
permLevel: "User"
};
exports.help = {
name: "shorturl",
category: "Fun",
description: "Shortens a URL with v.gd.",
usage: "shortenurl <url>"
};

33
commands/Fun/sigh.js Normal file
View File

@ -0,0 +1,33 @@
const ascii = `
\`\`\`
_______ _________ _________ , ,
/ | / | |
| | | | |
| | | | |
\\_____, | | _______, |________|
\\ | | | | |
| | | | | |
| | | | | |
______/ ____|____ \\________| | |
\u200b
\`\`\`
`;
exports.run = async (client, message, args, level) => {
message.delete();
message.channel.send(ascii);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "sigh",
category: "Fun",
description: "Siiiiggggghhh...",
usage: "sigh"
};

32
commands/Fun/space.js Normal file
View File

@ -0,0 +1,32 @@
exports.run = async (client, message, args, level) => {
if(!args[0]) {
message.delete();
return (await message.reply("you didn't provide any text to space out!")).delete(5000);
}
let amount = 2;
if(!isNaN(args[0])) {
amount = parseInt(args[0]);
(amount < 1) && (amount = 1);
(amount > 15) && (amount = 15);
args = args.slice(1);
}
message.delete();
message.channel.send(args.join(' '.repeat(amount / 2)).split('').join(' '.repeat(amount)));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "space",
category: "Fun",
description: "Spaces out text to look all dramatic n' stuff.",
usage: "space [amount] <text>"
};

36
commands/Fun/talk.js Normal file
View File

@ -0,0 +1,36 @@
const Discord = require("discord.js");
exports.run = async (client, message, args) => {
msg = args.join(" ");
await message.delete();
if(args.length === 0) {
return (await message.reply("please insert a valid message.")).delete(5000);
}
let location = message.channel;
guild = message.guild;
if(args[0].startsWith('<#') && args[0].endsWith('>')) {
location = args[0].slice(2, -1);
location = guild.channels.find(c => c.id === location);
msg = msg.slice(args[0].length);
console.log(`msg is ${msg}`)
}
location.send(msg);
}
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "talk",
category: "Fun",
description: "Talk as the bot in the current channel you're in.",
usage: "talk <text>"
};

66
commands/Fun/tiny.js Normal file
View File

@ -0,0 +1,66 @@
const mappings = (function (object) {
let output = [];
for (let key in object) {
output.push({
regex: new RegExp(key, 'ig'),
replacement: object[key]
});
}
return output;
})({
a: '\u1D00',
b: '\u0299',
c: '\u1D04',
d: '\u1D05',
e: '\u1D07',
f: '\uA730',
g: '\u0262',
h: '\u029C',
i: '\u026A',
j: '\u1D0A',
k: '\u1D0B',
l: '\u029F',
m: '\u1D0D',
n: '\u0274',
o: '\u1D0F',
p: '\u1D18',
q: '\u0071',
r: '\u0280',
s: '\uA731',
t: '\u1D1B',
u: '\u1D1C',
v: '\u1D20',
w: '\u1D21',
x: '\u0078',
y: '\u028F',
z: '\u1D22'
});
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.delete();
return (await message.reply("you must provide some text to shrink!")).delete(5000);
}
let output = args.join(' ');
mappings.forEach(replacer => output = output.replace(replacer.regex, replacer.replacement));
message.delete();
message.channel.send(output);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "tiny",
category: "Fun",
description: "Super tiny text!",
usage: "tiny <text>"
};

40
commands/Fun/urban.js Normal file
View File

@ -0,0 +1,40 @@
const webdict = require('webdict');
exports.run = async (client, message, args, level) => {
message.delete();
if (!args[0]) return message.reply("you didn't provide a valid word to look up.");
const parsed = client.parseArgs(args, ['e']);
const word = parsed.leftover.join(' ');
webdict('urbandictionary', word).then(res => {
let result;
if (!res || !res.definition || !res.definition[0]) {
result = 'No results found.';
} else {
result = res.definition[0];
}
if (parsed.options.e) {
message.channel.send(result);
return;
}
message.channel.send(client.embed(`:book: ${word}`, result));
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ['urban'],
permLevel: "User"
};
exports.help = {
name: "urbandictionary",
category: "Utility",
description: "Looks up a word in the urban dictionary.",
usage: "urbandictionary <word>"
};

58
commands/Fun/xkcd.js Normal file
View File

@ -0,0 +1,58 @@
const got = require('got');
async function getInfo(id) {
return (await got(`http://xkcd.com/${id}/info.0.json`, { json: true })).body;
}
async function getLatest() {
return (await got('http://xkcd.com/info.0.json', { json: true })).body;
}
async function getRandom() {
const latest = await getLatest();
const max = latest.num;
return Math.floor(Math.random() * max);
}
exports.run = async (client, message, args, level) => {
let id;
if (args[0] === 'latest') {
id = (await getLatest()).num;
} else {
id = parseInt(args[0]);
if (isNaN(id)) {
id = await getRandom();
}
}
while (id === 404) {
id = await getRandom();
}
const info = await getInfo(id);
message.delete();
message.channel.send({
embed: client.embed(`[${id}] ${info.title}`, '', [], {
image: info.img,
color: [150, 168, 199],
url: `http://xkcd.com/${id}`
}).setFooter(info.alt)
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "xkcd",
category: "Fun",
description: "Fetches random or specific XKCD comics.",
usage: "xkcd [latest|<id>]"
};

View File

@ -0,0 +1,77 @@
const { RichEmbed } = require('discord.js');
const rgbToHSL = (red, green, blue) => {
let r = red / 255;
let g = green / 255;
let b = blue / 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0;
} else {
let d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
h = Math.round(h * 360);
s = Math.round(s * 100);
l = Math.round(l * 100);
return { hue: h, saturation: s, lightness: l };
};
const resolveColor = input => {
if (input.startsWith('#')) input = input.substr(1);
if (input.length === 3) input = input.split('').map(c => c + c).join('');
let hex = input;
let [red, green, blue] = [hex.substr(0, 2), hex.substr(2, 2), hex.substr(4, 2)]
.map(value => parseInt(value, 16));
let { hue, saturation, lightness } = rgbToHSL(red, green, blue);
return { hex, red, green, blue, hue, saturation, lightness };
};
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
(await message.reply('please provide a color.')).delete(5000);
return message.delete();
}
if (!/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(args[0])) {
(await message.reply('the color must be in the format of `#RRGGBB` or `#RGB`.')).delete(5000);
return message.delete();
}
let color = resolveColor(args[0]);
message.delete();
message.channel.send({
embed: new RichEmbed()
.setDescription(`Hex: \`#${color.hex}\`\nRGB: \`${color.red}, ${color.green}, ${color.blue}\`\nHSL: \`${color.hue}, ${color.saturation}, ${color.lightness}\``)
.setImage(`http://placehold.it/500/${color.hex}/${color.hex}`)
.setColor(`${color.hex}`)
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["hex","rgb"],
permLevel: "User"
};
exports.help = {
name: "color",
category: "Information",
description: "Shows some information and a preview of a specified hex color.",
usage: "color <hex>"
};

View File

@ -0,0 +1,63 @@
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.reply("please provide an emoji to gather info on!")).delete(5000);
}
if (args[0].charCodeAt(0) >= 55296) {
message.delete();
return (await message.channel.send({
embed: client.embed(args[0], 'Built-in **Discord** emoji.')
})).delete(15000);
}
const match = args[0].match(/<:[a-zA-Z0-9_-]+:(\d{18})>/);
if (!match || !match[1]) {
message.delete();
return (await message.reply("please provide a valid emoji!")).delete(5000);
}
const emoji = client.emojis.get(match[1]);
if (!emoji) {
message.delete();
return (await message.reply("that emoji could not be identified.")).delete(5000);
}
message.delete();
(await message.channel.send({
embed: client.embed('', '', [
{
name: 'Name',
value: emoji.name
},
{
name: 'From Guild',
value: emoji.guild.name
},
{
name: 'ID',
value: emoji.id
},
{
name: 'Download URL',
value: emoji.url
}
], { thumbnail: emoji.url })
})).delete(15000);
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "emoji",
category: "Information",
description: "Provides some information on a specified emoticon.",
usage: "emoji <emoji>"
};

View File

@ -0,0 +1,66 @@
const { RichEmbed } = require('discord.js');
exports.run = (client, message, args, level) => {
message.delete();
if (!args[0]) {
const myCommands = message.guild ? client.commands.filter(cmd => client.levelCache[cmd.conf.permLevel] <= level) : client.commands.filter(cmd => client.levelCache[cmd.conf.permLevel] <= level && cmd.conf.guildOnly !== true);
const commandNames = myCommands.keyArray();
const longest = commandNames.reduce((long, str) => Math.max(long, str.length), 0);
let currentCategory = "";
let output = `= Command List =\n\n[Use ${message.settings.prefix}help <commandname> for details]\n`;
const sorted = myCommands.array().sort((p, c) => p.help.category > c.help.category ? 1 : p.help.name > c.help.name && p.help.category === c.help.category ? 1 : -1 );
sorted.forEach( c => {
const cat = c.help.category.toProperCase();
if (currentCategory !== cat) {
output += `\u200b\n== ${cat} ==\n`;
currentCategory = cat;
}
output += `${message.settings.prefix}${c.help.name}${" ".repeat(longest - c.help.name.length)} :: ${c.help.description}\n`;
});
message.channel.send(output, {code: "asciidoc", split: { char: "\u200b" }});
} else {
let command = args[0];
if (client.commands.has(command)) {
command = client.commands.get(command);
if (level < client.levelCache[command.conf.permLevel]) return;
message.channel.send({embed: client.embed(`Command information: "${command.help.name}"`, command.help.description, [
{
name: 'Category',
value: command.help.category !== "" ? command.help.category : "none",
inline: true
},
{
name: 'Usage',
value: command.help.usage,
inline: true
},
{
name: 'Aliases',
value: command.conf.aliases.join(", ") !== "" ? command.conf.aliases.join(", ") : "none",
inline: true
}
],
{
author: message.author.tag,
authorIcon: message.author.avatarURL
})
});
}
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["h", "halp"],
permLevel: "User"
};
exports.help = {
name: "help",
category: "Information",
description: "Displays all the available commands for your permission level.",
usage: "help [command]"
};

View File

@ -0,0 +1,60 @@
const got = require('got');
const cheerio = require('cheerio');
exports.run = async (client, message, args, level) => {
if (args.length < 1) {
message.delete();
return (await message.reply("please provide the username of a player.")).delete(5000);
}
const username = args[0];
const uuid = await getUUID(username);
if (!uuid) {
message.delete();
return (await message.reply("that player could not be found.")).delete(5000);
}
message.delete();
return message.channel.send({
embed: client.embed('', '', [
{
name: 'Username',
value: username
},
{
name: 'UUID',
value: `\`${uuid}\``
},
{
name: 'Skin',
value: `[Download](https://crafatar.com/skins/${uuid}.png)`
}
], { thumbnail: `https://crafatar.com/avatars/${uuid}.png?size=250&overlay=true` })
});
};
async function getUUID(username) {
const res = await got(`https://mcuuid.net/?q=${username}`);
const $ = cheerio.load(res.body);
const input = $('input')[1];
if (!input) {
return;
}
return input.attribs['value'];
}
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["minecraftuser"],
permLevel: "User"
};
exports.help = {
name: "mcinfo",
category: "Information",
description: "Shows information about a Minecraft player.",
usage: "mcinfo <username>"
};

View File

@ -0,0 +1,18 @@
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
const msg = await message.channel.send("Ping?");
msg.edit(`Pong! Latency is ${msg.createdTimestamp - message.createdTimestamp}ms. API Latency is ${Math.round(client.ping)}ms`);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "ping",
category: "Information",
description: "It like... Pings. Then Pongs. And it's not Ping Pong.",
usage: "ping"
};

View File

@ -0,0 +1,82 @@
const dateFormat = require('dateformat');
const now = new Date();
dateFormat(now, 'dddd, mmmm dS, yyyy, h:MM:ss TT');
exports.run = async (client, message, args, level) => {
message.delete();
const millis = new Date().getTime() - message.guild.createdAt.getTime();
const days = millis / 1000 / 60 / 60 / 24;
const owner = message.guild.owner.user || {};
const verificationLevels = ['None', 'Low', 'Medium', 'Insane', 'Extreme'];
let embed = client.embed(
`${message.guild.name}`,
'***This message will dissappear in 60 seconds.***',
[
{
name: 'Created On',
value: `${dateFormat(message.guild.createdAt)}`,
},
{
name: 'Days Since Creation',
value: `${days.toFixed(0)}`,
},
{
name: 'Region',
value: `${message.guild.region}`,
},
{
name: 'Member Count',
value: `**${message.guild.members.filter(m => m.presence.status !== 'offline' && !m.bot).size}** online / **${message.guild.members.filter(m => !m.bot).size}** total`,
},
{
name: 'Owner',
value: `${owner.username || 'None'}`,
},
{
name: 'Text Channels',
value: `${message.guild.channels.filter(m => m.type === 'text').size}`,
},
{
name: 'Voice Channels',
value: `${message.guild.channels.filter(m => m.type === 'voice').size}`,
},
{
name: 'Verification Level',
value: `${verificationLevels[message.guild.verificationLevel]}`,
},
{
name: 'Roles',
value: `${message.guild.roles.size}`,
},
],
{
inline: true,
footer: `Guild ID: ${message.guild.id}`
}
);
if (message.guild.iconURL != null) {
embed.setThumbnail(`${message.guild.iconURL}`);
}
(await message.channel.send({ embed })).delete(60000);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ["server","guildinfo","guild"],
permLevel: "User"
};
exports.help = {
name: "serverinfo",
category: "Information",
description: "Returns the current guild's information.",
usage: "serverinfo"
};

View File

@ -0,0 +1,29 @@
const { version } = require("discord.js");
const moment = require("moment");
require("moment-duration-format");
exports.run = (client, message, args, level) => {
const duration = moment.duration(client.uptime).format(" D [days], H [hrs], m [mins], s [secs]");
message.channel.send(`= STATISTICS =
Mem Usage :: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB
Uptime :: ${duration}
Users :: ${client.users.size.toLocaleString()}
Servers :: ${client.guilds.size.toLocaleString()}
Channels :: ${client.channels.size.toLocaleString()}
Discord.js :: v${version}
Node :: ${process.version}`, {code: "asciidoc"});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "stats",
category: "Information",
description: "Gives some useful bot statistics.",
usage: "stats"
};

View File

@ -0,0 +1,91 @@
const dateFormat = require('dateformat');
dateFormat('dddd, mmmm dS, yyyy, h:MM:ss TT');
exports.run = async (client, message, args, level) => {
let user = message.mentions.users.first() || message.author;
let member = message.guild.member(user);
if (!member) {
message.delete();
return (await message.reply("that member could not be found!")).delete(5000);
}
const millisCreated = new Date().getTime() - user.createdAt.getTime();
const daysCreated = millisCreated / 1000 / 60 / 60 / 24;
const millisJoined = new Date().getTime() - member.joinedAt.getTime();
const daysJoined = millisJoined / 1000 / 60 / 60 / 24;
let roles = member.roles.array().slice(1).sort((a, b) => a.comparePositionTo(b)).reverse().map(role => role.name);
if (roles.length < 1) roles = ['None'];
let game = (user.presence.game && user.presence.game && user.presence.game.name) || 'Not playing a game.'
await message.delete();
message.channel.send({ embed: client.embed(
`User info: ${member.user.tag}`,
"**This message will be deleted in 60 seconds.** :bomb:",
[
{
name: "= Status =",
value: `${user.presence.status[0].toUpperCase() + user.presence.status.slice(1)}`
},
{
name: "= Game =",
value: `${(user.presence.game && user.presence.game && user.presence.game.name) || 'Not playing a game.'}`
},
{
name: "= Nickname =",
value: member.nickname || "None"
},
{
name: "= Unique User ID (UUID) =",
value: member.user.id !== null ? member.user.id : "Unknown"
},
{
name: "= Account Creation Date =",
value: dateFormat(user.createdAt) !== null ? dateFormat(user.createdAt) : "Unknown"
},
{
name: "= Days Since Creation =",
value: daysCreated.toFixed(0) !== null ? daysCreated.toFixed(0) : "Unknown"
},
{
name: "= Server Join Date =",
value: member.joinedAt !== null ? member.joinedAt : "Unknown"
},
{
name: "= Days Since Joining =",
value: daysJoined.toFixed(0) !== null ? daysJoined.toFixed(0) : "Unknown"
},
{
name: "= Last Spoken =",
value: member.lastMessage !== null ? `**${member.lastMessage.createdAt}**, at server **${member.lastMessage.guild}**` : "Unknown"
},
{
name: "= User Roles =",
value: `\`${roles.join('\`, \`')}\``
}
],
{
inline: true,
footer: `Sent by ${message.author.tag}`,
footerIcon: message.author.avatarURL
})
})
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["user", "uinfo"],
permLevel: "User"
};
exports.help = {
name: "userinfo",
category: "Information",
description: "Gather information about a user, and returns the gathered stuff into an embed.",
usage: "userinfo [ping:self]"
};

View File

@ -0,0 +1,57 @@
function hasRole(member, roleName) {
return member.roles.map(role => role.name.toLowerCase()).indexOf(roleName.toLowerCase()) > -1;
}
exports.run = async (client, message, args, level) => {
if (!message.guild || !message.guild.members) {
message.delete();
return (await message.reply("You must run this command from within a server.")).delete(5000);
}
let members = message.guild.members.array().sort((a, b) => a.user.username.localeCompare(b.user.username));
if (args.length > 0) {
members = members.filter(member => hasRole(member, args[0]));
}
if (members.length < 1) {
message.delete();
return (await message.reply("No members could be found.")).delete(5000);
}
message.delete();
let users = members.map(m => `${m.user}${(m.user.bot ? ' [BOT]' : '')}`);
const body = users.join('\n');
if (body.length < 2000) {
(await message.channel.send({
embed: client.embed('', body)
})).delete(60000);
} else {
let raw = members.map(m => `${m.user.username}${m.user.bot ? ' [BOT]' : ''}`).join('\n');
const { url } = await client.textUpload(raw);
let trimmed = body.substr(0, 1500);
trimmed = trimmed.slice(0, trimmed.lastIndexOf('\n'));
message.channel.send({
embed: client.embed('', trimmed, [{ name: 'Full list', value: url }])
});
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "users",
category: "Information",
description: "Lists all users in the current server. This command may be long, only use it in places you know you can use it.",
usage: "users"
};

View File

@ -0,0 +1,41 @@
const Compliments = [
"You're my kind of weird.", "I love you more than tea.", "You're a Michelin Star human.",
"You're a chocolate Hobnob in a jar of Rich Teas.", "You've really nailed being a human. Good job.",
"You are wonderfully odd.", "You are the last minute goal in the cup final.",
"You're tougher than my wifi password.", "You are a walking high-five.", "Youd win the Saturday Kitchen omelette challenge.",
"You could be a part-time model.", "I like your face.", "You're wise and all knowing, like a mighty owl.",
"You're an awesome friend.", "You're a gift to those around you.", "You're a smart cookie.", "You are awesome!",
"You have impeccable manners.", "I like your style.", "You have the best laugh.", "I appreciate you.",
"You are the most perfect you there is.", "You are enough.", "You're strong.", "Your perspective is refreshing.",
"I'm grateful to know you.", "You light up the room.", "You deserve a hug right now.", "You should be proud of yourself.",
"You're more helpful than you realize.", "You have a great sense of humor.", "You've got an awesome sense of humor!",
"You are really courageous.", "Your kindness is a balm to all who encounter it.", "You're all that and a super-size bag of chips.",
"On a scale from 1 to 10, you're an 11.", "You are strong.", "You're even more beautiful on the inside than you are on the outside.",
"You have the courage of your convictions.", "I'm inspired by you.", "You're like a ray of sunshine on a really dreary day.",
"You are making a difference.", "Thank you for being there for me.", "You bring out the best in other people.", "You make the rating scale skyrocket through the roof!"
];
exports.run = async (client, message, args, level) => {
let target = message.author;
if(args[0]) {
target = args[0];
}
message.delete();
message.channel.send(`Hey, ${target}! Wanna know something? ${Compliments[Math.floor(Math.random() * Compliments.length)]}`);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "compliment",
category: "Jokes",
description: "Aww, you're so nice and sweet~! <3",
usage: "compliment [target]"
};

25
commands/Jokes/dadjoke.js Normal file
View File

@ -0,0 +1,25 @@
exports.run = async (client, message, args, level) => {
const { joke } = await client.fetchURL("https://icanhazdadjoke.com/", { headers: { Accept: "application/json" } });
return message.channel.send({ embed: client.embed(`Funny Dad Joke from ${client.user.username}`, joke,
{
author: message.author.tag,
authorIcon: message.author.avatarURL,
thumbnail: "https://i.imgur.com/IxosIBh.png"
})
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "dadjoke",
category: "Jokes",
description: `Hey there kiddo, I'm dad!`,
usage: "dadjoke"
};

28
commands/Jokes/insult.js Normal file
View File

@ -0,0 +1,28 @@
const start = ["a lazy", "a stupid", "an insecure", "an idiotic", "a slimy", "a slutty", "a smelly", "a pompous", "a communist", "a dicknose", "a pie-eating", "a racist", "an elitist", "a white trash", "a drug-loving", "a butterface", "a tone deaf", "a ugly", "a creepy", "an artless", "a bawdy", "a beslubbering", "a bootless", "a churlish", "a cockered", "a clouted", "a craven", "a currish", "a dankish", "a dissembling", "a droning", "an errant", "a fawning", "a fobbing", "a frothy", "a gleeking", "a goatfish", "a gorbellied", "an impertinent", "an infectious", "a jarring", "a loggerheaded", "a lumpish", "a mammering", "a mangled", "a mewling", "a paunchy", "a pribbling", "a puking", "a puny", "a qualling", "a rank", "a reeky", "a roguish", "a ruttish", "a saucy", "a spleeny", "a spongy", "a surly", "a tottering", "an unmuzzled", "a vain", "a venomed", "a villainous", "a warped", "a wayward", "a weedy", "a yeasty", "a lilly-livered", "a rotten", "a stinky", "a lame", "a dim-witted", "a funky", "a crusty", "a steamy", "a drizzly", "a grizzly", "a squirty", "an uptight", "a hairy", "a husky", "an arrogant", "a nippy", "a chunky", "a smelly", "a drooling", "a crusty", "a decrepic", "a stupid", "a moronic", "a greasy", "a poxy", "an ugly", "a smelly", "a putrid", "a shitty", "an assinine", "a sickening"]; // eslint-disable-line
const middle = ["douche", "ass", "turd", "rectum", "butt", "shit", "crotch", "turd", "prick", "taint", "boner", "shart", "nut", "sphincter", "base-court", "bat-fowling", "beef-witted", "beetle-headed", "boil-brained", "clapper-clawed", "clay-brained", "common-kissing", "crook-pated", "dismal-dreaming", "dizzy-eyed", "doghearted", "dread-bolted", "earth-vexing", "elf-skinned", "fat-kidneyed", "fen-sucked", "flap-mouthed", "fly-bitten", "folly-fallen", "fool-born", "full-gorged", "guts-gripping", "half-faced", "hasty-witted", "hedge-born", "hell-hated", "idle-headed", "ill-breeding", "ill-nurtured", "knotty-pated", "milk-livered", "motly-minded", "onion-eyed", "plume-plucked", "pottle-deep", "pox-marked", "reeling-ripe", "rough-hewn", "rude-growing", "rump-red", "shard-borne", "sheep-biting", "spur-galled", "swag-bellied", "tardy-gaited", "tickle-brained", "toad-spotted", "unchin-snouted", "weather-bitten", "hiney", "poop", "toot", "wedgie", "stool", "fudge", "bum", "potty", "dookie", "pudding", "sphincter", "booger", "feces", "snot", "crust", "badonk-a", "crud", "sludge", "tool", "shit-kicking", "monkey-licking", "crotch-sniffing", "donkey-spanking", "fashion-illiterate", "worm-ridden", "lathered-up", "pasty-waisted", "snot-flicking", "fart-eating"]; // eslint-disable-line
const end = ["pilot", "canoe", "captain", "pirate", "hammer", "knob", "box", "jockey", "nazi", "waffle", "goblin", "blossom", "biscuit", "clown", "socket", "monster", "hound", "dragon", "balloon", "apple-john", "baggage", "barnacle", "bladder", "boar-pig", "bugbear", "bum-bailey", "canker-blossom", "clack-dish", "clotpole", "coxcomb", "codpiece", "death-token", "dewberry", "flap-dragon", "flax-wench", "flirt-gill", "foot-licker", "fustilarian", "giglet", "gudgeon", "haggard", "harpy", "hedge-pig", "horn-beast", "hugger-mugger", "joithead", "lewdster", "lout", "maggot-pie", "malt-worm", "mammet", "measle", "minnow", "miscreant", "moldwarp", "mumble-news", "nut-hook", "pigeon-egg", "pignut", "puttock", "pumpion", "ratsbane", "scut", "skinsmate", "strumpet", "varlot", "vassal", "whey-face", "wagtail", "squeegee", "turtle", "cabbage", "bomb", "sniffer", "binkie", "stump", "nugget", "whistle", "twig", "knuckle", "burger", "hotdog", "loaf", "freckle", "soldier", "kernal", "shingle", "warrior", "hemorrhoid", "fuckface", "asshole", "scumbucket", "toerag", "hackwack", "imbecile", "stunodigan", "maggot", "hipster", "gargabe", "jerkstore"]; // eslint-disable-line
exports.run = async (client, message, args, level) => {
let target = message.author;
if(args[0]) {
target = args[0];
}
message.delete();
message.channel.send(`Hey ${target}, you're nothing but ${start[Math.floor(Math.random() * start.length)]} ${middle[Math.floor(Math.random() * middle.length)]} ${end[Math.floor(Math.random() * end.length)]}.`);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "insult",
category: "Jokes",
description: "Someone a meanie? Shoot them down with me!",
usage: "insult [usertag]"
};

View File

@ -0,0 +1,36 @@
exports.run = async (client, message, args, level) => {
const data = await fetchURL("https://uselessfacts.jsph.pl/random.json");
return message.channel.send({ embed: client.embed(`Fun fact!`, data.text, [
{
name: "Source",
value: data.source,
inline: true
},
{
name: "Source URL",
value: data.source_url,
inline: true
}
],
{
author: message.author.tag,
authorIcon: message.author.avatarURL,
thumbnail: "https://i.imgur.com/IxosIBh.png"
})
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "randomfact",
category: "Jokes",
description: "Knowledge is power!",
usage: "randomfact"
};

View File

@ -0,0 +1,33 @@
exports.run = async (client, message, args, level) => {
const user = message.mentions.users.first() || client.users.get("username", args[0]).id;
parseUser(message, user);
const modlog = client.channels.find('name', config.modLogChannel);
const caseNum = await caseNumber(client, modlog);
if (!modlog) return message.reply('I cannot find a valid modlog channel.');
if (message.mentions.users.size < 1) return message.reply('You must mention someone to ban them.').catch(console.error);
// message.guild.ban(user, 2);
const reason = args.splice(1, args.length).join(' ') || `Awaiting moderator's input. Use ${settings.prefix}reason ${caseNum} <reason>.`;
const embed = new RichEmbed()
.setColor(0x2f3136)
.setTimestamp()
.setDescription(`**Action:** Ban\n**Target:** ${user.tag}\n**Moderator:** ${message.author.tag}\n**Reason:** ${reason}`)
.setFooter(`Case ${caseNum}`);
return client.channels.get(modlog.id).send({embed});
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "ban",
category: "Moderation",
description: "You've been hit by Banny Billy!",
usage: "ban <user> <reason>"
};

View File

@ -0,0 +1,34 @@
exports.run = async (client, message, args, level) => {
const user = message.mentions.users.first();
parseUser(message, user);
const modlog = client.channels.find('name', config.modLogChannel);
const caseNum = await caseNumber(client, modlog);
if (!modlog) return message.reply('I cannot find a mod-log channel');
if (message.mentions.users.size < 1) return message.reply('You must mention someone to kick them.').catch(console.error);
// message.guild.member(user).kick();
const reason = args.splice(1, args.length).join(' ') || `Awaiting moderator's input. Use ${settings.prefix}reason ${caseNum} <reason>.`;
const embed = new RichEmbed()
.setColor(0x2f3136)
.setTimestamp()
.setDescription(`**Action:** Kick\n**Target:** ${user.tag}\n**Moderator:** ${message.author.tag}\n**Reason:** ${reason}`)
.setFooter(`Case ${caseNum}`);
return client.channels.get(modlog.id).send({embed});
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Moderator"
};
exports.help = {
name: "kick",
category: "Moderation",
description: "Boot someone out!",
usage: "kick <user> <reason>"
};

View File

@ -0,0 +1,51 @@
const ms = require('ms');
exports.run = async (client, message, args, level) => {
if (!client.lockit) client.lockit = [];
const time = args.join(' ');
const validUnlocks = ['release', 'unlock'];
if (!time) return message.reply('you must set a duration for the lockdown in either hours, minutes or seconds.');
if (validUnlocks.includes(time)) {
message.channel.overwritePermissions(message.guild.id, {
SEND_MESSAGES: null
}).then(() => {
message.channel.send('Lockdown has been lifted.');
clearTimeout(client.lockit[message.channel.id]);
delete client.lockit[message.channel.id];
}).catch(error => {
console.log(error);
});
} else {
message.channel.overwritePermissions(message.guild.id, {
SEND_MESSAGES: false
}).then(() => {
message.channel.send(`Channel has been locked down for ${ms(ms(time), { long:true })}.`).then(() => {
client.lockit[message.channel.id] = setTimeout(() => {
message.channel.overwritePermissions(message.guild.id, {
SEND_MESSAGES: null
}).then(message.channel.send('Lockdown has been lifted.')).catch(console.error);
delete client.lockit[message.channel.id];
}, ms(time));
}).catch(error => {
console.log(error);
});
});
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["ld"],
permLevel: "Administrator"
};
exports.help = {
name: "lockdown",
category: "Moderation",
description: "People being a pest somewhere? Lock it down!",
usage: "lockdown [duration]"
};

View File

@ -0,0 +1,43 @@
exports.run = async (client, message, args, level) => {
const user = message.mentions.users.first();
parseUser(message, user);
const modlog = client.channels.find('name', 'mod-log');
const caseNum = await client.caseNumber(client, modlog);
const muteRole = client.guilds.get(message.guild.id).roles.find('name', 'muted');
if (!modlog) return message.reply('I cannot find a mod-log channel').catch(console.error);
if (!muteRole) return message.reply('I cannot find a mute role').catch(console.error);
if (message.mentions.users.size < 1) return message.reply('You must mention someone to mute them.').catch(console.error);
const reason = args.splice(1, args.length).join(' ') || `Awaiting moderator's input. Use ${settings.prefix}reason ${caseNum} <reason>.`;
const embed = new RichEmbed()
.setColor(0x00AE86)
.setTimestamp()
.setDescription(`**Action:** Un/mute\n**Target:** ${user.tag}\n**Moderator:** ${message.author.tag}\n**Reason:** ${reason}`)
.setFooter(`Case ${caseNum}`);
if (!message.guild.member(client.user).hasPermission('MANAGE_ROLES_OR_PERMISSIONS')) return message.reply('I do not have the correct permissions.').catch(console.error);
if (message.guild.member(user).roles.has(muteRole.id)) {
message.guild.member(user).removeRole(muteRole).then(() => {
client.channels.get(modlog.id).send({embed}).catch(console.error);
});
} else {
message.guild.member(user).addRole(muteRole).then(() => {
client.channels.get(modlog.id).send({embed}).catch(console.error);
});
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["unmute"],
permLevel: "Moderator"
};
exports.help = {
name: "mute",
category: "Moderation",
description: "Mute or unmute a specified user.",
usage: "mute <user> <reason>"
};

View File

@ -0,0 +1,29 @@
exports.run = async (client, message, args, level) => {
let filter = true
let count = parseInt(args[0]) || 1;
let responsePlace = message.channel;
await message.delete();
const messages = await message.channel.fetchMessages({ limit: Math.min(count, 100), before: message.id });
const deleted = messages.size;
message.channel.bulkDelete(messages.size);
(await responsePlace.send(`:white_check_mark: **Cleared \`${messages.size}\` message${deleted === 1 ? '' : 's'}.**`)).delete(2000);
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "purge",
category: "System",
description: "Get those messages out of here, will ya?",
usage: "purge <amount>"
};

View File

@ -0,0 +1,46 @@
async function embedSan(embed) {
embed.message ? delete embed.message : null;
embed.footer ? delete embed.footer.embed : null;
embed.provider ? delete embed.provider.embed : null;
embed.thumbnail ? delete embed.thumbnail.embed : null;
embed.image ? delete embed.image.embed : null;
embed.author ? delete embed.author.embed : null;
embed.fields ? embed.fields.forEach(f => {delete f.embed;}) : null;
return embed;
}
exports.run = async (client, message, args, level) => {
const modlog = client.channels.find('name', config.modLogChannel);
const caseNumber = args.shift();
const newReason = args.join(' ');
await modlog.fetchMessages({limit:100}).then((messages) => {
const caseLog = messages.filter(m => m.author.id === client.user.id &&
m.embeds[0] &&
m.embeds[0].type === 'rich' &&
m.embeds[0].footer &&
m.embeds[0].footer.text.startsWith('Case') &&
m.embeds[0].footer.text === `Case ${caseNumber}`
).first();
modlog.fetchMessage(caseLog.id).then(logMsg => {
const embed = logMsg.embeds[0];
embedSan(embed);
embed.description = embed.description.replace(`Awaiting moderator's input. Use ${settings.prefix}reason ${caseNumber} <reason>.`, newReason);
logMsg.edit({embed});
});
});
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "reason",
category: "Moderation",
description: "Modify the reason for a cited punishment.",
usage: "reason <caseID> <reason>"
}

View File

@ -0,0 +1,29 @@
exports.run = async (client, message, args, level) => {
message.delete();
let rateLimit = +args[0];
if(typeof rateLimit !== "number") return (await message.reply("you need to provide a valid time limit!")).delete(5000);
if(rateLimit < 0 || rateLimit > 21600) return (await message.reply(`I can't complete this request because the Discord API is only able to handle times from 0 seconds to 21600 seconds (6 hours), and you requested a time of ${rateLimit} seconds.`)).delete(5000);
message.channel.setRateLimitPerUser(rateLimit, `command handle from ${message.author.tag}.`);
if(rateLimit === 0) {
(await message.channel.send(`${message.author} has removed ${message.channel}'s slowmode.`)).delete(5000);
} else {
(await message.channel.send(`${message.author} has set ${message.channel}'s slowmode to ${rateLimit} seconds.`)).delete(5000);
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["sm","setslowmode"],
permLevel: "Administrator"
};
exports.help = {
name: "slowmode",
category: "Utility",
description: "Set the slowmode for a channel. If no arguments are provided, this command will disable it.",
emoji: "sparkle",
usage: "slowmode <time>"
};

View File

@ -0,0 +1,25 @@
exports.run = async (client, message, args, level) => {
const reason = args.slice(1).join(' ');
client.unbanReason = reason;
client.unbanAuth = message.author;
const user = args[0];
const modlog = client.channels.find('name', config.modLogChannel);
if (!modlog) return message.reply('I could not find a valid modlog channel.');
if (reason.length < 1) return message.reply('You must supply a reason for the unban.');
if (!user) return message.reply('You must supply a User Resolvable, such as a user id.').catch(console.error);
message.guild.unban(user);
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "unban",
category: "Moderation",
description: "Unbans a specified user.",
usage: "unban <user> <reason>"
};

View File

@ -0,0 +1,30 @@
exports.run = async (client, message, args, level) => {
const user = message.mentions.users.first();
client.parseUser(message, user);
const modlog = client.channels.find('name', config.modLogChannel);
const caseNum = await caseNumber(client, modlog);
if (!modlog) return message.reply('I cannot find a modlog channel.');
if (message.mentions.users.size < 1) return message.reply('You must mention someone to warn them.').catch(console.error);
const reason = args.splice(1, args.length).join(' ') || `Awaiting moderator's input. Use ${message.settings.prefix}reason ${caseNum} <reason>.`;
const embed = new RichEmbed()
.setColor(0x2f3136)
.setTimestamp()
.setDescription(`**Action:** Warning\n**Target:** ${user.tag}\n**Moderator:** ${message.author.tag}\n**Reason:** ${reason}`)
.setFooter(`Case ${caseNum}`);
return client.channels.get(modlog.id).send({embed});
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["w","citation"],
permLevel: "Moderator"
};
exports.help = {
name: "warn",
category: "Moderation",
description: "Issues a citation to a specified user.",
usage: "warn <user> <reason>"
};

73
commands/System/conf.js Normal file
View File

@ -0,0 +1,73 @@
const { inspect } = require("util");
exports.run = async (client, message, [action, key, ...value], level) => {
const defaults = client.settings.get("default");
if (action === "add") {
if (!key) return message.reply("please specify a key to add.");
if (defaults[key]) return message.reply("this key already exists in the default settings!");
if (value.length < 1) return message.reply("please specify a value.");
defaults[key] = value.join(" ");
client.settings.set("default", defaults);
message.reply(`${key} successfully added with the value of ${value.join(" ")}.`);
} else
if (action === "edit") {
if (!key) return message.reply("please specify a key to edit.");
if (!defaults[key]) return message.reply("this key does not exist in the settings!");
if (value.length < 1) return message.reply("please specify a new value!");
defaults[key] = value.join(" ");
client.settings.set("default", defaults);
message.reply(`${key} successfully edited to ${value.join(" ")}.`);
} else
if (action === "del") {
if (!key) return message.reply("please specify a key to delete.");
if (!defaults[key]) return message.reply("this key does not exist in the settings!");
const response = await client.awaitReply(message, `Are you sure you want to permanently delete ${key} from all guilds? This **CANNOT** be undone.`);
if (["y", "yes"].includes(response)) {
delete defaults[key];
client.settings.set("default", defaults);
for (const [guildid, conf] of client.settings.filter((setting, id) => setting[key] && id !== "default")) {
delete conf[key];
client.settings.set(guildid, conf);
}
message.reply(`${key} was successfully deleted.`);
} else
if (["n","no","cancel"].includes(response)) {
message.reply("action cancelled.");
}
} else
if (action === "get") {
if (!key) return message.reply("please specify a key to view.");
if (!defaults[key]) return message.reply("this key does not exist in the settings!");
message.reply(`the value of ${key} is currently ${defaults[key]}.`);
} else {
await message.channel.send(`***__Bot Default Settings__***\n\`\`\`json\n${inspect(defaults)}\n\`\`\``);
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["defaults"],
permLevel: "Bot Admin"
};
exports.help = {
name: "conf",
category: "System",
description: "Modify the default configuration for all guilds.",
usage: "conf <view/get/edit> <key> <value>"
};

24
commands/System/eval.js Normal file
View File

@ -0,0 +1,24 @@
exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars
const code = args.join(" ");
try {
const evaled = eval(code);
const clean = await client.clean(client, evaled);
message.channel.send(`\`\`\`js\n${clean}\n\`\`\``);
} catch (err) {
message.channel.send(`\`ERROR\` \`\`\`xl\n${await client.clean(client, err)}\n\`\`\``);
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "eval",
category: "System",
description: "Evaluates arbitrary javascript.",
usage: "eval [...code]"
};

63
commands/System/set.js Normal file
View File

@ -0,0 +1,63 @@
exports.run = async (client, message, [action, key, ...value], level) => {
const settings = message.settings;
const defaults = client.settings.get("default");
const overrides = client.settings.get(message.guild.id);
if (!client.settings.has(message.guild.id)) client.settings.set(message.guild.id, {});
if (action === "edit") {
if (!key) return message.reply("please specify a key to edit");
if (!defaults[key]) return message.reply("this key does not exist in the settings");
const joinedValue = value.join(" ");
if (joinedValue.length < 1) return message.reply("please specify a new value.");
if (joinedValue === settings[key]) return message.reply("this setting already has that value!");
if (!client.settings.has(message.guild.id)) client.settings.set(message.guild.id, {});
client.settings.set(message.guild.id, joinedValue, key);
message.reply(`${key} successfully edited to ${joinedValue}`);
} else
if (action === "del" || action === "reset") {
if (!key) return message.reply("please specify a key to reset.");
if (!defaults[key]) return message.reply("this key does not exist in the settings");
if (!overrides[key]) return message.reply("this key does not have an override and is already using defaults.");
const response = await client.awaitReply(message, `Are you sure you want to reset ${key} to the default value?`);
if (["y", "yes"].includes(response.toLowerCase())) {
client.settings.delete(message.guild.id, key);
message.reply(`${key} was successfully reset to default.`);
} else
if (["n","no","cancel"].includes(response)) {
message.reply(`your setting for \`${key}\` remains at \`${settings[key]}\``);
}
} else
if (action === "get") {
if (!key) return message.reply("please specify a key to view.");
if (!defaults[key]) return message.reply("this key does not exist in the settings!");
const isDefault = !overrides[key] ? "\nthis is the default global default value." : "";
message.reply(`the value of ${key} is currently ${settings[key]}${isDefault}.`);
} else {
const array = [];
Object.entries(settings).forEach(([key, value]) => {
array.push(`${key}${" ".repeat(20 - key.length)}:: ${value}`);
});
await message.channel.send(`= Current Guild Settings =\n${array.join("\n")}`, {code: "asciidoc"});
}
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: ["setting", "settings", "conf"],
permLevel: "Administrator"
};
exports.help = {
name: "set",
category: "System",
description: "View or change settings for your server.",
usage: "set <view/get/edit> <key> <value>"
};

View File

@ -0,0 +1,50 @@
exports.run = async (client, message, args, level) => {
message.delete();
client.user.setStatus("idle");
let response = await client.awaitReply(message, "**Are you sure you want to shut me down?** Respond with \`yes\` to proceed.");
message.author.lastMessage.delete();
let msg = client.user.lastMessage;
if(response === "yes") {
await msg.edit(`**Confirmed by ${message.author}.** Shutting down...`);
client.user.setStatus("dnd");
await Promise.all(client.commands.map(cmd =>
client.unloadCommand(cmd)
));
await msg.edit(`Successfully shut down by ${message.author}.`);
client.user.setStatus("invisible");
process.exit(0);
} else if(response === "no") {
await msg.edit(`**Shutdown cancelled by ${message.author}.** This message will be removed in 5 seconds.`).then(msg => {
msg.delete(5000)
});
client.user.setStatus("online");
return;
} else {
await msg.edit(`**${message.author} has provided an invalid response**; shutdown aborted. This message will be deleted in 5 seconds.`).then(msg => {
msg.delete(5000)
});
client.user.setStatus("online");
return;
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "shutdown",
category: "System",
description: "Shuts down the bot.",
usage: "shutdown"
};

52
commands/System/status.js Normal file
View File

@ -0,0 +1,52 @@
const validStatuses = [
{
internal: 'online',
display: 'online',
emoji: ':zap:'
},
{
internal: 'idle',
display: 'idle',
emoji: ':beach_umbrella:'
},
{
internal: 'dnd',
display: 'do-not-disturb',
emoji: ':mute:'
},
{
internal: 'invisible',
display: 'invisible',
emoji: ':ghost:'
}
];
const validStatusRegex = new RegExp(`^(${validStatuses.map(status => status.internal).join('|')})$`);
const validStatusString = validStatuses.map(status => `\`${status.internal}\``).join(', ');
exports.run = async (client, message, args, level) => {
if (args.length < 1 || !validStatusRegex.test(args[0])) {
(await message.reply(`please provide a status to set from this list: ${validStatusString}`)).delete(5000);
}
message.delete();
const status = validStatuses.find(status => status.internal === args[0].toLowerCase());
client.user.setStatus(status.internal);
(await message.channel.send(`${status.emoji} Set status to ${status.display}.`)).delete(5000);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Admin"
};
exports.help = {
name: "status",
category: "System",
description: "Sets the bot's status. Pretty straightforward.",
usage: `status <${validStatuses.map(status => status.internal).join('|')}>`
};

View File

@ -0,0 +1,37 @@
exports.run = async (client, message, args, level) => {
let user;
if (!args[0] || !message.mentions.users.first()) {
user = message.author;
author = message.author
message.delete();
} else {
user = message.mentions.users.first();
author = message.author;
message.delete();
}
if (!user.avatarURL) {
await message.delete();
await message.reply("that user does not have an avatar.");
await client.user.lastMessage.delete(5000);
return;
}
(await message.channel.send(
client.embed(user === message.author ? `Your Avatar` : `${user.username}'s Avatar`, `**Sent by ${author}** | [Download](${user.avatarURL}) | :timer: Deleting in 30 seconds`, [], { image: user.avatarURL })
)).delete(30000)
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "avatar",
category: "Utility",
description: "Gives you the avatar of a user.",
usage: "avatar <user>"
};

View File

@ -0,0 +1,35 @@
const math = require('math-expression-evaluator');
const stripIndents = require('common-tags').stripIndents;
exports.run = async (client, message, args, level) => {
message.delete();
if (!args[0]) {
return (await message.reply("you must provide an equation to be solved!")).delete(5000);
}
const question = args.join(" ");
let answer;
try {
answer = math.eval(question);
} catch (err) {
message.channel.send(`**Invalid math equation:** ${err}`);
}
message.channel.send(client.embed('', stripIndents`**Equation:**\n\`\`\`\n${question}\n\`\`\`**Answer:**\n\`\`\`\n${answer}\n\`\`\``));
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ['calc', 'math'],
permLevel: "User"
};
exports.help = {
name: "calculator",
category: "Utility",
description: "Calculates almost any math equation",
usage: "calculate <equation>"
};

View File

@ -0,0 +1,141 @@
exports.run = async (client, message, args, level) => {
let channel = message.channel;
if(args[0]) {
wantedChannel = args[0].substring("<#".length);
wantedChannel = wantedChannel.substring(wantedChannel.length-1,0);
wantedChannel = message.guild.channels.find(c => c.id === wantedChannel);
} else {
return message.reply("you need to use a valid channel.");
}
if(!args[1]) {
return message.reply("no message ID was found.");
}
if(!args[2]) {
message.reply("no raffle emote is present.");
return;
}
let limit = 1;
if(args[3]) {
limit = args[3];
}
let safeEmote = args[2].replace(/\:/g, '');
message.delete();
channel.startTyping();
wantedChannel.fetchMessage(args[1])
.then(function(wantedMessage) {
let out = [];
channel.stopTyping(true);
let reactions = wantedMessage.reactions.array();
let wantedReaction;
for(let ridx in reactions) {
let r = reactions[ridx];
let emote = r.emoji;
if(emote.name === safeEmote) {
wantedReaction = r;
break;
}
}
wantedReaction.fetchUsers()
.then(function(usersCollection) {
let users = usersCollection.array();
// console.log(users);
let exclude = [];
let allowedMembers = [];
wantedMessage.guild.fetchMembers()
.then(function(guildMembersCollectionISuppose) {
let eout = [];
for(let uidx in users) {
let skip = false;
let u = users[uidx];
let m;
let guildMembers = guildMembersCollectionISuppose.members.array();
for(let gmidx in guildMembers) {
let guildMember = guildMembers[gmidx];
let guildUser = guildMember.user;
if(guildUser.id === u.id) {
m = guildMember;
break;
}
}
if(typeof m === "undefined") {
client.logger.log(`[GR] ${u.tag} doesn't exist; they probably aren't in the server.`);
continue;
}
if(m.roles.has(617444320487669828)) {
m.removeRole(617444320487669828);
exclude.push(m);
eout.push(`${u.tag} -- *previously Staff Watch*`);
skip = true;
}
else if(m.roles.has(388397130525573121)) {
exclude.push(m);
eout.push(`${u.tag} -- *muted*`);
skip = true;
}
for(let kdjfs in 489143932207366144) {
if(m.roles.has(489143932207366144[kdjfs])) {
exclude.push(m);
eout.push(`${u.tag} -- *is considered a staff member*`);
skip = true;
}
}
if(!skip) {
allowedMembers.push(m);
}
}
exclude = [...new Set(exclude)];
let chosen = [];
let out = [];
for(let i = 0; i < limit; i++) {
let wants = allowedMembers[Math.floor(Math.random() * allowedMembers.length)];
while(chosen.indexOf(wants) != -1) {
wants = allowedMembers[Math.floor(Math.random() * allowedMembers.length)];
}
chosen.push(wants);
}
for(cidx in chosen) {
let theChose = chosen[cidx];
out.push(`${theChose.user.tag} ${args[4] && args[4] === "-noping" ? "" : `(${theChose.user})`}`);
}
message.channel.send(`**I've selected these people:**\n${out.join("\n")}${eout.length < 1 ? '' : `\n\n**Excluded from drawing:**\n${eout.join("\n")}`}`);
})
})
})
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Administrator"
};
exports.help = {
name: "getrandom",
category: "Utility",
description: "Gets a certain amount of users who reacted to a specific message with a specified emoji, and returns what it got in your current channel.",
usage: "getrandom <channel> <msgid> <amount> <emoji>"
};

View File

@ -0,0 +1,89 @@
const got = require('got');
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.reply('you must specify a repository or search term!');
message.delete();
return;
}
const input = args.join(' ');
if (input.indexOf('/') !== -1) {
let repo = safeRepo(input);
let msg = await message.channel.send(`:arrows_counterclockwise: Loading info for '${repo}'...`);
const res = await got(`https://api.github.com/repos/${repo}`, { json: true });
const json = res.body;
if (json.message === 'Not Found') {
msg.edit('That repository could not be found!');
}
message.delete();
msg.edit({
embed: client.embed('', getInfo(json))
});
} else {
let msg = await message.channel.send(`:arrows_counterclockwise: Searching for '${input}'...`);
const res = await got(`https://api.github.com/search/repositories?q=${args.join('+')}`, { json: true });
const json = res.body;
if (json.total_count < 1) msg.edit(`No results found for '${args.join(' ')}'`);
message.delete();
msg.edit(':white_check_mark: Top 3 results:');
json.items.slice(0, 3).forEach(item => {
message.channel.send({
embed: client.embed('', getInfo(item))
});
});
}
};
function safeRepo(input) {
if (input.indexOf('/') === -1) {
return;
}
let user = input.substr(0, input.indexOf('/'));
input = input.substr(input.indexOf('/') + 1);
let repo = input.indexOf('/') === -1 ? input : input.substr(0, input.indexOf('/'));
return `${user}/${repo}`;
}
function getInfo(json) {
return `**${json.full_name}**
\t**Description:** _${json.description || 'None provided'}_
\t**Owner:** [${json.owner.login}](${json.owner.html_url})
\t**Primary Language:** \`${json.language}\`
\t:house: [Home page](${json.html_url}) :small_red_triangle_down: [Downloads](${json.html_url}/releases) :exclamation: [Issues](${json.html_url}/issues)
\t:negative_squared_cross_mark: \`${json.open_issues_count}\` open issues :star: \`${json.stargazers_count}\` stargazers :eyes: \`${json.subscribers_count || json.watchers_count}\` watchers
\tDo \`git clone ${json.clone_url}\` to clone this repository
`;
}
exports.info = {
name: 'github',
usage: 'github <user/repo>',
description: 'Links to a GitHub repository'
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ['git'],
permLevel: "User"
};
exports.help = {
name: "github",
category: "Utility",
description: "Look up a GitHub repository, and links to it. If you want a specific repository, use [user/repo] as the additional argument. Otherwise, just use [repo].",
usage: "github <user/repo>"
};

43
commands/Utility/image.js Normal file
View File

@ -0,0 +1,43 @@
const IMAGE_NAME = /\.(jpe?g|png|gif|webp)$/i;
exports.run = async (client, message, args, level) => {
if (!args[0]) {
message.reply("please provide an image URL to send.")
message.delete();
return;
}
message.delete();
const url = args[0];
let name;
if (!IMAGE_NAME.test(url)) {
name = 'image.png';
}
try {
let msg = await message.channel.send({
file: {
name,
attachment: url
}
});
} catch (ignore) {
return msg.edit('Failed to send image.');
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "image",
category: "Utility",
description: "Sends an image from a URL.",
usage: "image <url>"
};

View File

@ -0,0 +1,18 @@
exports.run = async (client, message, args, level) => {
const friendly = client.config.permLevels.find(l => l.level === level).name;
message.reply(`Your permission level is: ${level} - ${friendly}`);
};
exports.conf = {
enabled: true,
guildOnly: true,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "mylevel",
category: "Utility",
description: "Tells you your permission level for the current message location.",
usage: "mylevel"
};

65
commands/Utility/quote.js Normal file
View File

@ -0,0 +1,65 @@
exports.run = async (client, message, args, level) => {
let channel = message.channel;
if (args.length < 1 || !/^\d{18}$/.test(args[0])) {
(await message.reply('you must provide a message ID.')).delete(5000);
return message.delete();
}
if (args[1] && /^<#\d{18}>$|^\d{18}$/.test(args[1])) {
channel = client.channels.get(args[1].replace(/[<#>]/g, ''));
}
if (!channel) {
(await message.reply('the channel you requested could not be found.')).delete(5000);
return message.delete();
}
const messages = await channel.fetchMessages({ around: args[0], limit: 1 });
if (!messages || messages.size < 1) {
(await message.reply('the message you requested could not be found.')).delete(5000);
return message.delete();
}
let quotedMsg = messages.first();
let options = {
timestamp: quotedMsg.editedTimestamp || quotedMsg.createdTimestamp,
footer: false
};
let attachment = quotedMsg.attachments.first();
if (attachment && (attachment.width || attachment.height)) {
options.image = attachment.url;
}
let field = '';
if ((message.guild || {}).id !== (channel.guild || {}).id) {
field = `**in ${(channel.guild || { name: 'DMs' }).name} <#${channel.id}>:**`;
} else if (channel.id !== message.channel.id) {
field = `**in <#${channel.id}>:**`;
}
message.delete();
message.channel.send({
embed: client.embed('', field + '\n\n' + quotedMsg.toString(), [], options)
.setAuthor(quotedMsg.author.username, quotedMsg.author.avatarURL)
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "quote",
category: "Utility",
description: "Quotes the message with the ID of the message (and channel the message is in).",
usage: "quote <id> [#channel | channel ID]"
};

View File

@ -0,0 +1,48 @@
exports.run = async (client, message, args, level) => {
message.delete();
if (!args || args.length < 1) return message.reply("why are you trying to reload a command without actually naming it?").then(msg => {
msg.delete(5000);
});
const commandName = args[0].toLowerCase();
const command = message.client.commands.get(commandName)
|| message.client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (!command) return message.reply(`there is no command with name or alias \`${commandName}\`, ${message.author}.`).then(msg => {
msg.delete(5000);
});
let msg = await message.channel.send(`**Beginning reload process;** initiated by ${message.author.tag}. Finding and reloading command \`${args[0]}\`...`);
delete require.cache[require.resolve(`../${command.help.category}/${commandName}.js`)];
try {
const newCommand = require(`../${command.help.category}/${commandName}.js`);
message.client.commands.set(newCommand.help.name, newCommand);
} catch (error) {
console.log(error);
msg.edit(`Hey ${message.author}, there was an error while reloading command \`${commandName}\`:\n\`${error.message}\``).then(msg => {
msg.delete(10000);
});
return;
}
msg.edit(`**Command \`${command.help.name}\` has been reloaded successfully.**`).then(msg => {
msg.delete(5000);
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Admin"
};
exports.help = {
name: "reload",
category: "System",
description: "Reloads a command that\'s been modified.",
usage: "reload [command]"
};

View File

@ -0,0 +1,64 @@
const got = require('got');
exports.run = async (client, message, args, level) => {
if (!args[0]) {
(await message.reply("you must specify a time to convert!")).delete(5000);
message.delete();
return;
}
let input = args.join(' ');
let url = `https://api.duckduckgo.com/?q=${encodeURIComponent(input)}&format=json`;
let msg = await message.channel.send(':arrows_counterclockwise: **Loading conversion...**');
const res = await got(url, { json: true });
if (!res || !res.body) {
(await msg.edit(`${message.author}, I couldn't load data from DDG.`)).delete(5000);
}
let data = res.body;
let answer = data['Answer'];
let response;
if (data['AnswerType'] === 'timezone_converter') {
message.delete();
let matches = input.match(/(.*?)\s*(to|in)\s*(.*)/);
let prefix;
if (matches) {
prefix = matches[1];
} else {
prefix = input;
}
response = client.embed('', '', [
{
name: 'Timezone:',
value: `${prefix} \u2794 ${answer}`
}
]);
msg.edit({ embed: response });
} else {
(await msg.edit(`I couldn't find a conversion for \`${input}\`.`)).delete(5000);
return;
}
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "timezone",
category: "Utility",
description: "Converts between timezones using DuckDuckGo.",
usage: "timezone <time> to <zone>"
};

40
commands/Utility/urban.js Normal file
View File

@ -0,0 +1,40 @@
const webdict = require('webdict');
exports.run = async (client, message, args, level) => {
message.delete();
if (!args[0]) return message.reply("you didn't provide a valid word to look up.");
const parsed = client.parseArgs(args, ['e']);
const word = parsed.leftover.join(' ');
webdict('urbandictionary', word).then(res => {
let result;
if (!res || !res.definition || !res.definition[0]) {
result = 'No results found.';
} else {
result = res.definition[0];
}
if (parsed.options.e) {
message.channel.send(result);
return;
}
message.channel.send(client.embed(`:book: ${word}`, result));
});
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: ['urban'],
permLevel: "User"
};
exports.help = {
name: "urbandictionary",
category: "Utility",
description: "Looks up a word in the urban dictionary.",
usage: "urbandictionary <word>"
};

28
commands/_base.js Normal file
View File

@ -0,0 +1,28 @@
/*
This file is ignored by the bot's command loader because of the underscore at the start of the file name. The purpose of this file is for developers to get a headstart on implementing their own commands.
Developers, remember to insert all three exports; these are required. Your command won't load if any of these are missing.
*/
exports.run = async (client, message, args, level) => {
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "User"
};
exports.help = {
name: "",
category: "",
description: "",
usage: ""
};
// Basic message auto-deletion
if (!args[0]) {
message.delete();
return (await message.reply("text")).delete(5000).catch(() => { });
}

35
commands/test.js Normal file
View File

@ -0,0 +1,35 @@
const got = require("got");
exports.run = async (client, message, args, level) => {
const pgAmount = args[0] || 2;
const out = await got(`https://loripsum.net/api/${pgAmount}/decorate/code`);
let final = out.body;
final = final.then(
.replace("<b>", "**")).then(
.replace("</b>", "**")).then(
.replace("<p>", "")).then(
.replace(" </p>", "")).then(
.replace("<i>", "_")).then(
.replace("</i>", "_")).then(
.replace("<mark>", "||").then(
.replace("</mark>", "||").then(
.replace("<pre>","```").then(
.replace("</pre>","```");
message.channel.send(final);
};
exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: "Bot Owner"
};
exports.help = {
name: "test",
category: "",
description: "Sends randomly-generated Lorem Ipsum demo text.",
usage: "test [paragraphCount:2]"
};

73
config.js.example Normal file
View File

@ -0,0 +1,73 @@
const config = {
"ownerID": "123456789123456",
"admins": [],
"support": [],
"token": "mfa.VkO_2G4Qv3T--NO--lWetW_tjND--TOKEN--QFTm6YGtzq9PH--4U--tG0",
"defaultSettings" : {
"prefix": "/",
"modLogChannel": "mod-log",
"modRole": "Moderator",
"adminRole": "Administrator",
"systemNotice": "true",
"welcomeChannel": "welcome",
"welcomeMessage": "Say hello to {{user}}, everyone! We all need a warm welcome sometimes :D",
"welcomeEnabled": "false"
},
permLevels: [
{ level: 0,
name: "User",
check: () => true
},
{ level: 2,
name: "Moderator",
check: (message) => {
try {
const modRole = message.guild.roles.find(r => r.name.toLowerCase() === message.settings.modRole.toLowerCase());
if (modRole && message.member.roles.has(modRole.id)) return true;
} catch (e) {
return false;
}
}
},
{ level: 3,
name: "Administrator",
check: (message) => {
try {
const adminRole = message.guild.roles.find(r => r.name.toLowerCase() === message.settings.adminRole.toLowerCase());
return (adminRole && message.member.roles.has(adminRole.id));
} catch (e) {
return false;
}
}
},
{ level: 4,
name: "Server Owner",
check: (message) => message.channel.type === "text" ? (message.guild.ownerID === message.author.id ? true : false) : false
},
{ level: 8,
name: "Bot Support",
check: (message) => config.support.includes(message.author.id)
},
{ level: 9,
name: "Bot Admin",
check: (message) => config.admins.includes(message.author.id)
},
{ level: 10,
name: "Bot Owner",
check: (message) => message.client.config.ownerID === message.author.id
}
]
};
module.exports = config;

62
config_base.txt Normal file
View File

@ -0,0 +1,62 @@
const config = {
"ownerID": "{{ownerID}}",
"admins": [],
"support": [],
"token": {{token}},
permLevels: [
{ level: 0,
name: "User",
check: () => true
},
{ level: 2,
name: "Moderator",
check: (message) => {
try {
const modRole = message.guild.roles.find(r => r.name.toLowerCase() === message.settings.modRole.toLowerCase());
if (modRole && message.member.roles.has(modRole.id)) return true;
} catch (e) {
return false;
}
}
},
{ level: 3,
name: "Administrator",
check: (message) => {
try {
const adminRole = message.guild.roles.find(r => r.name.toLowerCase() === message.settings.adminRole.toLowerCase());
return (adminRole && message.member.roles.has(adminRole.id));
} catch (e) {
return false;
}
}
},
{ level: 4,
name: "Server Owner",
check: (message) => message.channel.type === "text" ? (message.guild.ownerID === message.author.id ? true : false) : false
},
{ level: 8,
name: "Bot Support",
check: (message) => config.support.includes(message.author.id)
},
{ level: 9,
name: "Bot Admin",
check: (message) => config.admins.includes(message.author.id)
},
{ level: 10,
name: "Bot Owner",
check: (message) => message.client.config.ownerID === message.author.id
}
]
};
module.exports = config;

3
events/disconnect.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = client => { // eslint-disable-line no-unused-vars
console.log(`You have been disconnected at ${new Date()}`);
};

3
events/error.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = async (client, error) => {
client.logger.log(`An error event was sent by Discord.js: \n${JSON.stringify(error)}`, "error");
};

3
events/guildCreate.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = (client, guild) => {
client.logger.cmd(`[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner.user.tag} (${guild.owner.user.id})`);
};

7
events/guildDelete.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = (client, guild) => {
client.logger.cmd(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot.`);
if (client.settings.has(guild.id)) {
client.settings.delete(guild.id);
}
};

7
events/guildMemberAdd.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = (client, member) => {
const settings = client.getSettings(member.guild);
if (settings.welcomeEnabled !== "true") return;
const welcomeMessage = settings.welcomeMessage.replace("{{user}}", member.user.tag);
member.guild.channels.find(c => c.name === settings.welcomeChannel).send(welcomeMessage).catch(console.error);
};

57
events/message.js Normal file
View File

@ -0,0 +1,57 @@
module.exports = async (client, message) => {
if (message.author.bot) return;
const settings = message.settings = client.getSettings(message.guild);
const prefixMention = new RegExp(`^<@!?${client.user.id}>( |)$`);
if (message.content.match(prefixMention)) {
return message.reply(`my prefix for this guild is \`${settings.prefix}\`.`);
}
if (message.content.indexOf(settings.prefix) !== 0) return;
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (message.guild && !message.member) await message.guild.fetchMember(message.author);
const level = client.permlevel(message);
const cmd = client.commands.get(command) || client.commands.get(client.aliases.get(command));
if (!cmd) return;
if (cmd && !message.guild && cmd.conf.guildOnly)
return message.channel.send("This command is unavailable via private message. Please run this command in a guild.");
if (level < client.levelCache[cmd.conf.permLevel]) {
if (settings.systemNotice === "true") {
message.delete();
return message.channel.send({embed: client.embed("**Invalid permissions!**",`You don't have the proper user level to use command ${cmd.help.name}.`, [
{
name: "**__Required permissions level:__**",
value: `**${client.levelCache[cmd.conf.permLevel]}** (${cmd.conf.permLevel})`
},
{
name: "**__Your permissions level:__**",
value: `**${level}** (${client.config.permLevels.find(l => l.level === level).name})`
}
],
{
author: message.author.tag,
authorIcon: message.author.avatarURL
})
});
} else {
return;
}
}
message.author.permLevel = level;
message.flags = [];
while (args[0] && args[0][0] === "-") {
message.flags.push(args.shift().slice(1));
}
client.logger.cmd(`[CMD] ${client.config.permLevels.find(l => l.level === level).name} ${message.author.username} (${message.author.id}) ran command ${cmd.help.name}`);
cmd.run(client, message, args, level);
};

6
events/ready.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = async client => {
client.logger.log(`${client.user.tag}, ready to serve ${client.users.filter(user => !user.bot).size} users in ${client.guilds.size} ${client.guilds.size > 1 ? "servers" : "server"}.`, "ready");
client.user.setActivity(`${client.users.filter(user => !user.bot).size} humans | ${client.settings.get("default").prefix}help`, {type: "WATCHING"});
client.user.setStatus("online");
};

3
events/reconnecting.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = client => { // eslint-disable-line no-unused-vars
console.log(`Reconnecting at ${new Date()}`);
};

64
index.js Normal file
View File

@ -0,0 +1,64 @@
if (Number(process.version.slice(1).split(".")[0]) < 8) throw new Error("Node 8.0.0 or higher is required. Update Node on your system.");
const Discord = require("discord.js");
const { promisify } = require("util");
const readdir = promisify(require("fs").readdir);
const Enmap = require("enmap");
const path = require("path");
const client = new Discord.Client();
client.config = require("./config.js");
client.logger = require("./modules/Logger");
require("./modules/functions.js")(client);
client.commands = new Enmap();
client.aliases = new Enmap();
client.settings = new Enmap({name: "settings"});
async function crawl(directory, filesArray) {
const dirs = await readdir(directory, {
withFileTypes: true
});
for (let i = 0; i < dirs.length; i++) {
const currentDir = dirs[i];
const newPath = path.join(directory, currentDir.name);
if (currentDir.isDirectory()) {
await crawl(newPath, filesArray);
}
else {
filesArray.push(newPath);
}
}
}
const init = async () => {
let cmdFiles = []
await crawl("commands",cmdFiles);
client.logger.log(`Loading a total of ${cmdFiles.length} commands.`);
cmdFiles.forEach(f => {
if (!f.endsWith(".js") || f.startsWith("commands\\_")) return;
const response = client.loadCommand(f);
if (response) console.log(response);
});
const evtFiles = await readdir("./events/");
client.logger.log(`Loading a total of ${evtFiles.length} events.`);
evtFiles.forEach(file => {
const eventName = file.split(".")[0];
client.logger.log(`Loading Event: ${eventName}`);
const event = require(`./events/${file}`);
client.on(eventName, event.bind(null, client));
});
client.levelCache = {};
for (let i = 0; i < client.config.permLevels.length; i++) {
const thisLevel = client.config.permLevels[i];
client.levelCache[thisLevel.name] = thisLevel.level;
}
client.login(client.config.token);
};
init();

35
modules/Logger.js Normal file
View File

@ -0,0 +1,35 @@
const chalk = require("chalk");
const moment = require("moment");
exports.log = (content, type = "log") => {
const timestamp = `[${moment().format("YYYY-MM-DD HH:mm:ss")}]:`;
switch (type) {
case "log": {
return console.log(`${timestamp} ${chalk.bgBlue(type.toUpperCase())} ${content} `);
}
case "warn": {
return console.log(`${timestamp} ${chalk.black.bgYellow(type.toUpperCase())} ${content} `);
}
case "error": {
return console.log(`${timestamp} ${chalk.bgRed(type.toUpperCase())} ${content} `);
}
case "debug": {
return console.log(`${timestamp} ${chalk.green(type.toUpperCase())} ${content} `);
}
case "cmd": {
return console.log(`${timestamp} ${chalk.black.bgWhite(type.toUpperCase())} ${content}`);
}
case "ready": {
return console.log(`${timestamp} ${chalk.black.bgGreen(type.toUpperCase())} ${content}`);
}
default: throw new TypeError("Logger type must be either warn, debug, log, ready, cmd or error.");
}
};
exports.error = (...args) => this.log(...args, "error");
exports.warn = (...args) => this.log(...args, "warn");
exports.debug = (...args) => this.log(...args, "debug");
exports.cmd = (...args) => this.log(...args, "cmd");

354
modules/functions.js Normal file
View File

@ -0,0 +1,354 @@
const Discord = require("discord.js");
const got = require("got");
const fetch = require("node-fetch");
module.exports = (client) => {
client.permlevel = message => {
let permlvl = 0;
const permOrder = client.config.permLevels.slice(0).sort((p, c) => p.level < c.level ? 1 : -1);
while (permOrder.length) {
const currentLevel = permOrder.shift();
if (message.guild && currentLevel.guildOnly) continue;
if (currentLevel.check(message)) {
permlvl = currentLevel.level;
break;
}
}
return permlvl;
};
const defaultSettings = {
"prefix": "b&",
"modLogChannel": "mod-log",
"modRole": "modinator",
"adminRole": "adminator",
"systemNotice": "true",
"welcomeChannel": "welcome",
"welcomeMessage": "Say hello to {{user}}, everyone! We all need a warm welcome sometimes :D",
"welcomeEnabled": "false"
};
client.getSettings = (guild) => {
client.settings.ensure("default", defaultSettings);
if(!guild) return client.settings.get("default");
const guildConf = client.settings.get(guild.id) || {};
return ({...client.settings.get("default"), ...guildConf});
};
client.awaitReply = async (msg, question, limit = 60000) => {
const filter = m => m.author.id === msg.author.id;
await msg.channel.send(question);
try {
const collected = await msg.channel.awaitMessages(filter, { max: 1, time: limit, errors: ["time"] });
return collected.first().content;
} catch (e) {
return false;
}
};
client.caseNumber = async (client, modlog) {
const messages = await modlog.fetchMessages({limit:5});
const log = messages.filter(m => m.author.id === client.user.id &&
m.embeds[0] &&
m.embeds[0].type === 'rich' &&
m.embeds[0].footer &&
m.embeds[0].footer.text.startsWith('Case')
).first();
if (!log) return 1;
const thisCase = /Case\s(\d+)/.exec(log.embeds[0].footer.text);
return thisCase ? parseInt(thisCase[1]) + 1 : 1;
};
client.clean = async (client, text) => {
if (text && text.constructor.name == "Promise")
text = await text;
if (typeof text !== "string")
text = require("util").inspect(text, {depth: 1});
text = text
.replace(/`/g, "`" + String.fromCharCode(8203))
.replace(/@/g, "@" + String.fromCharCode(8203))
.replace(client.token, "mfa.VkO_2G4Qv3T--NO--lWetW_tjND--TOKEN--QFTm6YGtzq9PH--4U--tG0");
return text;
};
client.loadCommand = (commandName) => {
try {
client.logger.log(`Loading Command: ${commandName}`);
const props = require(`../${commandName}`);
if (props.init) {
props.init(client);
}
client.commands.set(props.help.name, props);
props.conf.aliases.forEach(alias => {
client.aliases.set(alias, props.help.name);
});
return false;
} catch (e) {
return `Unable to load command ${commandName}: ${e}`;
}
};
client.unloadCommand = async (commandName) => {
let command;
if (client.commands.has(commandName)) {
command = client.commands.get(commandName);
} else if (client.aliases.has(commandName)) {
command = client.commands.get(client.aliases.get(commandName));
}
if (!command) return `The command \`${commandName}\` doesn\'t seem to exist, nor is it an alias. Try again!`;
if (command.shutdown) {
await command.shutdown(client);
}
const mod = require.cache[require.resolve(`../commands/${command.help.category}/${command.help.name}`)];
delete require.cache[require.resolve(`../commands/${command.help.category}/${command.help.name}.js`)];
for (let i = 0; i < mod.parent.children.length; i++) {
if (mod.parent.children[i] === mod) {
mod.parent.children.splice(i, 1);
break;
}
}
return false;
};
Object.defineProperty(String.prototype, "toProperCase", {
value: function() {
return this.replace(/([^\W_]+[^\s-]*) */g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
}
});
Object.defineProperty(Array.prototype, "random", {
value: function() {
return this[Math.floor(Math.random() * this.length)];
}
});
client.wait = require("util").promisify(setTimeout);
client.randomSelection = choices => choices[Math.floor(Math.random() * choices.length)];
process.on("uncaughtException", (err) => {
const errorMsg = err.stack.replace(new RegExp(`${__dirname}/`, "g"), "./");
client.logger.error(`Uncaught Exception: ${errorMsg}`);
console.error(err);
process.exit(1);
});
process.on("unhandledRejection", err => {
client.logger.error(`Unhandled rejection: ${err}`);
console.error(err);
});
client.parseArgs = (args, options) => {
if (!options)
return args;
if (typeof options === 'string')
options = [options];
let optionValues = {};
let i;
for (i = 0; i < args.length; i++) {
let arg = args[i];
if (!arg.startsWith('-')) {
break;
}
let label = arg.substr(1);
if (options.indexOf(label + ':') > -1) {
let leftover = args.slice(i + 1).join(' ');
let matches = leftover.match(/^"(.+?)"/);
if (matches) {
optionValues[label] = matches[1];
i += matches[0].split(' ').length;
} else {
i++;
optionValues[label] = args[i];
}
} else if (options.indexOf(label) > -1) {
optionValues[label] = true;
} else {
break;
}
}
return {
options: optionValues,
leftover: args.slice(i)
};
};
client.embed = (title, description = '', fields = [], options = {}) => {
let url = options.url || '';
let color = options.color || randomColor();
if (options.inline) {
if (fields.length % 3 === 2) {
fields.push({ name: '\u200b', value: '\u200b' });
}
fields.forEach(obj => {
obj.inline = true;
});
}
return new Discord.RichEmbed({ fields, video: options.video || url })
.setTitle(title)
.setColor(color)
.setDescription(description)
.setURL(url)
.setImage(options.image)
.setTimestamp(options.timestamp ? timestampToDate(options.timestamp) : null)
.setFooter(options.footer === true ? randomFooter() : (options.footer ? options.footer : ''), options.footerIcon === true ? options.footerIcon : client.user.avatarURL)
.setAuthor(options.author === undefined ? '' : options.author, options.authorIcon)
.setThumbnail(options.thumbnail);
};
const randomColor = () => [
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
];
const randomFooter = () => {
return randomSelection([
'just add water!',
'Powered by squirrels!',
'codeisluvcodeislife',
'Where did you get that?',
'WHAT DID YOU BREAK!?',
'D-D-D-DROP THE BASS',
'Eat, Sleep, JavaScript',
'#BlameRhearmas',
'SharpBot was the best, right?',
'I like turtles',
'Hi mom!'
]);
};
const timestampToDate = timestamp => {
if (timestamp === true) {
return new Date();
}
if (typeof timestamp === 'number') {
return new Date(timestamp);
}
return timestamp;
};
const parseArgs = (args, options) => {
if (!options)
return args;
if (typeof options === 'string')
options = [options];
let optionValues = {};
let i;
for (i = 0; i < args.length; i++) {
let arg = args[i];
if (!arg.startsWith('-')) {
break;
}
let label = arg.substr(1);
if (options.indexOf(label + ':') > -1) {
let leftover = args.slice(i + 1).join(' ');
let matches = leftover.match(/^"(.+?)"/);
if (matches) {
optionValues[label] = matches[1];
i += matches[0].split(' ').length;
} else {
i++;
optionValues[label] = args[i];
}
} else if (options.indexOf(label) > -1) {
optionValues[label] = true;
} else {
break;
}
}
return {
options: optionValues,
leftover: args.slice(i)
};
};
const multiSend = (channel, messages, delay) => {
delay = delay || 100;
messages.forEach((m, i) => {
setTimeout(() => {
channel.send(m);
}, delay * i);
});
};
client.textUpload = (text, options) => {
options = options || {};
let method = (options.method || uploadMethods.ix).toLowerCase();
if (method === uploadMethods.ix) {
return ixUpload(text);
} else if (method === uploadMethods.hastebin) {
return hastebinUpload(text);
}
};
const uploadMethods = {
hastebin: 'hastebin',
ix: 'ix.io'
};
const hastebinUpload = text => {
return got('https://hastebin.com/documents', { body: { 'contents': text }, form: true })
.then(res => {
if (res && res.body && res.body.key) {
const key = res.body.key;
return {
key: key,
success: true,
url: `https://hastebin.com/${key}`,
rawUrl: `https://hastebin.com/raw/${key}`
};
} else {
return {
success: false
};
}
});
};
const ixUpload = text => {
return got('http://ix.io', { body: { 'f:1': text }, form: true })
.then(res => {
if (res && res.body) {
return {
success: true,
url: res.body,
rawUrl: res.body
};
} else {
return {
success: false
};
}
});
};
client.quoteRegex = input => `${input}`.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&');
client.fetchURL = (url, options = {}) => {
options.headers = options.headers ? { ...options.headers, "User-Agent": client.user } : { "User-Agent": client.user };
return fetch(url, options, options.type || "json").catch(error => {
client.Logger.error(error);
});
}
};

811
package-lock.json generated Normal file
View File

@ -0,0 +1,811 @@
{
"name": "cardboardbot",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@cush/relative": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@cush/relative/-/relative-0.1.0.tgz",
"integrity": "sha512-pnF2c2hhHyC520CmYYKq3hGOS0kipkGBgRnp3z7wx7lDzykaUwQW3wPQmiX9YtbHUcgUu1qQtzstixmeYMwQoA=="
},
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"better-sqlite3": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-5.4.0.tgz",
"integrity": "sha512-Uj1ZYOcq1GtFyFgJgqMVDoDLTy1B1pM9+bULnlX8szRX4cPjE/7JbKxCzQGhYlZlLkHQvtXXhCZ3skqsQ2byMA==",
"requires": {
"integer": "^2.1.0",
"tar": "^4.4.6"
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"capture-stack-trace": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
"integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw=="
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
"cheerio": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
"integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
"requires": {
"css-select": "~1.2.0",
"dom-serializer": "~0.1.0",
"entities": "~1.1.1",
"htmlparser2": "^3.9.1",
"lodash.assignin": "^4.0.9",
"lodash.bind": "^4.1.4",
"lodash.defaults": "^4.0.1",
"lodash.filter": "^4.4.0",
"lodash.flatten": "^4.2.0",
"lodash.foreach": "^4.3.0",
"lodash.map": "^4.4.0",
"lodash.merge": "^4.4.0",
"lodash.pick": "^4.2.1",
"lodash.reduce": "^4.4.0",
"lodash.reject": "^4.4.0",
"lodash.some": "^4.4.0"
}
},
"chownr": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"requires": {
"restore-cursor": "^2.0.0"
}
},
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
},
"common-tags": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
"integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw=="
},
"crawl": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/crawl/-/crawl-0.3.1.tgz",
"integrity": "sha1-ljAMnWoY60nBbgM98+B9xYYfVd0=",
"requires": {
"colors": ">= 0.6.0-1",
"first": "0.0.x",
"mime": ">=1.4.1",
"node_hash": "0.2.x",
"optimist": "0.3.x",
"prettyjson": "0.7.x",
"simplecrawler": "0.0.x",
"underscore": "1.4.x"
}
},
"create-error-class": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
"requires": {
"capture-stack-trace": "^1.0.0"
}
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"requires": {
"boolbase": "~1.0.0",
"css-what": "2.1",
"domutils": "1.5.1",
"nth-check": "~1.0.1"
}
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
},
"discord-emoji": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/discord-emoji/-/discord-emoji-1.1.1.tgz",
"integrity": "sha1-GoJyxqohshyOdRddp1g1rvCT9xY="
},
"discord.js": {
"version": "11.5.1",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.5.1.tgz",
"integrity": "sha512-tGhV5xaZXE3Z+4uXJb3hYM6gQ1NmnSxp9PClcsSAYFVRzH6AJH74040mO3afPDMWEAlj8XsoPXXTJHTxesqcGw==",
"requires": {
"long": "^4.0.0",
"prism-media": "^0.0.3",
"snekfetch": "^3.6.4",
"tweetnacl": "^1.0.0",
"ws": "^6.0.0"
}
},
"dom-serializer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
"integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
"requires": {
"domelementtype": "^1.3.0",
"entities": "^1.1.1"
}
},
"domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"requires": {
"domelementtype": "1"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
}
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
},
"enmap": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/enmap/-/enmap-5.0.0.tgz",
"integrity": "sha512-ntA4u4/p4FYpfFHAHiuLviIgVJYa5Bb1m5J9xkA1Ch3dgarHIqxdsDbzQS9PM3z9iF2yae/ud8uKQDMYPmRx2w==",
"requires": {
"lodash": "^4.17.11"
}
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"external-editor": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
"integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
"requires": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"first": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/first/-/first-0.0.3.tgz",
"integrity": "sha1-EpqTHr+B01BdhffAXHti0MTOag4="
},
"fs-minipass": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz",
"integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==",
"requires": {
"minipass": "^2.2.1"
}
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"glob-regex": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/glob-regex/-/glob-regex-0.3.2.tgz",
"integrity": "sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw=="
},
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"requires": {
"create-error-class": "^3.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^3.0.0",
"is-redirect": "^1.0.0",
"is-retry-allowed": "^1.0.0",
"is-stream": "^1.0.0",
"lowercase-keys": "^1.0.0",
"safe-buffer": "^5.0.1",
"timed-out": "^4.0.0",
"unzip-response": "^2.0.1",
"url-parse-lax": "^1.0.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"requires": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"inquirer": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz",
"integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==",
"requires": {
"ansi-escapes": "^3.2.0",
"chalk": "^2.4.2",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^3.0.3",
"figures": "^2.0.0",
"lodash": "^4.17.11",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rxjs": "^6.4.0",
"string-width": "^2.1.0",
"strip-ansi": "^5.1.0",
"through": "^2.3.6"
}
},
"integer": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/integer/-/integer-2.1.0.tgz",
"integrity": "sha512-vBtiSgrEiNocWvvZX1RVfeOKa2mCHLZQ2p9nkQkQZ/BvEiY+6CcUz0eyjvIiewjJoeNidzg2I+tpPJvpyspL1w=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
},
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
},
"is-retry-allowed": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
"integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"lodash": {
"version": "4.17.14",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz",
"integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw=="
},
"lodash.assignin": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
"integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI="
},
"lodash.bind": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
},
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
},
"lodash.filter": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
"integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4="
},
"lodash.flatten": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
},
"lodash.foreach": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
},
"lodash.map": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
},
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.pick": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
"integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
},
"lodash.reduce": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
"integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
},
"lodash.reject": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
"integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU="
},
"lodash.some": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
"integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
},
"math-expression-evaluator": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
"integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw="
},
"mime": {
"version": ">=1.4.1",
"integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA="
},
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"minipass": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"requires": {
"minipass": "^2.2.1"
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"moment-duration-format": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/moment-duration-format/-/moment-duration-format-2.3.2.tgz",
"integrity": "sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ=="
},
"mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"node_hash": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/node_hash/-/node_hash-0.2.0.tgz",
"integrity": "sha1-nqZNeh8xb7+QPlzVOuCtL/2qZ9k="
},
"nth-check": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"requires": {
"boolbase": "~1.0.0"
}
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"requires": {
"mimic-fn": "^1.0.0"
}
},
"optimist": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
"integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
"requires": {
"wordwrap": "~0.0.2"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"prepend-http": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
},
"prettyjson": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-0.7.1.tgz",
"integrity": "sha1-LDjuqhXktq2wPrvmYkAtHqI5xcM=",
"requires": {
"colors": "0.6.0-1"
},
"dependencies": {
"colors": {
"version": "0.6.0-1",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.0-1.tgz",
"integrity": "sha1-bbtozri8YPKzE9zFzhWZ8G0Z5no="
}
}
},
"prism-media": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.3.tgz",
"integrity": "sha512-c9KkNifSMU/iXT8FFTaBwBMr+rdVcN+H/uNv1o+CuFeTThNZNTOrQ+RgXA1yL/DeLk098duAeRPP3QNPNbhxYQ=="
},
"readable-stream": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
"integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"recrawl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/recrawl/-/recrawl-2.0.0.tgz",
"integrity": "sha512-PmL1uxEDduB/qqWc06KZzGJ4j//sXAuMlTcNJFFAEMTffJIFrzFghOxkgHU8+auKOUILEdbyfuEvFyoeu4mJgQ==",
"requires": {
"@cush/relative": "^0.1.0",
"glob-regex": "^0.3.0",
"tslib": "^1.9.3"
}
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
}
},
"roll": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/roll/-/roll-1.2.0.tgz",
"integrity": "sha1-9LlQUZBpVXOe0+8ACkE5VaqwaiI="
},
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
"requires": {
"is-promise": "^2.1.0"
}
},
"rxjs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
"integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==",
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"simplecrawler": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/simplecrawler/-/simplecrawler-0.0.10.tgz",
"integrity": "sha1-1EzZFb3cyO4fCSDXpbxHuClwvuk="
},
"snekfetch": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
"integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"tar": {
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
"integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.5",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
}
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"tweetnacl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz",
"integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A=="
},
"underscore": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
},
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
},
"url-parse-lax": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
"integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
"requires": {
"prepend-http": "^1.0.1"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"webdict": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/webdict/-/webdict-0.3.0.tgz",
"integrity": "sha512-1mVKeb53VgY6ZWxUP1zeJE1kWlVk4Q8oQ80QTHK1EpG6kGdiADYth9KbWtjDA7/Q8vt4mew12A8Xc9gtEqLtEg==",
"requires": {
"cheerio": "^0.22.0",
"got": "^6.7.1"
}
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"requires": {
"async-limiter": "~1.0.0"
}
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
}

43
package.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "cardboardbot",
"version": "1.0.0",
"description": "Discord bot that manages everything at my discord server.",
"main": "index.js",
"scripts": {
"start": "node index.js",
"postinstall": "node setup.js"
},
"engines": {
"node": ">=8"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rhearmas/cardboardbot.git"
},
"author": "rhearmas <rhearmas@gmail.com> (http://rhearmas.carrd.co)",
"bugs": {
"url": "https://github.com/rhearmas/cardboardbot/issues"
},
"homepage": "https://github.com/rhearmas/cardboardbot#readme",
"dependencies": {
"better-sqlite3": "^5.4.0",
"chalk": "^2.4.2",
"common-tags": "^1.8.0",
"crawl": "^0.3.1",
"dateformat": "^3.0.3",
"discord-emoji": "^1.1.1",
"discord.js": "^11.5.1",
"enmap": "^5.0.0",
"inquirer": "^6.3.1",
"math-expression-evaluator": "^1.2.17",
"moment": "^2.24.0",
"moment-duration-format": "^2.3.2",
"node-fetch": "^2.6.0",
"recrawl": "^2.0.0",
"roll": "^1.2.0",
"webdict": "^0.3.0"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
}
}

68
setup.js Normal file
View File

@ -0,0 +1,68 @@
const inquirer = require("inquirer");
const Enmap = require("enmap");
const fs = require("fs");
let baseConfig = fs.readFileSync("./config_base.txt", "utf8");
const defaultSettings = {
"prefix": "b&",
"modLogChannel": "logs",
"modRole": "Moderator",
"adminRole": "Administrator",
"systemNotice": "true",
"welcomeChannel": "welcome",
"welcomeMessage": "Say hello to {{user}}, everyone! We all need a warm welcome sometimes :D",
"welcomeEnabled": "false"
};
const settings = new Enmap({
name: "settings",
cloneLevel: 'deep',
ensureProps: true
});
let prompts = [
{
type: "list",
name: "resetDefaults",
message: "Do you want to reset default settings?",
choices: ["Yes", "No"]
},
{
type: "input",
name: "token",
message: "Please enter the bot token from the application page."
},
{
type: "input",
name: "ownerID",
message: "Please enter the bot owner's User ID"
},
];
(async function () {
console.log("Setting Up Cardboard Bot Configuration...");
await settings.defer;
if (!settings.has("default")) {
prompts = prompts.slice(1);
console.log("First Start! Inserting default guild settings in the database...");
await settings.set("default", defaultSettings);
}
const answers = await inquirer.prompt(prompts);
if (answers.resetDefaults && answers.resetDefaults === "Yes") {
console.log("Resetting default guild settings...");
await settings.set("default", defaultSettings);
}
baseConfig = baseConfig
.replace("{{ownerID}}", answers.ownerID)
.replace("{{token}}", `"${answers.token}"`);
fs.writeFileSync("./config.js", baseConfig);
console.log("REMEMBER TO NEVER SHARE YOUR TOKEN WITH ANYONE!");
console.log("Configuration has been written, enjoy!");
await settings.close();
}());