From bb511abc03bb66848947e37a999502b813c77269 Mon Sep 17 00:00:00 2001
From: 8cy <50817549+8cy@users.noreply.github.com>
Date: Thu, 23 Jul 2020 23:24:17 -0700
Subject: goodbye old uwufier :cry:
---
.gitignore | 3 +-
.todo | 12 -
Procfile | 1 -
aki-web.code-workspace | 10 +
client/.env | 1 +
client/.gitignore | 23 ++
client/README.md | 68 +++++
client/package.json | 38 +++
client/public/favicon.ico | Bin 0 -> 3150 bytes
client/public/index.html | 43 +++
client/public/manifest.json | 25 ++
client/public/robots.txt | 3 +
client/src/components/App.js | 61 +++++
.../manageserver/ManageServerSettings.js | 71 +++++
client/src/components/navigation/NavigationBar.js | 50 ++++
client/src/components/selection/ServerCard.js | 37 +++
client/src/index.js | 20 ++
client/src/pages/ManageServer.js | 69 +++++
client/src/pages/ServerSelection.js | 33 +++
client/src/serviceWorker.js | 141 ++++++++++
client/src/styles/styles.css | 8 +
fix/anime/neko.ts | 283 --------------------
fix/fun/dm.ts | 63 -----
fix/utility/math.ts | 56 ----
fix/voice/fart.js | 208 ---------------
fix/voice/moan.js | 170 ------------
fix/voice/squeak.js | 172 ------------
fix/voice/wahoo.js | 32 ---
package.json | 70 -----
package.json.scripts | 13 -
scripts/build.bat | 3 -
scripts/heroku_application_log.bat | 1 -
scripts/install_prerequisites.bat | 3 -
scripts/logs.bat | 1 -
scripts/prepare_build.bat | 3 -
server/package.json | 35 +++
server/src/API/API.ts | 33 +++
server/src/API/routers/GuildRouter.ts | 44 +++
server/src/API/routers/OAuth2Router.ts | 71 +++++
server/src/Bot.ts | 5 +
server/src/Config.ts | 12 +
server/src/client/BotClient.ts | 103 +++++++
server/src/commands/.todo | 44 +++
server/src/commands/animals/Bunny.ts | 36 +++
server/src/commands/animals/Cat.ts | 36 +++
server/src/commands/animals/Dog.ts | 36 +++
server/src/commands/animals/Duck.ts | 36 +++
server/src/commands/animals/Fox.ts | 36 +++
server/src/commands/animals/Owl.ts | 36 +++
server/src/commands/anime/Darling.ts | 90 +++++++
server/src/commands/anime/Douse.ts | 27 ++
server/src/commands/anime/Waifu.ts | 32 +++
server/src/commands/bot/Info.ts | 61 +++++
server/src/commands/bot/Invite.ts | 27 ++
server/src/commands/bot/Ping.ts | 23 ++
server/src/commands/bot/Sin.ts | 35 +++
server/src/commands/bot/Suggest.ts | 35 +++
server/src/commands/emma/FanArt.ts | 132 +++++++++
server/src/commands/emma/UglyCat.ts | 27 ++
server/src/commands/fun/Advice.ts | 29 ++
server/src/commands/fun/Clapify.ts | 39 +++
server/src/commands/fun/DateFact.ts | 52 ++++
server/src/commands/fun/DayFact.ts | 41 +++
server/src/commands/fun/FML.ts | 33 +++
server/src/commands/fun/Fact | 67 +++++
server/src/commands/fun/GitHubZen.ts | 29 ++
server/src/commands/fun/Hello.ts | 23 ++
server/src/commands/fun/Insult.ts | 29 ++
server/src/commands/fun/NumberFact.ts | 41 +++
server/src/commands/fun/Onion.ts | 35 +++
server/src/commands/fun/Opinion.ts | 34 +++
server/src/commands/fun/PayRespects.ts | 24 ++
server/src/commands/fun/Rate.ts | 33 +++
server/src/commands/fun/Say.ts | 40 +++
server/src/commands/fun/Spoiler.ts | 39 +++
server/src/commands/fun/Uwufy.ts | 48 ++++
server/src/commands/fun/YearFact.ts | 41 +++
server/src/commands/fun/YoMomma.ts | 29 ++
server/src/commands/minigames/8Ball.ts | 31 +++
server/src/commands/minigames/Coinflip.ts | 50 ++++
server/src/commands/minigames/RollDie.ts | 29 ++
server/src/commands/minigames/RussianRoulette.ts | 27 ++
server/src/commands/mod/Ban.ts | 64 +++++
server/src/commands/mod/Kick.ts | 57 ++++
server/src/commands/mod/Prune.ts | 39 +++
server/src/commands/mod/Slowmode.ts | 61 +++++
server/src/commands/mod/Unban.ts | 38 +++
server/src/commands/nsfw/Danbooru.ts | 77 ++++++
server/src/commands/nsfw/Gelbooru.ts | 77 ++++++
server/src/commands/nsfw/Rule34.ts | 68 +++++
server/src/commands/owner/DM.ts | 90 +++++++
server/src/commands/owner/IP.ts | 27 ++
server/src/commands/owner/Reload.ts | 40 +++
server/src/commands/owner/ServerCount.ts | 24 ++
server/src/commands/owner/Status.ts | 35 +++
server/src/commands/owner/Username.ts | 35 +++
server/src/commands/reaction/List.ts | 46 ++++
server/src/commands/reaction/New.ts | 168 ++++++++++++
server/src/commands/reaction/Remove.ts | 61 +++++
server/src/commands/server/Goodbye.ts | 81 ++++++
server/src/commands/server/MemberCount.ts | 24 ++
server/src/commands/server/OldestMember.ts | 39 +++
server/src/commands/server/PFP.ts | 63 +++++
server/src/commands/server/Poll.ts | 42 +++
server/src/commands/server/RandomMember.ts | 23 ++
server/src/commands/server/Server.ts | 39 +++
server/src/commands/server/User.ts | 75 ++++++
server/src/commands/server/Welcome.ts | 81 ++++++
server/src/commands/util/Categories.ts | 155 +++++++++++
server/src/commands/util/Help.ts | 100 +++++++
server/src/database/index.ts | 5 +
server/src/database/models/DarlingModel.ts | 10 +
server/src/database/models/FanArtModel.ts | 12 +
server/src/database/models/GoodbyeModel.ts | 12 +
server/src/database/models/ReactionGuildModel.ts | 16 ++
server/src/database/models/ReactionModel.ts | 36 +++
server/src/database/models/WelcomeModel.ts | 12 +
server/src/database/structures/SettingsProvider.ts | 212 +++++++++++++++
server/src/database/utils/Constants.ts | 8 +
server/src/inhibitors/sendMessages.ts | 18 ++
server/src/json/8ball.json | 37 +++
server/src/listeners/client/ReadyListener.ts | 59 ++++
server/src/listeners/client/channelDelete.ts | 21 ++
server/src/listeners/client/debug.ts | 15 ++
server/src/listeners/client/emojiDelete.ts | 20 ++
server/src/listeners/client/guildCreate.ts | 23 ++
server/src/listeners/client/messageDelete.ts | 20 ++
server/src/listeners/client/messageReactionAdd.ts | 65 +++++
.../src/listeners/client/messageReactionRemove.ts | 55 ++++
server/src/listeners/client/roleDelete.ts | 17 ++
server/src/structures/Interfaces.ts | 16 ++
server/src/structures/OAuth2.ts | 62 +++++
server/src/utils/Logger.ts | 44 +++
server/src/utils/Utils.ts | 5 +
server/tsconfig.json | 30 +++
spike/Procfile | 1 +
spike/package.json.scripts | 13 +
spike/tsconfig.json.dev | 70 +++++
spike/ws/css/main.css | 116 --------
spike/ws/favicon.ico | Bin 19125 -> 0 bytes
spike/ws/layouts/layout.hbs | 1 -
spike/ws/views/index.hbs | 1 -
src/app.ts | 8 -
src/assets/audio/farts/1.mp3 | Bin 1201502 -> 0 bytes
src/assets/audio/farts/2.mp3 | Bin 975523 -> 0 bytes
src/assets/audio/farts/3.mp3 | Bin 1396550 -> 0 bytes
src/assets/audio/farts/4.mp3 | Bin 928142 -> 0 bytes
src/assets/audio/farts/5.mp3 | Bin 10123976 -> 0 bytes
src/assets/audio/farts/6.mp3 | Bin 754181 -> 0 bytes
src/assets/audio/farts/7.mp3 | Bin 32409192 -> 0 bytes
src/assets/audio/farts/8.mp3 | Bin 2992326 -> 0 bytes
src/assets/audio/longest_fart_ever.mp3 | Bin 1041482 -> 0 bytes
src/assets/audio/squeak.wav | Bin 194146 -> 0 bytes
src/assets/audio/uhhhh.wav | Bin 133344 -> 0 bytes
src/assets/audio/wahoo.mp3 | Bin 1726068 -> 0 bytes
src/assets/json/meme.json | 15 --
src/assets/json/month.json | 14 -
src/bot.ts | 207 --------------
src/commands/animals/bunny.ts | 39 ---
src/commands/animals/cat.ts | 39 ---
src/commands/animals/cow.ts | 27 --
src/commands/animals/dog.ts | 39 ---
src/commands/animals/duck.ts | 39 ---
src/commands/animals/fox.ts | 39 ---
src/commands/animals/owl.ts | 39 ---
src/commands/anime/uwufy.ts | 39 ---
src/commands/anime/waifu.ts | 35 ---
src/commands/bot/api.ts | 27 --
src/commands/bot/clientid.ts | 25 --
src/commands/bot/commandsamount.ts | 28 --
src/commands/bot/email.ts | 27 --
src/commands/bot/generatecommands.ts | 42 ---
src/commands/bot/generateservers.ts | 36 ---
src/commands/bot/github.ts | 27 --
src/commands/bot/guildbackdoor.ts | 54 ----
src/commands/bot/invite.ts | 27 --
src/commands/bot/ip.ts | 38 ---
src/commands/bot/joinmessage.ts | 32 ---
src/commands/bot/leaveserver.ts | 39 ---
src/commands/bot/memorystats.ts | 39 ---
src/commands/bot/memoryusage.ts | 31 ---
src/commands/bot/npm.ts | 26 --
src/commands/bot/owner.ts | 26 --
src/commands/bot/servercount.ts | 29 --
src/commands/bot/status.ts | 99 -------
src/commands/bot/suggest.ts | 26 --
src/commands/bot/support.ts | 26 --
src/commands/bot/test.ts | 23 --
src/commands/bot/twitch.ts | 27 --
src/commands/bot/twitter.ts | 26 --
src/commands/bot/uptime.ts | 39 ---
src/commands/bot/version.ts | 35 ---
src/commands/bot/vote.ts | 25 --
src/commands/bot/website.ts | 27 --
src/commands/bot/youtube.ts | 27 --
src/commands/crypto/btc.ts | 42 ---
src/commands/crypto/btcchange.ts | 54 ----
src/commands/crypto/securitykey.ts | 27 --
src/commands/emma/art.ts | 202 --------------
src/commands/emma/verify.ts | 54 ----
src/commands/fun/8ball.ts | 76 ------
src/commands/fun/advice.ts | 27 --
src/commands/fun/aesthetic.ts | 37 ---
src/commands/fun/clapify.ts | 31 ---
src/commands/fun/coinflip.ts | 31 ---
src/commands/fun/culturedtext.ts | 53 ----
src/commands/fun/datefact.ts | 52 ----
src/commands/fun/dayfact.ts | 43 ---
src/commands/fun/dicksize.ts | 37 ---
src/commands/fun/dogeify.ts | 38 ---
src/commands/fun/drawcards.ts | 61 -----
src/commands/fun/embed.ts | 35 ---
src/commands/fun/emoji.ts | 24 --
src/commands/fun/fml.ts | 36 ---
src/commands/fun/gay.ts | 39 ---
src/commands/fun/githubzen.ts | 35 ---
src/commands/fun/hello.ts | 24 --
src/commands/fun/howify.ts | 39 ---
src/commands/fun/insult.ts | 25 --
src/commands/fun/iq.ts | 46 ----
src/commands/fun/kissmarrykill.ts | 74 -----
src/commands/fun/lorem.ts | 34 ---
src/commands/fun/modplz.ts | 29 --
src/commands/fun/motivate.ts | 29 --
src/commands/fun/numberfact.ts | 47 ----
src/commands/fun/oddcase.ts | 36 ---
src/commands/fun/offspring.ts | 25 --
src/commands/fun/onion.ts | 36 ---
src/commands/fun/opinion.ts | 36 ---
src/commands/fun/quantumcoinflip.ts | 31 ---
src/commands/fun/quote.ts | 80 ------
src/commands/fun/randomfacts.ts | 66 -----
src/commands/fun/rate.ts | 31 ---
src/commands/fun/respect.ts | 26 --
src/commands/fun/roastwilly.ts | 45 ----
src/commands/fun/roastwillyc.ts | 36 ---
src/commands/fun/rockpaperscissors.ts | 80 ------
src/commands/fun/rolldie.ts | 31 ---
src/commands/fun/russianroulette.ts | 29 --
src/commands/fun/say.ts | 46 ----
src/commands/fun/showerthought.ts | 29 --
src/commands/fun/smashorpass.ts | 42 ---
src/commands/fun/spoiler.ts | 31 ---
src/commands/fun/spongebob.ts | 39 ---
src/commands/fun/stretch.ts | 37 ---
src/commands/fun/subreddit.ts | 46 ----
src/commands/fun/surreal.ts | 36 ---
src/commands/fun/uglycat.ts | 23 --
src/commands/fun/yearfact.ts | 41 ---
src/commands/fun/yomomma.ts | 32 ---
src/commands/minecraft/getbody.ts | 45 ----
src/commands/minecraft/getface.ts | 43 ---
src/commands/minecraft/gethead.ts | 43 ---
src/commands/minecraft/getminime.ts | 59 ----
src/commands/minecraft/getskin.ts | 43 ---
src/commands/minecraft/minecraftserverstatus.ts | 76 ------
src/commands/moderation/addrole.ts | 57 ----
src/commands/moderation/ban.ts | 46 ----
src/commands/moderation/clear.ts | 100 -------
src/commands/moderation/kick.ts | 47 ----
src/commands/moderation/nickname.ts | 53 ----
src/commands/moderation/removerole.ts | 57 ----
src/commands/nsfw/danbooru.ts | 80 ------
src/commands/nsfw/gelbooru.ts | 80 ------
src/commands/nsfw/rule34.ts | 67 -----
src/commands/roleplay/blush.ts | 32 ---
src/commands/roleplay/celebrate.ts | 25 --
src/commands/roleplay/eat.ts | 25 --
src/commands/roleplay/fistbump.ts | 33 ---
src/commands/roleplay/highfive.ts | 33 ---
src/commands/roleplay/holdhands.ts | 33 ---
src/commands/roleplay/hug.ts | 32 ---
src/commands/roleplay/inhale.ts | 32 ---
src/commands/roleplay/kill.ts | 32 ---
src/commands/roleplay/kiss.ts | 32 ---
src/commands/roleplay/pat.ts | 32 ---
src/commands/roleplay/poke.ts | 32 ---
src/commands/roleplay/punch.ts | 32 ---
src/commands/roleplay/slap.ts | 32 ---
src/commands/roleplay/sleep.ts | 25 --
src/commands/roleplay/wakeup.ts | 26 --
src/commands/roleplay/wave.ts | 32 ---
src/commands/roleplay/wink.ts | 32 ---
src/commands/server/goodbye.ts | 101 -------
src/commands/server/membercount.ts | 31 ---
src/commands/server/oldestmember.ts | 53 ----
src/commands/server/poll.ts | 53 ----
src/commands/server/quotemessage.ts | 88 ------
src/commands/server/randommember.ts | 35 ---
src/commands/server/roleinfo.ts | 60 -----
src/commands/server/roles.ts | 40 ---
src/commands/server/server.ts | 54 ----
src/commands/server/welcome.ts | 101 -------
src/commands/user/age.ts | 42 ---
src/commands/user/id.ts | 32 ---
src/commands/user/nickname.ts | 36 ---
src/commands/user/nitro.ts | 31 ---
src/commands/user/pfp.ts | 45 ----
src/commands/utility/average.ts | 46 ----
src/commands/utility/csgoserverstatus.ts | 79 ------
src/commands/utility/fortnitestats.ts | 106 --------
src/commands/utility/gmodserverstatus.ts | 78 ------
src/commands/utility/google.ts | 34 ---
src/commands/utility/iss.ts | 33 ---
src/commands/utility/romannumeral.ts | 57 ----
src/commands/utility/rustserverstatus.ts | 78 ------
src/commands/utility/starboundserverstatus.ts | 99 -------
src/commands/utility/time.ts | 24 --
src/commands/utility/whois.ts | 39 ---
src/commands/utility/xorstr.ts | 23 --
src/commands/voice/abee.ts | 206 --------------
src/commands/voice/itemshop.ts | 213 ---------------
src/commands/voice/join.ts | 25 --
src/commands/voice/leave.ts | 43 ---
src/commands/voice/loop.ts | 40 ---
src/commands/voice/minecraft.ts | 209 ---------------
src/commands/voice/pause.ts | 39 ---
src/commands/voice/play.ts | 297 ---------------------
src/commands/voice/psycho.ts | 203 --------------
src/commands/voice/queue.ts | 60 -----
src/commands/voice/remove.ts | 48 ----
src/commands/voice/resume.ts | 38 ---
src/commands/voice/shuffle.ts | 63 -----
src/commands/voice/skip.ts | 36 ---
src/commands/voice/skipall.ts | 53 ----
src/commands/voice/skipto.ts | 53 ----
src/commands/voice/volume.ts | 52 ----
src/commands/zerotwo/darling.ts | 116 --------
src/commands/zerotwo/douse.ts | 28 --
src/commands/zerotwo/zerotwo.ts | 26 --
src/config.json | 15 --
src/models/Client.ts | 26 --
src/models/Command.ts | 19 --
src/models/FanArt.ts | 13 -
src/models/MemePoster.ts | 39 ---
src/models/MusicGuild.ts | 16 --
src/models/Verify.ts | 11 -
src/models/commands/AutoReply.ts | 17 --
src/models/commands/ImgurAlbum.ts | 43 ---
src/models/commands/Subreddit.ts | 78 ------
src/models/darling.ts | 11 -
src/models/goodbye.ts | 13 -
src/models/welcome.ts | 13 -
src/server.ts | 50 ----
src/types/image.ts | 41 ---
src/types/month.ts | 21 --
src/utils/Canvas.ts | 185 -------------
src/utils/Util.ts | 189 -------------
src/utils/gameDigHelper.ts | 47 ----
src/utils/genCmdURL.ts | 1 -
src/utils/simpleFormat.ts | 9 -
src/utils/stripWebhookURL.ts | 12 -
src/utils/truncateText.ts | 6 -
src/utils/wait.ts | 6 -
src/utils/winPercentage.ts | 16 --
tsconfig.json | 70 -----
356 files changed, 5241 insertions(+), 10854 deletions(-)
delete mode 100644 .todo
delete mode 100644 Procfile
create mode 100644 aki-web.code-workspace
create mode 100644 client/.env
create mode 100644 client/.gitignore
create mode 100644 client/README.md
create mode 100644 client/package.json
create mode 100644 client/public/favicon.ico
create mode 100644 client/public/index.html
create mode 100644 client/public/manifest.json
create mode 100644 client/public/robots.txt
create mode 100644 client/src/components/App.js
create mode 100644 client/src/components/manageserver/ManageServerSettings.js
create mode 100644 client/src/components/navigation/NavigationBar.js
create mode 100644 client/src/components/selection/ServerCard.js
create mode 100644 client/src/index.js
create mode 100644 client/src/pages/ManageServer.js
create mode 100644 client/src/pages/ServerSelection.js
create mode 100644 client/src/serviceWorker.js
create mode 100644 client/src/styles/styles.css
delete mode 100644 fix/anime/neko.ts
delete mode 100644 fix/fun/dm.ts
delete mode 100644 fix/utility/math.ts
delete mode 100644 fix/voice/fart.js
delete mode 100644 fix/voice/moan.js
delete mode 100644 fix/voice/squeak.js
delete mode 100644 fix/voice/wahoo.js
delete mode 100644 package.json
delete mode 100644 package.json.scripts
delete mode 100644 scripts/build.bat
delete mode 100644 scripts/heroku_application_log.bat
delete mode 100644 scripts/install_prerequisites.bat
delete mode 100644 scripts/logs.bat
delete mode 100644 scripts/prepare_build.bat
create mode 100644 server/package.json
create mode 100644 server/src/API/API.ts
create mode 100644 server/src/API/routers/GuildRouter.ts
create mode 100644 server/src/API/routers/OAuth2Router.ts
create mode 100644 server/src/Bot.ts
create mode 100644 server/src/Config.ts
create mode 100644 server/src/client/BotClient.ts
create mode 100644 server/src/commands/.todo
create mode 100644 server/src/commands/animals/Bunny.ts
create mode 100644 server/src/commands/animals/Cat.ts
create mode 100644 server/src/commands/animals/Dog.ts
create mode 100644 server/src/commands/animals/Duck.ts
create mode 100644 server/src/commands/animals/Fox.ts
create mode 100644 server/src/commands/animals/Owl.ts
create mode 100644 server/src/commands/anime/Darling.ts
create mode 100644 server/src/commands/anime/Douse.ts
create mode 100644 server/src/commands/anime/Waifu.ts
create mode 100644 server/src/commands/bot/Info.ts
create mode 100644 server/src/commands/bot/Invite.ts
create mode 100644 server/src/commands/bot/Ping.ts
create mode 100644 server/src/commands/bot/Sin.ts
create mode 100644 server/src/commands/bot/Suggest.ts
create mode 100644 server/src/commands/emma/FanArt.ts
create mode 100644 server/src/commands/emma/UglyCat.ts
create mode 100644 server/src/commands/fun/Advice.ts
create mode 100644 server/src/commands/fun/Clapify.ts
create mode 100644 server/src/commands/fun/DateFact.ts
create mode 100644 server/src/commands/fun/DayFact.ts
create mode 100644 server/src/commands/fun/FML.ts
create mode 100644 server/src/commands/fun/Fact
create mode 100644 server/src/commands/fun/GitHubZen.ts
create mode 100644 server/src/commands/fun/Hello.ts
create mode 100644 server/src/commands/fun/Insult.ts
create mode 100644 server/src/commands/fun/NumberFact.ts
create mode 100644 server/src/commands/fun/Onion.ts
create mode 100644 server/src/commands/fun/Opinion.ts
create mode 100644 server/src/commands/fun/PayRespects.ts
create mode 100644 server/src/commands/fun/Rate.ts
create mode 100644 server/src/commands/fun/Say.ts
create mode 100644 server/src/commands/fun/Spoiler.ts
create mode 100644 server/src/commands/fun/Uwufy.ts
create mode 100644 server/src/commands/fun/YearFact.ts
create mode 100644 server/src/commands/fun/YoMomma.ts
create mode 100644 server/src/commands/minigames/8Ball.ts
create mode 100644 server/src/commands/minigames/Coinflip.ts
create mode 100644 server/src/commands/minigames/RollDie.ts
create mode 100644 server/src/commands/minigames/RussianRoulette.ts
create mode 100644 server/src/commands/mod/Ban.ts
create mode 100644 server/src/commands/mod/Kick.ts
create mode 100644 server/src/commands/mod/Prune.ts
create mode 100644 server/src/commands/mod/Slowmode.ts
create mode 100644 server/src/commands/mod/Unban.ts
create mode 100644 server/src/commands/nsfw/Danbooru.ts
create mode 100644 server/src/commands/nsfw/Gelbooru.ts
create mode 100644 server/src/commands/nsfw/Rule34.ts
create mode 100644 server/src/commands/owner/DM.ts
create mode 100644 server/src/commands/owner/IP.ts
create mode 100644 server/src/commands/owner/Reload.ts
create mode 100644 server/src/commands/owner/ServerCount.ts
create mode 100644 server/src/commands/owner/Status.ts
create mode 100644 server/src/commands/owner/Username.ts
create mode 100644 server/src/commands/reaction/List.ts
create mode 100644 server/src/commands/reaction/New.ts
create mode 100644 server/src/commands/reaction/Remove.ts
create mode 100644 server/src/commands/server/Goodbye.ts
create mode 100644 server/src/commands/server/MemberCount.ts
create mode 100644 server/src/commands/server/OldestMember.ts
create mode 100644 server/src/commands/server/PFP.ts
create mode 100644 server/src/commands/server/Poll.ts
create mode 100644 server/src/commands/server/RandomMember.ts
create mode 100644 server/src/commands/server/Server.ts
create mode 100644 server/src/commands/server/User.ts
create mode 100644 server/src/commands/server/Welcome.ts
create mode 100644 server/src/commands/util/Categories.ts
create mode 100644 server/src/commands/util/Help.ts
create mode 100644 server/src/database/index.ts
create mode 100644 server/src/database/models/DarlingModel.ts
create mode 100644 server/src/database/models/FanArtModel.ts
create mode 100644 server/src/database/models/GoodbyeModel.ts
create mode 100644 server/src/database/models/ReactionGuildModel.ts
create mode 100644 server/src/database/models/ReactionModel.ts
create mode 100644 server/src/database/models/WelcomeModel.ts
create mode 100644 server/src/database/structures/SettingsProvider.ts
create mode 100644 server/src/database/utils/Constants.ts
create mode 100644 server/src/inhibitors/sendMessages.ts
create mode 100644 server/src/json/8ball.json
create mode 100644 server/src/listeners/client/ReadyListener.ts
create mode 100644 server/src/listeners/client/channelDelete.ts
create mode 100644 server/src/listeners/client/debug.ts
create mode 100644 server/src/listeners/client/emojiDelete.ts
create mode 100644 server/src/listeners/client/guildCreate.ts
create mode 100644 server/src/listeners/client/messageDelete.ts
create mode 100644 server/src/listeners/client/messageReactionAdd.ts
create mode 100644 server/src/listeners/client/messageReactionRemove.ts
create mode 100644 server/src/listeners/client/roleDelete.ts
create mode 100644 server/src/structures/Interfaces.ts
create mode 100644 server/src/structures/OAuth2.ts
create mode 100644 server/src/utils/Logger.ts
create mode 100644 server/src/utils/Utils.ts
create mode 100644 server/tsconfig.json
create mode 100644 spike/Procfile
create mode 100644 spike/package.json.scripts
create mode 100644 spike/tsconfig.json.dev
delete mode 100644 spike/ws/css/main.css
delete mode 100644 spike/ws/favicon.ico
delete mode 100644 spike/ws/layouts/layout.hbs
delete mode 100644 spike/ws/views/index.hbs
delete mode 100644 src/app.ts
delete mode 100644 src/assets/audio/farts/1.mp3
delete mode 100644 src/assets/audio/farts/2.mp3
delete mode 100644 src/assets/audio/farts/3.mp3
delete mode 100644 src/assets/audio/farts/4.mp3
delete mode 100644 src/assets/audio/farts/5.mp3
delete mode 100644 src/assets/audio/farts/6.mp3
delete mode 100644 src/assets/audio/farts/7.mp3
delete mode 100644 src/assets/audio/farts/8.mp3
delete mode 100644 src/assets/audio/longest_fart_ever.mp3
delete mode 100644 src/assets/audio/squeak.wav
delete mode 100644 src/assets/audio/uhhhh.wav
delete mode 100644 src/assets/audio/wahoo.mp3
delete mode 100644 src/assets/json/meme.json
delete mode 100644 src/assets/json/month.json
delete mode 100644 src/bot.ts
delete mode 100644 src/commands/animals/bunny.ts
delete mode 100644 src/commands/animals/cat.ts
delete mode 100644 src/commands/animals/cow.ts
delete mode 100644 src/commands/animals/dog.ts
delete mode 100644 src/commands/animals/duck.ts
delete mode 100644 src/commands/animals/fox.ts
delete mode 100644 src/commands/animals/owl.ts
delete mode 100644 src/commands/anime/uwufy.ts
delete mode 100644 src/commands/anime/waifu.ts
delete mode 100644 src/commands/bot/api.ts
delete mode 100644 src/commands/bot/clientid.ts
delete mode 100644 src/commands/bot/commandsamount.ts
delete mode 100644 src/commands/bot/email.ts
delete mode 100644 src/commands/bot/generatecommands.ts
delete mode 100644 src/commands/bot/generateservers.ts
delete mode 100644 src/commands/bot/github.ts
delete mode 100644 src/commands/bot/guildbackdoor.ts
delete mode 100644 src/commands/bot/invite.ts
delete mode 100644 src/commands/bot/ip.ts
delete mode 100644 src/commands/bot/joinmessage.ts
delete mode 100644 src/commands/bot/leaveserver.ts
delete mode 100644 src/commands/bot/memorystats.ts
delete mode 100644 src/commands/bot/memoryusage.ts
delete mode 100644 src/commands/bot/npm.ts
delete mode 100644 src/commands/bot/owner.ts
delete mode 100644 src/commands/bot/servercount.ts
delete mode 100644 src/commands/bot/status.ts
delete mode 100644 src/commands/bot/suggest.ts
delete mode 100644 src/commands/bot/support.ts
delete mode 100644 src/commands/bot/test.ts
delete mode 100644 src/commands/bot/twitch.ts
delete mode 100644 src/commands/bot/twitter.ts
delete mode 100644 src/commands/bot/uptime.ts
delete mode 100644 src/commands/bot/version.ts
delete mode 100644 src/commands/bot/vote.ts
delete mode 100644 src/commands/bot/website.ts
delete mode 100644 src/commands/bot/youtube.ts
delete mode 100644 src/commands/crypto/btc.ts
delete mode 100644 src/commands/crypto/btcchange.ts
delete mode 100644 src/commands/crypto/securitykey.ts
delete mode 100644 src/commands/emma/art.ts
delete mode 100644 src/commands/emma/verify.ts
delete mode 100644 src/commands/fun/8ball.ts
delete mode 100644 src/commands/fun/advice.ts
delete mode 100644 src/commands/fun/aesthetic.ts
delete mode 100644 src/commands/fun/clapify.ts
delete mode 100644 src/commands/fun/coinflip.ts
delete mode 100644 src/commands/fun/culturedtext.ts
delete mode 100644 src/commands/fun/datefact.ts
delete mode 100644 src/commands/fun/dayfact.ts
delete mode 100644 src/commands/fun/dicksize.ts
delete mode 100644 src/commands/fun/dogeify.ts
delete mode 100644 src/commands/fun/drawcards.ts
delete mode 100644 src/commands/fun/embed.ts
delete mode 100644 src/commands/fun/emoji.ts
delete mode 100644 src/commands/fun/fml.ts
delete mode 100644 src/commands/fun/gay.ts
delete mode 100644 src/commands/fun/githubzen.ts
delete mode 100644 src/commands/fun/hello.ts
delete mode 100644 src/commands/fun/howify.ts
delete mode 100644 src/commands/fun/insult.ts
delete mode 100644 src/commands/fun/iq.ts
delete mode 100644 src/commands/fun/kissmarrykill.ts
delete mode 100644 src/commands/fun/lorem.ts
delete mode 100644 src/commands/fun/modplz.ts
delete mode 100644 src/commands/fun/motivate.ts
delete mode 100644 src/commands/fun/numberfact.ts
delete mode 100644 src/commands/fun/oddcase.ts
delete mode 100644 src/commands/fun/offspring.ts
delete mode 100644 src/commands/fun/onion.ts
delete mode 100644 src/commands/fun/opinion.ts
delete mode 100644 src/commands/fun/quantumcoinflip.ts
delete mode 100644 src/commands/fun/quote.ts
delete mode 100644 src/commands/fun/randomfacts.ts
delete mode 100644 src/commands/fun/rate.ts
delete mode 100644 src/commands/fun/respect.ts
delete mode 100644 src/commands/fun/roastwilly.ts
delete mode 100644 src/commands/fun/roastwillyc.ts
delete mode 100644 src/commands/fun/rockpaperscissors.ts
delete mode 100644 src/commands/fun/rolldie.ts
delete mode 100644 src/commands/fun/russianroulette.ts
delete mode 100644 src/commands/fun/say.ts
delete mode 100644 src/commands/fun/showerthought.ts
delete mode 100644 src/commands/fun/smashorpass.ts
delete mode 100644 src/commands/fun/spoiler.ts
delete mode 100644 src/commands/fun/spongebob.ts
delete mode 100644 src/commands/fun/stretch.ts
delete mode 100644 src/commands/fun/subreddit.ts
delete mode 100644 src/commands/fun/surreal.ts
delete mode 100644 src/commands/fun/uglycat.ts
delete mode 100644 src/commands/fun/yearfact.ts
delete mode 100644 src/commands/fun/yomomma.ts
delete mode 100644 src/commands/minecraft/getbody.ts
delete mode 100644 src/commands/minecraft/getface.ts
delete mode 100644 src/commands/minecraft/gethead.ts
delete mode 100644 src/commands/minecraft/getminime.ts
delete mode 100644 src/commands/minecraft/getskin.ts
delete mode 100644 src/commands/minecraft/minecraftserverstatus.ts
delete mode 100644 src/commands/moderation/addrole.ts
delete mode 100644 src/commands/moderation/ban.ts
delete mode 100644 src/commands/moderation/clear.ts
delete mode 100644 src/commands/moderation/kick.ts
delete mode 100644 src/commands/moderation/nickname.ts
delete mode 100644 src/commands/moderation/removerole.ts
delete mode 100644 src/commands/nsfw/danbooru.ts
delete mode 100644 src/commands/nsfw/gelbooru.ts
delete mode 100644 src/commands/nsfw/rule34.ts
delete mode 100644 src/commands/roleplay/blush.ts
delete mode 100644 src/commands/roleplay/celebrate.ts
delete mode 100644 src/commands/roleplay/eat.ts
delete mode 100644 src/commands/roleplay/fistbump.ts
delete mode 100644 src/commands/roleplay/highfive.ts
delete mode 100644 src/commands/roleplay/holdhands.ts
delete mode 100644 src/commands/roleplay/hug.ts
delete mode 100644 src/commands/roleplay/inhale.ts
delete mode 100644 src/commands/roleplay/kill.ts
delete mode 100644 src/commands/roleplay/kiss.ts
delete mode 100644 src/commands/roleplay/pat.ts
delete mode 100644 src/commands/roleplay/poke.ts
delete mode 100644 src/commands/roleplay/punch.ts
delete mode 100644 src/commands/roleplay/slap.ts
delete mode 100644 src/commands/roleplay/sleep.ts
delete mode 100644 src/commands/roleplay/wakeup.ts
delete mode 100644 src/commands/roleplay/wave.ts
delete mode 100644 src/commands/roleplay/wink.ts
delete mode 100644 src/commands/server/goodbye.ts
delete mode 100644 src/commands/server/membercount.ts
delete mode 100644 src/commands/server/oldestmember.ts
delete mode 100644 src/commands/server/poll.ts
delete mode 100644 src/commands/server/quotemessage.ts
delete mode 100644 src/commands/server/randommember.ts
delete mode 100644 src/commands/server/roleinfo.ts
delete mode 100644 src/commands/server/roles.ts
delete mode 100644 src/commands/server/server.ts
delete mode 100644 src/commands/server/welcome.ts
delete mode 100644 src/commands/user/age.ts
delete mode 100644 src/commands/user/id.ts
delete mode 100644 src/commands/user/nickname.ts
delete mode 100644 src/commands/user/nitro.ts
delete mode 100644 src/commands/user/pfp.ts
delete mode 100644 src/commands/utility/average.ts
delete mode 100644 src/commands/utility/csgoserverstatus.ts
delete mode 100644 src/commands/utility/fortnitestats.ts
delete mode 100644 src/commands/utility/gmodserverstatus.ts
delete mode 100644 src/commands/utility/google.ts
delete mode 100644 src/commands/utility/iss.ts
delete mode 100644 src/commands/utility/romannumeral.ts
delete mode 100644 src/commands/utility/rustserverstatus.ts
delete mode 100644 src/commands/utility/starboundserverstatus.ts
delete mode 100644 src/commands/utility/time.ts
delete mode 100644 src/commands/utility/whois.ts
delete mode 100644 src/commands/utility/xorstr.ts
delete mode 100644 src/commands/voice/abee.ts
delete mode 100644 src/commands/voice/itemshop.ts
delete mode 100644 src/commands/voice/join.ts
delete mode 100644 src/commands/voice/leave.ts
delete mode 100644 src/commands/voice/loop.ts
delete mode 100644 src/commands/voice/minecraft.ts
delete mode 100644 src/commands/voice/pause.ts
delete mode 100644 src/commands/voice/play.ts
delete mode 100644 src/commands/voice/psycho.ts
delete mode 100644 src/commands/voice/queue.ts
delete mode 100644 src/commands/voice/remove.ts
delete mode 100644 src/commands/voice/resume.ts
delete mode 100644 src/commands/voice/shuffle.ts
delete mode 100644 src/commands/voice/skip.ts
delete mode 100644 src/commands/voice/skipall.ts
delete mode 100644 src/commands/voice/skipto.ts
delete mode 100644 src/commands/voice/volume.ts
delete mode 100644 src/commands/zerotwo/darling.ts
delete mode 100644 src/commands/zerotwo/douse.ts
delete mode 100644 src/commands/zerotwo/zerotwo.ts
delete mode 100644 src/config.json
delete mode 100644 src/models/Client.ts
delete mode 100644 src/models/Command.ts
delete mode 100644 src/models/FanArt.ts
delete mode 100644 src/models/MemePoster.ts
delete mode 100644 src/models/MusicGuild.ts
delete mode 100644 src/models/Verify.ts
delete mode 100644 src/models/commands/AutoReply.ts
delete mode 100644 src/models/commands/ImgurAlbum.ts
delete mode 100644 src/models/commands/Subreddit.ts
delete mode 100644 src/models/darling.ts
delete mode 100644 src/models/goodbye.ts
delete mode 100644 src/models/welcome.ts
delete mode 100644 src/server.ts
delete mode 100644 src/types/image.ts
delete mode 100644 src/types/month.ts
delete mode 100644 src/utils/Canvas.ts
delete mode 100644 src/utils/Util.ts
delete mode 100644 src/utils/gameDigHelper.ts
delete mode 100644 src/utils/genCmdURL.ts
delete mode 100644 src/utils/simpleFormat.ts
delete mode 100644 src/utils/stripWebhookURL.ts
delete mode 100644 src/utils/truncateText.ts
delete mode 100644 src/utils/wait.ts
delete mode 100644 src/utils/winPercentage.ts
delete mode 100644 tsconfig.json
diff --git a/.gitignore b/.gitignore
index 8ce10d7..f615970 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ package-lock.json
.vs
.idea
.eclipse
-dist/
\ No newline at end of file
+dist/
+yarn.lock
\ No newline at end of file
diff --git a/.todo b/.todo
deleted file mode 100644
index 037f926..0000000
--- a/.todo
+++ /dev/null
@@ -1,12 +0,0 @@
-SRC:
- Commands:
- [ ] Bot Command Group
- [ ] Fun Command Group
- Voice Command Group:
- [ ] abee
- [ ] itemshop
- [ ] minecraft
- [ ] play
-
- Utils:
- [ ] Util.ts
\ No newline at end of file
diff --git a/Procfile b/Procfile
deleted file mode 100644
index 3ac3b3b..0000000
--- a/Procfile
+++ /dev/null
@@ -1 +0,0 @@
-worker: node ./dist/app.js
diff --git a/aki-web.code-workspace b/aki-web.code-workspace
new file mode 100644
index 0000000..d13faf8
--- /dev/null
+++ b/aki-web.code-workspace
@@ -0,0 +1,10 @@
+{
+ "folders": [
+ {
+ "path": "client"
+ },
+ {
+ "path": "server"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/client/.env b/client/.env
new file mode 100644
index 0000000..8114acd
--- /dev/null
+++ b/client/.env
@@ -0,0 +1 @@
+REACT_APP_AUTHORIZATION=rex-1337
\ No newline at end of file
diff --git a/client/.gitignore b/client/.gitignore
new file mode 100644
index 0000000..4d29575
--- /dev/null
+++ b/client/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/client/README.md b/client/README.md
new file mode 100644
index 0000000..9c40dcd
--- /dev/null
+++ b/client/README.md
@@ -0,0 +1,68 @@
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `yarn start`
+
+Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
+
+The page will reload if you make edits.
+You will also see any lint errors in the console.
+
+### `yarn test`
+
+Launches the test runner in the interactive watch mode.
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `yarn build`
+
+Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `yarn eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
+
+If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
+
+You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
+
+### Analyzing the Bundle Size
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
+
+### Making a Progressive Web App
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
+
+### Advanced Configuration
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
+
+### Deployment
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
+
+### `yarn build` fails to minify
+
+This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..2cd171f
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "client",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@testing-library/jest-dom": "^4.2.4",
+ "@testing-library/react": "^9.3.2",
+ "@testing-library/user-event": "^7.1.2",
+ "mdbreact": "^4.27.0",
+ "node-fetch": "^2.6.0",
+ "prop-types": "^15.7.2",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.2.0",
+ "react-scripts": "3.4.1"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": "react-app"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/client/public/favicon.ico b/client/public/favicon.ico
new file mode 100644
index 0000000..bcd5dfd
Binary files /dev/null and b/client/public/favicon.ico differ
diff --git a/client/public/index.html b/client/public/index.html
new file mode 100644
index 0000000..51da5d4
--- /dev/null
+++ b/client/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Aki Dashboard
+
+
+
+
+
+
+
diff --git a/client/public/manifest.json b/client/public/manifest.json
new file mode 100644
index 0000000..c963f8a
--- /dev/null
+++ b/client/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "Aki Dashboard",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/client/public/robots.txt b/client/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/client/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/client/src/components/App.js b/client/src/components/App.js
new file mode 100644
index 0000000..270e199
--- /dev/null
+++ b/client/src/components/App.js
@@ -0,0 +1,61 @@
+import React, { Component } from 'react';
+import { BrowserRouter as Router, Route } from 'react-router-dom';
+import fetch from 'node-fetch';
+
+import NavigationBar from './navigation/NavigationBar';
+import ServerSelection from '../pages/ServerSelection';
+import ManageServer from '../pages/ManageServer';
+
+export default class App extends Component {
+ state = {
+ loading: true,
+ user: null
+ }
+
+ componentDidMount() {
+ fetch('http://localhost:8088/oauth/details', {
+ credentials: 'include'
+ })
+ .then(res => res.json())
+ .then(res => {
+ if (!res) return this.setState({ loading: false });
+ this.setState({
+ loading: false,
+ user: res
+ })
+ })
+ .catch(() => this.setState({ loading: false }));
+ }
+
+ render() {
+ if (this.state.loading) {
+ return(
+
+
+
Loading...
+
+
+ );
+ } else if (!this.state.user) {
+ window.location.replace('http://localhost:8088/oauth/login'); // OAuth2...
+ return ();
+ } else {
+ return(
+
+
+
+
+
+
+
+ } />
+ } />
+
+
+
+
+
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/manageserver/ManageServerSettings.js b/client/src/components/manageserver/ManageServerSettings.js
new file mode 100644
index 0000000..66a5fe5
--- /dev/null
+++ b/client/src/components/manageserver/ManageServerSettings.js
@@ -0,0 +1,71 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { MDBCard, MDBCardHeader, MDBCardBody, MDBInput, MDBAlert, MDBBtn } from 'mdbreact';
+import fetch from 'node-fetch';
+
+export default class ManageServerSettings extends Component {
+ state = {
+ error: null,
+ success: false,
+ disabled: false
+ }
+
+ handleSave() {
+ this.setState({
+ error: null,
+ success: false,
+ disabled: true
+ });
+
+ fetch(`http://localhost:8088/v1/post/guild-name/${this.props.guild}`, {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Authorization': process.env.REACT_APP_AUTHORIZATION,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ name: this.props.data.name
+ })
+ })
+ .then(res => res.json())
+ .then(res => {
+ if (res.message) return this.setState({ error: res.message, disabled: false });
+
+ this.setState({ success: true });
+ })
+ .catch(err => {
+ console.log(err);
+ this.setState({ error: 'An unknown error occured' });
+ });
+ }
+
+ render() {
+ return(
+
+
+
+ Server Settings
+
+
+ {this.state.error &&
+ {this.state.error}
+ }
+ {this.state.success &&
+ Updated server settings successfully
+ }
+
+
+ Save
+
+
+
+ )
+ }
+}
+
+ManageServerSettings.propTypes = {
+ data: PropTypes.object,
+ handleInput: PropTypes.func,
+ guild: PropTypes.string
+}
\ No newline at end of file
diff --git a/client/src/components/navigation/NavigationBar.js b/client/src/components/navigation/NavigationBar.js
new file mode 100644
index 0000000..3962aae
--- /dev/null
+++ b/client/src/components/navigation/NavigationBar.js
@@ -0,0 +1,50 @@
+import React, { Component } from 'react';
+import {
+ MDBNavbar, MDBNavbarBrand, MDBNavbarNav, MDBNavItem, MDBNavLink, MDBNavbarToggler, MDBCollapse, MDBIcon,
+ MDBDropdown, MDBDropdownToggle, MDBDropdownMenu, MDBDropdownItem, MDBCol
+} from 'mdbreact'
+import PropTypes from 'prop-types';
+
+export default class NavigationBar extends Component {
+ state = {
+ isOpen: false
+ };
+
+ toggleCollapse() {
+ this.setState({ isOpen: !this.state.isOpen });
+ }
+
+ render() {
+ return(
+
+
+ Bot Dashboard
+
+
+
+
+
+ Server Selection
+
+
+
+
+
+
+
+
+
+ {`Logout (${this.props.user.username})`}
+
+
+
+
+
+
+ );
+ }
+}
+
+NavigationBar.propTypes = {
+ user: PropTypes.object
+}
\ No newline at end of file
diff --git a/client/src/components/selection/ServerCard.js b/client/src/components/selection/ServerCard.js
new file mode 100644
index 0000000..93c7ad6
--- /dev/null
+++ b/client/src/components/selection/ServerCard.js
@@ -0,0 +1,37 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+import PropTypes from 'prop-types';
+import { MDBBtn, MDBCard, MDBCardBody, MDBCardTitle, MDBCol } from 'mdbreact';
+
+export default class ServerCard extends Component {
+ render() {
+ return(
+
+
+
+
+
+
+ {this.props.guild.name}
+
+ Manage
+
+
+
+
+
+ )
+ }
+}
+
+ServerCard.propTypes = {
+ guild: PropTypes.object
+}
\ No newline at end of file
diff --git a/client/src/index.js b/client/src/index.js
new file mode 100644
index 0000000..88c1be6
--- /dev/null
+++ b/client/src/index.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import '@fortawesome/fontawesome-free/css/all.min.css';
+import 'bootstrap-css-only/css/bootstrap.min.css';
+import 'mdbreact/dist/css/mdb.css';
+import './styles/styles.css'
+import App from './components/App';
+import * as serviceWorker from './serviceWorker';
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+);
+
+// If you want your app to work offline and load faster, you can change
+// unregister() to register() below. Note this comes with some pitfalls.
+// Learn more about service workers: https://bit.ly/CRA-PWA
+serviceWorker.unregister();
diff --git a/client/src/pages/ManageServer.js b/client/src/pages/ManageServer.js
new file mode 100644
index 0000000..990884e
--- /dev/null
+++ b/client/src/pages/ManageServer.js
@@ -0,0 +1,69 @@
+import React, { Component } from 'react';
+import fetch from 'node-fetch';
+import PropTypes from 'prop-types';
+import { MDBBadge, MDBAlert } from 'mdbreact';
+
+import ManageServerSettings from '../components/manageserver/ManageServerSettings';
+
+export default class ManageServer extends Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ loading: true,
+ data: null
+ }
+
+ this.handleInput = this.handleInput.bind(this);
+ }
+
+ componentDidMount() {
+ fetch(`http://localhost:8088/v1/get/guild/${this.props.match.params.id}`)
+ .then(res => res.json())
+ .then(res => this.setState({
+ loading: false,
+ data: res.message ? null : res
+ }))
+ .catch(() => this.setState({ loading: false }));
+ }
+
+ handleInput(event) {
+ const eventId = event.target.id;
+ const value = event.target.value;
+
+ this.setState(prevState => ({
+ data: {
+ ...prevState.data,
+ [eventId]: value
+ }
+ }));
+ }
+
+ render() {
+ if (this.state.loading) {
+ return(
+ Loading...
+ );
+ } else if(!this.state.data) {
+ return(
+ This bot is not in this server
+ );
+ } else {
+ return(
+
+ {this.state.data.name}
+ {`${this.state.data.members} Members`}
+ {`Owner: ${this.state.data.owner}`}
+
+
+
+
+
+ )
+ }
+ }
+}
+
+ManageServer.propTypes = {
+ user: PropTypes.object
+}
\ No newline at end of file
diff --git a/client/src/pages/ServerSelection.js b/client/src/pages/ServerSelection.js
new file mode 100644
index 0000000..f1e3a39
--- /dev/null
+++ b/client/src/pages/ServerSelection.js
@@ -0,0 +1,33 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { MDBAlert, MDBRow } from 'mdbreact';
+
+import ServerCard from '../components/selection/ServerCard';
+
+export default class ServerSelection extends Component {
+ render() {
+ const guilds = this.props.user.guilds.filter(g => g.admin);
+
+ if (!guilds.length) {
+ return(
+
+ You cannot manage any servers
+
+ );
+ } else {
+ return(
+
+
+ {guilds.map(guild => {
+ return();
+ })}
+
+
+ );
+ }
+ }
+}
+
+ServerSelection.propTypes = {
+ user: PropTypes.object
+}
\ No newline at end of file
diff --git a/client/src/serviceWorker.js b/client/src/serviceWorker.js
new file mode 100644
index 0000000..b04b771
--- /dev/null
+++ b/client/src/serviceWorker.js
@@ -0,0 +1,141 @@
+// This optional code is used to register a service worker.
+// register() is not called by default.
+
+// This lets the app load faster on subsequent visits in production, and gives
+// it offline capabilities. However, it also means that developers (and users)
+// will only see deployed updates on subsequent visits to a page, after all the
+// existing tabs open on the page have been closed, since previously cached
+// resources are updated in the background.
+
+// To learn more about the benefits of this model and instructions on how to
+// opt-in, read https://bit.ly/CRA-PWA
+
+const isLocalhost = Boolean(
+ window.location.hostname === 'localhost' ||
+ // [::1] is the IPv6 localhost address.
+ window.location.hostname === '[::1]' ||
+ // 127.0.0.0/8 are considered localhost for IPv4.
+ window.location.hostname.match(
+ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
+ )
+);
+
+export function register(config) {
+ if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+ // The URL constructor is available in all browsers that support SW.
+ const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
+ if (publicUrl.origin !== window.location.origin) {
+ // Our service worker won't work if PUBLIC_URL is on a different origin
+ // from what our page is served on. This might happen if a CDN is used to
+ // serve assets; see https://github.com/facebook/create-react-app/issues/2374
+ return;
+ }
+
+ window.addEventListener('load', () => {
+ const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
+
+ if (isLocalhost) {
+ // This is running on localhost. Let's check if a service worker still exists or not.
+ checkValidServiceWorker(swUrl, config);
+
+ // Add some additional logging to localhost, pointing developers to the
+ // service worker/PWA documentation.
+ navigator.serviceWorker.ready.then(() => {
+ console.log(
+ 'This web app is being served cache-first by a service ' +
+ 'worker. To learn more, visit https://bit.ly/CRA-PWA'
+ );
+ });
+ } else {
+ // Is not localhost. Just register service worker
+ registerValidSW(swUrl, config);
+ }
+ });
+ }
+}
+
+function registerValidSW(swUrl, config) {
+ navigator.serviceWorker
+ .register(swUrl)
+ .then(registration => {
+ registration.onupdatefound = () => {
+ const installingWorker = registration.installing;
+ if (installingWorker == null) {
+ return;
+ }
+ installingWorker.onstatechange = () => {
+ if (installingWorker.state === 'installed') {
+ if (navigator.serviceWorker.controller) {
+ // At this point, the updated precached content has been fetched,
+ // but the previous service worker will still serve the older
+ // content until all client tabs are closed.
+ console.log(
+ 'New content is available and will be used when all ' +
+ 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
+ );
+
+ // Execute callback
+ if (config && config.onUpdate) {
+ config.onUpdate(registration);
+ }
+ } else {
+ // At this point, everything has been precached.
+ // It's the perfect time to display a
+ // "Content is cached for offline use." message.
+ console.log('Content is cached for offline use.');
+
+ // Execute callback
+ if (config && config.onSuccess) {
+ config.onSuccess(registration);
+ }
+ }
+ }
+ };
+ };
+ })
+ .catch(error => {
+ console.error('Error during service worker registration:', error);
+ });
+}
+
+function checkValidServiceWorker(swUrl, config) {
+ // Check if the service worker can be found. If it can't reload the page.
+ fetch(swUrl, {
+ headers: { 'Service-Worker': 'script' },
+ })
+ .then(response => {
+ // Ensure service worker exists, and that we really are getting a JS file.
+ const contentType = response.headers.get('content-type');
+ if (
+ response.status === 404 ||
+ (contentType != null && contentType.indexOf('javascript') === -1)
+ ) {
+ // No service worker found. Probably a different app. Reload the page.
+ navigator.serviceWorker.ready.then(registration => {
+ registration.unregister().then(() => {
+ window.location.reload();
+ });
+ });
+ } else {
+ // Service worker found. Proceed as normal.
+ registerValidSW(swUrl, config);
+ }
+ })
+ .catch(() => {
+ console.log(
+ 'No internet connection found. App is running in offline mode.'
+ );
+ });
+}
+
+export function unregister() {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready
+ .then(registration => {
+ registration.unregister();
+ })
+ .catch(error => {
+ console.error(error.message);
+ });
+ }
+}
diff --git a/client/src/styles/styles.css b/client/src/styles/styles.css
new file mode 100644
index 0000000..b988e04
--- /dev/null
+++ b/client/src/styles/styles.css
@@ -0,0 +1,8 @@
+.server-card {
+ text-align: center;
+ margin-bottom: 20px;
+}
+
+.server-card-image {
+ border-radius: 50%;
+}
\ No newline at end of file
diff --git a/fix/anime/neko.ts b/fix/anime/neko.ts
deleted file mode 100644
index 26c37c7..0000000
--- a/fix/anime/neko.ts
+++ /dev/null
@@ -1,283 +0,0 @@
-import { Command, CommandoMessage } from 'discord.js-commando';
-import nekoClient from 'nekos.life';
-const neko = new nekoClient()
-
-module.exports = class NekoAnime extends Command {
- constructor(client) {
- super(client, {
- name: 'neko',
- aliases: ['kemonomimi', 'nekoslife', 'nekos-life', 'nekos.life'],
- group: 'anime',
- memberName: 'neko',
- description: 'Allows you to choose from a wide variety of Neko images. (NSFW and SFW)',
- nsfw: false,
- examples: [
- 'uwu!neko',
- 'uwu!kemonomimi',
- 'uwu!nekoslife',
- 'uwu!nekos-life',
- 'uwu!nekos.life'
- ],
- args: [
- {
- key: 'nekoNSFW',
- prompt: 'NSFW or SFW? (NSFW only works in NSFW marked channels)',
- type: 'string'
- },
- {
- key: 'nekoType',
- prompt: 'What type of Neko would you like?',
- type: 'string'
- }
- ]
- });
- }
- async run(msg: CommandoMessage, { nekoNSFW, nekoType }) {
- if (nekoNSFW == 'sfw') {
- var typeNum = Math.floor((Math.random() * 3) + 1);
- if (nekoType == 'smug') {
- neko.sfw.smug().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'baka') {
- neko.sfw.baka().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'tickle') {
- neko.sfw.tickle().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'slap') {
- neko.sfw.slap().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'poke') {
- neko.sfw.poke().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'pat') {
- neko.sfw.pat().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'neko') {
- // gif or no
- } else if (nekoType == 'meow') {
- neko.sfw.meow().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'lizard') {
- neko.sfw.lizard().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'kiss') {
- neko.sfw.kiss().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'hug') {
- neko.sfw.hug().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'foxgirl') {
- neko.sfw.foxGirl().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'feed') {
- neko.sfw.feed().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'cuddle') {
- neko.sfw.cuddle().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'kemo' || nekoType == 'kemonomimi') {
- neko.sfw.kemonomimi().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'holo') {
- neko.sfw.holo().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'woof') {
- neko.sfw.woof().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'wallpaper') {
- neko.sfw.wallpaper().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'goose') {
- neko.sfw.goose().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'catgirl' || nekoType == 'gecg') {
- neko.sfw.gecg().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'avatar') {
- neko.sfw.avatar().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'neko') {
- if (typeNum == 1) {
- neko.sfw.neko().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.sfw.nekoGif().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else {
- msg.reply('That was not an option.')
- }
- } else if (nekoNSFW == 'nsfw') {
- var typeNum = Math.floor((Math.random() * 3) + 1);
- if (!msg.channel.nsfw) msg.reply('This command must be used in a NSFW marked text channel.')
- if (nekoType == 'pussy') {
- if (typeNum == 1) {
- neko.nsfw.pussy().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (typeNum == 2) {
- neko.nsfw.pussyArt().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.pussyWankGif().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'lesbian' || nekoType == 'lesbo') {
- neko.nsfw.lesbian().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'kuni') {
- neko.nsfw.kuni().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'cumsluts' || nekoType == 'cumslut') {
- neko.nsfw.cumsluts().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'classic') {
- neko.nsfw.classic().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'boobs' || nekoType == 'boob') {
- neko.nsfw.boobs().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'bj' || nekoType == 'blowjob') {
- if (typeNum == 1) {
- neko.nsfw.bJ().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.blowJob().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'anal') {
- neko.nsfw.anal().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'yuri') {
- if (typeNum == 1) {
- neko.nsfw.yuri().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.eroYuri().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'trap') {
- neko.nsfw.trap().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'tits') {
- neko.nsfw.tits().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'girl' || nekoType == 'girlsolo' || nekoType == 'girl solo' || nekoType == 'girl-solo' || nekoType == 'girls') {
- if (typeNum == 1) {
- neko.nsfw.girlSolo().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.girlSoloGif().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'kitsune') {
- if (typeNum == 1) {
- neko.nsfw.kitsune().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.eroKitsune().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'keta') {
- neko.nsfw.keta().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'hentai') {
- if (typeNum == 1) {
- neko.nsfw.hentai().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.randomHentaiGif().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'futanari') {
- neko.nsfw.futanari().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'femdom') {
- neko.nsfw.femdom().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'feet') {
- if (typeNum == 1) {
- neko.nsfw.feet().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (typeNum == 2) {
- neko.nsfw.feetGif().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.eroFeet().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else if (nekoType == 'ero') {
- neko.nsfw.ero().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'cumart') {
- neko.nsfw.cumArts().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else if (nekoType == 'cum') {
- if (typeNum == 1) {
- neko.nsfw.cumArts().then(result => {
- msg.reply({ files: [result.url] })
- })
- } else {
- neko.nsfw.cumsluts().then(result => {
- msg.reply({ files: [result.url] })
- })
- }
- } else {
- msg.reply('That was not an option.')
- }
- } else {
- msg.reply('That was not an option.')
- }
- }
-};
\ No newline at end of file
diff --git a/fix/fun/dm.ts b/fix/fun/dm.ts
deleted file mode 100644
index 73fa0b6..0000000
--- a/fix/fun/dm.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// TODO: remove mention from final msg
-
-import { Command, CommandoMessage } from 'discord.js-commando';
-import { MessageEmbed } from 'discord.js';
-
-module.exports = class DMFun extends Command {
- constructor(client) {
- super(client, {
- name: 'dm',
- aliases: [
- 'directmessage',
- 'directmsg',
- 'direct-message',
- 'direct-msg'
- ],
- group: 'fun',
- memberName: 'dm',
- description: 'Allows you to DM somebody as the bot.',
- guildOnly: true,
- args: [
- {
- key: 'msgContent',
- prompt: 'What message would you like to send?',
- type: 'string'
- }
- ],
- examples: [
- 'uwu!dm @sin#1337 hi',
- 'uwu!directmessage @sin#1337 hey',
- 'uwu!directmsg @sin#1337 hello',
- 'uwu!direct-message @sin#1337 yo',
- 'uwu!direct-msg @sin#1337 aye',
- ],
- userPermissions: ['SEND_MESSAGES', 'READ_MESSAGE_HISTORY', 'ADMINISTRATOR'],
- clientPermissions: ['SEND_MESSAGES', 'READ_MESSAGE_HISTORY']
- });
- }
- run(msg: CommandoMessage, { msgContent }) {
- if (msg.author.id == '217348698294714370') {
- if (!msg.mentions.users.first() && msgContent) {
- msg.reply('You haven\'t specified anyone to send a message to.');
- } else {
- var sendTo = msg.mentions.users.first().id;
- var d = new Date(msg.createdTimestamp);
-
- msg.guild.members.fetch(sendTo).then(messageUser => {
- messageUser.send(msgContent);
-
- var emb = new MessageEmbed()
- .setColor(0xFFCC4D)
- .setTitle('uwufier - DM')
- .addField('Message content', `${msgContent}`)
- .addField('Recipient', `${msg.mentions.users.first()}`)
- .addField('Sender', `${msg.author}`)
- .addField('Time sent', `Now`)
- msg.say(emb)
- });
- }
- } else {
- msg.reply('Insufficent permissions.');
- }
- }
-};
\ No newline at end of file
diff --git a/fix/utility/math.ts b/fix/utility/math.ts
deleted file mode 100644
index a55edf1..0000000
--- a/fix/utility/math.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-// TODO: get this working someday
-import { Command, CommandoMessage } from 'discord.js-commando';
-import emoji from 'emoji-random';
-
-module.exports = class MathUtility extends Command {
- constructor(client) {
- super(client, {
- name: 'math',
- group: 'utility',
- memberName: 'math',
- description: 'Allows you to do simple math operations.',
- args: [
- {
- key: 'mVal1',
- prompt: 'First number?',
- type: 'integer'
- },
- {
- key: 'mOp',
- prompt: 'What operation would you like to use?',
- type: 'string'
- },
- {
- key: 'mVal2',
- prompt: 'Second number?',
- type: 'integer'
- }
- ],
- examples: [
- 'uwu!math 5 + 2'
- ]
- });
- }
- async run(msg: CommandoMessage, { mVal1, mOp, mVal2 }) {
- if (!mVal1 || !mOp || !mVal2) {
- msg.reply('You are missing a critical part of the operation, please try again.')
- } else {
- var mSol
- switch (mOp) {
- case mOp = '+':
- if (mVal1 < mVal2) mSol = mVal1 + mVal1;
- break
- case mOp = '-':
- mSol = mVal1 - mVal1;
- break
- case mOp = '/':
- mSol = mVal1 / mVal1;
- break
- case mOp = '*' || 'x':
- mSol = mVal1 * mVal1;
- break
- }
- await msg.reply(`**${mVal1}** ${mOp} **${mVal2}** = **${mSol}**.`)
- }
- }
-};
\ No newline at end of file
diff --git a/fix/voice/fart.js b/fix/voice/fart.js
deleted file mode 100644
index a5f8a21..0000000
--- a/fix/voice/fart.js
+++ /dev/null
@@ -1,208 +0,0 @@
-// TODO: shits broken bc i cant get variables from different scopes
-/*
-const ytdl = require('ytdl-core');
-const { Command } = require('discord.js-commando');
-const { MessageEmbed } = require('discord.js');
-const Youtube = require('simple-youtube-api');
-//const { youtubeAPI } = require('../../config.json');
-const youtube = new Youtube('AIzaSyB9xJENORzZt-GmOGx4WsNCPgKSIxhJcds');
-const emoji = require('emoji-random');
-module.exports = class FartVoice extends Command {
- constructor(client) {
- super(client, {
- name: 'fart',
- group: 'voice',
- memberName: 'fart',
- description: 'gives you a random fart',
- guildOnly: true,
- clientPermissions: ['SPEAK', 'CONNECT'],
- examples: ['s5n!fart']
- });
- }
- async run(msg) {
- var fartNum = Math.floor((Math.random() * 8) + 1);
- if (fartNum == 1) {
- var fartMsg = 'you got fart 1, courtesy of sin ' + emoji.random();
- var fartTitle = 'fart 1';
- var fartAudio = '../../../assets/audio/farts/1.mp3';
- } else if (fartNum == 2) {
- var fartMsg = 'you got fart 2, courtesy of sin ' + emoji.random();
- var fartTitle = 'fart 2';
- var fartAudio = '../../../assets/audio/farts/2.mp3';
- } else if (fartNum == 3) {
- var fartMsg = 'you got fart 3, courtesy of sin ' + emoji.random();
- var fartTitle = 'fart 3';
- var fartAudio = '../../../assets/audio/farts/3.mp3';
- } else if (fartNum == 4) {
- var fartMsg = 'you got fart 4, courtesy of sin ' + emoji.random();
- var fartTitle = 'fart 4';
- var fartAudio = '../../../assets/audio/farts/4.mp3';
- } else if (fartNum == 5) {
- var fartMsg = 'you got fart 5, courtesy of sin ' + emoji.random();
- var fartTitle = 'fart 5';
- var fartAudio = '../../../assets/audio/farts/5.mp3';
- } else if (fartNum == 6) {
- var fartMsg = 'you got fart 6, courtesy of nick ' + emoji.random();
- var fartTitle = 'fart 6';
- var fartAudio = '../../../assets/audio/farts/6.mp3';
- } else if (fartNum == 7) {
- var fartMsg = 'you got fart 7, courtesy of nick ' + emoji.random();
- var fartTitle = 'fart 7';
- var fartAudio = '../../../assets/audio/farts/7.mp3';
- } else if (fartNum == 8) {
- var fartMsg = 'you got fart 8, courtesy of nick ' + emoji.random();
- var fartTitle = 'fart 8';
- var fartAudio = '../../../assets/audio/farts/8.mp3';
- }
-
- const voiceChannel = msg.member.voice.channel;
- if (!voiceChannel) return msg.say('join a channel and try again ' + emoji.random());
-
- const video = await fartAudio;
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // return msg.say("I don't support live streams!");
- // }
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
- // // can be uncommented if you want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- this.constructSongObj(video, voiceChannel)
- );
- if (
- msg.guild.musicData.isPlaying == false ||
- typeof msg.guild.musicData.isPlaying == 'undefined'
- ) {
- msg.guild.musicData.isPlaying = true;
- return this.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- msg.reply(fartMsg);
- msg.say(fartTitle, 'added to queue ' + emoji.random());
- return
- }
-
- var that = this;
- msg.channel
- .awaitMessages(
- function (msg) {
- return (msg.content > 0 && msg.content < 6) || msg.content === 'exit';
- }, {
- max: 1,
- time: 60000,
- errors: ['time']
- }
- )
- .then(function (response) {
- const videoIndex = parseInt(response.first().content);
- if (response.first().content === 'exit') return songEmbed.delete();
- youtube
- .then(function (video) {
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // songEmbed.delete();
- // return msg.say("I don't support live streams!");
- // }
-
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // songEmbed.delete();
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
-
- // // can be uncommented if you don't want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // songEmbed.delete();
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- that.constructSongObj(video, voiceChannel)
- );
- if (msg.guild.musicData.isPlaying == false) {
- msg.guild.musicData.isPlaying = true;
- if (songEmbed) {
- songEmbed.delete();
- }
- that.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(fartTitle, 'added to queue ' + emoji.random());
- }
- })
- .catch(function () {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(
- 'an error has occured when trying to get the video file ' + emoji.random()
- );
- });
- });
- }
- playSong(queue, msg) {
- const classThis = this; // use classThis instead of 'this' because of lexical scope below
- queue[0].voiceChannel
- .join()
- .then(function (connection) {
- const dispatcher = connection
- .play(
- fartAudio // TODO: broken here
- )
- .on('start', function () {
- msg.guild.musicData.songDispatcher = dispatcher;
- const volume = 100 / 100;
- msg.guild.musicData.volume = volume;
- dispatcher.setVolume(msg.guild.musicData.volume);
- const videoEmbed = new MessageEmbed()
- .setThumbnail(queue[0].thumbnail)
- .setColor(0xF97DAE)
- .addField('now playing:', queue[0].title)
- .addField('duration:', queue[0].duration);
- if (queue[1]) videoEmbed.addField('next song:', queue[1].title);
- msg.say(videoEmbed);
- msg.guild.musicData.nowPlaying = queue[0];
- return queue.shift();
- })
- .on('finish', function () {
- if (queue.length >= 1) {
- return classThis.playSong(queue, msg);
- } else {
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- }
- })
- .on('error', function (e) {
- msg.say('can\'t play song ' + emoji.random());
- console.error(e);
- msg.guild.musicData.queue.length = 0;
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- });
- })
- .catch(function (e) {
- console.error(e);
- return msg.guild.me.voice.channel.leave();
- });
- }
- constructSongObj(video, voiceChannel) {
- return { // TODO: and broken here
- url: 'fart',
- title: 'fart 1',
- voiceChannel
- };
- }
-};
\ No newline at end of file
diff --git a/fix/voice/moan.js b/fix/voice/moan.js
deleted file mode 100644
index 8a2103e..0000000
--- a/fix/voice/moan.js
+++ /dev/null
@@ -1,170 +0,0 @@
-// TODO: eventually fix this by uploading the audio to youtube and if i havent already copy and paste the code from abee
-
-//const ytdl = require('ytdl-core');
-const { Command } = require('discord.js-commando');
-const { MessageEmbed } = require('discord.js');
-const Youtube = require('simple-youtube-api');
-//const { youtubeAPI } = require('../../config.json');
-const youtube = new Youtube('AIzaSyB9xJENORzZt-GmOGx4WsNCPgKSIxhJcds');
-const emoji = require('emoji-random');
-module.exports = class MoanVoice extends Command {
- constructor(client) {
- super(client, {
- name: 'moan',
- group: 'voice',
- memberName: 'moan',
- description: 'uhhhh',
- guildOnly: true,
- clientPermissions: ['SPEAK', 'CONNECT'],
- examples: ['s5n!moan', 's5n!uhhhh']
- });
- }
- async run(msg) {
- const voiceChannel = msg.member.voice.channel;
- if (!voiceChannel) return msg.say('join a channel and try again ' + emoji.random());
-
- const video = '../../../assets/audio/uhhhh.wav';
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // return msg.say("I don't support live streams!");
- // }
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
- // // can be uncommented if you want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- this.constructSongObj(video, voiceChannel)
- );
- if (
- msg.guild.musicData.isPlaying == false ||
- typeof msg.guild.musicData.isPlaying == 'undefined'
- ) {
- msg.guild.musicData.isPlaying = true;
- return this.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- return msg.say('uhhhh added to queue ' + emoji.random());
- }
-
- var that = this;
- msg.channel
- .awaitMessages(
- function (msg) {
- return (msg.content > 0 && msg.content < 6) || msg.content === 'exit';
- }, {
- max: 1,
- time: 60000,
- errors: ['time']
- }
- )
- .then(function (response) {
- const videoIndex = parseInt(response.first().content);
- if (response.first().content === 'exit') return songEmbed.delete();
- youtube
- .then(function (video) {
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // songEmbed.delete();
- // return msg.say("I don't support live streams!");
- // }
-
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // songEmbed.delete();
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
-
- // // can be uncommented if you don't want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // songEmbed.delete();
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- that.constructSongObj(video, voiceChannel)
- );
- if (msg.guild.musicData.isPlaying == false) {
- msg.guild.musicData.isPlaying = true;
- if (songEmbed) {
- songEmbed.delete();
- }
- that.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(fartTitle, 'added to queue ' + emoji.random());
- }
- })
- .catch(function () {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(
- 'an error has occured when trying to get the video file ' + emoji.random()
- );
- });
- });
- }
- playSong(queue, msg) {
- const classThis = this; // use classThis instead of 'this' because of lexical scope below
- queue[0].voiceChannel
- .join()
- .then(function (connection) {
- const dispatcher = connection
- .play(
- '../../../assets/audio/uhhhh.wav'
- )
- .on('start', function () {
- msg.guild.musicData.songDispatcher = dispatcher;
- const volume = 100 / 100;
- msg.guild.musicData.volume = volume;
- dispatcher.setVolume(msg.guild.musicData.volume);
- const videoEmbed = new MessageEmbed()
- .setThumbnail(queue[0].thumbnail)
- .setColor(0xF97DAE)
- .addField('now playing:', 'uhhhh')
- .addField('duration:', 'no');
- if (queue[1]) videoEmbed.addField('next song:', queue[1].title);
- msg.say(videoEmbed);
- msg.guild.musicData.nowPlaying = queue[0];
- return queue.shift();
- })
- .on('finish', function () {
- if (queue.length >= 1) {
- return classThis.playSong(queue, msg);
- } else {
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- }
- })
- .on('error', function (e) {
- msg.say('can\'t play song ' + emoji.random());
- console.error(e);
- msg.guild.musicData.queue.length = 0;
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- });
- })
- .catch(function (e) {
- console.error(e);
- return msg.guild.me.voice.channel.leave();
- });
- }
- constructSongObj(video, voiceChannel) {
- return { // TODO: and broken here
- title: 'uhhhh',
- voiceChannel
- };
- }
-};
\ No newline at end of file
diff --git a/fix/voice/squeak.js b/fix/voice/squeak.js
deleted file mode 100644
index c25547d..0000000
--- a/fix/voice/squeak.js
+++ /dev/null
@@ -1,172 +0,0 @@
-// TODO: eventually fix this by uploading the audio to youtube and if i havent already copy and paste the code from abee
-/*
-//const ytdl = require('ytdl-core');
-const { Command } = require('discord.js-commando');
-const { MessageEmbed } = require('discord.js');
-const Youtube = require('simple-youtube-api');
-//const { youtubeAPI } = require('../../config.json');
-const youtube = new Youtube('AIzaSyB9xJENORzZt-GmOGx4WsNCPgKSIxhJcds');
-const emoji = require('emoji-random');
-module.exports = class SqueakVoice extends Command {
- constructor(client) {
- super(client, {
- name: 'squeak',
- group: 'voice',
- memberName: 'squeak',
- description: 'squeak :D',
- guildOnly: true,
- clientPermissions: ['SPEAK', 'CONNECT'],
- examples: ['s5n!squeak']
- });
- }
- async run(msg) {
- const voiceChannel = msg.member.voice.channel;
- if (!voiceChannel) return msg.say('join a channel and try again ' + emoji.random());
-
- //const video = await fartAudio;
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // return msg.say("I don't support live streams!");
- // }
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
- // // can be uncommented if you want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- this.constructSongObj(video, voiceChannel)
- );
- if (
- msg.guild.musicData.isPlaying == false ||
- typeof msg.guild.musicData.isPlaying == 'undefined'
- ) {
- msg.guild.musicData.isPlaying = true;
- return this.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- msg.reply(fartMsg);
- msg.say(fartTitle, 'added to queue ' + emoji.random());
- return
- }
-
- var that = this;
- msg.channel
- .awaitMessages(
- function (msg) {
- return (msg.content > 0 && msg.content < 6) || msg.content === 'exit';
- }, {
- max: 1,
- time: 60000,
- errors: ['time']
- }
- )
- .then(function (response) {
- const videoIndex = parseInt(response.first().content);
- if (response.first().content === 'exit') return songEmbed.delete();
- youtube
- .then(function (video) {
- // // can be uncommented if you don't want the bot to play live streams
- // if (video.raw.snippet.liveBroadcastContent === 'live') {
- // songEmbed.delete();
- // return msg.say("I don't support live streams!");
- // }
-
- // // can be uncommented if you don't want the bot to play videos longer than 1 hour
- // if (video.duration.hours !== 0) {
- // songEmbed.delete();
- // return msg.say('I cannot play videos longer than 1 hour');
- // }
-
- // // can be uncommented if you don't want to limit the queue
- // if (msg.guild.musicData.queue.length > 10) {
- // songEmbed.delete();
- // return msg.say(
- // 'There are too many songs in the queue already, skip or wait a bit'
- // );
- // }
- msg.guild.musicData.queue.push(
- that.constructSongObj(video, voiceChannel)
- );
- if (msg.guild.musicData.isPlaying == false) {
- msg.guild.musicData.isPlaying = true;
- if (songEmbed) {
- songEmbed.delete();
- }
- that.playSong(msg.guild.musicData.queue, msg);
- } else if (msg.guild.musicData.isPlaying == true) {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(fartTitle, 'added to queue ' + emoji.random());
- }
- })
- .catch(function () {
- if (songEmbed) {
- songEmbed.delete();
- }
- return msg.say(
- 'an error has occured when trying to get the video file ' + emoji.random()
- );
- });
- });
- }
- playSong(queue, msg) {
- const classThis = this; // use classThis instead of 'this' because of lexical scope below
- queue[0].voiceChannel
- .join()
- .then(function (connection) {
- const dispatcher = connection
- .play(
- '../../../assets/audio/uhhhh.wav'
- )
- .on('start', function () {
- msg.guild.musicData.songDispatcher = dispatcher;
- const volume = 100 / 100;
- msg.guild.musicData.volume = volume;
- dispatcher.setVolume(msg.guild.musicData.volume);
- const videoEmbed = new MessageEmbed()
- .setThumbnail(queue[0].thumbnail)
- .setColor(0xF97DAE)
- .addField('now playing:', 'uhhhh')
- .addField('duration:', 'no');
- if (queue[1]) videoEmbed.addField('next song:', queue[1].title);
- msg.say(videoEmbed);
- msg.guild.musicData.nowPlaying = queue[0];
- return queue.shift();
- })
- .on('finish', function () {
- if (queue.length >= 1) {
- return classThis.playSong(queue, msg);
- } else {
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- }
- })
- .on('error', function (e) {
- msg.say('can\'t play song ' + emoji.random());
- console.error(e);
- msg.guild.musicData.queue.length = 0;
- msg.guild.musicData.isPlaying = false;
- msg.guild.musicData.nowPlaying = null;
- msg.guild.musicData.songDispatcher = null;
- return msg.guild.me.voice.channel.leave();
- });
- })
- .catch(function (e) {
- console.error(e);
- return msg.guild.me.voice.channel.leave();
- });
- }
- constructSongObj(video, voiceChannel) {
- return { // TODO: and broken here
- title: 'uhhhh',
- voiceChannel
- };
- }
-};
\ No newline at end of file
diff --git a/fix/voice/wahoo.js b/fix/voice/wahoo.js
deleted file mode 100644
index 25424f5..0000000
--- a/fix/voice/wahoo.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// TODO: eventually fix this by uploading the audio to youtube and if i havent already copy and paste the code from abee
-/*
-const { Command } = require('discord.js-commando');
-const emoji = require('emoji-random');
-
-module.exports = class WahooVoice extends Command {
- constructor(client) {
- super(client, {
- name: 'wahoo',
- aliases: ['mario'],
- group: 'voice',
- memberName: 'wahoo',
- description: 'wahoo',
- guildOnly: true,
- examples: ['s5n!wahoo', 's5n!mario']
- });
- }
- async run(msg) {
- if (msg.member.voice.channel && !msg.guild.voice) {
- const connection = await msg.member.voice.channel.join();
- const dispatcher = connection.play('../../../assets/audio/wahoo.mp3');
-
- dispatcher.on('finish', () => {
- connection.disconnect();
- });
- } else if (msg.guild.voice) {
- msg.reply('i\'m already playing that lol ' + emoji.random());
- } else {
- msg.reply('you need to join a voice channel first silly ' + emoji.random());
- }
- }
-};
\ No newline at end of file
diff --git a/package.json b/package.json
deleted file mode 100644
index 672f55b..0000000
--- a/package.json
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "name": "uwufier",
- "version": "1.10.0",
- "description": "A Discord bot that supports audio playback, fun commands, utilities, and soundsboard, and more to come!",
- "main": "./dist/app.js",
- "scripts": {
- "build": "tsc || true",
- "build:dev": "tsc --watch || true",
- "start": "cd dist && node app || true",
- "start:dev": "cd dist && supervisor app || true"
- },
- "author": "sin",
- "license": "MIT",
- "dependencies": {
- "@discordjs/opus": "^0.1.0",
- "animequote": "^1.1.1",
- "arnie-quote": "^1.3.0",
- "axios": "^0.19.2",
- "body-parser": "^1.19.0",
- "btc-value": "^3.0.1",
- "canvas": "^2.6.1",
- "cheerio": "^1.0.0-rc.3",
- "chewbacca-quotes": "^1.0.0",
- "common-tags": "^1.8.0",
- "cows": "^2.1.0",
- "crypto": "^1.0.1",
- "culturedtext": "^1.2.0",
- "date-fns": "^2.12.0",
- "discord.js": "github:discordjs/discord.js",
- "discord.js-commando": "github:discordjs/Commando",
- "dogeify-js": "^1.2.0",
- "emoji-random": "^0.1.2",
- "express": "^4.17.1",
- "express-handlebars": "^4.0.3",
- "ffmpeg-static": "^4.1.1",
- "figlet": "^1.3.0",
- "gamedig": "^2.0.20",
- "harvey-specter-quotes": "^1.4.1",
- "howifier": "^1.2.2",
- "insult": "0.0.3",
- "is-image-url": "^1.1.8",
- "lorem-memesum": "^1.2.0",
- "mathjs": "^6.6.4",
- "moment": "^2.24.0",
- "moment-duration-format": "^2.3.2",
- "mongoose": "^5.9.10",
- "motivations": "^1.1.2",
- "nekos.life": "^2.0.6",
- "node-opus": "^0.3.3",
- "node-superfetch": "^0.1.10",
- "popular-movie-quotes": "^1.2.4",
- "random-js": "^2.1.0",
- "romanize": "^1.1.1",
- "rss-parser": "^3.7.6",
- "simple-youtube-api": "^5.2.1",
- "spongibobu.js": "^1.1.0",
- "uwufy": "^1.0.5",
- "whois": "^2.12.1",
- "winston": "^3.2.1",
- "ytdl-core": "^2.1.0",
- "zero-two-quotes": "^1.0.2"
- },
- "devDependencies": {
- "@types/express": "^4.17.6",
- "@types/express-handlebars": "^3.1.0",
- "@types/mongoose": "^5.7.15",
- "supervisor": "^0.12.0",
- "typescript": "^3.8.3"
- }
-}
diff --git a/package.json.scripts b/package.json.scripts
deleted file mode 100644
index dc57768..0000000
--- a/package.json.scripts
+++ /dev/null
@@ -1,13 +0,0 @@
- "scripts": {
- "build": "tsc -p tsconfig.json && npm run copy:all",
- "start": "npm run start:prod",
- "start:debug": "supervisor ./dist/app.js",
- "start:prod": "node dist/app.js",
- "copy:assets": "cpx './src/assets/**/**/*' './dist/assets'",
- "copy:cache": "cpx './src/Cache/*' './dist/Cache'",
- "copy:all": "concurrently \"npm:copy:assets\" \"npm:copy:cache\""
- },
-
- "copy:assets": "cp -r 'src/assets/' 'dist/'",
- "copy:ws": "cp -r 'src/ws/' 'dist/'",
- "copy": "concurrently \"npm:copy:assets\" \"npm:copy:ws\"",
\ No newline at end of file
diff --git a/scripts/build.bat b/scripts/build.bat
deleted file mode 100644
index 59c629b..0000000
--- a/scripts/build.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-%cd%
-cd ..
-tsc
\ No newline at end of file
diff --git a/scripts/heroku_application_log.bat b/scripts/heroku_application_log.bat
deleted file mode 100644
index 49f71a4..0000000
--- a/scripts/heroku_application_log.bat
+++ /dev/null
@@ -1 +0,0 @@
-heroku logs -t --app uwufier
\ No newline at end of file
diff --git a/scripts/install_prerequisites.bat b/scripts/install_prerequisites.bat
deleted file mode 100644
index 7b67ea9..0000000
--- a/scripts/install_prerequisites.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-%cd%
-cd ..
-npm npm i
\ No newline at end of file
diff --git a/scripts/logs.bat b/scripts/logs.bat
deleted file mode 100644
index 49f71a4..0000000
--- a/scripts/logs.bat
+++ /dev/null
@@ -1 +0,0 @@
-heroku logs -t --app uwufier
\ No newline at end of file
diff --git a/scripts/prepare_build.bat b/scripts/prepare_build.bat
deleted file mode 100644
index 27f4874..0000000
--- a/scripts/prepare_build.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-%cd%
-cd ..
-@RD /S /Q "dist"
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..44fd26f
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,35 @@
+{
+ "dependencies": {
+ "@types/cheerio": "^0.22.21",
+ "@types/mongoose": "^5.7.32",
+ "axios": "^0.19.2",
+ "cheerio": "^1.0.0-rc.3",
+ "common-tags": "^1.8.0",
+ "cors": "^2.8.5",
+ "date-fns": "^2.15.0",
+ "discord-akairo": "1Computer1/discord-akairo",
+ "discord.js": "discordjs/discord.js",
+ "express": "^4.17.1",
+ "express-session": "^1.17.1",
+ "moment": "^2.27.0",
+ "moment-duration-format": "^2.3.2",
+ "mongoose": "^5.9.25",
+ "node-emoji": "^1.10.0",
+ "node-superfetch": "^0.1.10",
+ "rss-parser": "^3.9.0",
+ "winston": "^3.3.3"
+ },
+ "devDependencies": {
+ "@types/express": "^4.17.7",
+ "@types/express-session": "^1.17.0",
+ "@types/node": "^14.0.24",
+ "rimraf": "^3.0.2"
+ },
+ "main": "dist/Bot.js",
+ "scripts": {
+ "build": "yarn cleardir && yarn compile",
+ "cleardir": "rimraf dist/*",
+ "compile": "tsc",
+ "start": "yarn build && node ."
+ }
+}
diff --git a/server/src/API/API.ts b/server/src/API/API.ts
new file mode 100644
index 0000000..8a6eb0f
--- /dev/null
+++ b/server/src/API/API.ts
@@ -0,0 +1,33 @@
+import { AkairoClient } from 'discord-akairo';
+import express, { Application } from 'express';
+import { createServer } from 'http';
+import cors from 'cors';
+import OAuth2 from '../structures/OAuth2';
+
+import OAuth2Router from './routers/OAuth2Router';
+import GuildRouter from './routers/GuildRouter';
+
+export default class API {
+ protected client: AkairoClient;
+ protected server: Application;
+ protected oauth: OAuth2;
+
+ public constructor(client: AkairoClient) {
+ this.client = client;
+ this.oauth = new OAuth2(this.client);
+ }
+
+ public start(): void {
+ this.server = express();
+ this.server.use(express.json());
+ this.server.use(cors({
+ origin: true,
+ credentials: true
+ }));
+
+ new OAuth2Router(this.server, this.client, this.oauth);
+ new GuildRouter(this.server, this.client);
+
+ createServer(this.server).listen(8088, (): void => console.log('API is online.'));
+ }
+}
\ No newline at end of file
diff --git a/server/src/API/routers/GuildRouter.ts b/server/src/API/routers/GuildRouter.ts
new file mode 100644
index 0000000..01a8a9b
--- /dev/null
+++ b/server/src/API/routers/GuildRouter.ts
@@ -0,0 +1,44 @@
+import { Router, Request, Response, Application } from 'express';
+import { AkairoClient } from 'discord-akairo';
+import { Guild } from 'discord.js';
+import { authorization } from '../../Config';
+
+export default class GuildRouter {
+ protected app: Application;
+ protected client: AkairoClient;
+ protected router: Router;
+
+ public constructor(app: Application, client: AkairoClient) {
+ this.app = app;
+ this.client = client;
+ this.router = Router();
+
+ this.app.use(this.router);
+
+ this.router.get('/v1/get/guild/:id', (req: Request, res: Response) => {
+ const guild: Guild = this.client.guilds.cache.get(req.params.id);
+ if (!guild) return res.status(404).send({ message: 'Guild Not Found' });
+
+ return res.status(200).send({
+ name: guild.name,
+ owner: guild.owner.user.tag,
+ members: guild.memberCount
+ });
+ });
+
+ this.router.post('/v1/post/guild-name/:id', (req: Request, res: Response) => {
+ if (req.headers.authorization !== authorization) return res.status(401).send({ message: 'Unauthorized' });
+
+ const guild: Guild = this.client.guilds.cache.get(req.params.id);
+ if (!guild) return res.status(404).send({ message: 'Guild Not Found' });
+
+ if (!req.body.name) return res.status(404).send({ message: 'No Guild Name Provided' });
+ if (req.body.name.length > 32) return res.status(400).send({ message: 'Guild Name Exceeds 32 Characters' });
+ if (!guild.me.permissions.has('MANAGE_GUILD')) return res.status(401).send({ message: 'Cannot Manage Guild' });
+
+ guild.setName(req.body.name);
+
+ return res.status(201).send(req.body);
+ });
+ }
+}
\ No newline at end of file
diff --git a/server/src/API/routers/OAuth2Router.ts b/server/src/API/routers/OAuth2Router.ts
new file mode 100644
index 0000000..60b0410
--- /dev/null
+++ b/server/src/API/routers/OAuth2Router.ts
@@ -0,0 +1,71 @@
+import { Router, Request, Response, Application } from 'express';
+import { AkairoClient } from 'discord-akairo';
+import fetch from 'node-fetch';
+import session from 'express-session';
+import OAuth2 from '../../structures/OAuth2';
+import { callbackUrl, authorization, clientID, redirectUri, clientSecret } from '../../Config';
+
+export default class OAuth2Router {
+ protected app: Application;
+ protected client: AkairoClient;
+ protected router: Router;
+ protected oauth: OAuth2;
+
+ public constructor(app: Application, client: AkairoClient, oauth: OAuth2) {
+ this.app = app;
+ this.client = client;
+ this.router = Router();
+ this.oauth = oauth;
+
+ this.app.use(session({
+ secret: authorization,
+ resave: false,
+ saveUninitialized: false,
+ cookie: {
+ secure: 'auto',
+ sameSite: false,
+ httpOnly: false,
+ maxAge: 6048e5
+ }
+ }));
+
+ this.app.use(this.router);
+
+ this.router.get('/oauth/login', (req: Request, res: Response) => {
+ return res.redirect(`https://discord.com/api/oauth2/authorize?client_id=${clientID}&redirect_uri=${encodeURIComponent(callbackUrl)}&response_type=code&scope=${encodeURIComponent('identify guilds')}`);
+ });
+
+ this.router.get('/oauth/logout', (req: Request, res: Response) => {
+ req.session.destroy(null);
+ return res.redirect(redirectUri);
+ });
+
+ this.router.get('/oauth/callback', (req: Request, res: Response) => {
+ fetch('https://discord.com/api/oauth2/token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ //@ts-ignore
+ body: new URLSearchParams({
+ 'client_id': clientID,
+ 'client_secret': clientSecret,
+ 'grant_type': 'authorization_code',
+ 'code': req.query.code,
+ 'redirect_uri': callbackUrl,
+ 'scope': 'identify'
+ })
+ })
+ .then(response => response.json())
+ .then(response => {
+ req.session.token = response['access_token'];
+ res.redirect(redirectUri);
+ });
+ });
+
+ this.router.get('/oauth/details', async (req: Request, res: Response) => {
+ const details = await this.oauth.resolveInformation(req);
+ return res.status(200).send(details);
+ });
+ }
+}
\ No newline at end of file
diff --git a/server/src/Bot.ts b/server/src/Bot.ts
new file mode 100644
index 0000000..899aff4
--- /dev/null
+++ b/server/src/Bot.ts
@@ -0,0 +1,5 @@
+import { token, owners } from './Config';
+import BotClient from './client/BotClient';
+
+const client: BotClient = new BotClient({ token, owners });
+client.start();
\ No newline at end of file
diff --git a/server/src/Config.ts b/server/src/Config.ts
new file mode 100644
index 0000000..8be44ed
--- /dev/null
+++ b/server/src/Config.ts
@@ -0,0 +1,12 @@
+export const token: string = "NzEyMDg4MzY5MjA2OTE5MjY5.XxZ9gQ.Qf-wdR-rRG4-4ImmOf7No3XdkP0";
+export const prefix: string = "uwu$";
+export const owners: string[] = ['217348698294714370'];
+export const authorization: string = "rex-1337";
+// This is all devifier's information, change this in production.
+export const clientID: string = "712088369206919269";
+export const clientSecret: string = "qpj1nsOo7HWVyNh6lY8Z8I8IvtV0bPiM";
+export const redirectUri: string = "http://localhost:3000";
+export const callbackUrl: string = "http://localhost:8088/oauth/callback"; // 8080
+export const colour: string = "ecb1d1";
+export const validIDs: string[] = [];
+export const mongoDBUri: string = "mongodb://sin:cSDZEygGZz232eJ5bS@ds047107.mlab.com:47107/heroku_4qrjvmb9";
\ No newline at end of file
diff --git a/server/src/client/BotClient.ts b/server/src/client/BotClient.ts
new file mode 100644
index 0000000..da23a7b
--- /dev/null
+++ b/server/src/client/BotClient.ts
@@ -0,0 +1,103 @@
+import { AkairoClient, CommandHandler, ListenerHandler, InhibitorHandler } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { join } from 'path';
+import { prefix, owners } from '../Config';
+import { logger } from '../utils/Logger';
+import { SettingsProvider } from '../database';
+import { Logger } from 'winston';
+
+declare module 'discord-akairo' {
+ interface AkairoClient {
+ commandHandler: CommandHandler;
+ listenerHandler: ListenerHandler;
+ logger: Logger;
+ settings: SettingsProvider;
+ }
+}
+
+interface BotOptions {
+ token?: string;
+ owners?: string[];
+ // prefix?: string;
+}
+
+export default class BotClient extends AkairoClient {
+ public readonly config: BotOptions;
+
+ public logger = logger;
+
+ public inhibitorHandler: InhibitorHandler = new InhibitorHandler(this, {
+ directory: join(__dirname, '..', 'inhibitors')
+ });
+
+ public listenerHandler: ListenerHandler = new ListenerHandler(this, {
+ directory: join(__dirname, '..', 'listeners')
+ });
+
+ public commandHandler: CommandHandler = new CommandHandler(this, {
+ directory: join(__dirname, '..', 'commands'),
+ /* prefix: async (msg: Message): Promise => {
+ if (msg.guild) {
+ const doc = this.settings.cache.guilds.get(msg.guild.id);
+ if (doc?.prefix) return doc.prefix;
+ }
+ return this.config.prefix
+ }, */
+ prefix,
+ allowMention: true,
+ defaultCooldown: 6e4, // 60000 - 6 - count the zeroes... = 4, so its 6e4
+ ignorePermissions: owners,
+ // Extra stuff
+ argumentDefaults: {
+ prompt: {
+ modifyStart: (_: Message, str: string): string => `${str}\n\nType \`cancel\` to cancel the command...`,
+ modifyRetry: (_: Message, str: string): string => `${str}\n\nType \`cancel\` to cancel the command...`,
+ timeout: 'You took too long, the command has now been cancelled...',
+ ended: 'You exceeded the maximum amount of tries, this command has now been cancelled...',
+ cancel: 'This command has been cancelled...',
+ retries: 3,
+ time: 3e4
+ },
+ otherwise: ''
+ }
+ });
+
+ public settings: SettingsProvider = new SettingsProvider(this);
+
+ public constructor(config: BotOptions) {
+ super({
+ ownerID: config.owners,
+ messageCacheMaxSize: 50,
+ messageSweepInterval: 900,
+ messageCacheLifetime: 300,
+ partials: ['MESSAGE', 'REACTION']
+ });
+
+ this.config = config;
+
+ this.on('shardError', (err: Error, id: any): Logger => this.logger.warn(`[SHARD ${id} ERROR] ${err.message}`, err.stack))
+ .on('warn', (warn: any): Logger => this.logger.warn(`[CLIENT WARN] ${warn}`));
+ }
+
+ private async init(): Promise {
+ await this.settings.init();
+ this.commandHandler.useInhibitorHandler(this.inhibitorHandler);
+ this.commandHandler.useListenerHandler(this.listenerHandler);
+ this.listenerHandler.setEmitters({
+ commandHandler: this.commandHandler,
+ listenerHandler: this.listenerHandler,
+ inhibitorHandler: this.inhibitorHandler,
+ process
+ });
+ this.commandHandler.loadAll();
+ this.listenerHandler.loadAll();
+ this.inhibitorHandler.loadAll();
+
+ return this;
+ }
+
+ public async start(): Promise {
+ await this.init();
+ return this.login(this.config.token);
+ }
+}
\ No newline at end of file
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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 | 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 {
+ 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 {
+ 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 {
+ // @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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 | 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ //@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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ //@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 {
+ //@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 {
+ //@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 {
+ 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} `)
+ .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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 => {
+ 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 => {
+ 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 {
+ 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 {
+ 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 | 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 | 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 {
+ 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 {
+ 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 \`.`
+ ]);
+
+ 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 {
+ const embed = this.client.util.embed()
+ .setColor(colour)
+ .addField('Category List', [
+ `To view details for a specific category, do \`${this.client.commandHandler.prefix}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 {
+ 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 `
+ },
+ {
+ 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
diff --git a/server/src/database/index.ts b/server/src/database/index.ts
new file mode 100644
index 0000000..dc10553
--- /dev/null
+++ b/server/src/database/index.ts
@@ -0,0 +1,5 @@
+import ReactionGuildModel from './models/ReactionGuildModel';
+import ReactionModel from './models/ReactionModel';
+import SettingsProvider from './structures/SettingsProvider';
+
+export { SettingsProvider };
\ No newline at end of file
diff --git a/server/src/database/models/DarlingModel.ts b/server/src/database/models/DarlingModel.ts
new file mode 100644
index 0000000..3f98037
--- /dev/null
+++ b/server/src/database/models/DarlingModel.ts
@@ -0,0 +1,10 @@
+import mongoose from 'mongoose';
+const darlingSchema = new mongoose.Schema({
+ _id: mongoose.Schema.Types.ObjectId,
+ username: String,
+ userID: String,
+ guildname: String,
+ guildID: String,
+ time: String
+});
+export = mongoose.model('Darling', darlingSchema);
\ No newline at end of file
diff --git a/server/src/database/models/FanArtModel.ts b/server/src/database/models/FanArtModel.ts
new file mode 100644
index 0000000..c2d983e
--- /dev/null
+++ b/server/src/database/models/FanArtModel.ts
@@ -0,0 +1,12 @@
+import mongoose from 'mongoose';
+const fanArtSchema = new mongoose.Schema({
+ _id: mongoose.Schema.Types.ObjectId,
+ username: String,
+ userID: String,
+ guildname: String,
+ guildID: String,
+ channelname: String,
+ channelID: String,
+ time: String
+});
+export = mongoose.model('FanArt', fanArtSchema);
\ No newline at end of file
diff --git a/server/src/database/models/GoodbyeModel.ts b/server/src/database/models/GoodbyeModel.ts
new file mode 100644
index 0000000..04cc7c4
--- /dev/null
+++ b/server/src/database/models/GoodbyeModel.ts
@@ -0,0 +1,12 @@
+import mongoose from 'mongoose';
+const goodbyeSchema = new mongoose.Schema({
+ _id: mongoose.Schema.Types.ObjectId,
+ username: String,
+ userID: String,
+ guildname: String,
+ guildID: String,
+ channelname: String,
+ channelID: String,
+ time: String
+});
+export = mongoose.model('Goodbye', goodbyeSchema);
\ No newline at end of file
diff --git a/server/src/database/models/ReactionGuildModel.ts b/server/src/database/models/ReactionGuildModel.ts
new file mode 100644
index 0000000..6389b56
--- /dev/null
+++ b/server/src/database/models/ReactionGuildModel.ts
@@ -0,0 +1,16 @@
+import { Document, Schema, model } from 'mongoose';
+
+export interface Guild extends Document {
+ id: string;
+ prefix: string;
+ premium: boolean;
+ expiresAt: Date;
+}
+
+const Guild: Schema = new Schema({
+ id: String,
+ prefix: String,
+ premium: Boolean,
+ expiresAt: Date
+}, { strict: false });
+export default model('Guild', Guild);
\ No newline at end of file
diff --git a/server/src/database/models/ReactionModel.ts b/server/src/database/models/ReactionModel.ts
new file mode 100644
index 0000000..509dadd
--- /dev/null
+++ b/server/src/database/models/ReactionModel.ts
@@ -0,0 +1,36 @@
+import { Document, Schema, model } from 'mongoose';
+
+export interface Reaction extends Document {
+ guildID: string;
+ messageID: string;
+ channelID: string;
+ userID: string;
+ id: string;
+ emoji: string;
+ emojiType: string;
+ roleID: string;
+ uses: number;
+ expiresAt?: Date;
+ type: number;
+ active: boolean;
+}
+
+const Reaction: Schema = new Schema({
+ guildID: String,
+ messageID: String,
+ channelID: String,
+ userID: String,
+ id: String,
+ emoji: String,
+ emojiType: String,
+ roleID: String,
+ uses: Number,
+ expiresAt: Date,
+ type: Number,
+ active: {
+ type: Boolean,
+ default: true,
+ },
+}, { strict: false });
+
+export default model('Reaction', Reaction);
diff --git a/server/src/database/models/WelcomeModel.ts b/server/src/database/models/WelcomeModel.ts
new file mode 100644
index 0000000..2a26a6f
--- /dev/null
+++ b/server/src/database/models/WelcomeModel.ts
@@ -0,0 +1,12 @@
+import mongoose from 'mongoose';
+const welcomeSchema = new mongoose.Schema({
+ _id: mongoose.Schema.Types.ObjectId,
+ username: String,
+ userID: String,
+ guildname: String,
+ guildID: String,
+ channelname: String,
+ channelID: String,
+ time: String
+});
+export = mongoose.model('Welcome', welcomeSchema);
\ No newline at end of file
diff --git a/server/src/database/structures/SettingsProvider.ts b/server/src/database/structures/SettingsProvider.ts
new file mode 100644
index 0000000..0a2325a
--- /dev/null
+++ b/server/src/database/structures/SettingsProvider.ts
@@ -0,0 +1,212 @@
+import { Collection } from 'discord.js';
+import { connect, Model, connection, Connection } from 'mongoose';
+import { Logger } from 'winston';
+import ReactionModel, { Reaction } from '../models/ReactionModel';
+import GuildModel, { Guild } from '../models/ReactionGuildModel';
+import { MONGO_EVENTS } from '../utils/Constants'
+import BotClient from '../../client/BotClient';
+import { mongoDBUri } from '../../Config';
+
+let i = 0;
+
+/**
+ * The key, model and cached collection of a database model.
+ * @interface
+ */
+interface Combo {
+ key: string;
+ model: Model;
+ cache: Collection;
+}
+
+/**
+ * The Settings Provider that handles all database reads and rights.
+ * @private
+ */
+export default class SettingsProvider {
+ protected readonly client: BotClient;
+
+ protected readonly guilds: Collection = new Collection();
+ protected readonly reactions: Collection = new Collection();
+
+ protected readonly GuildModel = GuildModel;
+ protected readonly ReactionModel = ReactionModel;
+
+ /**
+ *
+ * @param {GiveawayClient} client - The extended Akairo Client
+ */
+ public constructor(client: BotClient) {
+ this.client = client;
+ }
+
+ /**
+ * Retuns all the collection caches.
+ * @returns {Object}
+ */
+ public get cache() {
+ return {
+ guilds: this.guilds,
+ reactions: this.reactions,
+ };
+ }
+
+ /**
+ * Returns the database combos
+ * @returns {Combo[]}
+ */
+ public get combos(): Combo[] {
+ return [
+ {
+ key: 'guild',
+ model: this.GuildModel,
+ cache: this.guilds,
+ },
+ {
+ key: 'reaction',
+ model: this.ReactionModel,
+ cache: this.reactions,
+ },
+ ];
+ }
+
+ /**
+ * Creates a new database document with the provided collection name and data.
+ * @param {string} type - The collection name
+ * @param {object} data - The data for the new document
+ * @returns {Docuement}
+ */
+ public async new(type: 'guild', data: Partial): Promise;
+ public async new(type: 'reaction', data: Partial): Promise;
+ public async new(type: string, data: object): Promise