summaryrefslogtreecommitdiff
path: root/server/src/commands
diff options
context:
space:
mode:
author8cy <[email protected]>2020-07-23 23:24:17 -0700
committer8cy <[email protected]>2020-07-23 23:24:17 -0700
commitbb511abc03bb66848947e37a999502b813c77269 (patch)
tree612c010fc8317e1cdf11471a18aad0270819d33e /server/src/commands
parentfix: if clear amount equal or over 100, round down to 99 (diff)
downloaddep-core-bb511abc03bb66848947e37a999502b813c77269.tar.xz
dep-core-bb511abc03bb66848947e37a999502b813c77269.zip
goodbye old uwufier :cry:
Diffstat (limited to 'server/src/commands')
-rw-r--r--server/src/commands/.todo44
-rw-r--r--server/src/commands/animals/Bunny.ts36
-rw-r--r--server/src/commands/animals/Cat.ts36
-rw-r--r--server/src/commands/animals/Dog.ts36
-rw-r--r--server/src/commands/animals/Duck.ts36
-rw-r--r--server/src/commands/animals/Fox.ts36
-rw-r--r--server/src/commands/animals/Owl.ts36
-rw-r--r--server/src/commands/anime/Darling.ts90
-rw-r--r--server/src/commands/anime/Douse.ts27
-rw-r--r--server/src/commands/anime/Waifu.ts32
-rw-r--r--server/src/commands/bot/Info.ts61
-rw-r--r--server/src/commands/bot/Invite.ts27
-rw-r--r--server/src/commands/bot/Ping.ts23
-rw-r--r--server/src/commands/bot/Sin.ts35
-rw-r--r--server/src/commands/bot/Suggest.ts35
-rw-r--r--server/src/commands/emma/FanArt.ts132
-rw-r--r--server/src/commands/emma/UglyCat.ts27
-rw-r--r--server/src/commands/fun/Advice.ts29
-rw-r--r--server/src/commands/fun/Clapify.ts39
-rw-r--r--server/src/commands/fun/DateFact.ts52
-rw-r--r--server/src/commands/fun/DayFact.ts41
-rw-r--r--server/src/commands/fun/FML.ts33
-rw-r--r--server/src/commands/fun/Fact67
-rw-r--r--server/src/commands/fun/GitHubZen.ts29
-rw-r--r--server/src/commands/fun/Hello.ts23
-rw-r--r--server/src/commands/fun/Insult.ts29
-rw-r--r--server/src/commands/fun/NumberFact.ts41
-rw-r--r--server/src/commands/fun/Onion.ts35
-rw-r--r--server/src/commands/fun/Opinion.ts34
-rw-r--r--server/src/commands/fun/PayRespects.ts24
-rw-r--r--server/src/commands/fun/Rate.ts33
-rw-r--r--server/src/commands/fun/Say.ts40
-rw-r--r--server/src/commands/fun/Spoiler.ts39
-rw-r--r--server/src/commands/fun/Uwufy.ts48
-rw-r--r--server/src/commands/fun/YearFact.ts41
-rw-r--r--server/src/commands/fun/YoMomma.ts29
-rw-r--r--server/src/commands/minigames/8Ball.ts31
-rw-r--r--server/src/commands/minigames/Coinflip.ts50
-rw-r--r--server/src/commands/minigames/RollDie.ts29
-rw-r--r--server/src/commands/minigames/RussianRoulette.ts27
-rw-r--r--server/src/commands/mod/Ban.ts64
-rw-r--r--server/src/commands/mod/Kick.ts57
-rw-r--r--server/src/commands/mod/Prune.ts39
-rw-r--r--server/src/commands/mod/Slowmode.ts61
-rw-r--r--server/src/commands/mod/Unban.ts38
-rw-r--r--server/src/commands/nsfw/Danbooru.ts77
-rw-r--r--server/src/commands/nsfw/Gelbooru.ts77
-rw-r--r--server/src/commands/nsfw/Rule34.ts68
-rw-r--r--server/src/commands/owner/DM.ts90
-rw-r--r--server/src/commands/owner/IP.ts27
-rw-r--r--server/src/commands/owner/Reload.ts40
-rw-r--r--server/src/commands/owner/ServerCount.ts24
-rw-r--r--server/src/commands/owner/Status.ts35
-rw-r--r--server/src/commands/owner/Username.ts35
-rw-r--r--server/src/commands/reaction/List.ts46
-rw-r--r--server/src/commands/reaction/New.ts168
-rw-r--r--server/src/commands/reaction/Remove.ts61
-rw-r--r--server/src/commands/server/Goodbye.ts81
-rw-r--r--server/src/commands/server/MemberCount.ts24
-rw-r--r--server/src/commands/server/OldestMember.ts39
-rw-r--r--server/src/commands/server/PFP.ts63
-rw-r--r--server/src/commands/server/Poll.ts42
-rw-r--r--server/src/commands/server/RandomMember.ts23
-rw-r--r--server/src/commands/server/Server.ts39
-rw-r--r--server/src/commands/server/User.ts75
-rw-r--r--server/src/commands/server/Welcome.ts81
-rw-r--r--server/src/commands/util/Categories.ts155
-rw-r--r--server/src/commands/util/Help.ts100
68 files changed, 3321 insertions, 0 deletions
diff --git a/server/src/commands/.todo b/server/src/commands/.todo
new file mode 100644
index 0000000..0420950
--- /dev/null
+++ b/server/src/commands/.todo
@@ -0,0 +1,44 @@
+Commands:
+ [ ] Animals
+ [x] Anime
+ [ ] Bot
+ [ ] Crypto
+ [ ] Emma
+ [ ] Fun
+ [ ] Aesthetic
+ [ ] Cultured Text
+ [ ] Dogeify
+ [ ] Draw Cards
+ [ ] Embed
+ [ ] Emoji
+ [ ] FML
+ [ ] Gay
+ [ ] Howify
+ [ ] Insult
+ [ ] IQ
+ [ ] KMK
+ [ ] Lorem
+ [ ] Motivate
+ [ ] Oddcase
+ [ ] Offspring
+ [ ] Onion
+ [ ] Quote
+ [ ] RoastWilly/ RoastWillyC
+ [ ] Rock, Paper, Scissors
+ [ ] ShowerThoughts
+ [ ] Smash or Pass
+ [ ] Spongebob
+ [ ] Stretch
+ [ ] Subreddit
+ [ ] Surreal
+ [ ] Minecraft
+ [ ] Moderation
+ [ ] NSFW
+ [ ] Roleplay
+ [ ] Server
+ [ ] User
+ [ ] Utility
+ [ ] Voice
+ [ ] Zero Two
+ [x] Douse
+ [ ] Darling \ No newline at end of file
diff --git a/server/src/commands/animals/Bunny.ts b/server/src/commands/animals/Bunny.ts
new file mode 100644
index 0000000..5325002
--- /dev/null
+++ b/server/src/commands/animals/Bunny.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class BunnyAnimals extends Command {
+ public constructor() {
+ super('bunny', {
+ aliases: ['bunny'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random bunny!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`https://api.bunnies.io/v2/loop/random/?media=gif,png`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://api.bunies.io/) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('bunnies.io')
+ //@ts-ignore
+ .setImage(animal.data.media.gif);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/animals/Cat.ts b/server/src/commands/animals/Cat.ts
new file mode 100644
index 0000000..8c37d2c
--- /dev/null
+++ b/server/src/commands/animals/Cat.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class CatAnimals extends Command {
+ public constructor() {
+ super('cat', {
+ aliases: ['cat'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random cat!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`https://aws.random.cat/meow`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://random.cat/) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('bunnies.io', 'https://i.imgur.com/Ik0Gf0r.png', 'https://random.cat')
+ //@ts-ignore
+ .setImage(animal.data.file);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/animals/Dog.ts b/server/src/commands/animals/Dog.ts
new file mode 100644
index 0000000..c44131e
--- /dev/null
+++ b/server/src/commands/animals/Dog.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class DogAnimals extends Command {
+ public constructor() {
+ super('dog', {
+ aliases: ['dog'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random dog!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`https://dog.ceo/api/breeds/image/random`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://dog.ceo/api) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('dog.ceo', 'https://dog.ceo/img/favicon.png', 'https://dog.ceo/dog-api/')
+ //@ts-ignore
+ .setImage(animal.data.message);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/animals/Duck.ts b/server/src/commands/animals/Duck.ts
new file mode 100644
index 0000000..c1b75f9
--- /dev/null
+++ b/server/src/commands/animals/Duck.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class DuckAnimals extends Command {
+ public constructor() {
+ super('duck', {
+ aliases: ['duck'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random duck!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`https://random-d.uk/api/v1/random?type=gif`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://random-d.uk/api/) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('random-d.uk', 'https://random-d.uk/favicon.ico', 'https://random-d.uk')
+ //@ts-ignore
+ .setImage(animal.data.url);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/animals/Fox.ts b/server/src/commands/animals/Fox.ts
new file mode 100644
index 0000000..fc887ef
--- /dev/null
+++ b/server/src/commands/animals/Fox.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class FoxAnimals extends Command {
+ public constructor() {
+ super('fox', {
+ aliases: ['fox'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random fox!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`https://randomfox.ca/floof/`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://randomfox.ca/floof/) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('randomfox.ca')
+ //@ts-ignore
+ .setImage(animal.data.image);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/animals/Owl.ts b/server/src/commands/animals/Owl.ts
new file mode 100644
index 0000000..bec7a5b
--- /dev/null
+++ b/server/src/commands/animals/Owl.ts
@@ -0,0 +1,36 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class OwlAnimals extends Command {
+ public constructor() {
+ super('owl', {
+ aliases: ['owl'],
+ category: 'animals',
+ description: {
+ content: 'Gives you a random owl!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS']
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const animal = await Axios.get(`http://pics.floofybot.moe/owl`).catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error with the (http://pics.floofybot.moe/owl) API.');
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('pics.floofybot.moe/owl', 'http://pics.floofybot.moe/assets/favicon.svg', 'http://pics.floofybot.moe/')
+ //@ts-ignore
+ .setImage(animal.data.image);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/anime/Darling.ts b/server/src/commands/anime/Darling.ts
new file mode 100644
index 0000000..5b5a7bb
--- /dev/null
+++ b/server/src/commands/anime/Darling.ts
@@ -0,0 +1,90 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Darling from '../../database/models/DarlingModel';
+import mongoose from 'mongoose';
+import { mongoDBUri } from '../../Config';
+mongoose.connect(mongoDBUri, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true
+});
+
+export default class DarlingAnime extends Command {
+ public constructor() {
+ super('darling', {
+ aliases: ['darling'],
+ category: 'anime',
+ description: {
+ content: 'Allows you to set, check or delete the/ a server\'s darling.',
+ usage: '[type]',
+ examples: [
+ '',
+ 'set',
+ 'remove',
+ 'check'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'Would you like to set, check or delete the current darling?',
+ retries: 3,
+ retry: 'Sorry, that was not a valid type.'
+ }
+ }
+ ],
+ userPermissions: ['MANAGE_GUILD']
+ });
+ }
+
+ public exec(msg: Message, { type }): Promise<Message> | any {
+ const darling = new Darling({
+ _id: mongoose.Types.ObjectId(),
+ username: msg.author.username,
+ userID: msg.author.id,
+ guildname: msg.guild.name,
+ guildID: msg.guild.id,
+ channelname: msg.channel,
+ channelID: msg.channel.id,
+ time: msg.createdAt
+ });
+
+ return Darling.findOne({ guildID: msg.guild.id }, async (error, guild) => {
+ if (error) return console.error(error);
+
+ if (guild) {
+ if (type === 'remove') {
+ //@ts-ignore
+ if (msg.author.id !== guild.userID || msg.author.id !== msg.guild.owner.id)
+ return msg.reply('Only my darling or the guild owner can remove the current darling.');
+
+ await Darling.findOneAndDelete({ guildID: msg.guild.id });
+ return msg.channel.send('The current darling has been removed!');
+ } else if (type === 'set') {
+ //@ts-ignore
+ return msg.channel.send(`I already have a darling! It's **${guild.username}**! To set a new darling, either the current darling or the guild owner has to do \`${this.client.commandHandler.prefix}darling remove\`.`);
+ } else if (type === 'check') {
+ //@ts-ignore
+ return msg.channel.send(`My darling is ${guild.username}.`);
+ }
+ } else if (!guild) {
+ if (type === 'remove') {
+ return msg.channel.send('There is no darling set in this server.');
+ } else if (type === 'set') {
+ await darling.save().catch(err => console.error(err));
+ const quotes = [
+ 'I think I have taken a liking to you. Won\'t you be my darling?',
+ 'I like the look in your eyes. It makes my heart race. You are now my darling!',
+ 'Wow, your taste makes my heart race. It bites and lingers... The taste of danger. You are now my darling!'
+ ];
+ return msg.channel.send(quotes[Math.floor(Math.random() * quotes.length)]);
+ } else if (type === 'check') {
+ return msg.reply(`I haven't found my darling yet! To set one, do ${this.client.commandHandler.prefix}darling set.`);
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/anime/Douse.ts b/server/src/commands/anime/Douse.ts
new file mode 100644
index 0000000..02b5771
--- /dev/null
+++ b/server/src/commands/anime/Douse.ts
@@ -0,0 +1,27 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class DouseAnime extends Command {
+ public constructor() {
+ super('douse', {
+ aliases: ['douse'],
+ category: 'anime',
+ description: {
+ content: 'Douses Zero Two.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setImage('https://i.pinimg.com/originals/6a/c8/26/6ac826e3d0cbd64eb4f42c12a73fcdb8.gif');
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/anime/Waifu.ts b/server/src/commands/anime/Waifu.ts
new file mode 100644
index 0000000..043b8ac
--- /dev/null
+++ b/server/src/commands/anime/Waifu.ts
@@ -0,0 +1,32 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+import request from 'node-superfetch';
+import Util from '../../utils/Utils';
+
+export default class WaifuAnime extends Command {
+ public constructor() {
+ super('waifu', {
+ aliases: ['waifu', 'thiswaifudoesnotexist'],
+ category: 'anime',
+ description: {
+ content: 'Sends a randomly generated waifu with a backstory. WARNING: don\'t get too attatched.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const num = Math.floor(Math.random() * 100000);
+ const { text } = await request.get(`https://www.thiswaifudoesnotexist.net/snippet-${num}.txt`);
+ const embed = this.client.util.embed()
+ .setDescription(Util.shorten(text, 1000))
+ .setColor(colour)
+ .setThumbnail(`https://www.thiswaifudoesnotexist.net/example-${num}.jpg`);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/bot/Info.ts b/server/src/commands/bot/Info.ts
new file mode 100644
index 0000000..cda8f7a
--- /dev/null
+++ b/server/src/commands/bot/Info.ts
@@ -0,0 +1,61 @@
+import { Command, version as akairoversion } from 'discord-akairo';
+import { Message, version as djsversion } from 'discord.js';
+import { stripIndents } from 'common-tags';
+import * as moment from 'moment';
+import 'moment-duration-format';
+import { colour, owners } from '../../Config';
+
+export default class InfoBot extends Command {
+ public constructor() {
+ super('info', {
+ aliases: ['info', 'stats', 'uptime'],
+ category: 'bot',
+ description: {
+ content: 'Provides some information/ stats on the bot.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ // @ts-ignore
+ const duration = moment.duration(this.client.uptime!).format(' D[d] H[h] m[m] s[s]');
+ const embed = this.client.util.embed()
+ .setTitle(`${this.client.user!.username} Stats`)
+ .setColor(colour)
+ .setThumbnail(this.client.user!.displayAvatarURL())
+ .addField(`\`⏰\` Uptime`, duration, true)
+ .addField(`\`💾\`Memory Usage`, `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB`, true)
+ .addField(
+ `\`📊\` General Stats`,
+ // • Servers: ${this.client.guilds.cache.size.toLocaleString('en-US')}
+ stripIndents`
+ • Channels: ${this.client.channels.cache.size.toLocaleString('en-US')}
+ • Users: ${this.client.guilds.cache
+ .reduce((prev, val) => prev + val.memberCount, 0)
+ .toLocaleString('en-US')}
+ `, true)
+ /* .addField(
+ '`👴` Reaction Role Stats',
+ stripIndents`
+ • Current: ${this.client.settings.cache.reactions.filter(r => r.active).size}
+ • Lifetime: ${this.client.settings.cache.reactions.size}
+ `,
+ true,
+ ) */
+ .addField(
+ '`📚` Library Info',
+ stripIndents`
+ [\`Akairo Framework\`](https://discord-akairo.github.io/#/): ${akairoversion}
+ [\`Discord.js\`](https://discord.js.org/#/): ${djsversion}
+ `, true)
+ .addField('`👧` Lead Developer', (await this.client.fetchApplication()).owner!.toString(), true)
+ .setFooter(`For more information about ${this.client.users.resolve(owners[0]).tag}, use ${this.client.commandHandler.prefix}sin`,
+ `${this.client.users.resolve(owners[0]).avatarURL()}`);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/bot/Invite.ts b/server/src/commands/bot/Invite.ts
new file mode 100644
index 0000000..a25a20c
--- /dev/null
+++ b/server/src/commands/bot/Invite.ts
@@ -0,0 +1,27 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class InviteBot extends Command {
+ public constructor() {
+ super('invite', {
+ aliases: ['invite'],
+ category: 'bot',
+ description: {
+ content: 'Gives you the bot\'s invite link.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setDescription('To invite the bot, please use [this link](https://kyzer.co/discord/bots/uwufier/).');
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/bot/Ping.ts b/server/src/commands/bot/Ping.ts
new file mode 100644
index 0000000..d7857c7
--- /dev/null
+++ b/server/src/commands/bot/Ping.ts
@@ -0,0 +1,23 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class PingBot extends Command {
+ public constructor() {
+ super('ping', {
+ aliases: ['ping'],
+ category: 'bot',
+ description: {
+ content: 'Check the latency of the ping to the Discord API.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ return msg.channel.send(`Pong! \`${this.client.ws.ping}ms\`.`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/bot/Sin.ts b/server/src/commands/bot/Sin.ts
new file mode 100644
index 0000000..66e1f2f
--- /dev/null
+++ b/server/src/commands/bot/Sin.ts
@@ -0,0 +1,35 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+import { stripIndents } from 'common-tags';
+
+export default class SinBot extends Command {
+ public constructor() {
+ super('sin', {
+ aliases: ['sin'],
+ category: 'bot',
+ description: {
+ content: 'Will get you more information about Sin, the lead developer.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .addField('Sin\'s Stuff', stripIndents`
+ 💎 [GitHub](https://github.com/8cy)
+ 🎀 [NPM](https://www.npmjs.com/~sinny)
+ 🎨 [Twitter](https://twitter.com/__cpuid)
+ 🎁 [Website](https://kyzer.co)
+ ✨ [YouTube](https://youtube.com/s1nny)
+ 🎐 [Top.gg Vote](https://discordbots.org/bot/699473263998271489/vote)
+ `, false)
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/bot/Suggest.ts b/server/src/commands/bot/Suggest.ts
new file mode 100644
index 0000000..7478182
--- /dev/null
+++ b/server/src/commands/bot/Suggest.ts
@@ -0,0 +1,35 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { owners } from '../../Config';
+
+export default class SuggestBot extends Command {
+ public constructor() {
+ super('suggest', {
+ aliases: ['suggest'],
+ category: 'bot',
+ description: {
+ content: 'Suggest a feature that the bot should add.',
+ usage: '[suggestion]',
+ examples: [
+ 'walter command please'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'suggestion',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to suggest?'
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { suggestion }): Promise<Message> {
+ await this.client.users.resolve(owners[0]).send(`**${msg.author.tag}** suggest; *${suggestion}*`);
+ return msg.channel.send('Thank you for your suggestion!');
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/emma/FanArt.ts b/server/src/commands/emma/FanArt.ts
new file mode 100644
index 0000000..3cab365
--- /dev/null
+++ b/server/src/commands/emma/FanArt.ts
@@ -0,0 +1,132 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import FanArt from '../../database/models/FanArtModel';
+import mongoose from 'mongoose';
+import { mongoDBUri, colour } from '../../Config';
+mongoose.connect(mongoDBUri, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true
+});
+
+export default class FanArtEmma extends Command {
+ public constructor() {
+ super('fanart', {
+ aliases: ['fanart', 'art'],
+ category: 'emma',
+ description: {
+ content: 'Allows you to set, check or delete the/ a server fanart channel.',
+ usage: '[type]',
+ examples: [
+ '',
+ 'set',
+ 'remove',
+ 'check'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'Would you like to set, check or delete the fanart channel?',
+ retries: 3,
+ retry: 'Sorry, that was not a valid type.'
+ }
+ },
+ {
+ id: 'comment',
+ type: 'string'
+ }
+ ],
+ userPermissions: ['MANAGE_GUILD']
+ });
+ }
+
+ public exec(msg: Message, { type, comment }): Promise<Message> | any {
+ if (msg.guild.id.toString() !== '663964105983393793') return;
+ const welcome = new FanArt({
+ _id: mongoose.Types.ObjectId(),
+ username: msg.author.username,
+ userID: msg.author.id,
+ guildname: msg.guild.name,
+ guildID: msg.guild.id,
+ channelname: msg.channel,
+ channelID: msg.channel.id,
+ time: msg.createdAt
+ });
+
+ const validTypes = ['set', 'remove', 'check'];
+
+ if (type === 'submit') {
+ FanArt.findOne({ guildID: msg.guild.id }, async (error, guild) => {
+ if (error) return console.log(error);
+
+ //@ts-ignore
+ let fanartServer = this.client.guilds.cache.get(guild.guildID);
+ //@ts-ignore
+ let fanartChannel = guild.channelID;
+
+ if (msg.attachments.size) {
+ msg.attachments.forEach(fanart => {
+ if (fanart.url) {
+ //@ts-ignore
+ return fanartServer.channels.cache.get(fanartChannel).send(`**New fanart submitted!**\nFanart by <@${msg.author.id}>.\n\n**Comment**\n${comment ? comment : 'None.'}\n\n**Video** ` + fanart.url)
+ .then(m => {
+ m.react('😍');
+ m.react('😂');
+ m.react('😁');
+ m.react('😳');
+ m.react('😱');
+ });
+ } else {
+ return msg.reply(`No attachment was submitted! If you need help, please do \`${this.client.commandHandler.prefix}fanart help\`.`);
+ }
+ });
+ } else {
+ return msg.reply(`No attachment was submitted! If you need help, please do \`${this.client.commandHandler.prefix}fanart help\`.`);
+ }
+ });
+ } else if (type === 'help') {
+ const embed = this.client.util.embed()
+ .setTitle('Fanart - Help')
+ .setColor(colour)
+ .setDescription('How to submmit fanart:')
+ .setThumbnail(msg.guild.iconURL())
+ .addField('#1', 'Go to the `#media` channel.')
+ .addField('#2', 'Click on the add media button in the bottom left corner of your screen and select a video or image.')
+ .addField('#3', 'In the message section, please put `uwu!art submit`.')
+ .addField('#4 (Optional)', 'If you would like, you can also put a comment on your fanart, you can do this by adding an extra string to the end of your submit command. e.g. `uwu!art submit this is where the comment goes!`, if you followed the steps correctly, your comment should be `this is where the comment goes!')
+ .addField('Admin Stuff', `If you are an admin or moderator who would like to set/ remove a fanart channel, you can do this by going to to the channel you would like to set as the new fanart channel and doing \`${this.client.commandHandler.prefix}fanart set\`, this will set the current channel as the fanart channel. To remove a fanart channel, just do \`${this.client.commandHandler.prefix}fanart remove\`.`)
+ .addField('More Admin Info', 'You can only have **ONE** fanart channel (I think, I haven\'t tested it lol. If you change the name of the fanart channel, you will have to re-register with the bot by simply removing and re-setting the fanart channel');
+ return msg.channel.send(embed);
+ } else if (validTypes.includes(type)) {
+ return FanArt.findOne({ guildID: msg.guild.id }, async (error, guild) => {
+ if (error) return console.error(error);
+
+ if (guild) {
+ if (type === 'remove') {
+ await FanArt.findOneAndDelete({ guildID: msg.guild.id });
+ return msg.channel.send('The current fanart channel has been unset!');
+ } else if (type === 'set') {
+ //@ts-ignore
+ return msg.channel.send(`There already is a fanart channel set! It's ${guild.channelname}`);
+ } else if (type === 'check') {
+ //@ts-ignore
+ return msg.channel.send(`The current fanart channel is ${guild.channelname}!`);
+ }
+ } else if (!guild) {
+ if (type === 'remove') {
+ return msg.channel.send('There is no current fanart channel set for this guild!');
+ } else if (type === 'set') {
+ await welcome.save().catch(err => console.error(err));
+ return msg.channel.send(`The fanart channel has been set to ${msg.channel!}`);
+ } else if (type === 'check') {
+ return msg.reply(`There is no current fanart channel set for this guild! To set one, do ${this.client.commandHandler.prefix}fanart set in the channel you want to set it in!`);
+ }
+ }
+ });
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/emma/UglyCat.ts b/server/src/commands/emma/UglyCat.ts
new file mode 100644
index 0000000..95193fe
--- /dev/null
+++ b/server/src/commands/emma/UglyCat.ts
@@ -0,0 +1,27 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class UglyCatEmma extends Command {
+ public constructor() {
+ super('uglycat', {
+ aliases: ['uglycat', 'ugycat'],
+ category: 'fun',
+ description: {
+ content: 'Ugly Cat.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setImage('https://i.pinimg.com/originals/4d/19/0f/4d190f1307b35e7155bb4b898e19d545.jpg');
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Advice.ts b/server/src/commands/fun/Advice.ts
new file mode 100644
index 0000000..17035c0
--- /dev/null
+++ b/server/src/commands/fun/Advice.ts
@@ -0,0 +1,29 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class AdviceFun extends Command {
+ public constructor() {
+ super('advice', {
+ aliases: ['advice'],
+ category: 'fun',
+ description: {
+ content: 'Gives you a random piece of advice.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const response = await Axios.get('http://api.adviceslip.com/advice').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error regarding the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(response.data.slip.advice);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Clapify.ts b/server/src/commands/fun/Clapify.ts
new file mode 100644
index 0000000..3e6e0fd
--- /dev/null
+++ b/server/src/commands/fun/Clapify.ts
@@ -0,0 +1,39 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class ClapifyFun extends Command {
+ public constructor() {
+ super('clapify', {
+ aliases: ['clapify'],
+ category: 'fun',
+ description: {
+ content: 'Clapifies your specified text.',
+ usage: '[text]',
+ examples: [
+ 'clap this lol'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'text',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to clapify?'
+ },
+ match: 'rest'
+ },
+ {
+ id: 'deleteinitialmessage',
+ flag: ['-delete', '-d'],
+ match: 'flag'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { text, deleteinitialmessage }): Promise<Message> {
+ if (deleteinitialmessage) msg.delete();
+ return msg.channel.send(text.split(' ').join('👏'));
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/DateFact.ts b/server/src/commands/fun/DateFact.ts
new file mode 100644
index 0000000..6fe94bc
--- /dev/null
+++ b/server/src/commands/fun/DateFact.ts
@@ -0,0 +1,52 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class DateFactFun extends Command {
+ public constructor() {
+ super('datefact', {
+ aliases: ['datefact'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a fact about a specified date.',
+ usage: '[numeric day] [numeric month]',
+ examples: [
+ '8 4'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'day',
+ type: 'integer',
+ prompt: {
+ start: 'What day would you like to get facts for? (Numeric value)',
+ retry: 'That is not a valid day, please try again.',
+ retries: 3
+ },
+ default: 'random',
+ },
+ {
+ id: 'month',
+ type: 'integer',
+ prompt: {
+ start: 'What month would you like to get facts for? (Numeric value)',
+ retry: 'That is not a valid month, please try again.',
+ retries: 3
+ },
+ default: 'random',
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { day, month }): Promise<Message> {
+ const uri = `http://numbersapi.com/${month === 'random' || day === 'random' ? 'random' : `${month}/${day}/date`}`;
+ const fact = await Axios.get(uri).catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error regarding the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(fact.data);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/DayFact.ts b/server/src/commands/fun/DayFact.ts
new file mode 100644
index 0000000..df5d3b7
--- /dev/null
+++ b/server/src/commands/fun/DayFact.ts
@@ -0,0 +1,41 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class DayFactFun extends Command {
+ public constructor() {
+ super('dayfact', {
+ aliases: ['dayfact'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a fact about a specified day.',
+ usage: '[numeric day]',
+ examples: [
+ '8'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'day',
+ type: 'integer',
+ prompt: {
+ start: 'What day would you like to get facts for? (Numeric value)',
+ retry: 'That is not a valid day, please try again.',
+ retries: 3
+ },
+ default: 'random',
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { day }): Promise<Message> {
+ const fact = await Axios.get(`http://numbersapi.com/${day}/date`).catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(fact.data);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/FML.ts b/server/src/commands/fun/FML.ts
new file mode 100644
index 0000000..041622c
--- /dev/null
+++ b/server/src/commands/fun/FML.ts
@@ -0,0 +1,33 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import request from 'node-superfetch';
+import * as cheerio from 'cheerio';
+
+export default class FMLFun extends Command {
+ public constructor() {
+ super('fml', {
+ aliases: ['fml'],
+ category: 'fun',
+ description: {
+ content: 'Gives you a random FML.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ //@ts-ignore
+ const { text } = await request.get('http://www.fmylife.com/random').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (http://www.fmylife.com/random) API.');
+ });
+ const $ = cheerio.load(text, { normalizeWhitespace: true });
+ const fml = $('a.article-link').first().text().trim();
+ //@ts-ignore
+ return msg.reply(fml);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Fact b/server/src/commands/fun/Fact
new file mode 100644
index 0000000..c942b45
--- /dev/null
+++ b/server/src/commands/fun/Fact
@@ -0,0 +1,67 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import request from 'node-superfetch';
+
+export default class FactFun extends Command {
+ public constructor() {
+ super('fact', {
+ aliases: ['fact', 'facts'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a random fact.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const article = await this.randomWikipediaArticle();
+ const body = await request.get('https://en.wikipedia.org/w/api.php')
+ .query({
+ action: 'query',
+ prop: 'extracts',
+ format: 'json',
+ titles: article,
+ exintro: '',
+ explaintext: '',
+ redirects: '',
+ //@ts-ignore
+ formatversion: 2
+ })
+ .catch(err => {
+ console.error(err);
+ msg.reply('Woops, there was an error regarding the (http://en.wikipedia.org) API.');
+ });
+ //@ts-ignore
+ let fact = body.query.pages[0].extract;
+ if (fact.length > 200) {
+ const facts = fact.split('.');
+ fact = `${facts[0]}.`;
+ if (fact.length < 200 && facts.length > 1) fact += `${facts[1]}.`;
+ }
+ return msg.reply(fact);
+ }
+
+ public async randomWikipediaArticle() {
+ const { body } = await request.get('https://en.wikipedia.org/w/api.php')
+ .query({
+ action: 'query',
+ list: 'random',
+ //@ts-ignore
+ rnnamespace: 0,
+ //@ts-ignore
+ rnlimit: 1,
+ format: 'json',
+ //@ts-ignore
+ formatversion: 2
+ });
+ //@ts-ignore
+ if (!body.query.random[0].title) return 'Facts are hard to find sometimes.';
+ //@ts-ignore
+ return body.query.random[0].title;
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/GitHubZen.ts b/server/src/commands/fun/GitHubZen.ts
new file mode 100644
index 0000000..a1ffeee
--- /dev/null
+++ b/server/src/commands/fun/GitHubZen.ts
@@ -0,0 +1,29 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class GitHubZenFun extends Command {
+ public constructor() {
+ super('githubzen', {
+ aliases: ['githubzen', 'github-zen'],
+ category: 'fun',
+ description: {
+ content: 'Gives you a random GitHub design philosophy.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const text = await Axios.get('https://api.github.com/zen').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (http://api.github.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(text.data);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Hello.ts b/server/src/commands/fun/Hello.ts
new file mode 100644
index 0000000..a866d39
--- /dev/null
+++ b/server/src/commands/fun/Hello.ts
@@ -0,0 +1,23 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class HelloFun extends Command {
+ public constructor() {
+ super('hello', {
+ aliases: ['hello', 'hi'],
+ category: 'fun',
+ description: {
+ content: 'Say hello!',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ return msg.reply('Hi!');
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Insult.ts b/server/src/commands/fun/Insult.ts
new file mode 100644
index 0000000..941824f
--- /dev/null
+++ b/server/src/commands/fun/Insult.ts
@@ -0,0 +1,29 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class InsultFun extends Command {
+ public constructor() {
+ super('insult', {
+ aliases: ['insult', 'roast', 'roastwilly'],
+ category: 'fun',
+ description: {
+ content: 'Gives you a random insult.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const response = await Axios.get('https://evilinsult.com/generate_insult.php?lang=en&type=json').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error regarding the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(response.data.insult);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/NumberFact.ts b/server/src/commands/fun/NumberFact.ts
new file mode 100644
index 0000000..2739218
--- /dev/null
+++ b/server/src/commands/fun/NumberFact.ts
@@ -0,0 +1,41 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class NumberFactFun extends Command {
+ public constructor() {
+ super('numberfact', {
+ aliases: ['numberfact', 'number-fact', 'numfact', 'num-fact'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a facts about a specified number.',
+ usage: '[number]',
+ examples: [
+ '8'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'number',
+ type: 'integer',
+ prompt: {
+ start: 'What number would you like to get facts for? (Numeric value)',
+ retry: 'That is not a valid number, please try again.',
+ retries: 3
+ },
+ default: 'random',
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { number }): Promise<Message> {
+ const fact = await Axios.get(`http://numbersapi.com/${number}`).catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(fact.data);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Onion.ts b/server/src/commands/fun/Onion.ts
new file mode 100644
index 0000000..e056ec7
--- /dev/null
+++ b/server/src/commands/fun/Onion.ts
@@ -0,0 +1,35 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { stripIndents } from 'common-tags';
+import RSS from 'rss-parser';
+
+export default class OnionFun extends Command {
+ public constructor() {
+ super('onion', {
+ aliases: ['onion', 'theonion', 'the-onion'],
+ category: 'fun',
+ description: {
+ content: 'Gives you a random Onion article.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const parser = new RSS();
+ const feed = await parser.parseURL('https://www.theonion.com/rss').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error regarding the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ const article = feed.items[Math.floor(Math.random() * feed.items.length)];
+ return msg.reply(stripIndents`
+ ${article.title}
+ ${article.link}
+ `);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Opinion.ts b/server/src/commands/fun/Opinion.ts
new file mode 100644
index 0000000..012af7e
--- /dev/null
+++ b/server/src/commands/fun/Opinion.ts
@@ -0,0 +1,34 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class OpinionFun extends Command {
+ public constructor() {
+ super('opinion', {
+ aliases: ['opinion'],
+ category: 'fun',
+ description: {
+ content: 'Determines the bot\'s opinion on something. WARNING: do not take these seriously.',
+ usage: '[question]',
+ examples: [
+ 'avocadoes'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'question',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to get an opinion on?'
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { question }): Promise<Message> {
+ const opinions = ['👍', '👎'];
+ return msg.reply(`*${question}* ${opinions[Math.floor(Math.random() * opinions.length)]}`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/PayRespects.ts b/server/src/commands/fun/PayRespects.ts
new file mode 100644
index 0000000..a7c9c68
--- /dev/null
+++ b/server/src/commands/fun/PayRespects.ts
@@ -0,0 +1,24 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { MessageReaction } from 'discord.js';
+
+export default class PayRespectsFun extends Command {
+ public constructor() {
+ super('payrespects', {
+ aliases: ['payrespects', 'respect', 'f'],
+ category: 'fun',
+ description: {
+ content: 'Press F to pay respects.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message | MessageReaction> {
+ return msg.channel.send('Press F to pay respects').then(m => m.react('🇫'));
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Rate.ts b/server/src/commands/fun/Rate.ts
new file mode 100644
index 0000000..d377c11
--- /dev/null
+++ b/server/src/commands/fun/Rate.ts
@@ -0,0 +1,33 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class RateFun extends Command {
+ public constructor() {
+ super('rate', {
+ aliases: ['rate'],
+ category: 'fun',
+ description: {
+ content: 'Determines the bot\'s rating on something. WARNING: do not take these seriously.',
+ usage: '[question/ item/ topic]',
+ examples: [
+ 'avocadoes'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'item',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to get a rating on?'
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { item }): Promise<Message> {
+ return msg.reply(`I'd give *${item}* a rating of **${Math.floor(Math.random() * 10) + 1}/ 10**!`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Say.ts b/server/src/commands/fun/Say.ts
new file mode 100644
index 0000000..876f539
--- /dev/null
+++ b/server/src/commands/fun/Say.ts
@@ -0,0 +1,40 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { validIDs, owners } from '../../Config';
+
+export default class SayFun extends Command {
+ public constructor() {
+ super('say', {
+ aliases: ['say'],
+ category: 'fun',
+ description: {
+ content: 'Allows you to speak as the bot.',
+ usage: '[text]',
+ examples: [
+ 'hi this is bot'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'text',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to say?'
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { text }): Promise<Message> {
+ console.log(text)
+ if (validIDs.includes(msg.author.id) || owners.includes(msg.author.id)) {
+ msg.delete();
+ return msg.channel.send(text);
+ }
+
+ return msg.delete();
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Spoiler.ts b/server/src/commands/fun/Spoiler.ts
new file mode 100644
index 0000000..fb061f9
--- /dev/null
+++ b/server/src/commands/fun/Spoiler.ts
@@ -0,0 +1,39 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class SpoilerFun extends Command {
+ public constructor() {
+ super('spoiler', {
+ aliases: ['spoiler'],
+ category: 'fun',
+ description: {
+ content: 'Turn every character in a specified phrase as a ||s||||p||||o||||i||||l||||e||||r||.',
+ usage: '[text]',
+ examples: [
+ 'hide this lol'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'text',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to *spoil* (hide)?'
+ },
+ match: 'rest'
+ },
+ {
+ id: 'deleteinitialmessage',
+ flag: ['-delete', '-d'],
+ match: 'flag'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { text, deleteinitialmessage }): Promise<Message> {
+ if (deleteinitialmessage) msg.delete();
+ return msg.channel.send(text.replace(/./g, '||$&||'));
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/Uwufy.ts b/server/src/commands/fun/Uwufy.ts
new file mode 100644
index 0000000..15aff06
--- /dev/null
+++ b/server/src/commands/fun/Uwufy.ts
@@ -0,0 +1,48 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class UwufyFun extends Command {
+ public constructor() {
+ super('uwufy', {
+ aliases: ['uwufy', 'owofy'],
+ category: 'fun',
+ description: {
+ content: 'Uwufys a specified string.',
+ usage: '[text]',
+ examples: [
+ 'how are you doing today?'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'text',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to uwufy?'
+ },
+ match: 'rest'
+ },
+ {
+ id: 'deleteinitialmessage',
+ flag: ['-delete', '-d'],
+ match: 'flag'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { text, deleteinitialmessage }): Promise<Message> {
+ if (deleteinitialmessage) msg.delete();
+ text.replace(/(?:l|r)/g, 'w');
+ text.replace(/(?:L|R)/g, 'W');
+ text.replace(/!+/g, ` >w< `);
+
+ const f = (Math.random() < 0.25)
+ if (f) {
+ let c = text.charAt(0);
+ text = c + '-' + text
+ }
+ return msg.channel.send(`*${text}*`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/YearFact.ts b/server/src/commands/fun/YearFact.ts
new file mode 100644
index 0000000..e6b2208
--- /dev/null
+++ b/server/src/commands/fun/YearFact.ts
@@ -0,0 +1,41 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class YearFactFun extends Command {
+ public constructor() {
+ super('yearfact', {
+ aliases: ['yearfact'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a fact about a specified year.',
+ usage: '[numeric year]',
+ examples: [
+ '1995'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'year',
+ type: 'integer',
+ prompt: {
+ start: 'What year would you like to get facts for? (Numeric value)',
+ retry: 'That is not a valid year, please try again.',
+ retries: 3
+ },
+ default: 'random',
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { year }): Promise<Message> {
+ const fact = await Axios.get(`http://numbersapi.com/${year}/year`).catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (http://numbersapi.com) API.');
+ });
+ //@ts-ignore
+ return msg.reply(fact.data);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/fun/YoMomma.ts b/server/src/commands/fun/YoMomma.ts
new file mode 100644
index 0000000..f156e8d
--- /dev/null
+++ b/server/src/commands/fun/YoMomma.ts
@@ -0,0 +1,29 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+
+export default class YoMommaFun extends Command {
+ public constructor() {
+ super('yomomma', {
+ aliases: ['yomomma', 'yo-momma'],
+ category: 'fun',
+ description: {
+ content: 'Grabs a "Yo Momma" joke.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ const fact = await Axios.get('https://api.yomomma.info/').catch(err => {
+ console.error(err);
+ return msg.reply('Woops, there was an error with the (https://api.yomomma.info/) API.');
+ });
+ //@ts-ignore
+ return msg.reply(fact.data.joke);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/minigames/8Ball.ts b/server/src/commands/minigames/8Ball.ts
new file mode 100644
index 0000000..2ff3904
--- /dev/null
+++ b/server/src/commands/minigames/8Ball.ts
@@ -0,0 +1,31 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import * as EightBallResponses from '../../json/8ball.json'
+import { colour } from '../../Config';
+
+export default class EightBallMinigames extends Command {
+ public constructor() {
+ super('8ball', {
+ aliases: ['8ball', '8b', '8-ball', '8-b'],
+ category: 'minigames',
+ description: {
+ content: 'Shake the magic 8 Ball for a fortune!',
+ usage: '[question]',
+ examples: [
+ 'will I ever get married?'
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ let randomResponse = EightBallResponses.standard[Math.floor(Math.random() * EightBallResponses.standard.length)];
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('The 8-ball says',
+ 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fd/8-Ball_Pool.svg/500px-8-Ball_Pool.svg.png')
+ .setDescription(`\`${randomResponse}\``);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/minigames/Coinflip.ts b/server/src/commands/minigames/Coinflip.ts
new file mode 100644
index 0000000..1962b00
--- /dev/null
+++ b/server/src/commands/minigames/Coinflip.ts
@@ -0,0 +1,50 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class CoinflipMinigames extends Command {
+ public constructor() {
+ super('coinflip', {
+ aliases: ['coinflip', 'flipcoin', 'coin-flip', 'flip-coin'],
+ category: 'minigames',
+ description: {
+ content: 'Flip a coin.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'What type of coinflip would you like?',
+ retry: 'That is not a valid type',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { type }): Promise<Message> {
+ let outcomes;
+ let quantum = false;
+ if (type === 'quantum' || type === 'q') {
+ outcomes = ['NaN', '0', 'null', 'undefined', ''];
+ quantum = true;
+ } else {
+ outcomes = ['heads!', 'tails!'];
+ }
+ const side = outcomes[Math.floor(Math.random() * outcomes.length)];
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor(`The ${quantum ? 'quantum' : ''} coin landed on`,
+ 'https://i.imgur.com/pr7JCce.png')
+ .setDescription(`\`${side}\``);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/minigames/RollDie.ts b/server/src/commands/minigames/RollDie.ts
new file mode 100644
index 0000000..8631874
--- /dev/null
+++ b/server/src/commands/minigames/RollDie.ts
@@ -0,0 +1,29 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class RollDieMinigames extends Command {
+ public constructor() {
+ super('rolldie', {
+ aliases: ['rolldie', 'rolldice', 'roll-die', 'roll-dice'],
+ category: 'minigames',
+ description: {
+ content: 'Roll a die.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const sides = [1, 2, 3, 4, 5, 6];
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor('The die landed on', 'https://i.imgur.com/dK18NpV.png')
+ .setDescription(`\`${sides[Math.floor(Math.random() * sides.length)]}\``);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/minigames/RussianRoulette.ts b/server/src/commands/minigames/RussianRoulette.ts
new file mode 100644
index 0000000..c52a4c8
--- /dev/null
+++ b/server/src/commands/minigames/RussianRoulette.ts
@@ -0,0 +1,27 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class RussianRouletteMinigames extends Command {
+ public constructor() {
+ super('russianroulette', {
+ aliases: ['russianroulette', 'rr'],
+ category: 'minigames',
+ description: {
+ content: 'Play a round of Russian Roulette.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const chamber = Math.floor(Math.random() * 6);
+ if (chamber === 0)
+ return msg.reply('💥 *Bang.* You lose.');
+ else
+ return msg.reply("🔫 *Click.* You survived.");
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/mod/Ban.ts b/server/src/commands/mod/Ban.ts
new file mode 100644
index 0000000..d91731e
--- /dev/null
+++ b/server/src/commands/mod/Ban.ts
@@ -0,0 +1,64 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class BanMod extends Command {
+ public constructor() {
+ super('ban', {
+ aliases: ['ban', 'banish'],
+ category: 'moderation',
+ description: {
+ content: 'Ban a specified user from the server.',
+ usage: '[user] [reason(s)]',
+ examples: [
+ '@sin#1337',
+ '@sin#1337 too cool'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ clientPermissions: ['BAN_MEMBERS'],
+ userPermissions: ['BAN_MEMBERS'],
+ args: [
+ {
+ id: 'user',
+ type: 'string',
+ prompt: {
+ start: 'Which user would you like to ban?',
+ retry: 'That doesn\' seem to be a user, please try again!'
+ }
+ },
+ {
+ id: 'reason',
+ type: 'string',
+ prompt: {
+ start: 'For what reason would you like to ban this user?',
+ optional: true
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { user, reason }): Promise<Message> {
+ if (msg.mentions.members.first()) user = msg.mentions.members.first().id;
+
+ if (user === this.client.user.id) return msg.channel.send('You can\'t ban me!');
+ if (!reason) reason = 'No reason has been specified.';
+ if (user === msg.author.id) return msg.channel.send('You can\'t ban yourself!');
+
+ if (msg.mentions.members.first()) {
+ user = msg.mentions.members.first();
+ await user.send(`You have been banned from **${msg.guild.name}** for the following reason(s): "**${reason}**".`);
+ // .catch(() => console.log('[ERROR] Could not send message to banned user.'));
+
+ return user.ban({ reason: `Banned by: ${msg.author.username} for the following reason(s): ${reason}.`})
+ .then(() => msg.reply(`${user.user.username} was successfully banned with the following reason(s): "**${reason}**".`))
+ .catch(err => console.error(err));
+ } else {
+ msg.guild.members.ban(user, { reason: `Banned by: ${msg.author.username} for the following reason(s): ${reason}.`})
+ .then(() => msg.reply(`User ID ${user} was successfully banned with the following reason(s): "**${reason}**".`))
+ .catch(err => console.error(err));
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/mod/Kick.ts b/server/src/commands/mod/Kick.ts
new file mode 100644
index 0000000..3295c2a
--- /dev/null
+++ b/server/src/commands/mod/Kick.ts
@@ -0,0 +1,57 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class KickMod extends Command {
+ public constructor() {
+ super('kick', {
+ aliases: ['kick'],
+ category: 'moderation',
+ description: {
+ content: 'Kick a specified user from the server.',
+ usage: '[user] [reason(s)]',
+ examples: [
+ '@sin#1337',
+ '@sin#1337 too cool'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ clientPermissions: ['KICK_MEMBERS'],
+ userPermissions: ['KICK_MEMBERS'],
+ args: [
+ {
+ id: 'user',
+ type: 'string',
+ prompt: {
+ start: 'Which user would you like to kick?',
+ retry: 'That doesn\' seem to be a user, please try again!'
+ }
+ },
+ {
+ id: 'reason',
+ type: 'string',
+ prompt: {
+ start: 'For what reason would you like to kick this user?',
+ optional: true
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { user, reason }): Promise<Message> {
+ if (msg.mentions.members.first()) user = msg.mentions.members.first().id;
+
+ if (user === this.client.user.id) return msg.channel.send('You can\'t kick me!');
+ if (!reason) reason = 'No reason has been specified.';
+ if (user === msg.author.id) return msg.channel.send('You can\'t kick yourself!');
+
+ await user.send(`You have been kick from **${msg.guild.name}** for the following reason(s): "**${reason}**".`);
+ // .catch(() => console.log('[ERROR] Could not send message to banned user.'));
+
+ return user.kick({ reason: `Kicked by: ${msg.author.username} for the following reason(s): ${reason}.`})
+ .then(() => msg.reply(`${user.user.username} was successfully kick with the following reason(s): "**${reason}**".`))
+ .catch(err => console.error(err));
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/mod/Prune.ts b/server/src/commands/mod/Prune.ts
new file mode 100644
index 0000000..bf56846
--- /dev/null
+++ b/server/src/commands/mod/Prune.ts
@@ -0,0 +1,39 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { TextChannel } from 'discord.js';
+
+export default class PruneMod extends Command {
+ public constructor() {
+ super('prune', {
+ aliases: ['prune', 'clear', 'purge'],
+ category: 'moderation',
+ description: {
+ content: 'Bulk delete a specified amount of message from the server.',
+ usage: '[amount]',
+ examples: [
+ '50'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ clientPermissions: ['MANAGE_MESSAGES'],
+ userPermissions: ['MANAGE_MESSAGES'],
+ args: [
+ {
+ id: 'amount',
+ type: 'integer',
+ prompt: {
+ start: 'How many messages would you like to delete?'
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { amount }): Promise<Message> {
+ if (amount <= 100) amount = 99;
+ (msg.channel as TextChannel).bulkDelete(amount, true);
+ return msg.reply('Due to Discord API limitations, the amount of messages you have specified has been rounded down to **99**. (This message will automatically be deleted in 3 seconds.)')
+ .then(m => m.delete({ timeout: 3000 }));
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/mod/Slowmode.ts b/server/src/commands/mod/Slowmode.ts
new file mode 100644
index 0000000..1d626ec
--- /dev/null
+++ b/server/src/commands/mod/Slowmode.ts
@@ -0,0 +1,61 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class SlowmodeMod extends Command {
+ public constructor() {
+ super('slowmode', {
+ aliases: ['slowmode', 'slow', 'cooldown'],
+ category: 'moderation',
+ description: {
+ content: 'Add a specified amount of slowmode to the current channel.',
+ usage: '[amount 1-120] [time slowmode should be active for]',
+ examples: [
+ '5 60'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ clientPermissions: ['MANAGE_CHANNELS'],
+ userPermissions: ['MANAGE_CHANNELS'],
+ args: [
+ {
+ id: 'amount',
+ type: 'integer',
+ prompt: {
+ start: 'What amount of slowmode would you like to add to the channel?'
+ }
+ },
+ {
+ id: 'realtime',
+ type: 'integer',
+ prompt: {
+ start: 'How long would you like the slowmode to last?',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { amount, realtime }): Promise<Message> {
+ try {
+ if (amount > 120) return msg.channel.send('Due to Discord API limitations, slow mode can only be a max of **120** seconds or less!');
+
+ // msg.channel.setRateLimitPerUser(amount);
+
+ if (realtime) {
+ let time = 60000 * realtime;
+ msg.channel.send(`Slowmode has now been set to ${amount} seconds and will end in ${realtime} minutes!`);
+ setTimeout(() => {
+ // msg.channel.setRateLimitPerUser(0);
+ return msg.channel.send('Slowmode has now been disabled!');
+ }, time);
+ } else {
+ if (amount == 0) return msg.channel.send('Slowmode has now been disabled!');
+ return msg.channel.send(`Slowmode has now been set to ${amount} seconds!`);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/mod/Unban.ts b/server/src/commands/mod/Unban.ts
new file mode 100644
index 0000000..7e66af1
--- /dev/null
+++ b/server/src/commands/mod/Unban.ts
@@ -0,0 +1,38 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class UnbanMod extends Command {
+ public constructor() {
+ super('unban', {
+ aliases: ['unban'],
+ category: 'moderation',
+ description: {
+ content: 'Unban a specified user from the server.',
+ usage: '[user id]',
+ examples: [
+ '50'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ clientPermissions: ['BAN_MEMBERS'],
+ userPermissions: ['BAN_MEMBERS'],
+ args: [
+ {
+ id: 'user',
+ type: 'integer',
+ prompt: {
+ start: 'Which user would you like to unban?',
+ retry: 'That doesn\' seem to be a user ID, please try again!'
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { user }): Promise<Message> {
+ return msg.guild.members.unban(user.toString()) // Does this really need to be returned?
+ .then(() => { return msg.reply(`User ID ${user} was successfully unbanned!`)})
+ .catch(() => { return msg.reply('Could not unban the specified user, are they banned in the first place?')});
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/nsfw/Danbooru.ts b/server/src/commands/nsfw/Danbooru.ts
new file mode 100644
index 0000000..15e08fa
--- /dev/null
+++ b/server/src/commands/nsfw/Danbooru.ts
@@ -0,0 +1,77 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class DanbooruNSFW extends Command {
+ public constructor() {
+ super('danbooru', {
+ aliases: ['danbooru'],
+ category: 'nsfw',
+ description: {
+ content: 'Danbooru.',
+ usage: '[tag]',
+ examples: [
+ '',
+ 'minecraft'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'tag',
+ type: 'string',
+ prompt: {
+ start: 'What tag would you like? (Only one is supported at this time because I have no idea how this API works...)',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { tag }): Promise<Message> {
+ //@ts-ignore
+ if (!msg.channel.nsfw) return msg.reply('This is not an NSFW marked channel!');
+
+ const tags = await tag.trim().toLowerCase();
+ const denylist = ['loli', 'shota', 'cub', 'young', 'child', 'baby', 'guro', 'gore', 'vote', 'scat', 'poop', 'kid', 'kiddie', 'kiddy', 'cp', 'shit', 'turd', 'feces', 'excrement', 'excrete'];
+
+ if (tags && denylist.includes(tags)) return msg.reply('A denylisted word was used! ⛔');
+
+ const response = await Axios.get(`https://danbooru.donmai.us/posts.json?limit=200&tags=${tags}+-rating:safe`)
+ .catch(error => {
+ console.error(error);
+ return msg.reply('Woops, there was an error regarding the (https://danbooru.donmai.us) API.');
+ });
+
+ //@ts-ignore
+ const randomInt = Math.floor(Math.random() * response.data.length);
+
+ //@ts-ignore
+ if (denylist.includes(response.data[randomInt].tags)) {
+ return msg.reply('Sorry! This image had a tag that was denylisted! ⛔');
+ }
+
+ let getRating = (rating: string) => {
+ switch (rating) {
+ case 's': return 'Safe'; break;
+ case 'q': return 'Questionable'; break;
+ case 'e': return 'Explicit'; break;
+ case 'u': return 'Unrated'; break;
+ }
+ }
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle(`Danbooru - ${!tags ? 'Random Image' : tags}`)
+ //@ts-ignore
+ .setDescription(`[Source](https://danbooru.donmai.us/posts/${response.data[randomInt].id})`)
+ //@ts-ignore
+ .setImage(response.data[randomInt].file_url)
+ .setTimestamp()
+ //@ts-ignore
+ .setFooter(`Score: ${response.data[randomInt].score} | Rating: ${getRating(response.data[randomInt].rating)}`, msg.author.avatarURL());
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/nsfw/Gelbooru.ts b/server/src/commands/nsfw/Gelbooru.ts
new file mode 100644
index 0000000..a858ea1
--- /dev/null
+++ b/server/src/commands/nsfw/Gelbooru.ts
@@ -0,0 +1,77 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class GelbooruNSFW extends Command {
+ public constructor() {
+ super('gelbooru', {
+ aliases: ['gelbooru'],
+ category: 'nsfw',
+ description: {
+ content: 'Gelbooru.',
+ usage: '[tag]',
+ examples: [
+ '',
+ 'minecraft'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'tag',
+ type: 'string',
+ prompt: {
+ start: 'What tag would you like? (Only one is supported at this time because I have no idea how this API works...)',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { tag }): Promise<Message> {
+ //@ts-ignore
+ if (!msg.channel.nsfw) return msg.reply('This is not an NSFW marked channel!');
+
+ const tags = await tag.trim().toLowerCase();
+ const denylist = ['loli', 'shota', 'cub', 'young', 'child', 'baby', 'guro', 'gore', 'vote', 'scat', 'poop', 'kid', 'kiddie', 'kiddy', 'cp', 'shit', 'turd', 'feces', 'excrement', 'excrete'];
+
+ if (tags && denylist.includes(tags)) return msg.reply('A denylisted word was used! ⛔');
+
+ const response = await Axios.get(`https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags=${tags}+-rating:safe&json=1`)
+ .catch(error => {
+ console.error(error);
+ return msg.reply('Woops, there was an error regarding the (https://gelbooru.com) API.');
+ });
+
+ //@ts-ignore
+ const randomInt = Math.floor(Math.random() * response.data.length);
+
+ //@ts-ignore
+ if (denylist.includes(response.data[randomInt].tags)) {
+ return msg.reply('Sorry! This image had a tag that was denylisted! ⛔');
+ }
+
+ let getRating = (rating: string) => {
+ switch (rating) {
+ case 's': return 'Safe'; break;
+ case 'q': return 'Questionable'; break;
+ case 'e': return 'Explicit'; break;
+ case 'u': return 'Unrated'; break;
+ }
+ }
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle(`Gelbooru - ${!tags ? 'Random Image' : tags}`)
+ //@ts-ignore
+ .setDescription(`[Source](https://gelbooru.com/index.php?page=post&s=view&id=${response.data[randomInt].id})`)
+ //@ts-ignore
+ .setImage(response.data[randomInt].file_url)
+ .setTimestamp()
+ //@ts-ignore
+ .setFooter(`Score: ${response.data[randomInt].score} | Rating: ${getRating(response.data[randomInt].rating)}`, msg.author.avatarURL());
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/nsfw/Rule34.ts b/server/src/commands/nsfw/Rule34.ts
new file mode 100644
index 0000000..bea3af1
--- /dev/null
+++ b/server/src/commands/nsfw/Rule34.ts
@@ -0,0 +1,68 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Axios from 'axios';
+import { colour } from '../../Config';
+
+export default class Rule34NSFW extends Command {
+ public constructor() {
+ super('rule34', {
+ aliases: ['rule34', 'r34'],
+ category: 'nsfw',
+ description: {
+ content: 'If it exists, theres porn of it. If there isn\'t, there will be.',
+ usage: '[tag]',
+ examples: [
+ '',
+ 'minecraft'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'tag',
+ type: 'string',
+ prompt: {
+ start: 'What tag would you like? (Only one is supported at this time because I have no idea how this API works...)',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public async exec(msg: Message, { tag }): Promise<Message> {
+ //@ts-ignore
+ if (!msg.channel.nsfw) return msg.reply('This is not an NSFW marked channel!');
+
+ const tags = await tag.trim().toLowerCase();
+ const denylist = ['loli', 'shota', 'cub', 'young', 'child', 'baby', 'guro', 'gore', 'vote', 'scat', 'poop', 'kid', 'kiddie', 'kiddy', 'cp', 'shit', 'turd', 'feces', 'excrement', 'excrete'];
+
+ if (tags && denylist.includes(tags)) return msg.reply('A denylisted word was used! ⛔');
+
+ const response = await Axios.get(`http://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags=${tags}+-rating:safe&json=1`)
+ .catch(error => {
+ console.error(error);
+ return msg.reply('Woops, there was an error regarding the (https://rule34.xxx) API.');
+ });
+
+ //@ts-ignore
+ const randomInt = Math.floor(Math.random() * response.data.length);
+
+ //@ts-ignore
+ if (denylist.includes(response.data[randomInt].tags)) {
+ return msg.reply('Sorry! This image had a tag that was denylisted! ⛔');
+ }
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle(`Rule34 - ${!tags ? 'Random Image' : tags}`)
+ //@ts-ignore
+ .setDescription(`[Source](https://rule34.xxx/index.php?page=post&s=view&id=${response.data[randomInt].id})`)
+ //@ts-ignore
+ .setImage(`https://rule34.xxx/images/${response.data[randomInt].directory}/${response.data[randomInt].image}`)
+ .setTimestamp()
+ //@ts-ignore
+ .setFooter(`Score: ${response.data[randomInt].score} | Rating: ${getRating(response.data[randomInt].rating)}`, msg.author.avatarURL());
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/DM.ts b/server/src/commands/owner/DM.ts
new file mode 100644
index 0000000..e0e793a
--- /dev/null
+++ b/server/src/commands/owner/DM.ts
@@ -0,0 +1,90 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class DMOwner extends Command {
+ public constructor() {
+ super('dm', {
+ aliases: ['dm', 'pm'],
+ category: 'owner',
+ description: {
+ content: 'DM a specified user.',
+ usage: '[user id] [message]',
+ examples: [
+ '217348698294714370 hi'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'user',
+ type: 'string'
+ },
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'What type of DM would you like to send the specified user?',
+ retry: 'That is not a valid DM type!'
+ }
+ },
+ {
+ id: 'text',
+ type: 'string',
+ prompt: {
+ start: 'What would you like to send to the specified user?'
+ },
+ match: 'rest'
+ }
+ ],
+ ownerOnly: true
+ });
+ }
+
+ public exec(msg: Message, { user, type, text }): Promise<Message> {
+ if (type == 'embed') {
+ function uuidv4() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+ let r = Math.random() * 16 | 0, v = c == 'x' ? r : (4 & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ }
+
+ const uuid = uuidv4();
+
+ user = this.client.users.resolve(user);
+ if (!user) return msg.channel.send('An incorrect user ID was provided.');
+
+ const embed = this.client.util.embed()
+ .setTitle('You received a message from the developer!')
+ .setColor(colour)
+ .setDescription(text)
+ .setFooter(`If you wish to respond, use the following command: ${this.client.commandHandler.prefix}feedback --reply ${uuid} <message>`)
+ .setTimestamp();
+
+ let attachment = (msg.attachments).array();
+ if (attachment[0]) {
+ this.client.users.resolve(user).send(embed, { files: [attachment[0].url] })
+ .then(() => { return msg.channel.send(`A DM has successfully been sent to ${user.username}.`)})
+ .catch(() => { return msg.channel.send(`Could not send a DM to ${user.username}.`)});
+ } else {
+ this.client.users.resolve(user).send(embed)
+ .then(() => { return msg.channel.send(`A DM has successfully been sent to ${user.tag}.`)})
+ .catch(() => { return msg.channel.send(`Could not send a DM to ${user.tag}.`)});
+ }
+ } else if (type === 'normal') {
+ let attachment = (msg.attachments).array();
+ if (attachment[0]) {
+ this.client.users.resolve(user).send(text, { files: [attachment[0].url] })
+ .then(() => { return msg.channel.send(`A DM has successfully been sent to ${user.username}.`)})
+ .catch(() => { return msg.channel.send(`Could not send a DM to ${user.username}.`)});
+ } else {
+ this.client.users.resolve(user).send(text)
+ .then(() => { return msg.channel.send(`A DM has successfully been sent to ${user.tag}.`)})
+ .catch(() => { return msg.channel.send(`Could not send a DM to ${user.tag}.`)});
+ }
+ }
+
+ return;
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/IP.ts b/server/src/commands/owner/IP.ts
new file mode 100644
index 0000000..244c11f
--- /dev/null
+++ b/server/src/commands/owner/IP.ts
@@ -0,0 +1,27 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import request from 'node-superfetch';
+
+export default class IPOwner extends Command {
+ public constructor() {
+ super('ip', {
+ aliases: ['ip'],
+ category: 'owner',
+ description: {
+ content: 'Gives you the bot\'s IP address.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ ownerOnly: true
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message> {
+ let { body } = await request.get('https://api.ipify.org').query({ format: 'json' });
+ //@ts-ignore
+ return msg.reply(`${this.client.user.username}'s IP address is **${body.ip}**. *Which script kiddie in chat asked you to send this zzz. -Sin*`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/Reload.ts b/server/src/commands/owner/Reload.ts
new file mode 100644
index 0000000..ac7bd1f
--- /dev/null
+++ b/server/src/commands/owner/Reload.ts
@@ -0,0 +1,40 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class ReloadOwner extends Command {
+ public constructor() {
+ super('reload', {
+ aliases: ['reload', 'reboot', 'reloadlistener'],
+ category: 'owner',
+ description: {
+ content: 'Reload a command.',
+ usage: '[command]',
+ examples: [
+ 'ping'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'command',
+ type: 'string',
+ prompt: {
+ start: 'What command would you like to reload?',
+ },
+ match: 'rest'
+ }
+ ],
+ ownerOnly: true
+ });
+ }
+
+ public exec(msg: Message, { command }): Promise<Message> {
+ if (msg.util.parsed.alias == 'reloadlistener') {
+ this.client.listenerHandler.reload(command);
+ return msg.channel.send(`Successfully reloaded the listener ${command}`);
+ } else {
+ this.handler.reload(command);
+ return msg.channel.send(`Successfully reloaded the command ${command}`);
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/ServerCount.ts b/server/src/commands/owner/ServerCount.ts
new file mode 100644
index 0000000..5068c22
--- /dev/null
+++ b/server/src/commands/owner/ServerCount.ts
@@ -0,0 +1,24 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class ServerCountOwner extends Command {
+ public constructor() {
+ super('servercount', {
+ aliases: ['servercount', 'server-count'],
+ category: 'owner',
+ description: {
+ content: 'Check the amount of servers the bot is in.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ ownerOnly: true
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ return msg.channel.send(`${this.client.user.username} is currently in **${this.client.guilds.cache.size}** server(s).`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/Status.ts b/server/src/commands/owner/Status.ts
new file mode 100644
index 0000000..8bf8dad
--- /dev/null
+++ b/server/src/commands/owner/Status.ts
@@ -0,0 +1,35 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class StatusOwner extends Command {
+ public constructor() {
+ super('status', {
+ aliases: ['status'],
+ category: 'owner',
+ description: {
+ content: 'Change the status of the bot.',
+ usage: '[status message]',
+ examples: [
+ 'hello, world!'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'status',
+ type: 'string',
+ prompt: {
+ start: 'Which status would you like to give me?',
+ },
+ match: 'rest'
+ }
+ ],
+ ownerOnly: true
+ });
+ }
+
+ public exec(msg: Message, { status }): Promise<Message> {
+ this.client.user.setActivity(status);
+ return msg.channel.send(`My status has not been set to ${status}!`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/owner/Username.ts b/server/src/commands/owner/Username.ts
new file mode 100644
index 0000000..df6d6c4
--- /dev/null
+++ b/server/src/commands/owner/Username.ts
@@ -0,0 +1,35 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class UsernameOwner extends Command {
+ public constructor() {
+ super('username', {
+ aliases: ['username'],
+ category: 'owner',
+ description: {
+ content: 'Change the username of the bot.',
+ usage: '[username]',
+ examples: [
+ 'Aki'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'username',
+ type: 'string',
+ prompt: {
+ start: 'What username would you like to give me?',
+ },
+ match: 'rest'
+ }
+ ],
+ ownerOnly: true
+ });
+ }
+
+ public exec(msg: Message, { username }): Promise<Message> {
+ this.client.user.setUsername(username);
+ return msg.channel.send(`My username has now been set to ${username}!`)
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/reaction/List.ts b/server/src/commands/reaction/List.ts
new file mode 100644
index 0000000..36c1156
--- /dev/null
+++ b/server/src/commands/reaction/List.ts
@@ -0,0 +1,46 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { oneLine } from 'common-tags';
+import { colour } from '../../Config';
+
+export default class ListReaction extends Command {
+ public constructor() {
+ super('reactionlist', {
+ aliases: ['reactionlist', 'reactionls'],
+ category: 'reactions',
+ description: {
+ content: 'Lists all current reaction roles.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ userPermissions: ['MANAGE_ROLES'],
+ channel: 'guild'
+ });
+ }
+
+ public async exec(msg: Message): Promise<Message | Message[]> {
+ const reactions = this.client.settings.cache.reactions.filter(r => r.guildID === msg.guild!.id && r.active);
+ if (!reactions.size) return msg.reply('you have no live reaction roles!');
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle('Live Reaction Roles')
+ .setDescription(
+ reactions
+ .map(r => {
+ const emoji = r.emojiType === 'custom' ? this.client.emojis.cache.get(r.emoji) : r.emoji;
+ return oneLine`[\`${r.id}\`] ${emoji}
+ ${this.client.channels.cache.get(r.channelID) || '#deleted-channel'}
+ ${msg.guild!.roles.cache.get(r.roleID) || '@deleted-role'}
+ `;
+ })
+ .join('\n')
+ .substring(0, 2048),
+ );
+
+ return msg.reply({ embed });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/reaction/New.ts b/server/src/commands/reaction/New.ts
new file mode 100644
index 0000000..c695dbc
--- /dev/null
+++ b/server/src/commands/reaction/New.ts
@@ -0,0 +1,168 @@
+import { Command } from 'discord-akairo';
+import { Message, MessageReaction, Permissions, Role, TextChannel, User } from 'discord.js';
+import { stripIndents } from 'common-tags';
+import * as nodemoji from 'node-emoji';
+import { colour } from '../../Config';
+
+export default class NewReaction extends Command {
+ public constructor() {
+ super('reactionnew', {
+ aliases: ['reactionnew', 'reactionadd'],
+ category: 'reactions',
+ description: {
+ content: 'Create a new reaction role.',
+ usage: '[type] [channel] [message id] [emoji] [role]',
+ examples: [
+ '1 #welcome 603009228180815882 🍕 @Pizza Lover'
+ ]
+ },
+ ratelimit: 3,
+ userPermissions: ['MANAGE_ROLES'],
+ clientPermissions: ['ADD_REACTIONS', 'MANAGE_ROLES', 'MANAGE_MESSAGES'],
+ channel: 'guild'
+ });
+ }
+
+ public *args(m: Message): object {
+ const type = yield {
+ type: 'number',
+ prompt: {
+ start: stripIndents`
+ What type of reaction role do you wish to create?
+
+ \`[1]\` for react to add and remove. *Classic*
+ ~~\`[2]\` for react to add only.
+ \`[3]\` for react to delete only.~~
+ `,
+ restart: stripIndents`
+ Please provide a valid number for which type of reaction role do you wish to create?
+
+ \`[1]\` Both react to add and remove. *Classic*
+ ~~\`[2]\` Only react to add.
+ \`[3]\` Only react to remove role.~~
+ `,
+ },
+ };
+
+ const channel = yield {
+ type: 'textChannel',
+ prompt: {
+ start: "What channel of the message you'd like to add this reaction role to?",
+ retry: 'Please provide a valid channel.',
+ },
+ };
+
+ const message = yield {
+ type: async (_: Message, str: string): Promise<null | Message> => {
+ if (str) {
+ try {
+ const m = await channel.messages.fetch(str);
+ if (m) return m;
+ } catch {}
+ }
+ return null;
+ },
+ prompt: {
+ start: 'What is the ID of the message you want to add that reaction role to?',
+ retry: 'Please provide a valid message ID.',
+ },
+ };
+
+ const emoji = yield {
+ type: async (_: Message, str: string): Promise<string | null> => {
+ if (str) {
+ const unicode = nodemoji.find(str);
+ if (unicode) return unicode.emoji;
+
+ const custom = this.client.emojis.cache.find(r => r.toString() === str);
+ if (custom) return custom.id;
+ return null;
+ }
+
+ const message = await m.channel.send(
+ stripIndents`Please **react** to **this** message with the emoji you wish to use?
+ If it's a custom emoji, please ensure I'm in the server that it's from!`,
+ );
+ // Please **react** to **this** message or respond with the emoji you wish to use?
+ // If it's a custom emoji, please ensure I'm in the server that it's from!
+
+ const collector = await message.awaitReactions((_: MessageReaction, u: User): boolean => m.author.id === u.id, {
+ max: 1,
+ });
+ if (!collector || collector.size !== 1) return null;
+
+ const collected = collector.first()!;
+
+ if (collected.emoji.id) {
+ const emoji = this.client.emojis.cache.find(e => e.id === collected.emoji.id);
+ if (emoji) return emoji.id;
+ return null;
+ }
+
+ return null;
+ },
+ prompt: {
+ start:
+ "Please **respond** to **this** message with the emoji you wish to use? If it's a custom emoji, please ensure I'm in the server that it's from!",
+ retry:
+ "Please **respond** to **this** message with a valid emoji. If it's a custom emoji, please ensure I'm in the server that it's from!",
+ },
+ };
+
+ const role = yield {
+ type: 'role',
+ match: 'rest',
+ prompt: {
+ start: 'What role would you like to apply when they react?',
+ retry: 'Please provide a valid role.',
+ },
+ };
+
+ return { type, channel, message, emoji, role };
+ }
+
+ public async exec(msg: Message, { type, channel, message, emoji, role }: { type: number; channel: TextChannel; message: Message; emoji: string; role: Role }): Promise<Message | Message[] | void> {
+ if (!channel.permissionsFor(this.client.user!.id)!.has(Permissions.FLAGS.ADD_REACTIONS))
+ return msg.reply(`I'm missing the permissions to react in ${channel}!`);
+
+ const reaction = await message.react(emoji).catch((err: Error) => err);
+
+ if (reaction instanceof Error)
+ return msg.reply(`an error occurred when trying to react to that message: \`${reaction}\`.`);
+
+ const id = this.makeID();
+
+ await this.client.settings.new('reaction', {
+ guildID: msg.guild!.id,
+ messageID: message.id,
+ userID: msg.author.id,
+ channelID: channel.id,
+ id,
+ emoji,
+ emojiType: emoji.length >= 3 ? 'custom' : 'unicode',
+ roleID: role.id,
+ uses: 0,
+ type,
+ });
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle('New Reaction Role!')
+ .setDescription("Please make sure my highest role is above the one you're trying to assign!")
+ .addField('🔢 Reference ID', id)
+ .addField('🏠 Channel', `${channel} \`[${channel.id}]\``)
+ .addField('💬 Message', `\`${message.id}\``)
+ .addField('🍕 Emoji', emoji.length >= 3 ? `${emoji} \`[${emoji}]\`` : emoji)
+ .addField('💼 Role', `${role} \`[${role.id}]\``);
+ return msg.channel.send({ embed });
+ }
+
+ public makeID(times?: number): string {
+ const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ return 'X'
+ .repeat(times || 4)
+ .split('')
+ .map(() => possible.charAt(Math.floor(Math.random() * possible.length)))
+ .join('');
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/reaction/Remove.ts b/server/src/commands/reaction/Remove.ts
new file mode 100644
index 0000000..0a58cdd
--- /dev/null
+++ b/server/src/commands/reaction/Remove.ts
@@ -0,0 +1,61 @@
+import { Command } from 'discord-akairo';
+import { Message, TextChannel } from 'discord.js';
+import { Reaction } from '../../database/models/ReactionModel';
+
+export default class RemoveReaction extends Command {
+ public constructor() {
+ super('reactionremove', {
+ aliases: ['reactionremove', 'reactionrm', 'reactiondelete', 'reactiondel'],
+ category: 'reactions',
+ description: {
+ content: 'Removes a reaction from a message via an discriminator.',
+ usage: '[discriminator]',
+ examples: [
+ '[fV9k]'
+ ]
+ },
+ ratelimit: 3,
+ userPermissions: ['MANAGE_ROLES'],
+ channel: 'guild',
+ args: [
+ {
+ id: 'reaction',
+ type: (msg: Message, str: string): Reaction | null => {
+ const req = this.client.settings.cache.reactions.find(r => r.id === str && r.guildID === msg.guild!.id);
+ if (!req) return null;
+ return req;
+ },
+ match: 'rest',
+ prompt: {
+ start: "Please provide the unique identifier for the reaction you'd like to delete.",
+ retry:
+ "Please provide a valid identifier for the reaction role you'd like to delete. You can also delete the whole message to delete reaction roles on it.",
+ },
+ },
+ ],
+ });
+ }
+
+ public async exec(msg: Message, { reaction }: { reaction: Reaction }): Promise<Message | Message[] | void> {
+ this.client.logger.info(reaction);
+ try {
+ const chan = this.client.channels.cache.get(reaction.channelID) as TextChannel;
+ if (!chan) throw new Error("That channel doesn't exist!");
+ const message = await chan.messages.fetch(reaction.messageID);
+ if (!message) throw new Error("That message doesn't exist!");
+ await message.reactions.cache.get(reaction.emoji)!.users.remove(this.client.user!.id);
+ } catch (err) {
+ this.client.logger.error(`[ERROR in REMOVE CMD]: ${err}.`);
+ }
+
+ this.client.settings.set(
+ 'reaction',
+ { messageID: reaction.messageID },
+ {
+ active: false,
+ },
+ );
+
+ return msg.reply('successfully deleted that reaction role.');
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/Goodbye.ts b/server/src/commands/server/Goodbye.ts
new file mode 100644
index 0000000..bc341b8
--- /dev/null
+++ b/server/src/commands/server/Goodbye.ts
@@ -0,0 +1,81 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Goodbye from '../../database/models/GoodbyeModel';
+import mongoose from 'mongoose';
+import { mongoDBUri } from '../../Config';
+mongoose.connect(mongoDBUri, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true
+});
+
+export default class GoodbyeServer extends Command {
+ public constructor() {
+ super('goodbye', {
+ aliases: ['goodbye'],
+ category: 'server',
+ description: {
+ content: 'Allows you to set, check or delete the/ a server goodbye message.',
+ usage: '[type]',
+ examples: [
+ '',
+ 'set',
+ 'remove',
+ 'check'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'Would you like to set, check or delete the goodbye channel?',
+ retries: 3,
+ retry: 'Sorry, that was not a valid type.'
+ }
+ }
+ ],
+ userPermissions: ['MANAGE_GUILD']
+ });
+ }
+
+ public exec(msg: Message, { type }): Promise<Message> | any {
+ const goodbye = new Goodbye({
+ _id: mongoose.Types.ObjectId(),
+ username: msg.author.username,
+ userID: msg.author.id,
+ guildname: msg.guild.name,
+ guildID: msg.guild.id,
+ channelname: msg.channel,
+ channelID: msg.channel.id,
+ time: msg.createdAt
+ });
+
+ return Goodbye.findOne({ guildID: msg.guild.id }, async (error, guild) => {
+ if (error) return console.error(error);
+
+ if (guild) {
+ if (type === 'remove') {
+ await Goodbye.findOneAndDelete({ guildID: msg.guild.id });
+ return msg.channel.send('The current goodbye channel has been unset!');
+ } else if (type === 'set') {
+ //@ts-ignore
+ return msg.channel.send(`There already is a goodbye channel set! It's ${guild.channelname}`);
+ } else if (type === 'check') {
+ //@ts-ignore
+ return msg.channel.send(`The current goodbye channel is ${guild.channelname}!`);
+ }
+ } else if (!guild) {
+ if (type === 'remove') {
+ return msg.channel.send('There is no current goodbye channel set for this guild!');
+ } else if (type === 'set') {
+ await goodbye.save().catch(err => console.error(err));
+ return msg.channel.send(`The goodbye channel has been set to ${msg.channel!}`);
+ } else if (type === 'check') {
+ return msg.reply(`There is no current goodbye channel set for this guild! To set one, do ${this.client.commandHandler.prefix}goodbye set in the channel you want to set it in!`);
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/MemberCount.ts b/server/src/commands/server/MemberCount.ts
new file mode 100644
index 0000000..ee870fe
--- /dev/null
+++ b/server/src/commands/server/MemberCount.ts
@@ -0,0 +1,24 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class MemberCountServer extends Command {
+ public constructor() {
+ super('membercount', {
+ aliases: ['membercount', 'mc', 'member-count', 'members'],
+ category: 'bot',
+ description: {
+ content: 'Grabs the current server\'s member count.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild'
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ return msg.reply(`There are **${msg.guild.memberCount}** members in **${msg.guild.name}**.`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/OldestMember.ts b/server/src/commands/server/OldestMember.ts
new file mode 100644
index 0000000..6fea716
--- /dev/null
+++ b/server/src/commands/server/OldestMember.ts
@@ -0,0 +1,39 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { formatDistance, formatRelative } from 'date-fns';
+
+export default class OldestMemberServer extends Command {
+ public constructor() {
+ super('oldestmember', {
+ aliases: ['oldestmember'],
+ category: 'bot',
+ description: {
+ content: 'Grabs the current server\'s oldest member (registration time).',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild'
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const oldest = msg.guild.members.cache.sort((member1, member2) => {
+ const timestamp1 = member1.user.createdTimestamp;
+ const timestamp2 = member2.user.createdTimestamp;
+
+ if (timestamp1 > timestamp2)
+ return 1;
+ else if (timestamp1 < timestamp2)
+ return -1;
+ return 0
+ }).first().user;
+
+ const { createdAt } = oldest;
+ const age = formatDistance(createdAt, new Date());
+ const date = formatRelative(createdAt, new Date());
+ return msg.reply(`${oldest.tag} is the oldest member in this server! Their account's age is **${age}** old (created **${date}**).`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/PFP.ts b/server/src/commands/server/PFP.ts
new file mode 100644
index 0000000..d5ac00b
--- /dev/null
+++ b/server/src/commands/server/PFP.ts
@@ -0,0 +1,63 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class PFPServer extends Command {
+ public constructor() {
+ super('pfp', {
+ aliases: ['pfp', 'avatar', 'avi'],
+ category: 'server',
+ description: {
+ content: 'Grabs a specified user\'s profile picture.',
+ usage: '[user]',
+ examples: [
+ '@sin#1337'
+ ]
+ },
+ ratelimit: 3,
+ args: [
+ {
+ id: 'user',
+ type: 'user',
+ prompt: {
+ start: 'Which user\'s avatar would you like to grab?',
+ retries: 3,
+ retry: 'Please choose a valid user.',
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { user }): Promise<Message> {
+ let embed = this.client.util.embed()
+ .setColor(colour)
+
+ if (!user) {
+ let format = msg.author.displayAvatarURL({ dynamic: true }).substr(msg.author.displayAvatarURL({ dynamic: true}).length - 3);
+ if (format == 'gif') {
+ embed.setAuthor(msg.author.username);
+ embed.setDescription(`[gif](${msg.author.displayAvatarURL({ format: 'gif', size: 2048 })})`);
+ embed.setImage(msg.author.displayAvatarURL({ format: 'gif', size: 2048 }));
+ } else {
+ embed.setAuthor(msg.author.username);
+ embed.setDescription(`[png](${msg.author.displayAvatarURL({ format: 'png', size: 2048 })}) | [jpeg](${user.displayAvatarURL({ format: 'jpeg', size: 2048 })}) | [webp](${user.displayAvatarURL({ format: 'webp', size: 2048 })})`);
+ embed.setImage(msg.author.displayAvatarURL({ format: 'png', size: 2048 }));
+ }
+ return msg.channel.send(embed);
+ } else {
+ let format = user.displayAvatarURL({ dynamic: true }).substr(user.displayAvatarURL({ dynamic: true}).length - 3);
+ if (format == 'gif') {
+ embed.setAuthor(user.username);
+ embed.setDescription(`[gif](${user.displayAvatarURL({ format: 'gif', size: 2048 })})`);
+ embed.setImage(user.displayAvatarURL({ format: 'gif', size: 2048 }));
+ } else {
+ embed.setAuthor(user.username);
+ embed.setDescription(`[png](${user.displayAvatarURL({ format: 'png', size: 2048 })}) | [jpeg](${user.displayAvatarURL({ format: 'jpeg', size: 2048 })}) | [webp](${user.displayAvatarURL({ format: 'webp', size: 2048 })})`);
+ embed.setImage(user.displayAvatarURL({ format: 'png', size: 2048 }));
+ }
+ return msg.channel.send(embed);
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/Poll.ts b/server/src/commands/server/Poll.ts
new file mode 100644
index 0000000..54b617f
--- /dev/null
+++ b/server/src/commands/server/Poll.ts
@@ -0,0 +1,42 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class PollServer extends Command {
+ public constructor() {
+ super('poll', {
+ aliases: ['poll', 'vote'],
+ category: 'server',
+ description: {
+ content: 'Make a poll.',
+ usage: '[poll contents]',
+ examples: [
+ 'Is Ice Cream good?'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'poll',
+ type: 'string',
+ prompt: {
+ start: 'What is the poll about?'
+ }
+ }
+ ],
+ userPermissions: ['MANAGE_MESSAGES']
+ });
+ }
+
+ public exec(msg: Message, { poll }): Promise<Message | void> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setFooter('React to vote.')
+ .setDescription(poll)
+ .setTitle(`Poll created by ${msg.author.username}`);
+ return msg.channel.send(embed).then(m => {
+ m.react('✅'); m.react('❎');
+ });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/RandomMember.ts b/server/src/commands/server/RandomMember.ts
new file mode 100644
index 0000000..55b5cec
--- /dev/null
+++ b/server/src/commands/server/RandomMember.ts
@@ -0,0 +1,23 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+
+export default class RandomMemberServer extends Command {
+ public constructor() {
+ super('randommember', {
+ aliases: ['randommember', 'randomuser', 'random-member', 'random-user', 'someone', '@someone'],
+ category: 'server',
+ description: {
+ content: 'Gets a random member from the current server.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ return msg.reply(`I choose ${msg.guild.members.cache.random().displayName}!`);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/Server.ts b/server/src/commands/server/Server.ts
new file mode 100644
index 0000000..7caf899
--- /dev/null
+++ b/server/src/commands/server/Server.ts
@@ -0,0 +1,39 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class ServerServer extends Command {
+ public constructor() {
+ super('server', {
+ aliases: ['server', 'serverinfo', 'server-info', 'serverstats', 'server-stats'],
+ category: 'server',
+ description: {
+ content: 'Gives you information about the current server.',
+ usage: '',
+ examples: [
+ ''
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild'
+ });
+ }
+
+ public exec(msg: Message): Promise<Message> {
+ const online = msg.guild.members.cache.filter(m => m.presence.status === 'online').size;
+
+ const embed = this.client.util.embed()
+ .setAuthor(`${msg.guild.name} - ${msg.guild.id}`, `${msg.guild.iconURL()}`, `https://discordapp.com/channels/${msg.guild.id}/${msg.guild.id}`)
+ .setDescription(`Heres's all the information on \`${msg.guild.name}\``)
+ .setThumbnail(`${msg.guild.iconURL()}`)
+ .addField('Owner', `\`${msg.guild.owner.user.tag}\``)
+ .addField(`Members [${msg.guild.memberCount}]`, `${online} members are online.`, true)
+ .addField(`Region`, `${msg.guild.region}`, true)
+ .addField(`Text Channels`, `${msg.guild.channels.cache.filter(c => c.type === 'text').size}`, true)
+ .addField(`Voice Channels`, `${msg.guild.channels.cache.filter(c => c.type === 'voice').size}`, true)
+ .addField('Guild Created', `${msg.guild.createdAt}`, false)
+ .addField(`${this.client.user.username} joined`, `${msg.guild.members.cache.get(this.client.user.id).joinedAt}`)
+ .setColor(colour);
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/User.ts b/server/src/commands/server/User.ts
new file mode 100644
index 0000000..26a24e1
--- /dev/null
+++ b/server/src/commands/server/User.ts
@@ -0,0 +1,75 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class UserServer extends Command {
+ public constructor() {
+ super('user', {
+ aliases: ['user', 'userinfo', 'user-info'],
+ category: 'bot',
+ description: {
+ content: 'Grabs information on a specified user.',
+ usage: '[user]',
+ examples: [
+ '@sin#1337'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'user',
+ type: 'user',
+ prompt: {
+ optional: true
+ }
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { user }): Promise<Message> {
+ if (!user) user == msg.author;
+
+ const member = msg.guild.member(user);
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setAuthor(`${user.tag} (${user.id})`, user.displayAvatarURL())
+ .addField('Highest rank HEX colour', member ? member.displayHexColor : 'No rank colour', true)
+ .addField('Joined guild at', member ? member.joinedAt : 'Not in this guild', true)
+ .addField('Date when account was created', user.createdAt, true)
+ .setTimestamp();
+ // embed.addField('-', '-');
+
+ // User status
+ if (user.presence.activities[0]) {
+ embed.addField('Presence', user.presence.activities[0], true);
+ if (user.presence.activities[0].details) embed.addField('Details', user.presence.activities[0].details, true);
+ if (user.presence.activities[0].state) embed.addField('State', user.presence.activities[0].state, true);
+ }
+ // Bot status
+ if (user.bot) embed.addField('Is a bot?', '✅', true);
+
+ // Show user locale
+ if (user.locale) embed.addField('Locale Settings', user.locale, true);
+
+ // Show user platform
+ if (user.presence.clientStatus && !user.bot) {
+ // embed.addField('-', '-');
+ if (user.presence.clientStatus.mobile) embed.addField('Using Discord on', '📱 ' + user.presence.clientStatus.mobile, true);
+ if (user.presence.clientStatus.desktop) embed.addField('Using Discord on', '💻 ' + user.presence.clientStatus.desktop, true);
+ if (user.presence.clientStatus.web) embed.addField('Using Discord on', '☁️ ' + user.presence.clientStatus.web, true);
+ }
+
+ if (member) {
+ // Boosting since
+ if (member.premiumSince) embed.addField('Boosting this guild since', member.premiumSince, true);
+ // Nickname
+ if (member.nickname) embed.addField('Nickname', member.nickname, true);
+ // Roles
+ if (member.roles) embed.addField('Roles', `${member.roles.cache.array().join(', ')}`);
+ }
+
+ return msg.channel.send(embed);
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/server/Welcome.ts b/server/src/commands/server/Welcome.ts
new file mode 100644
index 0000000..6c116d9
--- /dev/null
+++ b/server/src/commands/server/Welcome.ts
@@ -0,0 +1,81 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import Welcome from '../../database/models/WelcomeModel';
+import mongoose from 'mongoose';
+import { mongoDBUri } from '../../Config';
+mongoose.connect(mongoDBUri, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true
+});
+
+export default class WelcomeServer extends Command {
+ public constructor() {
+ super('welcome', {
+ aliases: ['welcome'],
+ category: 'server',
+ description: {
+ content: 'Allows you to set, check or delete the/ a server welcome message.',
+ usage: '[type]',
+ examples: [
+ '',
+ 'set',
+ 'remove',
+ 'check'
+ ]
+ },
+ ratelimit: 3,
+ channel: 'guild',
+ args: [
+ {
+ id: 'type',
+ type: 'string',
+ prompt: {
+ start: 'Would you like to set, check or delete the welcome channel?',
+ retries: 3,
+ retry: 'Sorry, that was not a valid type.'
+ }
+ }
+ ],
+ userPermissions: ['MANAGE_GUILD']
+ });
+ }
+
+ public exec(msg: Message, { type }): Promise<Message> | any {
+ const welcome = new Welcome({
+ _id: mongoose.Types.ObjectId(),
+ username: msg.author.username,
+ userID: msg.author.id,
+ guildname: msg.guild.name,
+ guildID: msg.guild.id,
+ channelname: msg.channel,
+ channelID: msg.channel.id,
+ time: msg.createdAt
+ });
+
+ return Welcome.findOne({ guildID: msg.guild.id }, async (error, guild) => {
+ if (error) return console.error(error);
+
+ if (guild) {
+ if (type === 'remove') {
+ await Welcome.findOneAndDelete({ guildID: msg.guild.id });
+ return msg.channel.send('The current welcome channel has been unset!');
+ } else if (type === 'set') {
+ //@ts-ignore
+ return msg.channel.send(`There already is a welcome channel set! It's ${guild.channelname}`);
+ } else if (type === 'check') {
+ //@ts-ignore
+ return msg.channel.send(`The current welcome channel is ${guild.channelname}!`);
+ }
+ } else if (!guild) {
+ if (type === 'remove') {
+ return msg.channel.send('There is no current welcome channel set for this guild!');
+ } else if (type === 'set') {
+ await welcome.save().catch(err => console.error(err));
+ return msg.channel.send(`The welcome channel has been set to ${msg.channel!}`);
+ } else if (type === 'check') {
+ return msg.reply(`There is no current welcome channel set for this guild! To set one, do ${this.client.commandHandler.prefix}welcome set in the channel you want to set it in!`);
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/util/Categories.ts b/server/src/commands/util/Categories.ts
new file mode 100644
index 0000000..7b07027
--- /dev/null
+++ b/server/src/commands/util/Categories.ts
@@ -0,0 +1,155 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class CategoriesUtil extends Command {
+ public constructor() {
+ super('categories', {
+ aliases: ['categories', 'category'],
+ category: 'utility',
+ description: {
+ content: 'Displays a list of categories or lists all commands in a specified category.',
+ usage: '[command]',
+ examples: [
+ '',
+ 'ping'
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS'],
+ args: [
+ {
+ id: 'category',
+ type: 'string',
+ prompt: {
+ start: 'Which category do you need help with?',
+ retry: 'Please provide a valid category.',
+ optional: true
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { category }): Promise<Message> {
+ if (!category) return this.execCategoryList(msg);
+
+ let categories = [];
+ for (const category of this.handler.categories.values()) {
+ if (!categories.includes(category.id)) categories.push(category.id);
+ }
+ if (categories.includes(category)) return this.execCommandList(msg, category);
+ }
+
+ public async execCommandList(message, categorii): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .addField('Command List', [
+ `This is a list of commands in the **${categorii}** category.`,
+ `To view details for a specific command, do \`${this.client.commandHandler.prefix}help <command>\`.`
+ ]);
+
+ for (const category of this.handler.categories.values()) {
+ let title
+ if (message.channel.type != 'dm' && message.guild.id == '663964105983393793') {
+ title = {
+ general: '📝\u2000General',
+ fun: '🎉\u2000Fun',
+ minigames: '🕹\u2000Minigames (WIP)',
+ images: '🖼\u2000Images',
+ utility: '🔩\u2000Utility',
+ moderation: '⚡\u2000Moderation',
+ owner: '🛠️\u2000Owner',
+ voice: '🎧\u2000Voice',
+ bot: '🤖\u2000Bot',
+ server: '🖥\u2000Server',
+ anime: '🎀\u2000Anime',
+ animals: '🐛\u2000Animals',
+ emma: '🔥\u2000Emma',
+ nsfw: '🔞\u2000NSFW',
+ reactions: '😮\u2000Reactions'
+ }[category.id];
+ } else {
+ title = {
+ general: '📝\u2000General',
+ fun: '🎉\u2000Fun',
+ minigames: '🕹\u2000Minigames (WIP)',
+ images: '🖼\u2000Images',
+ utility: '🔩\u2000Utility',
+ moderation: '⚡\u2000Moderation',
+ owner: '🛠️\u2000Owner',
+ voice: '🎧\u2000Voice',
+ bot: '🤖\u2000Bot',
+ server: '🖥\u2000Server',
+ anime: '🎀\u2000Anime',
+ animals: '🐛\u2000Animals',
+ nsfw: '🔞\u2000NSFW',
+ reactions: '😮\u2000Reactions'
+ }[category.id];
+ }
+
+ if (title && (category.id == categorii)) embed.addField(title, `${category.map(cmd => '`' + cmd.aliases[0] + '` - ' + cmd.description.content).join('\n')}`); // .join('`\n`')
+ }
+
+ return message.channel.send({ embed });
+ }
+
+ public async execCategoryList(message): Promise<Message> {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .addField('Category List', [
+ `To view details for a specific category, do \`${this.client.commandHandler.prefix}category <category>\`.`
+ ]);
+
+ let count;
+ for (const category of this.handler.categories.values()) {
+ count++;
+ let title
+ if (message.channel.type != 'dm' && message.guild.id == '663964105983393793') {
+ title = {
+ general: '📝\u2000General',
+ fun: '🎉\u2000Fun',
+ minigames: '🕹\u2000Minigames (WIP)',
+ images: '🖼\u2000Images',
+ utility: '🔩\u2000Utility',
+ moderation: '⚡\u2000Moderation',
+ owner: '🛠️\u2000Owner',
+ voice: '🎧\u2000Voice',
+ bot: '🤖\u2000Bot',
+ server: '🖥\u2000Server',
+ anime: '🎀\u2000Anime',
+ animals: '🐛\u2000Animals',
+ emma: '🔥\u2000Emma',
+ nsfw: '🔞\u2000NSFW',
+ reactions: '😮\u2000Reactions'
+ }[category.id];
+ } else {
+ title = {
+ general: '📝\u2000General',
+ fun: '🎉\u2000Fun',
+ minigames: '🕹\u2000Minigames (WIP)',
+ images: '🖼\u2000Images',
+ utility: '🔩\u2000Utility',
+ moderation: '⚡\u2000Moderation',
+ owner: '🛠️\u2000Owner',
+ voice: '🎧\u2000Voice',
+ bot: '🤖\u2000Bot',
+ server: '🖥\u2000Server',
+ anime: '🎀\u2000Anime',
+ animals: '🐛\u2000Animals',
+ nsfw: '🔞\u2000NSFW',
+ reactions: '😮\u2000Reactions'
+ }[category.id];
+ }
+
+ if (title)
+ if (count % 3 == 0)
+ embed.addField(`${title}`, `${category.size} commands`, false);
+ else
+ embed.addField(`${title}`, `${category.size} commands`, true);
+ }
+
+ return message.channel.send({ embed });
+ }
+} \ No newline at end of file
diff --git a/server/src/commands/util/Help.ts b/server/src/commands/util/Help.ts
new file mode 100644
index 0000000..76d2bf6
--- /dev/null
+++ b/server/src/commands/util/Help.ts
@@ -0,0 +1,100 @@
+import { Command } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { colour } from '../../Config';
+
+export default class HelpUtil extends Command {
+ public constructor() {
+ super('help', {
+ aliases: ['help'],
+ category: 'utility',
+ description: {
+ content: 'List help features or get information on a specified command.',
+ usage: '[command]',
+ examples: [
+ '',
+ '8ball'
+ ]
+ },
+ ratelimit: 3,
+ clientPermissions: ['EMBED_LINKS'],
+ args: [
+ {
+ id: 'command',
+ type: 'commandAlias',
+ prompt: {
+ start: 'Which command do you need help with?',
+ retry: 'Please provide a valid command.',
+ optional: true
+ },
+ match: 'rest'
+ }
+ ]
+ });
+ }
+
+ public exec(msg: Message, { command }): Promise<void | Message> {
+ if (!command) {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .addFields([
+ {
+ name: 'Online Command List',
+ value: '*Coming soon!*'
+ },
+ {
+ name: 'Specific Command Help',
+ value: `${this.handler.prefix}help <command>`
+ },
+ {
+ name: 'List of all public categories',
+ value: `${this.handler.prefix}categories`
+ }
+ ]);
+ return msg.channel.send({ embed });
+ }
+
+ const description = Object.assign({
+ content: 'No description available.',
+ usage: '',
+ examples: [],
+ fields: []
+ }, command.description);
+
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .setTitle(`\`${this.client.commandHandler.prefix}${command.aliases[0]} ${description.usage}\``)
+ .addField('Description', description.content);
+
+ for (const field of description.fields) embed.addField(field.name, field.value);
+
+ if (command.aliases.length > 1) {
+ embed.addField('Aliases', `\`${command.aliases.join('`, `')}\``, true);
+ }
+
+ if (command.description.examples.length >= 1) {
+ embed.addField('Examples', `\`${this.client.commandHandler.prefix}${command.aliases[0]} ${command.description.examples.join(`, ${this.client.commandHandler.prefix}${command.aliases[0]} `)}\``, true);
+ }
+
+ if (command.userPermissions) {
+ embed.addField('User permission', `\`${command.userPermissions.join('` `')}\``, true);
+ }
+
+ if (command.clientPermissions) {
+ embed.addField('Bot permission', `\`${command.clientPermissions.join('` `')}\``, true);
+ }
+
+ if (command.contentParser.flagWords.length) {
+ embed.addField('Command flags', `\`${command.contentParser.flagWords.join('` `')}\``, true);
+ }
+
+ if (command.contentParser.optionFlagWords.length) {
+ embed.addField('Command options flags', `\`${command.contentParser.optionFlagWords.join('` `')}\``, true);
+ }
+
+ if (msg.channel.type === 'dm') return msg.author.send({ embed });
+ return msg.reply('sending you a DM with information...').then(async m => {
+ await msg.author.send({ embed });
+ m.edit('I\'ve send you a DM with information!');
+ });
+ }
+} \ No newline at end of file