v3.0 (#145)
* Change rules.txt to rules.json (#31) * Migrate to yarn * Add role configs to config template * Install packges and setup typescript * Migrate entry point * Migrate about command * Migrate ban command * Migrate clear command * Migrate kick command * Migrate mute command * Migrate poll command * Migrate bunny command * Update required roles checker * Migrate role command * Migrate unmute command * Migrate warn command * Migrate eval command * Migrate help command * Migrate rules command * Migrate events to typescript * Update about command to use the PublicEmbed class * Update ErrorMessage to ChannelNotFound * Update messageDelete event to ignore bots * Feature/74 merge vylbot core (#80) * Merge VylBot-Core * Update commands to new system * Fix issue where events would not load * Feature/12 create tests (#102) * Fix tests * Update coverage * Remove unrequired mock files * Add about command test * Update about tests * Ban command tests * eval command tests * Start help command tests * Add help command tests * Add kick command tests * Mute command tests * Poll command tests * Add role command tests Signed-off-by: Ethan Lane <ethan@vylpes.com> * Add rules command tests * Add unmute command tests * Add warn command tests * Add MemberEvents tests * Add GuildMemberUpdate tests Signed-off-by: Ethan Lane <ethan@vylpes.com> * Add MessageEvents tests * Add StringTools test Signed-off-by: Ethan Lane <ethan@vylpes.com> * Add embed tests Signed-off-by: Ethan Lane <ethan@vylpes.com> * Add GitHub Actions Signed-off-by: Ethan Lane <ethan@vylpes.com> * Move to tslint Signed-off-by: Ethan Lane <ethan@vylpes.com> * Remove tslint Signed-off-by: Ethan Lane <ethan@vylpes.com> * Remove linting script Signed-off-by: Ethan Lane <ethan@vylpes.com> * Update rules with blog website and event spoilers rule" (#106) Signed-off-by: Ethan Lane <ethan@vylpes.com> * Containerise bot (#107) * Add moderator names to audit reason (#108) * Feature/48 database (#114) * Add database and default values * Add ability to save a setting to the database * Get commands and events to use database * Setup and config command * Update commands to check roles per server * Different rules per server Signed-off-by: Ethan Lane <ethan@vylpes.com> * Different prefix per server Signed-off-by: Ethan Lane <ethan@vylpes.com> * Add verification system Signed-off-by: Ethan Lane <ethan@vylpes.com> * Disabled commands per server * Add devmode for default prefix * Update embeds * Fix broken tests * Feature/66 add different commands per server (#122) * Add ability for server exclusive commands * Add MankBot server-exclusive commands * Add lobby entity to database * Add documentation * Add setup command for lobby (#123) * Update bot to discord.js v13 (#125) * Update bot to discord.js v13 * Remove debug code * 110 commandshelp about command errors which causes command to not run (#126) * Change onMessage to onMessageCreate * Fix help command * Add override for bot owner and server owner (#135) * Change help command so exclusive commands can only be seen for the server they're assigned to (#136) * Change parsing to not crash if invalid (#142) * 137 role command cannot read properties of undefined (#141) * Fix issue with bot crashing * Fix server prefix not showing * Add easy way to configure role command * Move help text to its own directory * Make role config command to use role id * Get lobby command to use IDs instead of names (#144) Co-authored-by: Vylpes <getgravitysoftware@gmail.com>
This commit is contained in:
parent
1168898e57
commit
04a4a6204c
118 changed files with 13966 additions and 4027 deletions
11
.dockerignore
Normal file
11
.dockerignore
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
node_modules/
|
||||||
|
tests/
|
||||||
|
coverage/
|
||||||
|
.github/
|
||||||
|
.gitlab/
|
||||||
|
|
||||||
|
.env.template
|
||||||
|
.gitlab-ci.yml
|
||||||
|
jest.config.js
|
||||||
|
jest.setup.js
|
||||||
|
README.md
|
13
.env.template
Normal file
13
.env.template
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Security Warning! Do not commit this file to any VCS!
|
||||||
|
# This is a local file to speed up development process,
|
||||||
|
# so you don't have to change your environment variables.
|
||||||
|
#
|
||||||
|
# This is not applied to `.env.template`!
|
||||||
|
# Template files must be committed to the VCS, but must not contain
|
||||||
|
# any secret values.
|
||||||
|
|
||||||
|
BOT_TOKEN=
|
||||||
|
BOT_VER=3.0
|
||||||
|
BOT_AUTHOR=Vylpes
|
||||||
|
BOT_DATE=24 Apr 2022
|
||||||
|
BOT_OWNERID=147392775707426816
|
49
.eslintrc
49
.eslintrc
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 6
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"camelcase": "error",
|
|
||||||
"brace-style": [
|
|
||||||
"error",
|
|
||||||
"1tbs"
|
|
||||||
],
|
|
||||||
"comma-dangle": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"comma-spacing": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"before": false,
|
|
||||||
"after": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"comma-style": [
|
|
||||||
"error",
|
|
||||||
"last"
|
|
||||||
],
|
|
||||||
"arrow-body-style": [
|
|
||||||
"error",
|
|
||||||
"as-needed"
|
|
||||||
],
|
|
||||||
"arrow-parens": [
|
|
||||||
"error",
|
|
||||||
"as-needed"
|
|
||||||
],
|
|
||||||
"arrow-spacing": "error",
|
|
||||||
"no-var": "error",
|
|
||||||
"prefer-template": "error",
|
|
||||||
"prefer-const": "error"
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"exports": "writable",
|
|
||||||
"module": "writable",
|
|
||||||
"require": "writable",
|
|
||||||
"process": "writable",
|
|
||||||
"console": "writable"
|
|
||||||
}
|
|
||||||
}
|
|
27
.github/workflows/testing.yml
vendored
Normal file
27
.github/workflows/testing.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
name: Testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [16.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn build
|
||||||
|
- run: yarn test
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -103,4 +103,6 @@ dist
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
|
|
||||||
config.json
|
config.json
|
||||||
|
.DS_Store
|
||||||
|
ormconfig.json
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Create app and work directory
|
||||||
|
FROM node:16
|
||||||
|
WORKDIR /vylbot
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY package.json .
|
||||||
|
COPY yarn.lock .
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
# Bundle app source
|
||||||
|
COPY . .
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
# Run the app source
|
||||||
|
CMD [ "yarn", "start" ]
|
50
README.md
50
README.md
|
@ -1,6 +1,6 @@
|
||||||
# VylBot App
|
# VylBot App
|
||||||
|
|
||||||
Discord bot for Vylpes' Den Discord Server. Based on [VylBot Core](https://github.com/getgravitysoft/vylbot-core).
|
Discord bot for Vylpes' Den Discord Server.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -8,16 +8,48 @@ Download the latest version from the [releases page](https://github.com/Vylpes/v
|
||||||
|
|
||||||
Copy the config template file and fill in the strings.
|
Copy the config template file and fill in the strings.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- NodeJS v16
|
||||||
|
- Yarn
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Implement the client using something like:
|
Install the dependencies and build the app:
|
||||||
|
|
||||||
```js
|
```bash
|
||||||
const vylbot = require('vylbot-core');
|
yarn install
|
||||||
const config = require('./config.json');
|
yarn build
|
||||||
|
|
||||||
const client = new vylbot.client(config);
|
|
||||||
client.start();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See the `docs` folder for more information on how to use vylbot-core
|
Setup the database (Recommended to use the docker-compose file)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy and edit the settings files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.template .env
|
||||||
|
# Edit the .env file
|
||||||
|
|
||||||
|
cp ormconfig.json.template ormconfig.json
|
||||||
|
# Edit the ormconfig.json file
|
||||||
|
```
|
||||||
|
|
||||||
|
> **NOTE:** Make sure you do *not* check in these files! These contain sensitive information and should be treated as private.
|
||||||
|
|
||||||
|
Start the bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can start the bot in development mode using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn start --dev
|
||||||
|
```
|
||||||
|
|
||||||
|
> Dev mode ensures that the default prefix is different to the production mode, in case you have both running in the same server.
|
|
@ -1,45 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class about extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set execute method, description, and category
|
|
||||||
super("about");
|
|
||||||
super.description = "About the bot";
|
|
||||||
super.category = "General";
|
|
||||||
|
|
||||||
// Set required configs in the config.about json string.
|
|
||||||
// description: The bot description
|
|
||||||
// version: The bot version
|
|
||||||
// author: Bot author
|
|
||||||
// date: Date of build
|
|
||||||
super.requiredConfigs = "description";
|
|
||||||
super.requiredConfigs = "version";
|
|
||||||
super.requiredConfigs = "core-ver";
|
|
||||||
super.requiredConfigs = "author";
|
|
||||||
super.requiredConfigs = "date";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The execution method
|
|
||||||
about(context) {
|
|
||||||
// Create an embed containing data about the bot
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("About")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(context.client.config.about.description)
|
|
||||||
.addField("Version", context.client.config.about.version, true)
|
|
||||||
.addField("VylBot Core", context.client.config.about['core-ver'], true)
|
|
||||||
.addField("Author", context.client.config.about.author)
|
|
||||||
.addField("Date", context.client.config.about.date);
|
|
||||||
|
|
||||||
// Send embed to the channel the command was sent in
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the about class to be exported
|
|
||||||
module.exports = about;
|
|
|
@ -1,93 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class ban extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set execution method, description, category, and usage
|
|
||||||
super("ban");
|
|
||||||
super.description = "Bans the mentioned user with an optional reason";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<@user> [reason]";
|
|
||||||
|
|
||||||
// Set required configs in the config.ban json string
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredCofigs = "logchannel";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command execution method
|
|
||||||
ban(context) {
|
|
||||||
// If the user has the modrole (set in config.ban.modrole)
|
|
||||||
if (context.message.guild.roles.cache.find(role => role.name == context.client.config.ban.modrole)) {
|
|
||||||
// Gets the user pinged in the command
|
|
||||||
const user = context.message.mentions.users.first();
|
|
||||||
|
|
||||||
// If the user pinged is a valid user
|
|
||||||
if (user) {
|
|
||||||
// Get the guild member object from the pinged user
|
|
||||||
const member = context.message.guild.member(user);
|
|
||||||
|
|
||||||
// If the member object exists, i.e. if they are in the server
|
|
||||||
if (member) {
|
|
||||||
// Get the arguments and remove what isn't the reason
|
|
||||||
const reasonArgs = context.arguments;
|
|
||||||
reasonArgs.splice(0, 1);
|
|
||||||
|
|
||||||
// Join the array into a string
|
|
||||||
const reason = reasonArgs.join(" ");
|
|
||||||
|
|
||||||
// If the guild is available to work with
|
|
||||||
if (context.message.guild.available) {
|
|
||||||
// If the bot client is able to ban the member
|
|
||||||
if (member.bannable) {
|
|
||||||
// The Message Embed which goes into the bot log
|
|
||||||
const embedLog = new MessageEmbed()
|
|
||||||
.setTitle("Member Banned")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${user} \`${user.tag}\``, true)
|
|
||||||
.addField("Moderator", `${context.message.author} \`${context.message.author.tag}\``, true)
|
|
||||||
.addField("Reason", reason || "*none*")
|
|
||||||
.setThumbnail(user.displayAvatarURL);
|
|
||||||
|
|
||||||
// The Message Embed which goes into the public channel the message was sent in
|
|
||||||
const embedPublic = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${user} has been banned`);
|
|
||||||
|
|
||||||
// Ban the member and send the embeds into the appropriate channel, then delete the initial message
|
|
||||||
member.ban({ reason: reason }).then(() => {
|
|
||||||
context.message.guild.channels.cache.find(channel => channel.name == context.client.config.ban.logchannel).send(embedLog);
|
|
||||||
context.message.channel.send(embedPublic);
|
|
||||||
|
|
||||||
context.message.delete();
|
|
||||||
}).catch(err => { // If the bot couldn't ban the member, say so and log the error to the console
|
|
||||||
errorEmbed(context, "An error occurred");
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // If the member object doesn't exist
|
|
||||||
errorEmbed(context, "User is not in this server");
|
|
||||||
}
|
|
||||||
} else { // If the user object doesn't exist
|
|
||||||
errorEmbed(context, "User does not exist");
|
|
||||||
}
|
|
||||||
} else { // If the user doesn't have the mod role
|
|
||||||
errorEmbed(context, `You require the \`${context.client.config.ban.modrole}\` role to run this command`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post an error embed
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ban;
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
const { randomBunny } = require('random-bunny');
|
|
||||||
|
|
||||||
// Command variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command class
|
|
||||||
class bunny extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set run method, description, and category
|
|
||||||
super("bunny");
|
|
||||||
super.description = "Gives you a random bunny";
|
|
||||||
super.category = "Fun";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
bunny(context) {
|
|
||||||
// Get a random post from r/Rabbits
|
|
||||||
randomBunny('rabbits', 'hot', (res) => {
|
|
||||||
// Create an embed containing the random image
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle(res.title)
|
|
||||||
.setImage(res.url)
|
|
||||||
.setURL("https://reddit.com" + res.permalink)
|
|
||||||
.setFooter(`r/Rabbits · ${res.ups} upvotes`);
|
|
||||||
|
|
||||||
// Send the embed
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = bunny;
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class clear extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set execute method, description, category, and usage
|
|
||||||
super("clear");
|
|
||||||
super.description = "Bulk deletes the chat for up to 100 messages";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<amount>";
|
|
||||||
|
|
||||||
// Set required configs in the config.clear json string
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredConfigs = "logchannel";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute method
|
|
||||||
clear(context) {
|
|
||||||
// If the user has the config.clear.modrole role
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.clear.modrole)) {
|
|
||||||
// If the command specifies a number between 1 and 100
|
|
||||||
if (context.arguments.length > 0 && context.arguments[0] > 0 && context.arguments[0] < 101) {
|
|
||||||
// Attempt to bulk delete the amount of messages specified as an argument
|
|
||||||
context.message.channel.bulkDelete(context.arguments[0]).then(() => {
|
|
||||||
// Public embed
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${context.arguments[0]} messages were removed`);
|
|
||||||
|
|
||||||
// Send the embed into the channel the command was sent in
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}).catch(err => { // If the bot couldn't bulk delete
|
|
||||||
errorEmbed(context, "An error has occurred");
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
} else { // If the user didn't give a number valid (between 1 and 100)
|
|
||||||
errorEmbed(context, "Please specify an amount between 1 and 100");
|
|
||||||
}
|
|
||||||
} else { // If the user doesn't have the mod role
|
|
||||||
errorEmbed(context, `This command requires the \`${context.client.config.clear.modrole}\` role to run`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to send an error embed
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = clear;
|
|
|
@ -1,25 +0,0 @@
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
class evaluate extends command {
|
|
||||||
constructor() {
|
|
||||||
super("evaluate");
|
|
||||||
super.description = "Evaluates an expression";
|
|
||||||
super.category = "Administration";
|
|
||||||
super.requiredConfigs = "ownerid";
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluate(context) {
|
|
||||||
if (context.message.author.id == context.client.config.eval.ownerid) {
|
|
||||||
const result = eval(context.arguments.join(" "));
|
|
||||||
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setDescription(result)
|
|
||||||
.setColor(0x3050ba);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = evaluate;
|
|
150
commands/help.js
150
commands/help.js
|
@ -1,150 +0,0 @@
|
||||||
// Required Components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
const { readdirSync } = require('fs');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class help extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the execute method, description, category, and example usage
|
|
||||||
super("help");
|
|
||||||
super.description = "Gives a list of commands available in the bot";
|
|
||||||
super.category = "General";
|
|
||||||
super.usage = "[command]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute method
|
|
||||||
help(context) {
|
|
||||||
// Get the list of command folders the bot has been setup to check
|
|
||||||
const commandFolders = context.client.config.commands;
|
|
||||||
|
|
||||||
// Empty arrays for commands
|
|
||||||
// allCommands: Will contain objects of all commands with their related info
|
|
||||||
// categories: Will contain strings of all the categories the commands are set to, unique
|
|
||||||
const allCommands = [];
|
|
||||||
const categories = [];
|
|
||||||
|
|
||||||
// Loop through all the command folders set
|
|
||||||
// i = folder index
|
|
||||||
for (let i = 0; i < commandFolders.length; i++) {
|
|
||||||
// The current folder the bot is looking through
|
|
||||||
const folder = commandFolders[i];
|
|
||||||
|
|
||||||
// Read the directory of the current folder
|
|
||||||
const contents = readdirSync(`${process.cwd()}/${folder}`);
|
|
||||||
|
|
||||||
// Loop through the contents of the folder
|
|
||||||
// j = file index in folder i
|
|
||||||
for (let j = 0; j < contents.length; j++) {
|
|
||||||
// Get command in the current folder to read
|
|
||||||
const file = require(`${process.cwd()}/${folder}/${contents[j]}`);
|
|
||||||
|
|
||||||
// Initialise the command
|
|
||||||
const obj = new file();
|
|
||||||
|
|
||||||
// Data object containing the command information
|
|
||||||
const data = {
|
|
||||||
"name": contents[j].replace(".js", ""),
|
|
||||||
"description": obj.description,
|
|
||||||
"category": obj.category,
|
|
||||||
"usage": obj.usage,
|
|
||||||
"roles": obj.roles
|
|
||||||
};
|
|
||||||
|
|
||||||
// Push the command data to the allCommands Array
|
|
||||||
allCommands.push(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through all the commands discovered by the previous loop
|
|
||||||
for (let i = 0; i < allCommands.length; i++) {
|
|
||||||
// Get the current command category name, otherwise "none"
|
|
||||||
const category = allCommands[i].category || "none";
|
|
||||||
|
|
||||||
// If the command isn't already set, set it.
|
|
||||||
// This will then make the categories array be an array of all categories which have been used but only one of each.
|
|
||||||
if (!categories.includes(category)) categories.push(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an command name has been passed as an argument
|
|
||||||
// If so, send information about that command
|
|
||||||
// If not, send the help embed of all commands
|
|
||||||
if (context.arguments[0]) {
|
|
||||||
sendCommand(context, allCommands, context.arguments[0]);
|
|
||||||
} else {
|
|
||||||
sendAll(context, categories, allCommands);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send embed of all commands
|
|
||||||
// context: The command context json string
|
|
||||||
// categories: The array of categories found
|
|
||||||
// allCommands: The array of the commands found
|
|
||||||
function sendAll(context, categories, allCommands) {
|
|
||||||
// Embed to be sent
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle("Commands");
|
|
||||||
|
|
||||||
// Loop through each command
|
|
||||||
for (let i = 0; i < categories.length; i++) {
|
|
||||||
// The category name of the current one to check
|
|
||||||
const category = categories[i];
|
|
||||||
|
|
||||||
// Empty Array for the next loop to filter out the current category
|
|
||||||
const commandsFilter = [];
|
|
||||||
|
|
||||||
// Loop through allCommands
|
|
||||||
// If the command is set to the current category being checked, add it to the filter array
|
|
||||||
for (let j = 0; j < allCommands.length; j++) {
|
|
||||||
if (allCommands[j].category == category) commandsFilter.push(`\`${allCommands[j].name}\``);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a field to the embed which contains the category name and all the commands in that category
|
|
||||||
embed.addField(category, commandsFilter.join(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the embed
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send information about a specific command
|
|
||||||
// context: The command context json string
|
|
||||||
// allCommands: The array of categories found
|
|
||||||
// name: The command name to check
|
|
||||||
function sendCommand(context, allCommands, name) {
|
|
||||||
let command = {};
|
|
||||||
|
|
||||||
// Loop through all commands, if the command name is the same as the one we're looking for, select it
|
|
||||||
for (let i = 0; i < allCommands.length; i++) {
|
|
||||||
if (allCommands[i].name == name) command = allCommands[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a matching command has been found
|
|
||||||
if (command.name) {
|
|
||||||
// Create an embed containing the related information of the command
|
|
||||||
// The title is the command name but sets the first letter to be capitalised
|
|
||||||
// If a set of information isn't set, set it to say "none"
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle(command.name[0].toUpperCase() + command.name.substring(1))
|
|
||||||
.setDescription(command.description || "*none*")
|
|
||||||
.addField("Category", command.category || "*none*", true)
|
|
||||||
.addField("Usage", command.usage || "*none*", true)
|
|
||||||
.addField("Required Roles", command.roles.join(", ") || "*none*");
|
|
||||||
|
|
||||||
// Send the embed
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
} else { // If no command has been found, then send an embed which says this
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription("Command does not exist");
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = help;
|
|
|
@ -1,93 +0,0 @@
|
||||||
// Required Components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class kick extends command {
|
|
||||||
constructor() {
|
|
||||||
// Sets the command's run method, description, category, and usage
|
|
||||||
super("kick");
|
|
||||||
super.description = "Kicks the mentioned user with an optional reason";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<@user> [reason]";
|
|
||||||
|
|
||||||
// Sets the required configs for the command
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredConfigs = "logchannel";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The command's run method
|
|
||||||
kick(context) {
|
|
||||||
// Checks if the user has the mod role, set in the config json string
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.kick.modrole)) {
|
|
||||||
// Gets the first user pinged in the command
|
|
||||||
const user = context.message.mentions.users.first();
|
|
||||||
|
|
||||||
// If a user was pinged
|
|
||||||
if (user) {
|
|
||||||
// Gets the guild member object of the pinged user
|
|
||||||
const member = context.message.guild.member(user);
|
|
||||||
|
|
||||||
// If the member object exists, i.e if the user is in the server
|
|
||||||
if (member) {
|
|
||||||
// Gets the part of the argument array which holds the reason
|
|
||||||
const reasonArgs = context.arguments;
|
|
||||||
reasonArgs.splice(0, 1);
|
|
||||||
|
|
||||||
// Joins the reason into a string
|
|
||||||
const reason = reasonArgs.join(" ");
|
|
||||||
|
|
||||||
// If the server is available
|
|
||||||
if (context.message.guild.available) {
|
|
||||||
// If the bot client can kick the mentioned member
|
|
||||||
if (member.kickable) {
|
|
||||||
// The embed to go into the bot log
|
|
||||||
const embedLog = new MessageEmbed()
|
|
||||||
.setTitle("Member Kicked")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${user} \`${user.tag}\``, true)
|
|
||||||
.addField("Moderator", `${context.message.author} \`${context.message.author.tag}\``, true)
|
|
||||||
.addField("Reason", reason || "*none*")
|
|
||||||
.setThumbnail(user.displayAvatarURL);
|
|
||||||
|
|
||||||
// The embed to go into channel the command was sent in
|
|
||||||
const embedPublic = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${user} has been kicked`);
|
|
||||||
|
|
||||||
// Attemtp to kick the user, if successful send the embeds, if unsuccessful notify the chat and log the error
|
|
||||||
member.kick({ reason: reason }).then(() => {
|
|
||||||
context.message.guild.channels.cache.find(channel => channel.name == context.client.config.kick.logchannel).send(embedLog);
|
|
||||||
context.message.channel.send(embedPublic);
|
|
||||||
|
|
||||||
context.message.delete();
|
|
||||||
}).catch(err => {
|
|
||||||
errorEmbed(context, "An error has occurred");
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
} else { // If the user isn't kickable
|
|
||||||
errorEmbed(context, "I am unable to kick this user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // If the member object is invalid
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
} else { // If the user object is invalid
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to post an embed in case of an error
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = kick;
|
|
|
@ -1,98 +0,0 @@
|
||||||
// Required Components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class mute extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the command's run method, description, category, and usage
|
|
||||||
super("mute");
|
|
||||||
super.description = "Mutes the mentioned user with an optional reason";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<@user> [reason]";
|
|
||||||
|
|
||||||
// Set the required configs for the command
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredConfigs = "logchannel";
|
|
||||||
super.requiredConfigs = "muterole";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The command's run method
|
|
||||||
mute(context) {
|
|
||||||
// Check if the user has the mod role
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.mute.modrole)) {
|
|
||||||
// Get the user first pinged in the message
|
|
||||||
const user = context.message.mentions.users.first();
|
|
||||||
|
|
||||||
// If the user object exists
|
|
||||||
if (user) {
|
|
||||||
// Get the guild member object of the mentioned user
|
|
||||||
const member = context.message.guild.member(user);
|
|
||||||
|
|
||||||
// If the member object exists, i.e. if the user is in the server
|
|
||||||
if (member) {
|
|
||||||
// Get the part of the arguments array which contains the reason
|
|
||||||
const reasonArgs = context.arguments;
|
|
||||||
reasonArgs.splice(0, 1);
|
|
||||||
|
|
||||||
// Join the reason into a string
|
|
||||||
const reason = reasonArgs.join(" ");
|
|
||||||
|
|
||||||
// If the server is available
|
|
||||||
if (context.message.guild.available) {
|
|
||||||
// If the bot client can manage the user's roles
|
|
||||||
if (member.manageable) {
|
|
||||||
// The embed to go into the bot log
|
|
||||||
const embedLog = new MessageEmbed()
|
|
||||||
.setTitle("Member Muted")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${user} \`${user.tag}\``, true)
|
|
||||||
.addField("Moderator", `${context.message.author} \`${context.message.author.tag}\``, true)
|
|
||||||
.addField("Reason", reason || "*none*")
|
|
||||||
.setThumbnail(user.displayAvatarURL);
|
|
||||||
|
|
||||||
// The embed to go into the channel the command was sent in
|
|
||||||
const embedPublic = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${user} has been muted`)
|
|
||||||
.addField("Reason", reason || "*none*");
|
|
||||||
|
|
||||||
// Get the 'Muted' role
|
|
||||||
const mutedRole = context.message.guild.roles.cache.find(role => role.name == context.client.config.mute.muterole);
|
|
||||||
|
|
||||||
// Attempt to mute the user, if successful send the embeds, if not log the error
|
|
||||||
member.roles.add(mutedRole, reason).then(() => {
|
|
||||||
context.message.guild.channels.cache.find(channel => channel.name == context.client.config.mute.logchannel).send(embedLog);
|
|
||||||
context.message.channel.send(embedPublic);
|
|
||||||
|
|
||||||
context.message.delete();
|
|
||||||
}).catch(err => {
|
|
||||||
errorEmbed(context, "An error occurred");
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
} else { // If the bot can't manage the user
|
|
||||||
errorEmbed(context, "I am unable to mute this user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // If the member object doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
} else { // If the user object doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send an embed when an error occurs
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = mute;
|
|
|
@ -1,60 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
const { existsSync, readFileSync } = require('fs');
|
|
||||||
|
|
||||||
// Command Variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command class
|
|
||||||
class partner extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the command's run method, description, and category
|
|
||||||
super("partner");
|
|
||||||
super.description = "Generates the embeds for the partner from the partners.json file";
|
|
||||||
super.category = "Admin";
|
|
||||||
|
|
||||||
// Require in the config the name of the admin role and the rules file name
|
|
||||||
super.requiredConfigs = "adminrole";
|
|
||||||
super.requiredConfigs = "partnersfile";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
partner(context) {
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.partner.adminrole)) {
|
|
||||||
if (existsSync(context.client.config.partner.partnersfile)) {
|
|
||||||
const partnerJson = JSON.parse(readFileSync(context.client.config.partner.partnersfile));
|
|
||||||
|
|
||||||
for (const i in partnerJson) {
|
|
||||||
const serverName = partnerJson[i].name;
|
|
||||||
const serverInvite = partnerJson[i].invite;
|
|
||||||
const serverDescription = partnerJson[i].description;
|
|
||||||
const serverIcon = partnerJson[i].icon;
|
|
||||||
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle(serverName)
|
|
||||||
.setDescription(serverDescription)
|
|
||||||
.setURL(serverInvite)
|
|
||||||
.setThumbnail(serverIcon);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription('File does not exist');
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription('You do not have permission to run this command');
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = partner;
|
|
150
commands/poll.js
150
commands/poll.js
|
@ -1,150 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
const emojiRegex = require('emoji-regex/RGI_Emoji');
|
|
||||||
|
|
||||||
// Command variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command class
|
|
||||||
class poll extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the command's run method, description, category, and example usage
|
|
||||||
super("poll");
|
|
||||||
super.description = "Generates a poll with reaction numbers";
|
|
||||||
super.category = "General";
|
|
||||||
super.usage = "<title>;<option 1>;<option 2>...";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
poll(context) {
|
|
||||||
// Get the command's arguments, and split them by a semicolon rather than a space
|
|
||||||
// This allows the variables to be able to use spaces in them
|
|
||||||
let args = context.arguments;
|
|
||||||
const argsJoined = args.join(' ');
|
|
||||||
args = argsJoined.split(';');
|
|
||||||
|
|
||||||
// If the argument has 3 or more arguments and less than 11 arguments
|
|
||||||
// This allows the title and 2-9 options
|
|
||||||
if (args.length >= 3 && args.length < 11) {
|
|
||||||
// Set the title to the first argument
|
|
||||||
const title = args[0];
|
|
||||||
let optionString = "";
|
|
||||||
|
|
||||||
// Array used to get the numbers as their words
|
|
||||||
// arrayOfNumbers[n] = "n written in full words"
|
|
||||||
const arrayOfNumbers = [
|
|
||||||
':zero:',
|
|
||||||
':one:',
|
|
||||||
':two:',
|
|
||||||
':three:',
|
|
||||||
':four:',
|
|
||||||
':five:',
|
|
||||||
':six:',
|
|
||||||
':seven:',
|
|
||||||
':eight:',
|
|
||||||
':nine:'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Array containing the numbers as their emoji
|
|
||||||
const reactionEmojis = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"];
|
|
||||||
|
|
||||||
// Loop through all the arguments after the title
|
|
||||||
// Add them to the optionString, with their index turned into a number emoji
|
|
||||||
// Example: :one: Option 1
|
|
||||||
for (let i = 1; i < args.length; i++) {
|
|
||||||
// If the option contains an emoji, replace the emoji with it
|
|
||||||
const regex = emojiRegex();
|
|
||||||
const match = regex.exec(args[i]);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
const emoji = match[0];
|
|
||||||
reactionEmojis[i] = emoji;
|
|
||||||
arrayOfNumbers[i] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
optionString += `${arrayOfNumbers[i]} ${args[i]}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the embed with the title at the top of the description with the options below
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`**${title}**\n\n${optionString}`);
|
|
||||||
|
|
||||||
// Send the embed and then react with the numbers for users to react with,
|
|
||||||
// the bot will determine how many to react with for the amount of options inputted
|
|
||||||
context.message.channel.send(embed).then(message => {
|
|
||||||
if (args.length == 2) {
|
|
||||||
message.react(reactionEmojis[1]);
|
|
||||||
} else if (args.length == 3) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]));
|
|
||||||
} else if (args.length == 4) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]));
|
|
||||||
} else if (args.length == 5) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]));
|
|
||||||
} else if (args.length == 6) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]))
|
|
||||||
.then(() => message.react(reactionEmojis[5]));
|
|
||||||
} else if (args.length == 7) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]))
|
|
||||||
.then(() => message.react(reactionEmojis[5]))
|
|
||||||
.then(() => message.react(reactionEmojis[6]));
|
|
||||||
} else if (args.length == 8) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]))
|
|
||||||
.then(() => message.react(reactionEmojis[5]))
|
|
||||||
.then(() => message.react(reactionEmojis[6]))
|
|
||||||
.then(() => message.react(reactionEmojis[7]));
|
|
||||||
} else if (args.length == 9) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]))
|
|
||||||
.then(() => message.react(reactionEmojis[5]))
|
|
||||||
.then(() => message.react(reactionEmojis[6]))
|
|
||||||
.then(() => message.react(reactionEmojis[7]))
|
|
||||||
.then(() => message.react(reactionEmojis[8]));
|
|
||||||
} else if (args.length == 10) {
|
|
||||||
message.react(reactionEmojis[1])
|
|
||||||
.then(() => message.react(reactionEmojis[2]))
|
|
||||||
.then(() => message.react(reactionEmojis[3]))
|
|
||||||
.then(() => message.react(reactionEmojis[4]))
|
|
||||||
.then(() => message.react(reactionEmojis[5]))
|
|
||||||
.then(() => message.react(reactionEmojis[6]))
|
|
||||||
.then(() => message.react(reactionEmojis[7]))
|
|
||||||
.then(() => message.react(reactionEmojis[8]))
|
|
||||||
.then(() => message.react(reactionEmojis[9]));
|
|
||||||
}
|
|
||||||
}).catch(console.error);
|
|
||||||
|
|
||||||
// Delete the message
|
|
||||||
context.message.delete();
|
|
||||||
} else if (args.length >= 11) { // If the user inputted more than 9 options
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setDescription("The poll command can only accept up to 9 options");
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
} else { // If the user didn't give enough data
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setDescription("Please use the correct usage: <title>;<option 1>;<option 2>... (separate options with semicolons)");
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = poll;
|
|
105
commands/role.js
105
commands/role.js
|
@ -1,105 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Command variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command class
|
|
||||||
class role extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the command's run method, description, category, and example usage
|
|
||||||
super("role");
|
|
||||||
super.description = "Toggles a role for the user to gain/remove";
|
|
||||||
super.category = "General";
|
|
||||||
super.usage = "[name]";
|
|
||||||
|
|
||||||
// Require in the config the 'assignable roles' array
|
|
||||||
super.requiredConfigs = "assignable";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
role(context) {
|
|
||||||
// Get the array containing the assignable roles
|
|
||||||
const roles = context.client.config.role.assignable;
|
|
||||||
let requestedRole = "";
|
|
||||||
|
|
||||||
// If the arguments specifys a specific role
|
|
||||||
if (context.arguments.length > 0) {
|
|
||||||
// Loop through all the assignable roles and check against the first parameter
|
|
||||||
// Save the role name if they match, i.e. the role can be assignable
|
|
||||||
for (let i = 0; i < roles.length; i++) {
|
|
||||||
if (roles[i].toLowerCase() == context.arguments[0].toLowerCase()) {
|
|
||||||
requestedRole = roles[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a matching assignable role was found
|
|
||||||
if (requestedRole != "") {
|
|
||||||
// Get the role object from the server with the role name
|
|
||||||
const role = context.message.guild.roles.cache.find(r => r.name == requestedRole);
|
|
||||||
|
|
||||||
// If the user already has the role, remove the role from them and send an embed
|
|
||||||
// Otherwise, add the role and send an embed
|
|
||||||
if (context.message.member.roles.cache.find(r => r.name == requestedRole)) {
|
|
||||||
context.message.member.roles.remove(role).then(() => {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`Removed role: ${requestedRole}`);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription("An error occured. Please check logs");
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
});
|
|
||||||
} else { // If the user doesn't have the role
|
|
||||||
context.message.member.roles.add(role).then(() => {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`Gave role: ${requestedRole}`);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription("An error occured. Please check logs");
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else { // If the role can't be found, send an error embed
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription("This role does not exist, see assignable roles with the role command (no arguments)");
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
} else { // If no role was specified, Send a list of the roles you can assign
|
|
||||||
// The start of the embed text
|
|
||||||
let rolesString = `Do ${context.client.config.prefix}role <role> to get the role!\n`;
|
|
||||||
|
|
||||||
// Loop through all the roles, and add them to the embed text
|
|
||||||
for (let i = 0; i < roles.length; i++) {
|
|
||||||
rolesString += `${roles[i]}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an embed containing the text
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Roles")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(rolesString);
|
|
||||||
|
|
||||||
// Send the embed
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = role;
|
|
|
@ -1,75 +0,0 @@
|
||||||
// Required Components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
const { existsSync, readFileSync } = require('fs');
|
|
||||||
|
|
||||||
// Command variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command class
|
|
||||||
class rules extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the command's run method, description, and category
|
|
||||||
super("rules");
|
|
||||||
super.description = "Generates the rules embeds from the rules.txt file";
|
|
||||||
super.category = "Admin";
|
|
||||||
|
|
||||||
// Require in the config the name of the admin role and the rules file name
|
|
||||||
super.requiredConfigs = "adminrole";
|
|
||||||
super.requiredConfigs = "rulesfile";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
rules(context) {
|
|
||||||
// If the user is an Admin (has the admin role)
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.rules.adminrole)) {
|
|
||||||
// If the rulesfile exists
|
|
||||||
if (existsSync(context.client.config.rules.rulesfile)) {
|
|
||||||
// Get the contents of the rules file, and split it by "> "
|
|
||||||
// Each embed in the rules is set by the "> " syntax
|
|
||||||
let rulesText = readFileSync(context.client.config.rules.rulesfile).toString();
|
|
||||||
rulesText = rulesText.split("> ");
|
|
||||||
|
|
||||||
// Loop through each embed to be sent
|
|
||||||
for (let i = 0; i < rulesText.length; i++) {
|
|
||||||
// If the first line after "> " has a "#", create and embed with an image of the url specified after
|
|
||||||
if (rulesText[i].charAt(0) == '#') {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setImage(rulesText[i].substring(1));
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
} else { // If the file doesn't have a "#" at the start
|
|
||||||
// Split the embed into different lines, set the first line as the title, and the rest as the description
|
|
||||||
const rulesLines = rulesText[i].split("\n");
|
|
||||||
const rulesTitle = rulesLines[0];
|
|
||||||
const rulesDescription = rulesLines.slice(1).join("\n");
|
|
||||||
|
|
||||||
// Create the embed with the specified information above
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle(rulesTitle)
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(rulesDescription);
|
|
||||||
|
|
||||||
// Send the embed
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // If the rules file doesn't exist
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${context.client.config.rules.rulesfile} doesn't exist`);
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
}
|
|
||||||
} else { // If the user doesn't have the Admin role
|
|
||||||
const errorEmbed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription("You do not have permission to run this command");
|
|
||||||
|
|
||||||
context.message.channel.send(errorEmbed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = rules;
|
|
|
@ -1,98 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class unmute extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set run method, description, category, usage
|
|
||||||
super("unmute");
|
|
||||||
super.description = "Unmutes the mentioned user with an optional reason";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<@user> [reason]";
|
|
||||||
|
|
||||||
// Set required configs
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredConfigs = "logchannel";
|
|
||||||
super.requiredConfigs = "muterole";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The command's run method
|
|
||||||
unmute(context) {
|
|
||||||
// Check if the user has the mod role
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.mute.modrole)) {
|
|
||||||
// Get the user first pinged in the message
|
|
||||||
const user = context.message.mentions.users.first();
|
|
||||||
|
|
||||||
// If the user object exists
|
|
||||||
if (user) {
|
|
||||||
// Get the guild member object from the pinged user
|
|
||||||
const member = context.message.guild.member(user);
|
|
||||||
|
|
||||||
// If the member object exists, i.e. if the user is in the server
|
|
||||||
if (member) {
|
|
||||||
// Get the part of the argument array which contains the reason
|
|
||||||
const reasonArgs = context.arguments;
|
|
||||||
reasonArgs.splice(0, 1);
|
|
||||||
|
|
||||||
// Join the array into a string
|
|
||||||
const reason = reasonArgs.join(" ");
|
|
||||||
|
|
||||||
// If the server is available
|
|
||||||
if (context.message.guild.available) {
|
|
||||||
// If the bot client can manage the user
|
|
||||||
if (member.manageable) {
|
|
||||||
// The embed to go into the bot log
|
|
||||||
const embedLog = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle("Member Unmuted")
|
|
||||||
.addField("User", `${user} \`${user.tag}\``, true)
|
|
||||||
.addField("Moderator", `${context.message.author} \`${context.message.author.tag}\``, true)
|
|
||||||
.addField("Reason", reason || "*none*")
|
|
||||||
.setThumbnail(user.displayAvatarURL);
|
|
||||||
|
|
||||||
// The embed to go into the channel the command was sent in
|
|
||||||
const embedPublic = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${user} has been unmuted`)
|
|
||||||
.addField("Reason", reason || "*none*");
|
|
||||||
|
|
||||||
// Get the muted role
|
|
||||||
const mutedRole = context.message.guild.roles.cache.find(role => role.name == context.client.config.unmute.muterole);
|
|
||||||
|
|
||||||
// Attempt to remove the role from the user, and then send the embeds. If unsuccessful log the error
|
|
||||||
member.roles.remove(mutedRole, reason).then(() => {
|
|
||||||
context.message.guild.channels.cache.find(channel => channel.name == context.client.config.unmute.logchannel).send(embedLog);
|
|
||||||
context.message.channel.send(embedPublic);
|
|
||||||
|
|
||||||
context.message.delete();
|
|
||||||
}).catch(err => {
|
|
||||||
errorEmbed(context, "An error occurred");
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
} else { // If the bot can't manage the user
|
|
||||||
errorEmbed(context, "I am unable to unmute this user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // If the member object doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
} else { // If the user object doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send an embed in case of an error
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = unmute;
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Required Components
|
|
||||||
const { command } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
|
|
||||||
// Command Class
|
|
||||||
class warn extends command {
|
|
||||||
constructor() {
|
|
||||||
// Set the run method, description, category, and usage
|
|
||||||
super("warn");
|
|
||||||
super.description = "Warns the mentioned user with an optional reason";
|
|
||||||
super.category = "Moderation";
|
|
||||||
super.usage = "<@user> [reason]";
|
|
||||||
|
|
||||||
// Set the required configs
|
|
||||||
super.requiredConfigs = "modrole";
|
|
||||||
super.requiredConfigs = "logchannel";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The command's run method
|
|
||||||
warn(context) {
|
|
||||||
// If the user has the mod role
|
|
||||||
if (context.message.member.roles.cache.find(role => role.name == context.client.config.warn.modrole)) {
|
|
||||||
// Get the user first pinged in the message
|
|
||||||
const user = context.message.mentions.users.first();
|
|
||||||
|
|
||||||
// If the user object exists
|
|
||||||
if (user) {
|
|
||||||
// Get the guild member object from the user
|
|
||||||
const member = context.message.guild.member(user);
|
|
||||||
|
|
||||||
// If the member object exists. i.e. if the user is in the server
|
|
||||||
if (member) {
|
|
||||||
// Get the part of the argument array which the reason is in
|
|
||||||
const reasonArgs = context.arguments;
|
|
||||||
reasonArgs.splice(0, 1);
|
|
||||||
|
|
||||||
// Join the array into a string
|
|
||||||
const reason = reasonArgs.join(" ");
|
|
||||||
|
|
||||||
// If the server is available
|
|
||||||
if (context.message.guild.available) {
|
|
||||||
// The embed to go into the bot log
|
|
||||||
const embedLog = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setTitle("Member Warned")
|
|
||||||
.addField("User", `${user} \`${user.tag}\``, true)
|
|
||||||
.addField("Moderator", `${context.message.author} \`${context.message.author.tag}\``, true)
|
|
||||||
.addField("Reason", reason || "*none*")
|
|
||||||
.setThumbnail(user.displayAvatarURL);
|
|
||||||
|
|
||||||
// The embed to go into the channel the command was sent in
|
|
||||||
const embedPublic = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(`${user} has been warned`)
|
|
||||||
.addField("Reason", reason || "*none*");
|
|
||||||
|
|
||||||
// Send the embeds
|
|
||||||
context.message.guild.channels.cache.find(channel => channel.name == context.client.config.warn.logchannel).send(embedLog);
|
|
||||||
context.message.channel.send(embedPublic);
|
|
||||||
|
|
||||||
context.message.delete();
|
|
||||||
}
|
|
||||||
} else { // If the member objest doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
} else { // If the user object doesn't exist
|
|
||||||
errorEmbed(context, "Please specify a valid user");
|
|
||||||
}
|
|
||||||
} else { // If the user isn't mod
|
|
||||||
errorEmbed(context, "You do not have permission to run this command");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send an embed in case of an error
|
|
||||||
function errorEmbed(context, message) {
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setColor(embedColor)
|
|
||||||
.setDescription(message);
|
|
||||||
|
|
||||||
context.message.channel.send(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = warn;
|
|
|
@ -1,61 +0,0 @@
|
||||||
{
|
|
||||||
"token": "",
|
|
||||||
"prefix": "v!",
|
|
||||||
"commands": [
|
|
||||||
"commands"
|
|
||||||
],
|
|
||||||
"events": [
|
|
||||||
"events"
|
|
||||||
],
|
|
||||||
"about": {
|
|
||||||
"description": "Discord Bot for Vylpes' Den",
|
|
||||||
"version": "2.1.3",
|
|
||||||
"core-ver": "1.0.4",
|
|
||||||
"author": "Vylpes",
|
|
||||||
"date": "22-Jan-22"
|
|
||||||
},
|
|
||||||
"ban": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs"
|
|
||||||
},
|
|
||||||
"clear": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs"
|
|
||||||
},
|
|
||||||
"eval": {
|
|
||||||
"ownerid": "147392775707426816"
|
|
||||||
},
|
|
||||||
"kick": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs"
|
|
||||||
},
|
|
||||||
"mute": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs",
|
|
||||||
"muterole": "Muted"
|
|
||||||
},
|
|
||||||
"partner": {
|
|
||||||
"adminrole": "Admin",
|
|
||||||
"partnersfile": "data/partner/partner.json"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"adminrole": "Admin",
|
|
||||||
"rulesfile": "data/rules/rules.txt"
|
|
||||||
},
|
|
||||||
"unmute": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs",
|
|
||||||
"muterole": "Muted"
|
|
||||||
},
|
|
||||||
"warn": {
|
|
||||||
"modrole": "Moderator",
|
|
||||||
"logchannel": "mod-logs"
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"assignable": [
|
|
||||||
"Notify",
|
|
||||||
"VotePings",
|
|
||||||
"ProjectUpdates"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Cuzethstan",
|
|
||||||
"description": "Cuzeth Server. Yes.",
|
|
||||||
"invite": "http://discord.gg/uhEFNw7",
|
|
||||||
"icon": "https://cdn.discordapp.com/icons/720177983016665251/a_e4250e57b26559c6609dfe562774ee27.gif"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Boblin",
|
|
||||||
"description": "Official server of the... Boblin?\n- Multiple Topics\n- Lots of Very Active Members",
|
|
||||||
"invite": "https://discord.gg/Td4uzVu",
|
|
||||||
"icon": "https://cdn.discordapp.com/attachments/464708407010787328/487824441846267907/image0.png"
|
|
||||||
}
|
|
||||||
]
|
|
88
data/rules/290149509969739776.json
Normal file
88
data/rules/290149509969739776.json
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"image": "https://i.imgur.com/bjH1gza.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Vylpes' Den",
|
||||||
|
"description": [
|
||||||
|
"Welcome to Vylpes' Den! Make sure to say hi!",
|
||||||
|
"Invite link: https://discord.gg/UyAhAVp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Discord TOS",
|
||||||
|
"description": [
|
||||||
|
"All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!",
|
||||||
|
"https://discord.com/terms"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Rules",
|
||||||
|
"description": [
|
||||||
|
"**English Only**",
|
||||||
|
"In order for everyone to understand each other we would like to ask everyone to speak in English only.",
|
||||||
|
"",
|
||||||
|
"**No NSFW or Obscene Content**",
|
||||||
|
"This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.",
|
||||||
|
"",
|
||||||
|
"**Treat Everyone with Respect**",
|
||||||
|
"Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.",
|
||||||
|
"",
|
||||||
|
"**No spam or self promotion**",
|
||||||
|
"Outside of #self-promo. This includes DMing fellow members.",
|
||||||
|
"",
|
||||||
|
"**Keep Politics to #general**",
|
||||||
|
"And make sure it doesn't become too heated. Debate don't argue.",
|
||||||
|
"",
|
||||||
|
"**Drama From Other Servers**",
|
||||||
|
"Please don't bring up drama from other servers, keep that to DMs",
|
||||||
|
"",
|
||||||
|
"**Bot Abuse**",
|
||||||
|
"Don't abuse the bots or you will be blocked from using them",
|
||||||
|
"",
|
||||||
|
"**Event Spoilers**",
|
||||||
|
"Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Moderators Discretion",
|
||||||
|
"description": [
|
||||||
|
"Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Supporters",
|
||||||
|
"description": [
|
||||||
|
"If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Self-Assignable Roles",
|
||||||
|
"description": [
|
||||||
|
"If you want to assign yourself roles, go to #bot-stuff and type v!role <role>. The current roles you can get are:",
|
||||||
|
"Notify: Get pinged when a new stream or video releases.",
|
||||||
|
"VotePings: Get pinged when I start a new poll",
|
||||||
|
"ProjectUpdates: Get pinged when I update my projects as well as new for them"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "VylBot",
|
||||||
|
"description": [
|
||||||
|
"This server uses a bot made by me, VylBot, to help moderate the server.",
|
||||||
|
"For more information on it, see the GitHub repositories:",
|
||||||
|
"https://github.com/Vylpes/vylbot-core",
|
||||||
|
"https://github.com/Vylpes/vylbot-app"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Links",
|
||||||
|
"description": [
|
||||||
|
"YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA",
|
||||||
|
"Patreon: https://www.patreon.com/vylpes",
|
||||||
|
"Twitch: https://www.twitch.tv/vylpes_",
|
||||||
|
"Twitter: https://twitter.com/vylpes",
|
||||||
|
"Blog: https://vylpes.xyz"
|
||||||
|
],
|
||||||
|
"footer": "Last updated 01/02/2022"
|
||||||
|
}
|
||||||
|
]
|
88
data/rules/442730357897429002.json
Normal file
88
data/rules/442730357897429002.json
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"image": "https://i.imgur.com/bjH1gza.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Bot Testing Ground",
|
||||||
|
"description": [
|
||||||
|
"Welcome to Vylpes' Den! Make sure to say hi!",
|
||||||
|
"Invite link: https://discord.gg/UyAhAVp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Discord TOS",
|
||||||
|
"description": [
|
||||||
|
"All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!",
|
||||||
|
"https://discord.com/terms"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Rules",
|
||||||
|
"description": [
|
||||||
|
"**English Only**",
|
||||||
|
"In order for everyone to understand each other we would like to ask everyone to speak in English only.",
|
||||||
|
"",
|
||||||
|
"**No NSFW or Obscene Content**",
|
||||||
|
"This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.",
|
||||||
|
"",
|
||||||
|
"**Treat Everyone with Respect**",
|
||||||
|
"Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.",
|
||||||
|
"",
|
||||||
|
"**No spam or self promotion**",
|
||||||
|
"Outside of #self-promo. This includes DMing fellow members.",
|
||||||
|
"",
|
||||||
|
"**Keep Politics to #general**",
|
||||||
|
"And make sure it doesn't become too heated. Debate don't argue.",
|
||||||
|
"",
|
||||||
|
"**Drama From Other Servers**",
|
||||||
|
"Please don't bring up drama from other servers, keep that to DMs",
|
||||||
|
"",
|
||||||
|
"**Bot Abuse**",
|
||||||
|
"Don't abuse the bots or you will be blocked from using them",
|
||||||
|
"",
|
||||||
|
"**Event Spoilers**",
|
||||||
|
"Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Moderators Discretion",
|
||||||
|
"description": [
|
||||||
|
"Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Supporters",
|
||||||
|
"description": [
|
||||||
|
"If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Self-Assignable Roles",
|
||||||
|
"description": [
|
||||||
|
"If you want to assign yourself roles, go to #bot-stuff and type v!role <role>. The current roles you can get are:",
|
||||||
|
"Notify: Get pinged when a new stream or video releases.",
|
||||||
|
"VotePings: Get pinged when I start a new poll",
|
||||||
|
"ProjectUpdates: Get pinged when I update my projects as well as new for them"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "VylBot",
|
||||||
|
"description": [
|
||||||
|
"This server uses a bot made by me, VylBot, to help moderate the server.",
|
||||||
|
"For more information on it, see the GitHub repositories:",
|
||||||
|
"https://github.com/Vylpes/vylbot-core",
|
||||||
|
"https://github.com/Vylpes/vylbot-app"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Links",
|
||||||
|
"description": [
|
||||||
|
"YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA",
|
||||||
|
"Patreon: https://www.patreon.com/vylpes",
|
||||||
|
"Twitch: https://www.twitch.tv/vylpes_",
|
||||||
|
"Twitter: https://twitter.com/vylpes",
|
||||||
|
"Blog: https://vylpes.xyz"
|
||||||
|
],
|
||||||
|
"footer": "Last updated 01/02/2022"
|
||||||
|
}
|
||||||
|
]
|
70
data/rules/501231711271780357.json
Normal file
70
data/rules/501231711271780357.json
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Welcome to Mankalor's Discord Server!",
|
||||||
|
"description": [
|
||||||
|
"*You must follow Discord's TOS, including the rule where", "you must be 13 years or older.",
|
||||||
|
"If moderators know you're under 13, we will have to ban you!*",
|
||||||
|
"",
|
||||||
|
"You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.",
|
||||||
|
"If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message Vylpes#5725."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Server Rules",
|
||||||
|
"description": [
|
||||||
|
"1. We allow most things in *#general-off-topic*, but if it pertains to a topic that has a channel, post it in the correct chat.",
|
||||||
|
"",
|
||||||
|
"2. No spamming, except in *#bot-craziness* and *#spam* ",
|
||||||
|
" 2a. Those 'Hacker Warning!!! Copy Paste this to all servers!' messages and other copypastas are considered spam.",
|
||||||
|
"",
|
||||||
|
"3. Do not insult or harass anyone for race, religion, gender, gaming skills, social skills, etc.",
|
||||||
|
" 3a. Some people may be new to Discord or a game. Politely educate, don't belittle.",
|
||||||
|
"",
|
||||||
|
"4. Absolutely no NSFW content on this server. (Porn, Rule 34, etc.)",
|
||||||
|
"",
|
||||||
|
"5. Swearing is allowed, but certain words such as racial/homophobic/disability slurs or sex terms will still be filtered out. Bypassing this will result in punishment.",
|
||||||
|
" 5a. Swearing past the point of typical rager is still not allowed.",
|
||||||
|
" 5b. If you're unsure if a word is allowed, then don't use it.",
|
||||||
|
"",
|
||||||
|
"6. Avoid unnecessarily & excessively @ mentioning anyone, even in *#bot-craziness* and *#spam*",
|
||||||
|
"",
|
||||||
|
"7. Advertising your own content (videos, channels, servers, etc.) should only go in *#self-promo*",
|
||||||
|
"",
|
||||||
|
"8. Do not bring up drama from other places here, keep that to DMs.",
|
||||||
|
"",
|
||||||
|
"9. Keep it serious in the venting chats, don't joke around.",
|
||||||
|
" 9a. To access the vent channels, assign yourself the role from *#self-assign-roles*",
|
||||||
|
"",
|
||||||
|
"10. Do not ask to become a moderator.",
|
||||||
|
"",
|
||||||
|
"11. Please don't join to ask about how to download hacks, where to find them, etc. Requests will be ignored and if you continue to ask you will be muted.",
|
||||||
|
" 11a. Watch Mankalor's video on it here: https://www.youtube.com/watch?v=wps_4DBlEyM"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": [
|
||||||
|
"1. You can assign yourself a game role in *#self-assign-roles*. When you want to setup a lobby type m!lobby into the game's channel.",
|
||||||
|
" 1a. Do not ping a role excessively in a short time. The command's cooldown is 20 minutes.",
|
||||||
|
" 1b. Only use the ping to set up lobbies. Not for advertising, pointless announcements, etc.",
|
||||||
|
" 1c. Only give yourself a role if you don't mind Discord pings.",
|
||||||
|
" 1d. Do not complain about the pings if they're being used correctly. Remove the role, or you will be punished by moderators.",
|
||||||
|
"",
|
||||||
|
"2. Only server staff can use @ everyone & @ here.",
|
||||||
|
"3. If you are a Youtube Sponsor or Twitch Subscriber, you can get the role if you sync your Twitch/Youtube account with your Discord account by going into User Settings > Connections > Twitch/Youtube.",
|
||||||
|
" 3a. This might not work on mobile.",
|
||||||
|
" 3b. We cannot assign SponSub roles manually.",
|
||||||
|
"",
|
||||||
|
"4. Mankalor will ping @ notificationsquad for new videos and streams in #new-videos-streams. If you want these pings, type `m!role Notification Squad` in #self-assign-roles",
|
||||||
|
"",
|
||||||
|
"5. Server link in case you want to invite someone. This link is in the description of my videos, too: https://discord.gg/DQkWVbz",
|
||||||
|
"",
|
||||||
|
"Not following these rules will result in a warning, mute, or ban, depending on the severity and number of offenses.",
|
||||||
|
"",
|
||||||
|
"If you notice anything wrong, notify the *Server Staff*!",
|
||||||
|
"",
|
||||||
|
"Once you've sent the code, go say hi in *#general-off-topic*!",
|
||||||
|
"",
|
||||||
|
"**Update 01 Oct 2021:** Added `11.` and `11a.` to rules"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,43 +0,0 @@
|
||||||
> #https://i.imgur.com/bjH1gza.png
|
|
||||||
> Vylpes' Den
|
|
||||||
Welcome to Vylpes' Den! Make sure to say hi!
|
|
||||||
Invite link: https://discord.gg/UyAhAVp
|
|
||||||
> Discord TOS
|
|
||||||
All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you follow them! - https://discord.com/terms
|
|
||||||
> Rules
|
|
||||||
- **English Only**
|
|
||||||
In order for everyone to understand each other we would like to ask everyone to speak in English only.
|
|
||||||
|
|
||||||
- **No NSFW or Obscene Content**
|
|
||||||
This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.
|
|
||||||
|
|
||||||
- **Treat Everyone with Respect**
|
|
||||||
Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.
|
|
||||||
|
|
||||||
- **No spam or self promotion**
|
|
||||||
Outside of #self-promo. This includes DMing fellow members.
|
|
||||||
|
|
||||||
- **Keep Politics to #general**
|
|
||||||
And make sure it doesn't become too heated. Debate don't argue.
|
|
||||||
|
|
||||||
- **Drama From Other Servers**
|
|
||||||
Please don't bring up drama from other servers, keep that to DMs
|
|
||||||
|
|
||||||
- **Bot Abuse**
|
|
||||||
Don't abuse the bots or you will be blocked from using them
|
|
||||||
|
|
||||||
> Moderators Discretion
|
|
||||||
Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001).
|
|
||||||
> Supporters
|
|
||||||
If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!
|
|
||||||
> Self-Assignable Roles
|
|
||||||
If you want to assign yourself roles, go to #bot-stuff and type v!role <role>. The current roles you can get are:
|
|
||||||
Notify: Get pinged when a new stream or video releases.
|
|
||||||
> Links
|
|
||||||
YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA
|
|
||||||
Patreon: https://www.patreon.com/vylpes
|
|
||||||
Twitch: https://www.twitch.tv/vylpes_
|
|
||||||
Twitter: https://twitter.com/vylpes
|
|
||||||
Reddit: https://reddit.com/r/vylpes
|
|
||||||
Ko-fi: https://ko-fi.com/vylpes
|
|
||||||
|
|
22
data/usage/config.txt
Normal file
22
data/usage/config.txt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
USAGE: <key> <set|reset> [value]
|
||||||
|
|
||||||
|
===[ KEYS ]===
|
||||||
|
bot.prefix: The bot prefix for the server (Default: "v!")
|
||||||
|
|
||||||
|
commands.disabled: Disabled commands, separated by commas (Default: "")
|
||||||
|
|
||||||
|
role.assignable: List of roles assignable to user, separated by commas (Default: "")
|
||||||
|
role.moderator: The moderator role name (Default: "Moderator")
|
||||||
|
role.administrator: The administrator role name (Default: "Administrator")
|
||||||
|
role.muted: The muted role name (Default: "Muted")
|
||||||
|
|
||||||
|
rules.file: The location of the rules file (Default: "data/rules/rules")
|
||||||
|
|
||||||
|
channels.logs.message: The channel message events will be logged to (Default: "message-logs")
|
||||||
|
channels.logs.member: The channel member events will be logged to (Default: "member-logs")
|
||||||
|
channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs")
|
||||||
|
|
||||||
|
verification.enabled: Enables/Disables the verification feature (Default: "false")
|
||||||
|
verification.channel: The channel to listen to for entry codes (Default: "entry")
|
||||||
|
verification.role: The server access role (Default: "Entry")
|
||||||
|
verification.code: The entry code for the channel (Default: "")
|
8
data/usage/lobby.txt
Normal file
8
data/usage/lobby.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
USAGE: config <add|remove> <Channel ID> <Role ID> [cooldown] [Game Name]
|
||||||
|
|
||||||
|
===[ EXAMPLE ]===
|
||||||
|
To add a channel:
|
||||||
|
- config add 000000000000000000 000000000000000000 30 Game Name
|
||||||
|
|
||||||
|
To remove a channel:
|
||||||
|
- config remove 000000000000000000
|
8
data/usage/role.txt
Normal file
8
data/usage/role.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
USAGE: config <add|remove> <Role ID>
|
||||||
|
|
||||||
|
===[ EXAMPLE ]===
|
||||||
|
To add a role:
|
||||||
|
- config add 000000000000000000
|
||||||
|
|
||||||
|
To remove a role:
|
||||||
|
- config remove 000000000000000000
|
24
docker-compose.yml
Normal file
24
docker-compose.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
# discord:
|
||||||
|
# build: .
|
||||||
|
|
||||||
|
database:
|
||||||
|
image: mysql/mysql-server
|
||||||
|
command: --default-authentication-plugin=mysql_native_password
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- MYSQL_DATABASE=vylbot
|
||||||
|
- MYSQL_USER=dev
|
||||||
|
- MYSQL_PASSWORD=dev
|
||||||
|
- MYSQL_ROOT_PASSWORD=root
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8080:80
|
||||||
|
environment:
|
||||||
|
- PMA_ARBITRARY=1
|
31
docs/Registry.md
Normal file
31
docs/Registry.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Registry
|
||||||
|
|
||||||
|
The registry file is what is used to register the bot's commands and events. This is a script which is ran at startup and adds all the commands and events to the bot.
|
||||||
|
|
||||||
|
Although you can register these outside of the registry file, this script makes it a centralised place for it to be done at.
|
||||||
|
|
||||||
|
## Adding Commands
|
||||||
|
|
||||||
|
Commands are added in the `RegisterCommands` function.
|
||||||
|
|
||||||
|
The basic syntax is as follows:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
client.RegisterCommand("Name", new Command(), "ServerId");
|
||||||
|
```
|
||||||
|
|
||||||
|
- `"Name"`: The name of the command, will be used by the user to call the command
|
||||||
|
- `new Command()`: The command class to be executed, must inherit the Command class
|
||||||
|
- `"ServerId"` (Optional): If given, will only be usable in that specific server
|
||||||
|
|
||||||
|
## Adding Events
|
||||||
|
|
||||||
|
Events are added in the `RegisterEvents` function.
|
||||||
|
|
||||||
|
The basic syntax is as follows:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
client.RegisterEvent(new Events());
|
||||||
|
```
|
||||||
|
|
||||||
|
- `new Events()`: The event class to be executed
|
|
@ -1,32 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { event } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Event variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
const logchannel = "member-logs";
|
|
||||||
|
|
||||||
// Event class
|
|
||||||
class guildmemberadd extends event {
|
|
||||||
constructor() {
|
|
||||||
// Set the event's run method
|
|
||||||
super("guildmemberadd");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
guildmemberadd(member) {
|
|
||||||
// Create an embed with the user who joined's information
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Member Joined")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${member} \`${member.user.tag}\``)
|
|
||||||
.addField("Created", `${member.user.createdAt}`)
|
|
||||||
.setFooter(`User ID: ${member.user.id}`)
|
|
||||||
.setThumbnail(member.user.displayAvatarURL({ type: 'png', dynamic: true }));
|
|
||||||
|
|
||||||
// Send the embed in the mod's log channel
|
|
||||||
member.guild.channels.cache.find(channel => channel.name == logchannel).send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = guildmemberadd;
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { event } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Event variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
const logchannel = "member-logs";
|
|
||||||
|
|
||||||
// Event class
|
|
||||||
class guildmemberremove extends event {
|
|
||||||
constructor() {
|
|
||||||
// Set the event's run method
|
|
||||||
super("guildmemberremove");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
guildmemberremove(member) {
|
|
||||||
// Create an embed with the user's information
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Member Left")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${member} \`${member.user.tag}\``)
|
|
||||||
.addField("Joined", `${member.joinedAt}`)
|
|
||||||
.setFooter(`User ID: ${member.user.id}`)
|
|
||||||
.setThumbnail(member.user.displayAvatarURL({ type: 'png', dynamic: true }));
|
|
||||||
|
|
||||||
// Send the embed in the log channel
|
|
||||||
member.guild.channels.cache.find(channel => channel.name == logchannel).send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = guildmemberremove;
|
|
|
@ -1,41 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { event } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Event variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
const logchannel = "member-logs";
|
|
||||||
|
|
||||||
// Event class
|
|
||||||
class guildmemberupdate extends event {
|
|
||||||
constructor() {
|
|
||||||
// Set the event's run method
|
|
||||||
super("guildmemberupdate");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
guildmemberupdate(oldMember, newMember) {
|
|
||||||
// If the user's nickname was changed
|
|
||||||
if (oldMember.nickname != newMember.nickname) {
|
|
||||||
// Get the user's name with tag, their old nickname and their new nickname
|
|
||||||
// If they didn't have a nickname or they removed it, set it to "none" in italics
|
|
||||||
const oldNickname = oldMember.nickname || "*none*";
|
|
||||||
const newNickname = newMember.nickname || "*none*";
|
|
||||||
|
|
||||||
// Create the embed with the user's information
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Nickname Changed")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${newMember} \`${newMember.user.tag}\``)
|
|
||||||
.addField("Before", oldNickname, true)
|
|
||||||
.addField("After", newNickname, true)
|
|
||||||
.setFooter(`User ID: ${newMember.user.id}`)
|
|
||||||
.setThumbnail(newMember.user.displayAvatarURL({ type: 'png', dynamic: true }));
|
|
||||||
|
|
||||||
// Send the embed in the log channel
|
|
||||||
newMember.guild.channels.cache.find(channel => channel.name == logchannel).send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = guildmemberupdate;
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { event } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Event variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
const logchannel = "message-logs";
|
|
||||||
|
|
||||||
// Event class
|
|
||||||
class messagedelete extends event {
|
|
||||||
constructor() {
|
|
||||||
// The event's run method
|
|
||||||
super("messagedelete");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
messagedelete(message) {
|
|
||||||
// Create an embed with the message's information
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Message Deleted")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${message.author} \`${message.author.tag}\``)
|
|
||||||
.addField("Channel", message.channel)
|
|
||||||
.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``)
|
|
||||||
.setThumbnail(message.author.displayAvatarURL({ type: 'png', dynamic: true }));
|
|
||||||
|
|
||||||
// Send the embed in the logging channel
|
|
||||||
message.guild.channels.cache.find(channel => channel.name == logchannel).send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = messagedelete;
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Required components
|
|
||||||
const { event } = require('vylbot-core');
|
|
||||||
const { MessageEmbed } = require('discord.js');
|
|
||||||
|
|
||||||
// Event variables
|
|
||||||
const embedColor = "0x3050ba";
|
|
||||||
const logchannel = "message-logs";
|
|
||||||
|
|
||||||
// Event class
|
|
||||||
class messageupdate extends event {
|
|
||||||
constructor() {
|
|
||||||
// Set the event's run method
|
|
||||||
super("messageupdate");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method
|
|
||||||
messageupdate(oldMessage, newMessage) {
|
|
||||||
// If the user is a bot or the content didn't change, return
|
|
||||||
if (newMessage.author.bot) return;
|
|
||||||
if (oldMessage.content == newMessage.content) return;
|
|
||||||
|
|
||||||
// Create an embed with the message's information
|
|
||||||
const embed = new MessageEmbed()
|
|
||||||
.setTitle("Message Edited")
|
|
||||||
.setColor(embedColor)
|
|
||||||
.addField("User", `${newMessage.author} \`${newMessage.author.tag}\``)
|
|
||||||
.addField("Channel", newMessage.channel)
|
|
||||||
.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``)
|
|
||||||
.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``)
|
|
||||||
.setThumbnail(newMessage.author.displayAvatarURL({ type: 'png', dynamic: true }));
|
|
||||||
|
|
||||||
// Send the embed into the log channel
|
|
||||||
newMessage.guild.channels.cache.find(channel => channel.name == logchannel).send(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = messageupdate;
|
|
5
jest.config.json
Normal file
5
jest.config.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"preset": "ts-jest",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"setupFiles": ["./jest.setup.js"]
|
||||||
|
}
|
3
jest.setup.js
Normal file
3
jest.setup.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
jest.setTimeout(1 * 1000); // 1 second
|
||||||
|
jest.resetModules();
|
||||||
|
jest.resetAllMocks();
|
24
ormconfig.json.dev.template
Normal file
24
ormconfig.json.dev.template
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"type": "mysql",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 3306,
|
||||||
|
"username": "dev",
|
||||||
|
"password": "dev",
|
||||||
|
"database": "vylbot",
|
||||||
|
"synchronize": true,
|
||||||
|
"logging": false,
|
||||||
|
"entities": [
|
||||||
|
"dist/entity/**/*.js"
|
||||||
|
],
|
||||||
|
"migrations": [
|
||||||
|
"dist/migration/**/*.js"
|
||||||
|
],
|
||||||
|
"subscribers": [
|
||||||
|
"dist/subscriber/**/*.js"
|
||||||
|
],
|
||||||
|
"cli": {
|
||||||
|
"entitiesDir": "dist/entity",
|
||||||
|
"migrationsDir": "dist/migration",
|
||||||
|
"subscribersDir": "dist/subscriber"
|
||||||
|
}
|
||||||
|
}
|
24
ormconfig.json.template
Normal file
24
ormconfig.json.template
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"type": "mysql",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 3306,
|
||||||
|
"username": "dev",
|
||||||
|
"password": "dev",
|
||||||
|
"database": "vylbot",
|
||||||
|
"synchronize": false,
|
||||||
|
"logging": false,
|
||||||
|
"entities": [
|
||||||
|
"dist/entity/**/*.js"
|
||||||
|
],
|
||||||
|
"migrations": [
|
||||||
|
"dist/migration/**/*.js"
|
||||||
|
],
|
||||||
|
"subscribers": [
|
||||||
|
"dist/subscriber/**/*.js"
|
||||||
|
],
|
||||||
|
"cli": {
|
||||||
|
"entitiesDir": "dist/entity",
|
||||||
|
"migrationsDir": "dist/migration",
|
||||||
|
"subscribersDir": "dist/subscriber"
|
||||||
|
}
|
||||||
|
}
|
2485
package-lock.json
generated
2485
package-lock.json
generated
File diff suppressed because it is too large
Load diff
37
package.json
37
package.json
|
@ -1,29 +1,38 @@
|
||||||
{
|
{
|
||||||
"name": "vylbot-app",
|
"name": "vylbot-app",
|
||||||
"version": "2.1.1",
|
"version": "3.0",
|
||||||
"description": "",
|
"description": "A discord bot made for Vylpes' Den",
|
||||||
"main": "vylbot.js",
|
"main": "./dist/vylbot",
|
||||||
|
"typings": "./dist",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node vylbot.js",
|
"build": "tsc",
|
||||||
"lint": "eslint .",
|
"start": "node ./dist/vylbot",
|
||||||
"lint:fix": "eslint . --fix"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/Vylpes/vylbot-app.git"
|
"url": "git+https://github.com/Vylpes/vylbot-app.git"
|
||||||
},
|
},
|
||||||
"author": "Vylpes",
|
"author": "Vylpes",
|
||||||
"license": "ISC",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": "https://github.com/Vylpes/vylbot-app/issues",
|
||||||
"url": "https://github.com/Vylpes/vylbot-app/issues"
|
"homepage": "https://github.com/Vylpes/vylbot-app",
|
||||||
},
|
|
||||||
"homepage": "https://github.com/Vylpes/vylbot-app#readme",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/jest": "^27.0.3",
|
||||||
|
"@types/uuid": "^8.3.4",
|
||||||
|
"discord.js": "^13.6.0",
|
||||||
|
"dotenv": "^10.0.0",
|
||||||
"emoji-regex": "^9.2.0",
|
"emoji-regex": "^9.2.0",
|
||||||
"random-bunny": "^1.0.0",
|
"jest": "^27.4.5",
|
||||||
"vylbot-core": "^1.0.4"
|
"jest-mock-extended": "^2.0.4",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"random-bunny": "^2.0.0",
|
||||||
|
"ts-jest": "^27.1.2",
|
||||||
|
"typeorm": "^0.2.44",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^7.17.0"
|
"@types/node": "^16.11.10",
|
||||||
|
"typescript": "^4.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
79
src/client/client.ts
Normal file
79
src/client/client.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import * as dotenv from "dotenv";
|
||||||
|
import { createConnection } from "typeorm";
|
||||||
|
import DefaultValues from "../constants/DefaultValues";
|
||||||
|
import ICommandItem from "../contracts/ICommandItem";
|
||||||
|
import IEventItem from "../contracts/IEventItem";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
import { Event } from "../type/event";
|
||||||
|
|
||||||
|
import { Events } from "./events";
|
||||||
|
import { Util } from "./util";
|
||||||
|
|
||||||
|
export class CoreClient extends Client {
|
||||||
|
private static _commandItems: ICommandItem[];
|
||||||
|
private static _eventItems: IEventItem[];
|
||||||
|
|
||||||
|
private _events: Events;
|
||||||
|
private _util: Util;
|
||||||
|
|
||||||
|
public static get commandItems(): ICommandItem[] {
|
||||||
|
return this._commandItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get eventItems(): IEventItem[] {
|
||||||
|
return this._eventItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(intents: number[], devmode: boolean = false) {
|
||||||
|
super({ intents: intents });
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
DefaultValues.useDevPrefix = devmode;
|
||||||
|
|
||||||
|
CoreClient._commandItems = [];
|
||||||
|
CoreClient._eventItems = [];
|
||||||
|
|
||||||
|
this._events = new Events();
|
||||||
|
this._util = new Util();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start() {
|
||||||
|
if (!process.env.BOT_TOKEN) {
|
||||||
|
console.error("BOT_TOKEN is not defined in .env");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createConnection().catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
super.on("messageCreate", (message) => {
|
||||||
|
this._events.onMessageCreate(message, CoreClient._commandItems)
|
||||||
|
});
|
||||||
|
super.on("ready", this._events.onReady);
|
||||||
|
|
||||||
|
super.login(process.env.BOT_TOKEN);
|
||||||
|
|
||||||
|
this._util.loadEvents(this, CoreClient._eventItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegisterCommand(name: string, command: Command, serverId?: string) {
|
||||||
|
const item: ICommandItem = {
|
||||||
|
Name: name,
|
||||||
|
Command: command,
|
||||||
|
ServerId: serverId,
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreClient._commandItems.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegisterEvent(event: Event) {
|
||||||
|
const item: IEventItem = {
|
||||||
|
Event: event,
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreClient._eventItems.push(item);
|
||||||
|
}
|
||||||
|
}
|
37
src/client/events.ts
Normal file
37
src/client/events.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import ICommandItem from "../contracts/ICommandItem";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import { Util } from "./util";
|
||||||
|
|
||||||
|
export class Events {
|
||||||
|
private _util: Util;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._util = new Util();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit when a message is sent
|
||||||
|
// Used to check for commands
|
||||||
|
public async onMessageCreate(message: Message, commands: ICommandItem[]) {
|
||||||
|
if (!message.guild) return;
|
||||||
|
if (message.author.bot) return;
|
||||||
|
|
||||||
|
const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id);
|
||||||
|
|
||||||
|
if (!prefix) return;
|
||||||
|
|
||||||
|
if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) {
|
||||||
|
const args = message.content.substring(prefix.length).split(" ");
|
||||||
|
const name = args.shift();
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
await this._util.loadCommand(name, args, message, commands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit when bot is logged in and ready to use
|
||||||
|
public onReady() {
|
||||||
|
console.log("Ready");
|
||||||
|
}
|
||||||
|
}
|
101
src/client/util.ts
Normal file
101
src/client/util.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// Required Components
|
||||||
|
import { Client, Message } from "discord.js";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandItem from "../contracts/ICommandItem";
|
||||||
|
import IEventItem from "../contracts/IEventItem";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import StringTools from "../helpers/StringTools";
|
||||||
|
import { CommandResponse } from "../constants/CommandResponse";
|
||||||
|
import ErrorMessages from "../constants/ErrorMessages";
|
||||||
|
|
||||||
|
// Util Class
|
||||||
|
export class Util {
|
||||||
|
public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) {
|
||||||
|
if (!message.member) return;
|
||||||
|
if (!message.guild) return;
|
||||||
|
|
||||||
|
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id);
|
||||||
|
const disabledCommands = disabledCommandsString?.split(",");
|
||||||
|
|
||||||
|
if (disabledCommands?.find(x => x == name)) {
|
||||||
|
message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = commands.find(x => x.Name == name && !x.ServerId);
|
||||||
|
const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id);
|
||||||
|
|
||||||
|
let itemToUse: ICommandItem;
|
||||||
|
|
||||||
|
if (!itemForServer) {
|
||||||
|
if (!item) {
|
||||||
|
message.reply('Command not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemToUse = item;
|
||||||
|
} else {
|
||||||
|
itemToUse = itemForServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredRoles = itemToUse.Command.Roles;
|
||||||
|
|
||||||
|
if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) {
|
||||||
|
for (const i in requiredRoles) {
|
||||||
|
if (message.guild) {
|
||||||
|
const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id);
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
message.reply("Unable to verify if you have this role, please contact your bot administrator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.member.roles.cache.find(role => role.name == setting)) {
|
||||||
|
message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: name,
|
||||||
|
args: args,
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const precheckResponse = itemToUse.Command.precheck(context);
|
||||||
|
const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context);
|
||||||
|
|
||||||
|
if (precheckResponse != CommandResponse.Ok) {
|
||||||
|
message.reply(ErrorMessages.GetErrorMessage(precheckResponse));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precheckAsyncResponse != CommandResponse.Ok) {
|
||||||
|
message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemToUse.Command.execute(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the events
|
||||||
|
loadEvents(client: Client, events: IEventItem[]) {
|
||||||
|
events.forEach((e) => {
|
||||||
|
client.on('channelCreate', e.Event.channelCreate);
|
||||||
|
client.on('channelDelete', e.Event.channelDelete);
|
||||||
|
client.on('channelUpdate', e.Event.channelUpdate);
|
||||||
|
client.on('guildBanAdd', e.Event.guildBanAdd);
|
||||||
|
client.on('guildBanRemove', e.Event.guildBanRemove);
|
||||||
|
client.on('guildCreate', e.Event.guildCreate);
|
||||||
|
client.on('guildMemberAdd', e.Event.guildMemberAdd);
|
||||||
|
client.on('guildMemberRemove', e.Event.guildMemberRemove);
|
||||||
|
client.on('guildMemberUpdate', e.Event.guildMemberUpdate);
|
||||||
|
client.on('messageCreate', e.Event.messageCreate);
|
||||||
|
client.on('messageDelete', e.Event.messageDelete);
|
||||||
|
client.on('messageUpdate', e.Event.messageUpdate);
|
||||||
|
client.on('ready', e.Event.ready);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
25
src/commands/501231711271780357/entry.ts
Normal file
25
src/commands/501231711271780357/entry.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
import PublicEmbed from "../../helpers/embeds/PublicEmbed";
|
||||||
|
import SettingsHelper from "../../helpers/SettingsHelper";
|
||||||
|
import { Command } from "../../type/command";
|
||||||
|
|
||||||
|
export default class Entry extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) return;
|
||||||
|
|
||||||
|
const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules";
|
||||||
|
|
||||||
|
const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`);
|
||||||
|
|
||||||
|
embedInfo.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
143
src/commands/501231711271780357/lobby.ts
Normal file
143
src/commands/501231711271780357/lobby.ts
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
import { TextChannel } from "discord.js";
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
import { Command } from "../../type/command";
|
||||||
|
import { default as eLobby } from "../../entity/501231711271780357/Lobby";
|
||||||
|
import SettingsHelper from "../../helpers/SettingsHelper";
|
||||||
|
import PublicEmbed from "../../helpers/embeds/PublicEmbed";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import ErrorEmbed from "../../helpers/embeds/ErrorEmbed";
|
||||||
|
import BaseEntity from "../../contracts/BaseEntity";
|
||||||
|
|
||||||
|
export default class Lobby extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "General";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) return;
|
||||||
|
|
||||||
|
switch (context.args[0]) {
|
||||||
|
case "config":
|
||||||
|
await this.UseConfig(context);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await this.UseDefault(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======
|
||||||
|
// Default
|
||||||
|
// =======
|
||||||
|
|
||||||
|
private async UseDefault(context: ICommandContext) {
|
||||||
|
const channel = context.message.channel as TextChannel;
|
||||||
|
const channelId = channel.id;
|
||||||
|
|
||||||
|
const lobby = await eLobby.FetchOneByChannelId(channelId);
|
||||||
|
|
||||||
|
if (!lobby) {
|
||||||
|
this.SendDisabled(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeNow = Date.now();
|
||||||
|
const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms
|
||||||
|
const timeAgo = timeNow - timeLength;
|
||||||
|
|
||||||
|
// If it was less than x minutes ago
|
||||||
|
if (lobby.LastUsed.getTime() > timeAgo) {
|
||||||
|
this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.RequestLobby(context, lobby);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async RequestLobby(context: ICommandContext, lobby: eLobby) {
|
||||||
|
lobby.MarkAsUsed();
|
||||||
|
await lobby.Save(eLobby, lobby);
|
||||||
|
|
||||||
|
context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) {
|
||||||
|
const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60);
|
||||||
|
|
||||||
|
context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendDisabled(context: ICommandContext) {
|
||||||
|
context.message.reply("This channel hasn't been setup for lobbies.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======
|
||||||
|
// Config
|
||||||
|
// ======
|
||||||
|
private async UseConfig(context: ICommandContext) {
|
||||||
|
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
|
||||||
|
|
||||||
|
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (context.args[1]) {
|
||||||
|
case "add":
|
||||||
|
await this.AddLobbyConfig(context);
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
await this.RemoveLobbyConfig(context);
|
||||||
|
break;
|
||||||
|
case "help":
|
||||||
|
default:
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendConfigHelp(context: ICommandContext) {
|
||||||
|
const helpText = readFileSync(`${process.cwd()}/data/usage/lobby.txt`).toString();
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Configure Lobby Command", helpText);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async AddLobbyConfig(context: ICommandContext) {
|
||||||
|
const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]);
|
||||||
|
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[3]);
|
||||||
|
const cooldown = Number(context.args[4]) || 30;
|
||||||
|
const gameName = context.args.splice(5).join(" ");
|
||||||
|
|
||||||
|
if (!channel || !role) {
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = new eLobby(channel.id, role.id, cooldown, gameName);
|
||||||
|
await entity.Save(eLobby, entity);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes\` and will ping \`${role.name}\` on use`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async RemoveLobbyConfig(context: ICommandContext) {
|
||||||
|
const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]);
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = await eLobby.FetchOneByChannelId(channel.id);
|
||||||
|
|
||||||
|
if (entity) {
|
||||||
|
await BaseEntity.Remove<eLobby>(eLobby, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Removed \`${channel.name}\` from the list of lobby channels`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
25
src/commands/about.ts
Normal file
25
src/commands/about.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class About extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
super.Category = "General";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override execute(context: ICommandContext): ICommandReturnContext {
|
||||||
|
const embed = new PublicEmbed(context, "About", "")
|
||||||
|
.addField("Version", process.env.BOT_VER!)
|
||||||
|
.addField("Author", process.env.BOT_AUTHOR!)
|
||||||
|
.addField("Date", process.env.BOT_DATE!);
|
||||||
|
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
80
src/commands/ban.ts
Normal file
80
src/commands/ban.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import ErrorMessages from "../constants/ErrorMessages";
|
||||||
|
import LogEmbed from "../helpers/embeds/LogEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
|
||||||
|
export default class Ban extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const targetUser = context.message.mentions.users.first();
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
const embed = new ErrorEmbed(context, "User does not exist");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id);
|
||||||
|
|
||||||
|
if (!targetMember) {
|
||||||
|
const embed = new ErrorEmbed(context, "User is not in this server");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonArgs = context.args;
|
||||||
|
reasonArgs.splice(0, 1)
|
||||||
|
|
||||||
|
const reason = reasonArgs.join(" ");
|
||||||
|
|
||||||
|
if (!context.message.guild?.available) {
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetMember.bannable) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const logEmbed = new LogEmbed(context, "Member Banned");
|
||||||
|
logEmbed.AddUser("User", targetUser, true);
|
||||||
|
logEmbed.AddUser("Moderator", context.message.author);
|
||||||
|
logEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`);
|
||||||
|
|
||||||
|
await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` });
|
||||||
|
|
||||||
|
await logEmbed.SendToModLogsChannel();
|
||||||
|
publicEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [logEmbed, publicEmbed],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
50
src/commands/clear.ts
Normal file
50
src/commands/clear.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import { TextChannel } from "discord.js";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
|
||||||
|
export default class Clear extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
if (context.args.length == 0) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalToClear = Number.parseInt(context.args[0]);
|
||||||
|
|
||||||
|
if (!totalToClear || totalToClear <= 0 || totalToClear > 100) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await (context.message.channel as TextChannel).bulkDelete(totalToClear);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
94
src/commands/code.ts
Normal file
94
src/commands/code.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { CommandResponse } from "../constants/CommandResponse";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import StringTools from "../helpers/StringTools";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Code extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> {
|
||||||
|
if (!context.message.guild){
|
||||||
|
return CommandResponse.NotInServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id);
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return CommandResponse.FeatureDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnabled.toLocaleLowerCase() != 'true') {
|
||||||
|
return CommandResponse.FeatureDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResponse.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
const action = context.args[0];
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "randomise":
|
||||||
|
await this.Randomise(context);
|
||||||
|
break;
|
||||||
|
case "embed":
|
||||||
|
await this.SendEmbed(context);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await this.SendUsage(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SendUsage(context: ICommandContext) {
|
||||||
|
const description = [
|
||||||
|
"USAGE: <randomise|embed>",
|
||||||
|
"",
|
||||||
|
"randomise: Sets the server's entry code to a random code",
|
||||||
|
"embed: Sends an embed with the server's entry code"
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", description);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Randomise(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomCode = StringTools.RandomString(5);
|
||||||
|
|
||||||
|
await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SendEmbed(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id);
|
||||||
|
|
||||||
|
if (!code || code == "") {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup.");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Entry Code", code!);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
136
src/commands/config.ts
Normal file
136
src/commands/config.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import { Guild } from "discord.js";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { CommandResponse } from "../constants/CommandResponse";
|
||||||
|
import DefaultValues from "../constants/DefaultValues";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import Server from "../entity/Server";
|
||||||
|
import Setting from "../entity/Setting";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Config extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
super.Category = "Administration";
|
||||||
|
super.Roles = [
|
||||||
|
"administrator"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return CommandResponse.ServerNotSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [
|
||||||
|
"Settings",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
return CommandResponse.ServerNotSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResponse.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [
|
||||||
|
"Settings",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = context.args[0];
|
||||||
|
const action = context.args[1];
|
||||||
|
const value = context.args.splice(2).join(" ");
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
this.SendHelpText(context);
|
||||||
|
} else if (!action) {
|
||||||
|
this.GetValue(context, server, key);
|
||||||
|
} else {
|
||||||
|
switch(action) {
|
||||||
|
case 'reset':
|
||||||
|
this.ResetValue(context, server, key);
|
||||||
|
break;
|
||||||
|
case 'set':
|
||||||
|
if (!value) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Value is required when setting");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SetValue(context, server, key, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SendHelpText(context: ICommandContext) {
|
||||||
|
const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString();
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Config", description);
|
||||||
|
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async GetValue(context: ICommandContext, server: Server, key: string) {
|
||||||
|
const setting = server.Settings.filter(x => x.Key == key)[0];
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
} else {
|
||||||
|
const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} <DEFAULT>`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ResetValue(context: ICommandContext, server: Server, key: string) {
|
||||||
|
const setting = server.Settings.filter(x => x.Key == key)[0];
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
const embed = new PublicEmbed(context, "", "Setting has been reset");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Setting.Remove(Setting, setting);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", "Setting has been reset");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SetValue(context: ICommandContext, server: Server, key: string, value: string) {
|
||||||
|
const setting = server.Settings.filter(x => x.Key == key)[0];
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
setting.UpdateBasicDetails(key, value);
|
||||||
|
|
||||||
|
await setting.Save(Setting, setting);
|
||||||
|
} else {
|
||||||
|
const newSetting = new Setting(key, value);
|
||||||
|
|
||||||
|
await newSetting.Save(Setting, newSetting);
|
||||||
|
|
||||||
|
server.AddSettingToServer(newSetting);
|
||||||
|
|
||||||
|
await server.Save(Server, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", "Setting has been set");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
93
src/commands/disable.ts
Normal file
93
src/commands/disable.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Disable extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
const action = context.args[0];
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "add":
|
||||||
|
await this.Add(context);
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
await this.Remove(context);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await this.SendUsage(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SendUsage(context: ICommandContext) {
|
||||||
|
const description = [
|
||||||
|
"USAGE: <add|remove> <name>",
|
||||||
|
"",
|
||||||
|
"add: Adds the command name to the server's disabled command string",
|
||||||
|
"remove: Removes the command name from the server's disabled command string",
|
||||||
|
"name: The name of the command to enable/disable"
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", description);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Add(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandName = context.args[1];
|
||||||
|
|
||||||
|
if (!commandName) {
|
||||||
|
this.SendUsage(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
|
||||||
|
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
|
||||||
|
|
||||||
|
disabledCommands?.push(commandName);
|
||||||
|
|
||||||
|
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(","));
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Remove(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandName = context.args[1];
|
||||||
|
|
||||||
|
if (!commandName) {
|
||||||
|
this.SendUsage(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
|
||||||
|
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
|
||||||
|
|
||||||
|
const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName);
|
||||||
|
|
||||||
|
if (disabledCommandsInstance! > -1) {
|
||||||
|
disabledCommands?.splice(disabledCommandsInstance!, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(","));
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
70
src/commands/help.ts
Normal file
70
src/commands/help.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { existsSync, readdirSync } from "fs";
|
||||||
|
import { CoreClient } from "../client/client";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import StringTools from "../helpers/StringTools";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export interface ICommandData {
|
||||||
|
Exists: boolean;
|
||||||
|
Name?: string;
|
||||||
|
Category?: string;
|
||||||
|
Roles?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Help extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "General";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override execute(context: ICommandContext) {
|
||||||
|
if (context.args.length == 0) {
|
||||||
|
this.SendAll(context);
|
||||||
|
} else {
|
||||||
|
this.SendSingle(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendAll(context: ICommandContext) {
|
||||||
|
const allCommands = CoreClient.commandItems
|
||||||
|
.filter(x => !x.ServerId || x.ServerId == context.message.guild?.id);
|
||||||
|
const cateogries = [...new Set(allCommands.map(x => x.Command.Category))];
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Commands", "");
|
||||||
|
|
||||||
|
cateogries.forEach(category => {
|
||||||
|
let filtered = allCommands.filter(x => x.Command.Category == category);
|
||||||
|
|
||||||
|
embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", "));
|
||||||
|
});
|
||||||
|
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendSingle(context: ICommandContext) {
|
||||||
|
const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId);
|
||||||
|
const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id);
|
||||||
|
|
||||||
|
if (exclusiveCommand) {
|
||||||
|
const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), "");
|
||||||
|
embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised"));
|
||||||
|
embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone");
|
||||||
|
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
} else if (command) {
|
||||||
|
const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), "");
|
||||||
|
embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised"));
|
||||||
|
embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone");
|
||||||
|
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
} else {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Command does not exist");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
src/commands/kick.ts
Normal file
83
src/commands/kick.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import ErrorMessages from "../constants/ErrorMessages";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import LogEmbed from "../helpers/embeds/LogEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Kick extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const targetUser = context.message.mentions.users.first();
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
const embed = new ErrorEmbed(context, "User does not exist");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id);
|
||||||
|
|
||||||
|
if (!targetMember) {
|
||||||
|
const embed = new ErrorEmbed(context, "User is not in this server");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonArgs = context.args;
|
||||||
|
reasonArgs.splice(0, 1)
|
||||||
|
|
||||||
|
const reason = reasonArgs.join(" ");
|
||||||
|
|
||||||
|
if (!context.message.guild?.available) {
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetMember.kickable) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const logEmbed = new LogEmbed(context, "Member Kicked");
|
||||||
|
logEmbed.AddUser("User", targetUser, true);
|
||||||
|
logEmbed.AddUser("Moderator", context.message.author);
|
||||||
|
logEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`);
|
||||||
|
|
||||||
|
await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
|
||||||
|
|
||||||
|
await logEmbed.SendToModLogsChannel();
|
||||||
|
publicEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [logEmbed, publicEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
96
src/commands/mute.ts
Normal file
96
src/commands/mute.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import ErrorMessages from "../constants/ErrorMessages";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import LogEmbed from "../helpers/embeds/LogEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Mute extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const targetUser = context.message.mentions.users.first();
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
const embed = new ErrorEmbed(context, "User does not exist");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id);
|
||||||
|
|
||||||
|
if (!targetMember) {
|
||||||
|
const embed = new ErrorEmbed(context, "User is not in this server");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonArgs = context.args;
|
||||||
|
reasonArgs.splice(0, 1);
|
||||||
|
|
||||||
|
const reason = reasonArgs.join(" ");
|
||||||
|
|
||||||
|
if (!context.message.guild?.available) {
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetMember.manageable) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const logEmbed = new LogEmbed(context, "Member Muted");
|
||||||
|
logEmbed.AddUser("User", targetUser, true)
|
||||||
|
logEmbed.AddUser("Moderator", context.message.author);
|
||||||
|
logEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`);
|
||||||
|
publicEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
|
||||||
|
|
||||||
|
if (!mutedRole) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
|
||||||
|
|
||||||
|
await logEmbed.SendToModLogsChannel();
|
||||||
|
publicEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [logEmbed, publicEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
67
src/commands/poll.ts
Normal file
67
src/commands/poll.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Poll extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "General";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const argsJoined = context.args.join(" ");
|
||||||
|
const argsSplit = argsJoined.split(";");
|
||||||
|
|
||||||
|
if (argsSplit.length < 3 || argsSplit.length > 10) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = argsSplit[0];
|
||||||
|
|
||||||
|
const arrayOfNumbers = [
|
||||||
|
':one:',
|
||||||
|
':two:',
|
||||||
|
':three:',
|
||||||
|
':four:',
|
||||||
|
':five:',
|
||||||
|
':six:',
|
||||||
|
':seven:',
|
||||||
|
':eight:',
|
||||||
|
':nine:'
|
||||||
|
];
|
||||||
|
|
||||||
|
const reactionEmojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"];
|
||||||
|
|
||||||
|
const description = arrayOfNumbers.splice(0, argsSplit.length - 1);
|
||||||
|
|
||||||
|
description.forEach((value, index) => {
|
||||||
|
description[index] = `${value} ${argsSplit[index + 1]}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, title, description.join("\n"));
|
||||||
|
|
||||||
|
const message = await context.message.channel.send({ embeds: [ embed ]});
|
||||||
|
|
||||||
|
description.forEach(async (value, index) => {
|
||||||
|
await message.react(reactionEmojis[index]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (context.message.deletable) {
|
||||||
|
await context.message.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
209
src/commands/role.ts
Normal file
209
src/commands/role.ts
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Role as DiscordRole } from "discord.js";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
export default class Role extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "General";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) return;
|
||||||
|
|
||||||
|
switch (context.args[0]) {
|
||||||
|
case "config":
|
||||||
|
await this.UseConfig(context);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await this.UseDefault(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======
|
||||||
|
// Default
|
||||||
|
// =======
|
||||||
|
|
||||||
|
private async UseDefault(context: ICommandContext) {
|
||||||
|
const roles = await SettingsHelper.GetSetting("role.assignable", context.message.guild!.id);
|
||||||
|
|
||||||
|
if (!roles) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Unable to find any assignable roles");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rolesArray = roles.split(",");
|
||||||
|
|
||||||
|
if (context.args.length == 0) {
|
||||||
|
await this.SendRolesList(context, rolesArray, context.message.guild!.id);
|
||||||
|
} else {
|
||||||
|
await this.ToggleRole(context, rolesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendRolesList(context: ICommandContext, roles: String[], serverId: string): Promise<ICommandReturnContext> {
|
||||||
|
const botPrefix = await SettingsHelper.GetServerPrefix(serverId);
|
||||||
|
const description = `Do ${botPrefix}role <role> to get the role!\n${roles.join('\n')}`;
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Roles", description);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ToggleRole(context: ICommandContext, roles: String[]): Promise<ICommandReturnContext> {
|
||||||
|
const requestedRole = context.args.join(" ");
|
||||||
|
|
||||||
|
if (!roles.includes(requestedRole)) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole);
|
||||||
|
|
||||||
|
if (!assignRole) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = context.message.member?.roles.cache.find(x => x.name == requestedRole)
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
await this.AddRole(context, assignRole);
|
||||||
|
} else {
|
||||||
|
await this.RemoveRole(context, assignRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async AddRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
|
||||||
|
await context.message.member?.roles.add(role, "Toggled with role command");
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
|
||||||
|
await context.message.member?.roles.remove(role, "Toggled with role command");
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======
|
||||||
|
// Config
|
||||||
|
// ======
|
||||||
|
|
||||||
|
private async UseConfig(context: ICommandContext) {
|
||||||
|
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
|
||||||
|
|
||||||
|
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (context.args[1]) {
|
||||||
|
case "add":
|
||||||
|
await this.AddRoleConfig(context);
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
await this.RemoveRoleConfig(context);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendConfigHelp(context: ICommandContext) {
|
||||||
|
const helpText = readFileSync(`${process.cwd()}/data/usage/role.txt`).toString();
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Configure Role Command", helpText);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async AddRoleConfig(context: ICommandContext) {
|
||||||
|
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let setting = await SettingsHelper.GetSetting("role.assignable", context.message.guild!.id) || "";
|
||||||
|
|
||||||
|
const settingArray = setting.split(",");
|
||||||
|
|
||||||
|
settingArray.push(role.name);
|
||||||
|
|
||||||
|
setting = settingArray.join(",");
|
||||||
|
|
||||||
|
await SettingsHelper.SetSetting("role.assignable", context.message.guild!.id, setting);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Added \`${role.name}\` as a new assignable role`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async RemoveRoleConfig(context: ICommandContext) {
|
||||||
|
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
this.SendConfigHelp(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let setting = await SettingsHelper.GetSetting("role.assignable", context.message.guild!.id);
|
||||||
|
|
||||||
|
if (!setting) return;
|
||||||
|
|
||||||
|
const settingArray = setting.split(",");
|
||||||
|
|
||||||
|
const index = settingArray.findIndex(x => x == role.name);
|
||||||
|
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
settingArray.splice(index, 1);
|
||||||
|
|
||||||
|
setting = settingArray.join(",");
|
||||||
|
|
||||||
|
await SettingsHelper.SetSetting("role.assignable", context.message.guild!.id, setting);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "", `Removed \`${role.name}\` from the list of assignable roles`);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
57
src/commands/rules.ts
Normal file
57
src/commands/rules.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { existsSync, readFileSync } from "fs";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
interface IRules {
|
||||||
|
title?: string;
|
||||||
|
description?: string[];
|
||||||
|
image?: string;
|
||||||
|
footer?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Rules extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Admin";
|
||||||
|
super.Roles = [
|
||||||
|
"administrator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override execute(context: ICommandContext): ICommandReturnContext {
|
||||||
|
if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString();
|
||||||
|
const rules = JSON.parse(rulesFile) as IRules[];
|
||||||
|
|
||||||
|
const embeds: PublicEmbed[] = [];
|
||||||
|
|
||||||
|
rules.forEach(rule => {
|
||||||
|
const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || "");
|
||||||
|
|
||||||
|
embed.setImage(rule.image || "");
|
||||||
|
embed.setFooter(rule.footer || "");
|
||||||
|
|
||||||
|
embeds.push(embed);
|
||||||
|
});
|
||||||
|
|
||||||
|
embeds.forEach(x => x.SendToCurrentChannel());
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: embeds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
36
src/commands/setup.ts
Normal file
36
src/commands/setup.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import Server from "../entity/Server";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Setup extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
super.Category = "Administration";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext) {
|
||||||
|
if (!context.message.guild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = await Server.FetchOneById(Server, context.message.guild?.id);
|
||||||
|
|
||||||
|
if (server) {
|
||||||
|
const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newServer = new Server(context.message.guild?.id);
|
||||||
|
|
||||||
|
await newServer.Save(Server, newServer);
|
||||||
|
|
||||||
|
const embed = new PublicEmbed(context, "Success", "Please configure using the config command");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
}
|
||||||
|
}
|
96
src/commands/unmute.ts
Normal file
96
src/commands/unmute.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import ErrorMessages from "../constants/ErrorMessages";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import LogEmbed from "../helpers/embeds/LogEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Unmute extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const targetUser = context.message.mentions.users.first();
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
const embed = new ErrorEmbed(context, "User does not exist");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id);
|
||||||
|
|
||||||
|
if (!targetMember) {
|
||||||
|
const embed = new ErrorEmbed(context, "User is not in this server");
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonArgs = context.args;
|
||||||
|
reasonArgs.splice(0, 1);
|
||||||
|
|
||||||
|
const reason = reasonArgs.join(" ");
|
||||||
|
|
||||||
|
if (!context.message.guild?.available) {
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetMember.manageable) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const logEmbed = new LogEmbed(context, "Member Unmuted");
|
||||||
|
logEmbed.AddUser("User", targetUser, true)
|
||||||
|
logEmbed.AddUser("Moderator", context.message.author);
|
||||||
|
logEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`);
|
||||||
|
publicEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
|
||||||
|
|
||||||
|
if (!mutedRole) {
|
||||||
|
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
|
||||||
|
embed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
|
||||||
|
|
||||||
|
await logEmbed.SendToModLogsChannel();
|
||||||
|
publicEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [logEmbed, publicEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
71
src/commands/warn.ts
Normal file
71
src/commands/warn.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||||
|
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||||
|
import LogEmbed from "../helpers/embeds/LogEmbed";
|
||||||
|
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default class Warn extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.Category = "Moderation";
|
||||||
|
super.Roles = [
|
||||||
|
"moderator"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
|
||||||
|
const user = context.message.mentions.users.first();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "User does not exist");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const member = context.message.guild?.members.cache.find(x => x.user.id == user.id);
|
||||||
|
|
||||||
|
if (!member) {
|
||||||
|
const errorEmbed = new ErrorEmbed(context, "User is not in this server");
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [errorEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonArgs = context.args;
|
||||||
|
reasonArgs.splice(0, 1);
|
||||||
|
|
||||||
|
const reason = reasonArgs.join(" ");
|
||||||
|
|
||||||
|
if (!context.message.guild?.available) {
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const logEmbed = new LogEmbed(context, "Member Warned");
|
||||||
|
logEmbed.AddUser("User", user, true);
|
||||||
|
logEmbed.AddUser("Moderator", context.message.author);
|
||||||
|
logEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`);
|
||||||
|
publicEmbed.AddReason(reason);
|
||||||
|
|
||||||
|
await logEmbed.SendToModLogsChannel();
|
||||||
|
publicEmbed.SendToCurrentChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
commandContext: context,
|
||||||
|
embeds: [logEmbed, publicEmbed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
7
src/constants/CommandResponse.ts
Normal file
7
src/constants/CommandResponse.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export enum CommandResponse {
|
||||||
|
Ok,
|
||||||
|
Unauthorised,
|
||||||
|
ServerNotSetup,
|
||||||
|
NotInServer,
|
||||||
|
FeatureDisabled,
|
||||||
|
}
|
56
src/constants/DefaultValues.ts
Normal file
56
src/constants/DefaultValues.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
export default class DefaultValues {
|
||||||
|
public static values: ISettingValue[] = [];
|
||||||
|
public static useDevPrefix: boolean = false;
|
||||||
|
|
||||||
|
public static GetValue(key: string): string | undefined {
|
||||||
|
this.SetValues();
|
||||||
|
|
||||||
|
const res = this.values.find(x => x.Key == key);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SetValues() {
|
||||||
|
if (this.values.length == 0) {
|
||||||
|
// Bot
|
||||||
|
if (this.useDevPrefix) {
|
||||||
|
this.values.push({ Key: "bot.prefix", Value: "d!" });
|
||||||
|
} else {
|
||||||
|
this.values.push({ Key: "bot.prefix", Value: "v!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
this.values.push({ Key: "commands.disabled", Value: "" });
|
||||||
|
this.values.push({ Key: "commands.disabled.message", Value: "This command is disabled." });
|
||||||
|
|
||||||
|
// Role (Command)
|
||||||
|
this.values.push({ Key: "role.assignable", Value: "" });
|
||||||
|
this.values.push({ Key: "role.moderator", Value: "Moderator" });
|
||||||
|
this.values.push({ Key: "role.administrator", Value: "Administrator"});
|
||||||
|
this.values.push({ Key: "role.muted", Value: "Muted" });
|
||||||
|
|
||||||
|
// Rules (Command)
|
||||||
|
this.values.push({ Key: "rules.file", Value: "data/rules/rules" });
|
||||||
|
|
||||||
|
// Channels
|
||||||
|
this.values.push({ Key: "channels.logs.message", Value: "message-logs" });
|
||||||
|
this.values.push({ Key: "channels.logs.member", Value: "member-logs" });
|
||||||
|
this.values.push({ Key: "channels.logs.mod", Value: "mod-logs" });
|
||||||
|
|
||||||
|
// Verification
|
||||||
|
this.values.push({ Key: "verification.enabled", Value: "false" });
|
||||||
|
this.values.push({ Key: "verification.channel", Value: "entry" });
|
||||||
|
this.values.push({ Key: "verification.role", Value: "Entry" });
|
||||||
|
this.values.push({ Key: "verification.code", Value: "" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISettingValue {
|
||||||
|
Key: string,
|
||||||
|
Value: string,
|
||||||
|
};
|
27
src/constants/ErrorMessages.ts
Normal file
27
src/constants/ErrorMessages.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { CommandResponse } from "./CommandResponse";
|
||||||
|
|
||||||
|
export default class ErrorMessages {
|
||||||
|
public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?";
|
||||||
|
public static readonly ChannelNotFound = "Unable to find channel";
|
||||||
|
public static readonly RoleNotFound = "Unable to find role";
|
||||||
|
|
||||||
|
public static readonly UserUnauthorised = "You are not authorised to use this command";
|
||||||
|
public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command";
|
||||||
|
public static readonly NotInServer = "This command requires to be ran inside of a server";
|
||||||
|
public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator";
|
||||||
|
|
||||||
|
public static GetErrorMessage(response: CommandResponse): string {
|
||||||
|
switch (response) {
|
||||||
|
case CommandResponse.Unauthorised:
|
||||||
|
return this.UserUnauthorised;
|
||||||
|
case CommandResponse.ServerNotSetup:
|
||||||
|
return this.ServerNotSetup;
|
||||||
|
case CommandResponse.NotInServer:
|
||||||
|
return this.NotInServer;
|
||||||
|
case CommandResponse.FeatureDisabled:
|
||||||
|
return this.FeatureDisabled;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
src/contracts/BaseEntity.ts
Normal file
68
src/contracts/BaseEntity.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { Column, DeepPartial, EntityTarget, getConnection, PrimaryColumn } from "typeorm";
|
||||||
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
|
export default class BaseEntity {
|
||||||
|
constructor() {
|
||||||
|
this.Id = v4();
|
||||||
|
|
||||||
|
this.WhenCreated = new Date();
|
||||||
|
this.WhenUpdated = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
Id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
WhenCreated: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
WhenUpdated: Date;
|
||||||
|
|
||||||
|
public async Save<T>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> {
|
||||||
|
this.WhenUpdated = new Date();
|
||||||
|
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository<T>(target);
|
||||||
|
|
||||||
|
await repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Remove<T>(target: EntityTarget<T>, entity: T): Promise<void> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository<T>(target);
|
||||||
|
|
||||||
|
await repository.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async FetchAll<T>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository<T>(target);
|
||||||
|
|
||||||
|
const all = await repository.find({ relations: relations || [] });
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async FetchOneById<T>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | undefined> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository<T>(target);
|
||||||
|
|
||||||
|
const single = await repository.findOne(id, { relations: relations || [] });
|
||||||
|
|
||||||
|
return single;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Any<T>(target: EntityTarget<T>): Promise<boolean> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository<T>(target);
|
||||||
|
|
||||||
|
const any = await repository.find();
|
||||||
|
|
||||||
|
return any.length > 0;
|
||||||
|
}
|
||||||
|
}
|
4
src/contracts/IBaseResponse.ts
Normal file
4
src/contracts/IBaseResponse.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface IBaseResponse {
|
||||||
|
valid: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
7
src/contracts/ICommandContext.ts
Normal file
7
src/contracts/ICommandContext.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
|
||||||
|
export interface ICommandContext {
|
||||||
|
name: string;
|
||||||
|
args: string[];
|
||||||
|
message: Message;
|
||||||
|
}
|
7
src/contracts/ICommandItem.ts
Normal file
7
src/contracts/ICommandItem.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { Command } from "../type/command";
|
||||||
|
|
||||||
|
export default interface ICommandItem {
|
||||||
|
Name: string,
|
||||||
|
Command: Command,
|
||||||
|
ServerId?: string,
|
||||||
|
}
|
7
src/contracts/ICommandReturnContext.ts
Normal file
7
src/contracts/ICommandReturnContext.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { MessageEmbed } from "discord.js";
|
||||||
|
import { ICommandContext } from "./ICommandContext";
|
||||||
|
|
||||||
|
export default interface ICommandReturnContext {
|
||||||
|
commandContext: ICommandContext,
|
||||||
|
embeds: MessageEmbed[]
|
||||||
|
}
|
6
src/contracts/IEventItem.ts
Normal file
6
src/contracts/IEventItem.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
import { Event } from "../type/event";
|
||||||
|
|
||||||
|
export default interface IEventItem {
|
||||||
|
Event: Event,
|
||||||
|
}
|
6
src/contracts/IEventReturnContext.ts
Normal file
6
src/contracts/IEventReturnContext.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { MessageEmbed } from "discord.js";
|
||||||
|
import { ICommandContext } from "./ICommandContext";
|
||||||
|
|
||||||
|
export default interface ICommandReturnContext {
|
||||||
|
embeds: MessageEmbed[]
|
||||||
|
}
|
45
src/entity/501231711271780357/Lobby.ts
Normal file
45
src/entity/501231711271780357/Lobby.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { Column, Entity, getConnection } from "typeorm";
|
||||||
|
import BaseEntity from "../../contracts/BaseEntity";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export default class Lobby extends BaseEntity {
|
||||||
|
constructor(channelId: string, roleId: string, cooldown: number, name: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.ChannelId = channelId;
|
||||||
|
this.RoleId = roleId;
|
||||||
|
this.Cooldown = cooldown;
|
||||||
|
this.Name = name;
|
||||||
|
|
||||||
|
this.LastUsed = new Date(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public ChannelId: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public RoleId: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public Cooldown: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public LastUsed: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public Name: string;
|
||||||
|
|
||||||
|
public MarkAsUsed() {
|
||||||
|
this.LastUsed = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | undefined> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository(Lobby);
|
||||||
|
|
||||||
|
const single = await repository.findOne({ ChannelId: channelId }, { relations: relations || [] });
|
||||||
|
|
||||||
|
return single;
|
||||||
|
}
|
||||||
|
}
|
19
src/entity/Server.ts
Normal file
19
src/entity/Server.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Column, Entity, getConnection, OneToMany } from "typeorm";
|
||||||
|
import BaseEntity from "../contracts/BaseEntity";
|
||||||
|
import Setting from "./Setting";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export default class Server extends BaseEntity {
|
||||||
|
constructor(serverId: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.Id = serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(() => Setting, x => x.Server)
|
||||||
|
Settings: Setting[];
|
||||||
|
|
||||||
|
public AddSettingToServer(setting: Setting) {
|
||||||
|
this.Settings.push(setting);
|
||||||
|
}
|
||||||
|
}
|
37
src/entity/Setting.ts
Normal file
37
src/entity/Setting.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Column, Entity, getConnection, ManyToOne } from "typeorm";
|
||||||
|
import BaseEntity from "../contracts/BaseEntity";
|
||||||
|
import Server from "./Server";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export default class Setting extends BaseEntity {
|
||||||
|
constructor(key: string, value: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.Key = key;
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Key: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
Value: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Server, x => x.Settings)
|
||||||
|
Server: Server;
|
||||||
|
|
||||||
|
public UpdateBasicDetails(key: string, value: string) {
|
||||||
|
this.Key = key;
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | undefined> {
|
||||||
|
const connection = getConnection();
|
||||||
|
|
||||||
|
const repository = connection.getRepository(Setting);
|
||||||
|
|
||||||
|
const single = await repository.findOne({ Key: key }, { relations: relations || [] });
|
||||||
|
|
||||||
|
return single;
|
||||||
|
}
|
||||||
|
}
|
49
src/events/MemberEvents.ts
Normal file
49
src/events/MemberEvents.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { Event } from "../type/event";
|
||||||
|
import { GuildMember } from "discord.js";
|
||||||
|
import EventEmbed from "../helpers/embeds/EventEmbed";
|
||||||
|
import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate";
|
||||||
|
import IEventReturnContext from "../contracts/IEventReturnContext";
|
||||||
|
|
||||||
|
export default class MemberEvents extends Event {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async guildMemberAdd(member: GuildMember): Promise<IEventReturnContext> {
|
||||||
|
const embed = new EventEmbed(member.guild, "Member Joined");
|
||||||
|
embed.AddUser("User", member.user, true);
|
||||||
|
embed.addField("Created", member.user.createdAt.toISOString());
|
||||||
|
embed.setFooter({ text: `Id: ${member.user.id}` });
|
||||||
|
|
||||||
|
await embed.SendToMemberLogsChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async guildMemberRemove(member: GuildMember): Promise<IEventReturnContext> {
|
||||||
|
const embed = new EventEmbed(member.guild, "Member Left");
|
||||||
|
embed.AddUser("User", member.user, true);
|
||||||
|
embed.addField("Joined", member.joinedAt?.toISOString() || "n/a");
|
||||||
|
embed.setFooter({ text: `Id: ${member.user.id}` });
|
||||||
|
|
||||||
|
await embed.SendToMemberLogsChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember): Promise<IEventReturnContext> {
|
||||||
|
const handler = new GuildMemberUpdate(oldMember, newMember);
|
||||||
|
|
||||||
|
if (oldMember.nickname != newMember.nickname) { // Nickname change
|
||||||
|
await handler.NicknameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
30
src/events/MemberEvents/GuildMemberUpdate.ts
Normal file
30
src/events/MemberEvents/GuildMemberUpdate.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { GuildMember } from "discord.js";
|
||||||
|
import IEventReturnContext from "../../contracts/IEventReturnContext";
|
||||||
|
import EventEmbed from "../../helpers/embeds/EventEmbed";
|
||||||
|
|
||||||
|
export default class GuildMemberUpdate {
|
||||||
|
public oldMember: GuildMember;
|
||||||
|
public newMember: GuildMember;
|
||||||
|
|
||||||
|
constructor(oldMember: GuildMember, newMember: GuildMember) {
|
||||||
|
this.oldMember = oldMember;
|
||||||
|
this.newMember = newMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async NicknameChanged(): Promise<IEventReturnContext> {
|
||||||
|
const oldNickname = this.oldMember.nickname || "*none*";
|
||||||
|
const newNickname = this.newMember.nickname || "*none*";
|
||||||
|
|
||||||
|
const embed = new EventEmbed(this.newMember.guild, "Nickname Changed");
|
||||||
|
embed.AddUser("User", this.newMember.user, true);
|
||||||
|
embed.addField("Before", oldNickname, true);
|
||||||
|
embed.addField("After", newNickname, true);
|
||||||
|
embed.setFooter({ text: `Id: ${this.newMember.user.id}` });
|
||||||
|
|
||||||
|
await embed.SendToMemberLogsChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
84
src/events/MessageEvents.ts
Normal file
84
src/events/MessageEvents.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { Event } from "../type/event";
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import EventEmbed from "../helpers/embeds/EventEmbed";
|
||||||
|
import IEventReturnContext from "../contracts/IEventReturnContext";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
import OnMessage from "./MessageEvents/OnMessage";
|
||||||
|
|
||||||
|
export default class MessageEvents extends Event {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async messageDelete(message: Message): Promise<IEventReturnContext> {
|
||||||
|
if (!message.guild) {
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.author.bot) {
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EventEmbed(message.guild, "Message Deleted");
|
||||||
|
embed.AddUser("User", message.author, true);
|
||||||
|
embed.addField("Channel", message.channel.toString(), true);
|
||||||
|
embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``);
|
||||||
|
|
||||||
|
if (message.attachments.size > 0) {
|
||||||
|
embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``);
|
||||||
|
}
|
||||||
|
|
||||||
|
await embed.SendToMessageLogsChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async messageUpdate(oldMessage: Message, newMessage: Message): Promise<IEventReturnContext> {
|
||||||
|
if (!newMessage.guild){
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMessage.author.bot) {
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldMessage.content == newMessage.content) {
|
||||||
|
return {
|
||||||
|
embeds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EventEmbed(newMessage.guild, "Message Edited");
|
||||||
|
embed.AddUser("User", newMessage.author, true);
|
||||||
|
embed.addField("Channel", newMessage.channel.toString(), true);
|
||||||
|
embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``);
|
||||||
|
embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``);
|
||||||
|
|
||||||
|
await embed.SendToMessageLogsChannel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [embed]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async messageCreate(message: Message) {
|
||||||
|
if (!message.guild) return;
|
||||||
|
if (message.author.bot) return;
|
||||||
|
|
||||||
|
const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id);
|
||||||
|
|
||||||
|
if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") {
|
||||||
|
await OnMessage.VerificationCheck(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/events/MessageEvents/OnMessage.ts
Normal file
59
src/events/MessageEvents/OnMessage.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { Message as Message } from "discord.js";
|
||||||
|
import SettingsHelper from "../../helpers/SettingsHelper";
|
||||||
|
|
||||||
|
export default class OnMessage {
|
||||||
|
public static async VerificationCheck(message: Message) {
|
||||||
|
if (!message.guild) return;
|
||||||
|
|
||||||
|
const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id);
|
||||||
|
|
||||||
|
if (!verificationChannel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = message.guild.channels.cache.find(x => x.name == verificationChannel);
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentChannel = message.guild.channels.cache.find(x => x == message.channel);
|
||||||
|
|
||||||
|
if (!currentChannel || currentChannel.name != verificationChannel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id);
|
||||||
|
|
||||||
|
if (!verificationCode || verificationCode == "") {
|
||||||
|
await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team.");
|
||||||
|
await message.delete();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id);
|
||||||
|
|
||||||
|
if (!verificationRoleName) {
|
||||||
|
await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team.");
|
||||||
|
await message.delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = message.guild.roles.cache.find(x => x.name == verificationRoleName);
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team.");
|
||||||
|
await message.delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) {
|
||||||
|
await message.delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await message.member?.roles.add(role);
|
||||||
|
await message.delete();
|
||||||
|
}
|
||||||
|
}
|
59
src/helpers/SettingsHelper.ts
Normal file
59
src/helpers/SettingsHelper.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import DefaultValues from "../constants/DefaultValues";
|
||||||
|
import Server from "../entity/Server";
|
||||||
|
import Setting from "../entity/Setting";
|
||||||
|
|
||||||
|
export default class SettingsHelper {
|
||||||
|
public static async GetSetting(key: string, serverId: string): Promise<string | undefined> {
|
||||||
|
const server = await Server.FetchOneById(Server, serverId, [
|
||||||
|
"Settings"
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
return DefaultValues.GetValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const setting = server.Settings.filter(x => x.Key == key)[0];
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
return DefaultValues.GetValue(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async SetSetting(key: string, serverId: string, value: string): Promise<void> {
|
||||||
|
const server = await Server.FetchOneById(Server, serverId, [
|
||||||
|
"Settings"
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setting = server.Settings.filter(x => x.Key == key)[0];
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
setting.UpdateBasicDetails(key, value);
|
||||||
|
|
||||||
|
await setting.Save(Setting, setting);
|
||||||
|
} else {
|
||||||
|
const newSetting = new Setting(key, value);
|
||||||
|
|
||||||
|
await newSetting.Save(Setting, newSetting);
|
||||||
|
|
||||||
|
server.AddSettingToServer(newSetting);
|
||||||
|
|
||||||
|
await server.Save(Server, server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async GetServerPrefix(serverId: string): Promise<string> {
|
||||||
|
const setting = await this.GetSetting("bot.prefix", serverId);
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
return "v!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
}
|
38
src/helpers/StringTools.ts
Normal file
38
src/helpers/StringTools.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
export default class StringTools {
|
||||||
|
public static Capitalise(str: string): string {
|
||||||
|
const words = str.split(" ");
|
||||||
|
let result: string[] = [];
|
||||||
|
|
||||||
|
words.forEach(word => {
|
||||||
|
const firstLetter = word.substring(0, 1).toUpperCase();
|
||||||
|
const rest = word.substring(1);
|
||||||
|
|
||||||
|
result.push(firstLetter + rest);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CapitaliseArray(str: string[]): string[] {
|
||||||
|
const res: string[] = [];
|
||||||
|
|
||||||
|
str.forEach(s => {
|
||||||
|
res.push(StringTools.Capitalise(s));
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RandomString(length: number) {
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
const characters = 'abcdefghkmnpqrstuvwxyz23456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
|
||||||
|
for ( var i = 0; i < length; i++ ) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
19
src/helpers/embeds/ErrorEmbed.ts
Normal file
19
src/helpers/embeds/ErrorEmbed.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { MessageEmbed } from "discord.js";
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
|
||||||
|
export default class ErrorEmbed extends MessageEmbed {
|
||||||
|
public context: ICommandContext;
|
||||||
|
|
||||||
|
constructor(context: ICommandContext, message: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.setColor(0xd52803);
|
||||||
|
super.setDescription(message);
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendToCurrentChannel() {
|
||||||
|
this.context.message.channel.send({ embeds: [ this ]});
|
||||||
|
}
|
||||||
|
}
|
72
src/helpers/embeds/EventEmbed.ts
Normal file
72
src/helpers/embeds/EventEmbed.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import { MessageEmbed, TextChannel, User, Guild } from "discord.js";
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
import SettingsHelper from "../SettingsHelper";
|
||||||
|
|
||||||
|
export default class EventEmbed extends MessageEmbed {
|
||||||
|
public guild: Guild;
|
||||||
|
|
||||||
|
constructor(guild: Guild, title: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.setColor(0x3050ba);
|
||||||
|
super.setTitle(title);
|
||||||
|
|
||||||
|
this.guild = guild;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail methods
|
||||||
|
public AddUser(title: string, user: User, setThumbnail: boolean = false) {
|
||||||
|
this.addField(title, `${user} \`${user.tag}\``, true);
|
||||||
|
|
||||||
|
if (setThumbnail) {
|
||||||
|
this.setThumbnail(user.displayAvatarURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddReason(message: string) {
|
||||||
|
this.addField("Reason", message || "*none*");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send methods
|
||||||
|
public SendToChannel(name: string) {
|
||||||
|
const channel = this.guild.channels.cache
|
||||||
|
.find(channel => channel.name == name) as TextChannel;
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
console.error(`Unable to find channel ${name}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.send({embeds: [ this ]});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToMessageLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToMemberLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToModLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
}
|
79
src/helpers/embeds/LogEmbed.ts
Normal file
79
src/helpers/embeds/LogEmbed.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { MessageEmbed, TextChannel, User } from "discord.js";
|
||||||
|
import ErrorMessages from "../../constants/ErrorMessages";
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
import SettingsHelper from "../SettingsHelper";
|
||||||
|
import ErrorEmbed from "./ErrorEmbed";
|
||||||
|
|
||||||
|
export default class LogEmbed extends MessageEmbed {
|
||||||
|
public context: ICommandContext;
|
||||||
|
|
||||||
|
constructor(context: ICommandContext, title: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.setColor(0x3050ba);
|
||||||
|
super.setTitle(title);
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail methods
|
||||||
|
public AddUser(title: string, user: User, setThumbnail: boolean = false) {
|
||||||
|
this.addField(title, `${user} \`${user.tag}\``, true);
|
||||||
|
|
||||||
|
if (setThumbnail) {
|
||||||
|
this.setThumbnail(user.displayAvatarURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddReason(message: string) {
|
||||||
|
this.addField("Reason", message || "*none*");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send methods
|
||||||
|
public SendToCurrentChannel() {
|
||||||
|
this.context.message.channel.send({ embeds: [ this ]});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendToChannel(name: string) {
|
||||||
|
const channel = this.context.message.guild?.channels.cache
|
||||||
|
.find(channel => channel.name == name) as TextChannel;
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound);
|
||||||
|
errorEmbed.SendToCurrentChannel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.send({ embeds: [ this ]});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToMessageLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToMemberLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async SendToModLogsChannel() {
|
||||||
|
const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!);
|
||||||
|
|
||||||
|
if (!channelName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SendToChannel(channelName);
|
||||||
|
}
|
||||||
|
}
|
26
src/helpers/embeds/PublicEmbed.ts
Normal file
26
src/helpers/embeds/PublicEmbed.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { MessageEmbed } from "discord.js";
|
||||||
|
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||||
|
|
||||||
|
export default class PublicEmbed extends MessageEmbed {
|
||||||
|
public context: ICommandContext;
|
||||||
|
|
||||||
|
constructor(context: ICommandContext, title: string, description: string) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.setColor(0x3050ba);
|
||||||
|
super.setTitle(title);
|
||||||
|
super.setDescription(description);
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail methods
|
||||||
|
public AddReason(message: string) {
|
||||||
|
this.addField("Reason", message || "*none*");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send methods
|
||||||
|
public SendToCurrentChannel() {
|
||||||
|
this.context.message.channel.send({ embeds: [ this ]});
|
||||||
|
}
|
||||||
|
}
|
59
src/registry.ts
Normal file
59
src/registry.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { CoreClient } from "./client/client";
|
||||||
|
|
||||||
|
// Command Imports
|
||||||
|
import About from "./commands/about";
|
||||||
|
import Ban from "./commands/ban";
|
||||||
|
import Clear from "./commands/clear";
|
||||||
|
import Code from "./commands/code";
|
||||||
|
import Config from "./commands/config";
|
||||||
|
import Disable from "./commands/disable";
|
||||||
|
import Help from "./commands/help";
|
||||||
|
import Kick from "./commands/kick";
|
||||||
|
import Mute from "./commands/mute";
|
||||||
|
import Poll from "./commands/poll";
|
||||||
|
import Role from "./commands/role";
|
||||||
|
import Rules from "./commands/rules";
|
||||||
|
import Setup from "./commands/setup";
|
||||||
|
import Unmute from "./commands/unmute";
|
||||||
|
import Warn from "./commands/warn";
|
||||||
|
|
||||||
|
// Command Imports: MankBot
|
||||||
|
import Entry from "./commands/501231711271780357/entry";
|
||||||
|
import Lobby from "./commands/501231711271780357/lobby";
|
||||||
|
|
||||||
|
// Event Imports
|
||||||
|
import MemberEvents from "./events/MemberEvents";
|
||||||
|
import MessageEvents from "./events/MessageEvents";
|
||||||
|
|
||||||
|
export default class Registry {
|
||||||
|
public static RegisterCommands() {
|
||||||
|
CoreClient.RegisterCommand("about", new About());
|
||||||
|
CoreClient.RegisterCommand("ban", new Ban());
|
||||||
|
CoreClient.RegisterCommand("clear", new Clear());
|
||||||
|
CoreClient.RegisterCommand("help", new Help());
|
||||||
|
CoreClient.RegisterCommand("kick", new Kick());
|
||||||
|
CoreClient.RegisterCommand("mute", new Mute());
|
||||||
|
CoreClient.RegisterCommand("poll", new Poll());
|
||||||
|
CoreClient.RegisterCommand("role", new Role());
|
||||||
|
CoreClient.RegisterCommand("rules", new Rules());
|
||||||
|
CoreClient.RegisterCommand("unmute", new Unmute());
|
||||||
|
CoreClient.RegisterCommand("warn", new Warn());
|
||||||
|
CoreClient.RegisterCommand("setup", new Setup());
|
||||||
|
CoreClient.RegisterCommand("config", new Config());
|
||||||
|
CoreClient.RegisterCommand("code", new Code());
|
||||||
|
CoreClient.RegisterCommand("disable", new Disable());
|
||||||
|
|
||||||
|
// Exclusive Commands: MankBot
|
||||||
|
CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357");
|
||||||
|
CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357");
|
||||||
|
|
||||||
|
// Add Exclusive Commands to Test Server
|
||||||
|
CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002");
|
||||||
|
CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegisterEvents() {
|
||||||
|
CoreClient.RegisterEvent(new MemberEvents());
|
||||||
|
CoreClient.RegisterEvent(new MessageEvents());
|
||||||
|
}
|
||||||
|
}
|
23
src/type/command.ts
Normal file
23
src/type/command.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { CommandResponse } from "../constants/CommandResponse";
|
||||||
|
import { ICommandContext } from "../contracts/ICommandContext";
|
||||||
|
|
||||||
|
export class Command {
|
||||||
|
public Roles: string[];
|
||||||
|
public Category?: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Roles = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public precheck(context: ICommandContext): CommandResponse {
|
||||||
|
return CommandResponse.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async precheckAsync(context: ICommandContext): Promise<CommandResponse> {
|
||||||
|
return CommandResponse.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public execute(context: ICommandContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
55
src/type/event.ts
Normal file
55
src/type/event.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { Channel, Guild, GuildMember, Message, PartialDMChannel, PartialGuildMember, PartialMessage, GuildBan } from "discord.js";
|
||||||
|
|
||||||
|
export class Event {
|
||||||
|
public channelCreate(channel: Channel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public channelDelete(channel: Channel | PartialDMChannel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public channelUpdate(oldChannel: Channel, newChannel: Channel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildBanAdd(ban: GuildBan) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildBanRemove(ban: GuildBan) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildCreate(guild: Guild) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildMemberAdd(member: GuildMember) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildMemberRemove(member: GuildMember | PartialGuildMember) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public guildMemberUpdate(oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public messageCreate(message: Message) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public messageDelete(message: Message | PartialMessage) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public messageUpdate(oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ready() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
33
src/vylbot.ts
Normal file
33
src/vylbot.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { CoreClient } from "./client/client";
|
||||||
|
import * as dotenv from "dotenv";
|
||||||
|
import registry from "./registry";
|
||||||
|
import { Intents } from "discord.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const requiredConfigs: string[] = [
|
||||||
|
"BOT_TOKEN",
|
||||||
|
"BOT_VER",
|
||||||
|
"BOT_AUTHOR",
|
||||||
|
"BOT_DATE",
|
||||||
|
"BOT_OWNERID",
|
||||||
|
];
|
||||||
|
|
||||||
|
requiredConfigs.forEach(config => {
|
||||||
|
if (!process.env[config]) {
|
||||||
|
throw `${config} is required in .env`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const devmode = process.argv.find(x => x.toLowerCase() == "--dev") != null;
|
||||||
|
|
||||||
|
const client = new CoreClient([
|
||||||
|
Intents.FLAGS.GUILDS,
|
||||||
|
Intents.FLAGS.GUILD_MESSAGES,
|
||||||
|
Intents.FLAGS.GUILD_MEMBERS,
|
||||||
|
], devmode);
|
||||||
|
|
||||||
|
registry.RegisterCommands();
|
||||||
|
registry.RegisterEvents();
|
||||||
|
|
||||||
|
client.start();
|
10
tests/_mocks/commands/mockCmd.ts
Normal file
10
tests/_mocks/commands/mockCmd.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Command } from "../../../src/type/command";
|
||||||
|
|
||||||
|
export default class MockCmd extends Command {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super._category = "General";
|
||||||
|
super._roles = ["Moderator"];
|
||||||
|
}
|
||||||
|
}
|
13
tests/_mocks/rules/rules.json
Normal file
13
tests/_mocks/rules/rules.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"image": "IMAGEURL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "TITLE 1",
|
||||||
|
"description": [
|
||||||
|
"DESCRIPTION 1A",
|
||||||
|
"DESCRIPTION 1B"
|
||||||
|
],
|
||||||
|
"footer": "FOOTER 1"
|
||||||
|
}
|
||||||
|
]
|
153
tests/client/client.test.ts
Normal file
153
tests/client/client.test.ts
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
|
const connectionMock = mock<Connection>();
|
||||||
|
const qbuilderMock = mock<SelectQueryBuilder<any>>();
|
||||||
|
|
||||||
|
let repositoryMock = mock<Repository<any>>();
|
||||||
|
let settingMock = mock<Setting>();
|
||||||
|
|
||||||
|
jest.mock('typeorm', () => {
|
||||||
|
qbuilderMock.where.mockReturnThis();
|
||||||
|
qbuilderMock.select.mockReturnThis();
|
||||||
|
repositoryMock.createQueryBuilder.mockReturnValue(qbuilderMock);
|
||||||
|
repositoryMock.findOne.mockImplementation(async () => {
|
||||||
|
return settingMock;
|
||||||
|
});
|
||||||
|
connectionMock.getRepository.mockReturnValue(repositoryMock);
|
||||||
|
|
||||||
|
return {
|
||||||
|
getConnection: () => connectionMock,
|
||||||
|
createConnection: () => connectionMock,
|
||||||
|
|
||||||
|
BaseEntity: class Mock {},
|
||||||
|
ObjectType: () => {},
|
||||||
|
Entity: () => {},
|
||||||
|
InputType: () => {},
|
||||||
|
Index: () => {},
|
||||||
|
PrimaryColumn: () => {},
|
||||||
|
Column: () => {},
|
||||||
|
CreateDateColumn: () => {},
|
||||||
|
UpdateDateColumn: () => {},
|
||||||
|
OneToMany: () => {},
|
||||||
|
ManyToOne: () => {},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock("discord.js");
|
||||||
|
jest.mock("dotenv");
|
||||||
|
jest.mock("../../src/client/events");
|
||||||
|
jest.mock("../../src/client/util");
|
||||||
|
jest.mock("../../src/constants/DefaultValues");
|
||||||
|
|
||||||
|
import { CoreClient } from "../../src/client/client";
|
||||||
|
|
||||||
|
import { Client } from "discord.js";
|
||||||
|
import * as dotenv from "dotenv";
|
||||||
|
import { Events } from "../../src/client/events";
|
||||||
|
import { Util } from "../../src/client/util";
|
||||||
|
import { Command } from "../../src/type/command";
|
||||||
|
import { Event } from "../../src/type/event";
|
||||||
|
import DefaultValues from "../../src/constants/DefaultValues";
|
||||||
|
import { Connection, Repository, SelectQueryBuilder } from "typeorm";
|
||||||
|
import Setting from "../../src/entity/Setting";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.resetModules();
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect Successful Initialisation', () => {
|
||||||
|
const coreClient = new CoreClient();
|
||||||
|
|
||||||
|
expect(coreClient).toBeInstanceOf(Client);
|
||||||
|
expect(dotenv.config).toBeCalledTimes(1);
|
||||||
|
expect(Events).toBeCalledTimes(1);
|
||||||
|
expect(Util).toBeCalledTimes(1);
|
||||||
|
expect(DefaultValues.useDevPrefix).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given devmode parameter is true, Expect devmode prefix to be true', () => {
|
||||||
|
const coreClient = new CoreClient(true);
|
||||||
|
|
||||||
|
expect(coreClient).toBeInstanceOf(Client);
|
||||||
|
expect(dotenv.config).toBeCalledTimes(1);
|
||||||
|
expect(Events).toBeCalledTimes(1);
|
||||||
|
expect(Util).toBeCalledTimes(1);
|
||||||
|
expect(DefaultValues.useDevPrefix).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Start', () => {
|
||||||
|
test('Given Env Is Valid, Expect Successful Start', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: "TOKEN",
|
||||||
|
};
|
||||||
|
|
||||||
|
const coreClient = new CoreClient();
|
||||||
|
|
||||||
|
await coreClient.start();
|
||||||
|
|
||||||
|
expect(coreClient.on).toBeCalledWith("message", expect.any(Function));
|
||||||
|
expect(coreClient.on).toBeCalledWith("ready", expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given BOT_TOKEN Is Null, Expect Failure', async () => {
|
||||||
|
process.env = {};
|
||||||
|
|
||||||
|
const consoleError = jest.fn();
|
||||||
|
|
||||||
|
console.error = consoleError;
|
||||||
|
|
||||||
|
const coreClient = new CoreClient();
|
||||||
|
|
||||||
|
await coreClient.start();
|
||||||
|
|
||||||
|
expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env");
|
||||||
|
expect(coreClient.on).not.toBeCalled();
|
||||||
|
expect(coreClient.login).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given BOT_TOKEN Is Empty, Expect Failure', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
const consoleError = jest.fn();
|
||||||
|
|
||||||
|
console.error = consoleError;
|
||||||
|
|
||||||
|
const coreClient = new CoreClient();
|
||||||
|
|
||||||
|
await coreClient.start();
|
||||||
|
|
||||||
|
expect(consoleError).toBeCalledWith("BOT_TOKEN is not defined in .env");
|
||||||
|
expect(coreClient.on).not.toBeCalled();
|
||||||
|
expect(coreClient.login).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('RegisterCommand', () => {
|
||||||
|
test('Expect command added to register', () => {
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const client = new CoreClient();
|
||||||
|
client.RegisterCommand("test", cmd);
|
||||||
|
|
||||||
|
expect(client.commandItems.length).toBe(1);
|
||||||
|
expect(client.commandItems[0].Name).toBe("test");
|
||||||
|
expect(client.commandItems[0].Command).toBe(cmd);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('RegisterEvent', () => {
|
||||||
|
test('Expect event added to register', () => {
|
||||||
|
const evt = mock<Event>();
|
||||||
|
|
||||||
|
const client = new CoreClient();
|
||||||
|
client.RegisterEvent(evt);
|
||||||
|
|
||||||
|
expect(client.eventItems.length).toBe(1);
|
||||||
|
expect(client.eventItems[0].Event).toBe(evt);
|
||||||
|
});
|
||||||
|
});
|
241
tests/client/events.test.ts
Normal file
241
tests/client/events.test.ts
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
import { Events } from "../../src/client/events";
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import { Util } from "../../src/client/util";
|
||||||
|
import ICommandItem from "../../src/contracts/ICommandItem";
|
||||||
|
import { Command } from "../../src/type/command";
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
|
jest.mock("../../src/client/util");
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Util.prototype.loadCommand = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('OnMessage', () => {
|
||||||
|
test('Given Message Is Valid Expect Message Sent', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: {},
|
||||||
|
author: {
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
content: "!test first",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
|
||||||
|
expect(result.context?.prefix).toBe('!');
|
||||||
|
expect(result.context?.name).toBe('test');
|
||||||
|
expect(result.context?.args.length).toBe(1);
|
||||||
|
expect(result.context?.args[0]).toBe('first');
|
||||||
|
expect(result.context?.message).toBe(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Guild Is Null, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: null,
|
||||||
|
author: {
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
content: "!test first",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Message was not sent in a guild, ignoring.");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Author Is A Bot, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: {},
|
||||||
|
author: {
|
||||||
|
bot: true,
|
||||||
|
},
|
||||||
|
content: "!test first",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Message was sent by a bot, ignoring.");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Message Content Was Not A Command, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: {},
|
||||||
|
author: {
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
content: "This is a standard message",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Message was not a command, ignoring.");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Message Had No Command Name, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: true });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: {},
|
||||||
|
author: {
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
content: "!",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Command name was not found");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Command Failed To Execute, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.prototype.loadCommand = jest.fn().mockReturnValue({ valid: false, message: "Command failed" });
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
guild: {},
|
||||||
|
author: {
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
content: "!test first",
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
const result = await events.onMessage(message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Command failed");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('OnReady', () => {
|
||||||
|
test('Expect Console Log', () => {
|
||||||
|
console.log = jest.fn();
|
||||||
|
|
||||||
|
const events = new Events();
|
||||||
|
|
||||||
|
events.onReady();
|
||||||
|
|
||||||
|
expect(console.log).toBeCalledWith("Ready");
|
||||||
|
});
|
||||||
|
});
|
370
tests/client/util.test.ts
Normal file
370
tests/client/util.test.ts
Normal file
|
@ -0,0 +1,370 @@
|
||||||
|
import { Util } from "../../src/client/util";
|
||||||
|
|
||||||
|
import { Client, Message } from "discord.js";
|
||||||
|
import fs from "fs";
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
import { Command } from "../../src/type/command";
|
||||||
|
import ICommandItem from "../../src/contracts/ICommandItem";
|
||||||
|
import IEventItem from "../../src/contracts/IEventItem";
|
||||||
|
import { Event } from "../../src/type/event";
|
||||||
|
|
||||||
|
jest.mock("fs");
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs.existsSync = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LoadCommand', () => {
|
||||||
|
test('Given Successful Exection, Expect Successful Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
expect(cmd.execute).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given Member Is Null, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: null
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Member is not part of message");
|
||||||
|
expect(cmd.execute).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given User Does Have Role, Expect Successful Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
expect(cmd.execute).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given User Does Not Have Role, Expect Failed Result', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
cmd._roles = [ "Moderator" ];
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("You require the `Moderator` role to run this command");
|
||||||
|
expect(cmd.execute).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given command is set to disabled, Expect command to not fire', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
COMMANDS_DISABLED: 'test',
|
||||||
|
COMMANDS_DISABLED_MESSAGE: 'disabled',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const messageReply = jest.spyOn(message, 'reply');
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Command is disabled");
|
||||||
|
expect(messageReply).toBeCalledWith("disabled");
|
||||||
|
expect(cmd.execute).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given command COMMANDS_DISABLED_MESSAGE is empty, Expect default message sent', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
COMMANDS_DISABLED: 'test',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const messageReply = jest.spyOn(message, 'reply');
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe("Command is disabled");
|
||||||
|
expect(messageReply).toBeCalledWith("This command is disabled.");
|
||||||
|
expect(cmd.execute).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given a different command is disabled, Expect command to still fire', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
COMMANDS_DISABLED: 'other',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const cmd = mock<Command>();
|
||||||
|
const otherCmd = mock<Command>();
|
||||||
|
|
||||||
|
const commandItem: ICommandItem = {
|
||||||
|
Name: "test",
|
||||||
|
Command: cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
const otherCommandItem: ICommandItem = {
|
||||||
|
Name: "other",
|
||||||
|
Command: otherCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [ commandItem, otherCommandItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
expect(cmd.execute).toBeCalled();
|
||||||
|
expect(otherCmd.execute).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given command is not found in register, expect command not found error', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
member: {
|
||||||
|
roles: {
|
||||||
|
cache: {
|
||||||
|
find: jest.fn().mockReturnValue(true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reply: jest.fn(),
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const commands: ICommandItem[] = [];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = await util.loadCommand("test", [ "first" ], message, commands);
|
||||||
|
|
||||||
|
expect(result.valid).toBeFalsy();
|
||||||
|
expect(result.message).toBe('Command not found');
|
||||||
|
expect(message.reply).toBeCalledWith('Command not found');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LoadEvents', () => {
|
||||||
|
test('Given Events Are Loaded, Expect Successful Result', () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
on: jest.fn(),
|
||||||
|
} as unknown as Client;
|
||||||
|
|
||||||
|
const evt = mock<Event>();
|
||||||
|
|
||||||
|
const eventItem: IEventItem = {
|
||||||
|
Event: evt
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventItems: IEventItem[] = [ eventItem ];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = util.loadEvents(client, eventItems);
|
||||||
|
|
||||||
|
const clientOn = jest.spyOn(client, 'on');
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
expect(clientOn).toBeCalledTimes(13);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given No Events Found, Expect Successful Result', () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_TOKEN: 'TOKEN',
|
||||||
|
BOT_PREFIX: '!',
|
||||||
|
FOLDERS_COMMANDS: 'commands',
|
||||||
|
FOLDERS_EVENTS: 'events',
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
on: jest.fn(),
|
||||||
|
} as unknown as Client;
|
||||||
|
|
||||||
|
const eventItems: IEventItem[] = [];
|
||||||
|
|
||||||
|
const util = new Util();
|
||||||
|
|
||||||
|
const result = util.loadEvents(client, eventItems);
|
||||||
|
|
||||||
|
const clientOn = jest.spyOn(client, 'on');
|
||||||
|
|
||||||
|
expect(result.valid).toBeTruthy();
|
||||||
|
expect(clientOn).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
|
});
|
152
tests/commands/about.test.ts
Normal file
152
tests/commands/about.test.ts
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
import About from "../../src/commands/about";
|
||||||
|
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||||
|
import PublicEmbed from "../../src/helpers/embeds/PublicEmbed";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect values set', () => {
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
expect(about._category).toBe("General");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Execute', () => {
|
||||||
|
test('Expect embed to be made and sent to the current channel', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_VER: "BOT_VER",
|
||||||
|
BOT_AUTHOR: "BOT_AUTHOR",
|
||||||
|
BOT_DATE: "BOT_DATE"
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = mock<Message>();
|
||||||
|
message.channel.send = jest.fn();
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: "about",
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
const result = await about.execute(context);
|
||||||
|
|
||||||
|
expect(message.channel.send).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Expect embed send to have values', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_VER: "BOT_VER",
|
||||||
|
BOT_AUTHOR: "BOT_AUTHOR",
|
||||||
|
BOT_DATE: "BOT_DATE"
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = mock<Message>();
|
||||||
|
message.channel.send = jest.fn();
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: "about",
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
const result = await about.execute(context);
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
const embed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(embed.title).toBe('About');
|
||||||
|
expect(embed.description).toBe('');
|
||||||
|
expect(embed.fields.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Expect version field to have values', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_VER: "BOT_VER",
|
||||||
|
BOT_AUTHOR: "BOT_AUTHOR",
|
||||||
|
BOT_DATE: "BOT_DATE"
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = mock<Message>();
|
||||||
|
message.channel.send = jest.fn();
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: "about",
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
const result = await about.execute(context);
|
||||||
|
|
||||||
|
const embed = result.embeds[0];
|
||||||
|
const field = embed.fields[0];
|
||||||
|
|
||||||
|
expect(field.name).toBe('Version');
|
||||||
|
expect(field.value).toBe('BOT_VER');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Expect author field to have values', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_VER: "BOT_VER",
|
||||||
|
BOT_AUTHOR: "BOT_AUTHOR",
|
||||||
|
BOT_DATE: "BOT_DATE"
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = mock<Message>();
|
||||||
|
message.channel.send = jest.fn();
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: "about",
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
const result = await about.execute(context);
|
||||||
|
|
||||||
|
const embed = result.embeds[0];
|
||||||
|
const field = embed.fields[1];
|
||||||
|
|
||||||
|
expect(field.name).toBe('Author');
|
||||||
|
expect(field.value).toBe('BOT_AUTHOR');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Expect version field to have values', async () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_VER: "BOT_VER",
|
||||||
|
BOT_AUTHOR: "BOT_AUTHOR",
|
||||||
|
BOT_DATE: "BOT_DATE"
|
||||||
|
};
|
||||||
|
|
||||||
|
const message = mock<Message>();
|
||||||
|
message.channel.send = jest.fn();
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: "about",
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const about = new About();
|
||||||
|
|
||||||
|
const result = await about.execute(context);
|
||||||
|
|
||||||
|
const embed = result.embeds[0];
|
||||||
|
const field = embed.fields[2];
|
||||||
|
|
||||||
|
expect(field.name).toBe('Date');
|
||||||
|
expect(field.value).toBe('BOT_DATE');
|
||||||
|
});
|
||||||
|
});
|
724
tests/commands/ban.test.ts
Normal file
724
tests/commands/ban.test.ts
Normal file
|
@ -0,0 +1,724 @@
|
||||||
|
import { GuildMember, Message, TextChannel, User } from "discord.js";
|
||||||
|
import Ban from "../../src/commands/ban";
|
||||||
|
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect values to be set', () => {
|
||||||
|
process.env.ROLES_MODERATOR = 'Moderator';
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
expect(ban._category).toBe('Moderation');
|
||||||
|
expect(ban._roles.length).toBe(1);
|
||||||
|
expect(ban._roles[0]).toBe('Moderator');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Execute', () => {
|
||||||
|
test('Given user has permission, expect user to be banned', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(logChannel.send).toBeCalledTimes(1);
|
||||||
|
expect(mentionedMember.ban).toBeCalledWith({ reason: 'Moderator: AUTHORTAG, Reason: Test Reason' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given moderator did not supply a reason, expect default message', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(logChannel.send).toBeCalledTimes(1);
|
||||||
|
expect(mentionedMember.ban).toBeCalledWith({ reason: 'Moderator: AUTHORTAG, Reason: *none*' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given user has permissions, expect embeds to be correct', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(2);
|
||||||
|
|
||||||
|
const logEmbed = result.embeds[0];
|
||||||
|
const publicEmbed = result.embeds[1];
|
||||||
|
|
||||||
|
expect(logEmbed.title).toBe('Member Banned');
|
||||||
|
expect(publicEmbed.title).toBe("");
|
||||||
|
expect(publicEmbed.description).toBe('[object Object] has been banned');
|
||||||
|
expect(logEmbed.fields.length).toBe(3);
|
||||||
|
expect(publicEmbed.fields.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given user has permission, expect logEmbed fields to be correct', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn().mockReturnValue('URL'),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
const logEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
const fieldUser = logEmbed.fields[0];
|
||||||
|
const fieldModerator = logEmbed.fields[1];
|
||||||
|
const fieldReason = logEmbed.fields[2];
|
||||||
|
|
||||||
|
expect(fieldUser.name).toBe("User");
|
||||||
|
expect(fieldUser.value).toBe("[object Object] `USERTAG`");
|
||||||
|
expect(logEmbed.thumbnail?.url).toBe("URL");
|
||||||
|
|
||||||
|
expect(fieldModerator.name).toBe('Moderator');
|
||||||
|
expect(fieldModerator.value).toBe('[object Object] `AUTHORTAG`');
|
||||||
|
|
||||||
|
expect(fieldReason.name).toBe('Reason');
|
||||||
|
expect(fieldReason.value).toBe('Test Reason');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given moderator did not supply a reason, expect reason field to be default message', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn().mockReturnValue('URL'),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
const logEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
const fieldUser = logEmbed.fields[0];
|
||||||
|
const fieldModerator = logEmbed.fields[1];
|
||||||
|
const fieldReason = logEmbed.fields[2];
|
||||||
|
|
||||||
|
expect(fieldUser.name).toBe("User");
|
||||||
|
expect(fieldUser.value).toBe("[object Object] `USERTAG`");
|
||||||
|
expect(logEmbed.thumbnail?.url).toBe("URL");
|
||||||
|
|
||||||
|
expect(fieldModerator.name).toBe('Moderator');
|
||||||
|
expect(fieldModerator.value).toBe('[object Object] `AUTHORTAG`');
|
||||||
|
|
||||||
|
expect(fieldReason.name).toBe('Reason');
|
||||||
|
expect(fieldReason.value).toBe('*none*');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given user is not mentioned, expect error embed to be sent', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(null);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(logChannel.send).not.toBeCalled();
|
||||||
|
expect(mentionedMember.ban).not.toBeCalled();
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
const embedError = result.embeds[0];
|
||||||
|
|
||||||
|
expect(embedError.description).toBe('User does not exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given member is not in server, expect error embed to be sent', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(null);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(logChannel.send).not.toBeCalled();
|
||||||
|
expect(mentionedMember.ban).not.toBeCalled();
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
const embedError = result.embeds[0];
|
||||||
|
|
||||||
|
expect(embedError.description).toBe('User is not in this server');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given guild is unavailable, expect return and do nothing', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: true,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: false
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).not.toBeCalled();
|
||||||
|
expect(logChannel.send).not.toBeCalled();
|
||||||
|
expect(mentionedMember.ban).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given bot cant ban user, expect error embed to be sent', async () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: 'Moderator',
|
||||||
|
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const mentionedUser = {
|
||||||
|
displayAvatarURL: jest.fn(),
|
||||||
|
tag: 'USERTAG'
|
||||||
|
} as unknown as User;
|
||||||
|
const mentionedMember = {
|
||||||
|
bannable: false,
|
||||||
|
ban: jest.fn()
|
||||||
|
} as unknown as GuildMember;
|
||||||
|
const logChannel = {
|
||||||
|
name: 'mod-logs',
|
||||||
|
send: jest.fn()
|
||||||
|
} as unknown as TextChannel;
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageMentionsUsersFirst = jest.fn()
|
||||||
|
.mockReturnValue(mentionedUser);
|
||||||
|
const messageGuildMember = jest.fn()
|
||||||
|
.mockReturnValue(mentionedMember);
|
||||||
|
const messageGuildChannelsCacheFind = jest.fn()
|
||||||
|
.mockImplementation((callback): TextChannel | undefined => {
|
||||||
|
const result = callback(logChannel);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logChannel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
users: {
|
||||||
|
first: messageMentionsUsersFirst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
guild: {
|
||||||
|
member: messageGuildMember ,
|
||||||
|
channels: {
|
||||||
|
cache: {
|
||||||
|
find: messageGuildChannelsCacheFind
|
||||||
|
}
|
||||||
|
},
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
tag: 'AUTHORTAG'
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'ban',
|
||||||
|
args: ['ban', 'Test', 'Reason'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const ban = new Ban();
|
||||||
|
|
||||||
|
const result = await ban.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(logChannel.send).not.toBeCalled();
|
||||||
|
expect(mentionedMember.ban).not.toBeCalled();
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
const embedError = result.embeds[0];
|
||||||
|
|
||||||
|
expect(embedError.description).toBe('Unable to do this action, am I missing permissions?');
|
||||||
|
});
|
||||||
|
});
|
178
tests/commands/clear.test.ts
Normal file
178
tests/commands/clear.test.ts
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import Clear from "../../src/commands/clear";
|
||||||
|
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect values to be set', () => {
|
||||||
|
process.env = {
|
||||||
|
ROLES_MODERATOR: "Moderator"
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
|
||||||
|
expect(clear._category).toBe('Moderation');
|
||||||
|
expect(clear._roles.length).toBe(1);
|
||||||
|
expect(clear._roles[0]).toBe('Moderator');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Execute', () => {
|
||||||
|
test('Given valid arguments, expect messages to be cleared', async () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageChannelBulkDelete = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend,
|
||||||
|
bulkDelete: messageChannelBulkDelete
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'clear',
|
||||||
|
args: ['5'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
const result = await clear.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(messageChannelBulkDelete).toBeCalledWith(5);
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// PublicEmbed
|
||||||
|
const publicEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(publicEmbed.title).toBe('');
|
||||||
|
expect(publicEmbed.description).toBe('5 message(s) were removed');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given argument is not given, expect error embed to be sent', async () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageChannelBulkDelete = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend,
|
||||||
|
bulkDelete: messageChannelBulkDelete
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'clear',
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
const result = await clear.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(messageChannelBulkDelete).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.title).toBeNull();
|
||||||
|
expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given argument is not a number, expect error embed to be sent', async () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageChannelBulkDelete = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend,
|
||||||
|
bulkDelete: messageChannelBulkDelete
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'clear',
|
||||||
|
args: ['A'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
const result = await clear.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(messageChannelBulkDelete).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.title).toBeNull();
|
||||||
|
expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given argument is less than 1, expect error embed to be sent', async () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageChannelBulkDelete = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend,
|
||||||
|
bulkDelete: messageChannelBulkDelete
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'clear',
|
||||||
|
args: ['0'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
const result = await clear.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(messageChannelBulkDelete).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.title).toBeNull();
|
||||||
|
expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given argument is more than 100, expect error embed to be sent', async () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
const messageChannelBulkDelete = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend,
|
||||||
|
bulkDelete: messageChannelBulkDelete
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'clear',
|
||||||
|
args: ['101'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = new Clear();
|
||||||
|
const result = await clear.execute(context);
|
||||||
|
|
||||||
|
expect(messageChannelSend).toBeCalledTimes(1);
|
||||||
|
expect(messageChannelBulkDelete).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.title).toBeNull();
|
||||||
|
expect(errorEmbed.description).toBe('Please specify an amount between 1 and 100');
|
||||||
|
});
|
||||||
|
});
|
136
tests/commands/eval.test.ts
Normal file
136
tests/commands/eval.test.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import Evaluate from "../../src/commands/eval";
|
||||||
|
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect values to be set', () => {
|
||||||
|
const evaluate = new Evaluate();
|
||||||
|
|
||||||
|
expect(evaluate._category).toBe('Owner');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Execute', () => {
|
||||||
|
test('Given user has permission, expect eval statement ran', () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_OWNERID: 'OWNERID'
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log = jest.fn();
|
||||||
|
global.eval = jest.fn()
|
||||||
|
.mockReturnValue('General Kenobi');
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
author: {
|
||||||
|
id: 'OWNERID'
|
||||||
|
},
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'eval',
|
||||||
|
args: ['echo', 'Hello', 'there'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const evaluate = new Evaluate();
|
||||||
|
|
||||||
|
const result = evaluate.execute(context);
|
||||||
|
|
||||||
|
expect(console.log).toBeCalledWith('Eval Statement: echo Hello there');
|
||||||
|
expect(global.eval).toBeCalledWith('echo Hello there');
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// PublicEmbed
|
||||||
|
const publicEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(publicEmbed.title).toBe('');
|
||||||
|
expect(publicEmbed.description).toBe('General Kenobi');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given user does not have permission, expect nothing to occur', () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_OWNERID: 'DIFFERENT'
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log = jest.fn();
|
||||||
|
global.eval = jest.fn()
|
||||||
|
.mockReturnValue('General Kenobi');
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
author: {
|
||||||
|
id: 'OWNERID'
|
||||||
|
},
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'eval',
|
||||||
|
args: ['echo', 'Hello', 'there'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const evaluate = new Evaluate();
|
||||||
|
|
||||||
|
const result = evaluate.execute(context);
|
||||||
|
|
||||||
|
expect(console.log).not.toBeCalled();
|
||||||
|
expect(global.eval).not.toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given eval failed, expect error embed to be sent', () => {
|
||||||
|
process.env = {
|
||||||
|
BOT_OWNERID: 'OWNERID'
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log = jest.fn();
|
||||||
|
global.eval = jest.fn()
|
||||||
|
.mockImplementation(() => {
|
||||||
|
throw new Error('Error message');
|
||||||
|
});
|
||||||
|
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
author: {
|
||||||
|
id: 'OWNERID'
|
||||||
|
},
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'eval',
|
||||||
|
args: ['echo', 'Hello', 'there'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const evaluate = new Evaluate();
|
||||||
|
|
||||||
|
const result = evaluate.execute(context);
|
||||||
|
|
||||||
|
expect(console.log).toBeCalledWith('Eval Statement: echo Hello there');
|
||||||
|
expect(global.eval).toBeCalledWith('echo Hello there');
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.title).toBeNull();
|
||||||
|
expect(errorEmbed.description).toBe('Error: Error message');
|
||||||
|
});
|
||||||
|
});
|
267
tests/commands/help.test.ts
Normal file
267
tests/commands/help.test.ts
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
import Help, { ICommandData } from "../../src/commands/help";
|
||||||
|
import { Message } from "discord.js";
|
||||||
|
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||||
|
|
||||||
|
const oldCwd = process.cwd();
|
||||||
|
|
||||||
|
describe('Constructor', () => {
|
||||||
|
test('Expect properties to be set', () => {
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
expect(help._category).toBe('General');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Execute', () => {
|
||||||
|
test('Given no arguments were given, expect SendAll to be executed', () => {
|
||||||
|
const message = {} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'help',
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
help.SendAll = jest.fn();
|
||||||
|
help.SendSingle = jest.fn();
|
||||||
|
|
||||||
|
help.execute(context);
|
||||||
|
|
||||||
|
expect(help.SendAll).toBeCalled();
|
||||||
|
expect(help.SendSingle).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given an argument was given, expect SendSingle to be executed', () => {
|
||||||
|
const message = {} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'help',
|
||||||
|
args: ['about'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
help.SendAll = jest.fn();
|
||||||
|
help.SendSingle = jest.fn();
|
||||||
|
|
||||||
|
help.execute(context);
|
||||||
|
|
||||||
|
expect(help.SendAll).not.toBeCalled();
|
||||||
|
expect(help.SendSingle).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SendAll', () => {
|
||||||
|
test('Expect embed with all commands to be sent', () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'help',
|
||||||
|
args: [],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
const commandData0: ICommandData = {
|
||||||
|
Exists: true,
|
||||||
|
Name: 'about',
|
||||||
|
Category: 'general',
|
||||||
|
Roles: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandData1: ICommandData = {
|
||||||
|
Exists: true,
|
||||||
|
Name: 'role',
|
||||||
|
Category: 'general',
|
||||||
|
Roles: []
|
||||||
|
};
|
||||||
|
|
||||||
|
help.GetAllCommandData = jest.fn()
|
||||||
|
.mockReturnValue([commandData0, commandData1]);
|
||||||
|
|
||||||
|
const result = help.SendAll(context);
|
||||||
|
|
||||||
|
expect(help.GetAllCommandData).toBeCalled();
|
||||||
|
expect(messageChannelSend).toBeCalled();
|
||||||
|
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// PublicEmbed
|
||||||
|
const publicEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(publicEmbed.fields.length).toBe(1);
|
||||||
|
|
||||||
|
// PublicEmbed -> GeneralCategory Field
|
||||||
|
const publicEmbedFieldGeneral = publicEmbed.fields[0];
|
||||||
|
|
||||||
|
expect(publicEmbedFieldGeneral.name).toBe('General');
|
||||||
|
expect(publicEmbedFieldGeneral.value).toBe('about, role');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SendSingle', () => {
|
||||||
|
test('Given command exists, expect embed to be sent with command fields', () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'help',
|
||||||
|
args: ['about'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandData: ICommandData = {
|
||||||
|
Exists: true,
|
||||||
|
Name: 'about',
|
||||||
|
Category: 'general',
|
||||||
|
Roles: ['role1', 'role2']
|
||||||
|
};
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
help.GetCommandData = jest.fn()
|
||||||
|
.mockReturnValue(commandData);
|
||||||
|
|
||||||
|
const result = help.SendSingle(context);
|
||||||
|
|
||||||
|
expect(help.GetCommandData).toBeCalledWith('about');
|
||||||
|
expect(messageChannelSend).toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// PublicEmbed
|
||||||
|
const publicEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(publicEmbed.title).toBe('About');
|
||||||
|
expect(publicEmbed.description).toBe('');
|
||||||
|
expect(publicEmbed.fields.length).toBe(2);
|
||||||
|
|
||||||
|
// PublicEmbed -> Category Field
|
||||||
|
const fieldCategory = publicEmbed.fields[0];
|
||||||
|
|
||||||
|
expect(fieldCategory.name).toBe('Category');
|
||||||
|
expect(fieldCategory.value).toBe('General');
|
||||||
|
|
||||||
|
// PublicEmbed -> RequiredRoles Field
|
||||||
|
const fieldRoles = publicEmbed.fields[1];
|
||||||
|
|
||||||
|
expect(fieldRoles.name).toBe('Required Roles');
|
||||||
|
expect(fieldRoles.value).toBe('Role1, Role2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given command does not exist, expect error embed to be sent', () => {
|
||||||
|
const messageChannelSend = jest.fn();
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
channel: {
|
||||||
|
send: messageChannelSend
|
||||||
|
}
|
||||||
|
} as unknown as Message;
|
||||||
|
|
||||||
|
const context: ICommandContext = {
|
||||||
|
name: 'help',
|
||||||
|
args: ['about'],
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandData: ICommandData = {
|
||||||
|
Exists: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
help.GetCommandData = jest.fn()
|
||||||
|
.mockReturnValue(commandData);
|
||||||
|
|
||||||
|
const result = help.SendSingle(context);
|
||||||
|
|
||||||
|
expect(help.GetCommandData).toBeCalledWith('about');
|
||||||
|
expect(messageChannelSend).toBeCalled();
|
||||||
|
expect(result.embeds.length).toBe(1);
|
||||||
|
|
||||||
|
// ErrorEmbed
|
||||||
|
const errorEmbed = result.embeds[0];
|
||||||
|
|
||||||
|
expect(errorEmbed.description).toBe('Command does not exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetAllCommandData', () => {
|
||||||
|
test('Expect array of command data to be returned', () => {
|
||||||
|
process.env = {
|
||||||
|
FOLDERS_COMMANDS: "commands"
|
||||||
|
};
|
||||||
|
|
||||||
|
process.cwd = jest.fn()
|
||||||
|
.mockReturnValue(`${oldCwd}/tests/_mocks`);
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
const result = help.GetAllCommandData();
|
||||||
|
|
||||||
|
expect(result.length).toBe(1);
|
||||||
|
|
||||||
|
// Mock Command
|
||||||
|
const mockCommand = result[0];
|
||||||
|
|
||||||
|
expect(mockCommand.Exists).toBeTruthy();
|
||||||
|
expect(mockCommand.Name).toBe("mockCmd");
|
||||||
|
expect(mockCommand.Category).toBe("General");
|
||||||
|
|
||||||
|
expect(mockCommand.Roles!.length).toBe(1);
|
||||||
|
expect(mockCommand.Roles![0]).toBe("Moderator");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetCommandData', () => {
|
||||||
|
test('Given command exists, expect data to be returned', () => {
|
||||||
|
process.env = {
|
||||||
|
FOLDERS_COMMANDS: "commands"
|
||||||
|
};
|
||||||
|
|
||||||
|
process.cwd = jest.fn()
|
||||||
|
.mockReturnValue(`${oldCwd}/tests/_mocks`);
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
const result = help.GetCommandData('mockCmd');
|
||||||
|
|
||||||
|
expect(result.Exists).toBeTruthy();
|
||||||
|
expect(result.Name).toBe("mockCmd");
|
||||||
|
expect(result.Category).toBe("General");
|
||||||
|
|
||||||
|
expect(result.Roles!.length).toBe(1);
|
||||||
|
expect(result.Roles![0]).toBe("Moderator");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Given command does not exist, expect exists false to be returned', () => {
|
||||||
|
process.env = {
|
||||||
|
FOLDERS_COMMANDS: "commands"
|
||||||
|
};
|
||||||
|
|
||||||
|
const oldCwd = process.cwd();
|
||||||
|
|
||||||
|
process.cwd = jest.fn()
|
||||||
|
.mockReturnValue(`${oldCwd}/tests/_mocks`);
|
||||||
|
|
||||||
|
const help = new Help();
|
||||||
|
|
||||||
|
const result = help.GetCommandData('none');
|
||||||
|
|
||||||
|
expect(result.Exists).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue