Compare commits
33 commits
develop
...
v3.0-beta1
Author | SHA1 | Date | |
---|---|---|---|
c8edd1b4c5 | |||
de236dfd30 | |||
97633451ed | |||
f61c4c728a | |||
2cc12d91be | |||
45d871fbf7 | |||
783c3a013d | |||
598a0b5a44 | |||
68b9ed34e4 | |||
ba51cbb28c | |||
44571d735a | |||
24818bcb44 | |||
6c90307754 | |||
acedbffdad | |||
c62488aa63 | |||
4ff88d0694 | |||
07c7155027 | |||
90ef4317cc | |||
35f7210b6e | |||
e7169d960a | |||
0d3134bf45 | |||
019966f25f | |||
be329d709f | |||
ecf9c5e4fc | |||
bb433749f8 | |||
c7417cf7a5 | |||
6fb2da2b18 | |||
19065dc3e6 | |||
ee7fe3fd19 | |||
5b9aac22d3 | |||
38a5f6fb29 | |||
|
021c495769 | ||
|
9854f3c60f |
96 changed files with 12212 additions and 4008 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
|
34
.env.template
Normal file
34
.env.template
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 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_PREFIX=v!
|
||||
BOT_VER=3.0
|
||||
BOT_AUTHOR=Vylpes
|
||||
BOT_DATE=28 Nov 2021
|
||||
BOT_OWNERID=147392775707426816
|
||||
|
||||
FOLDERS_COMMANDS=src/commands
|
||||
FOLDERS_EVENTS=src/events
|
||||
|
||||
COMMANDS_DISABLED=
|
||||
COMMANDS_DISABLED_MESSAGE=This command is disabled.
|
||||
|
||||
COMMANDS_ROLE_ROLES=Notify,VotePings,ProjectUpdates
|
||||
|
||||
COMMANDS_RULES_FILE=data/rules/rules.json
|
||||
|
||||
EMBED_COLOUR=0x3050ba
|
||||
EMBED_COLOUR_ERROR=0xD52803
|
||||
|
||||
ROLES_MODERATOR=Moderator
|
||||
ROLES_MUTED=Muted
|
||||
|
||||
CHANNELS_LOGS_MESSAGE=message-logs
|
||||
CHANNELS_LOGS_MEMBER=member-logs
|
||||
CHANNELS_LOGS_MOD=mod-logs
|
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: [12.x, 14.x, 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 --coverage
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -104,3 +104,4 @@ dist
|
|||
.tern-port
|
||||
|
||||
config.json
|
||||
.DS_Store
|
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" ]
|
|
@ -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,35 +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', (image, title) => {
|
||||
// Create an embed containing the random image
|
||||
const embed = new MessageEmbed()
|
||||
.setColor(embedColor)
|
||||
.setTitle(title)
|
||||
.setImage(image)
|
||||
.setFooter('r/Rabbits');
|
||||
|
||||
// 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,54 +0,0 @@
|
|||
{
|
||||
"token": "",
|
||||
"prefix": "d!",
|
||||
"commands": [
|
||||
"commands"
|
||||
],
|
||||
"events": [
|
||||
"events"
|
||||
],
|
||||
"about": {
|
||||
"description": "Discord Bot for Vylpes' Den",
|
||||
"version": "2.1",
|
||||
"core-ver": "1.0.4",
|
||||
"author": "Vylpes",
|
||||
"date": "17-Feb-21"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -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/rules.json
Normal file
88
data/rules/rules.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"
|
||||
}
|
||||
]
|
|
@ -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
|
||||
|
4
docker-compose.yml
Normal file
4
docker-compose.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
discord:
|
||||
build: .
|
|
@ -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;
|
6
jest.config.js
Normal file
6
jest.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
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();
|
2483
package-lock.json
generated
2483
package-lock.json
generated
File diff suppressed because it is too large
Load diff
33
package.json
33
package.json
|
@ -1,29 +1,34 @@
|
|||
{
|
||||
"name": "vylbot-app",
|
||||
"version": "2.1.1",
|
||||
"description": "",
|
||||
"main": "vylbot.js",
|
||||
"version": "3.0",
|
||||
"description": "A discord bot made for Vylpes' Den",
|
||||
"main": "./dist/vylbot",
|
||||
"typings": "./dist",
|
||||
"scripts": {
|
||||
"start": "node vylbot.js",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
"build": "tsc",
|
||||
"start": "node ./dist/vylbot",
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Vylpes/vylbot-app.git"
|
||||
},
|
||||
"author": "Vylpes",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Vylpes/vylbot-app/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Vylpes/vylbot-app#readme",
|
||||
"license": "MIT",
|
||||
"bugs": "https://github.com/Vylpes/vylbot-app/issues",
|
||||
"homepage": "https://github.com/Vylpes/vylbot-app",
|
||||
"dependencies": {
|
||||
"@types/jest": "^27.0.3",
|
||||
"discord.js": "12.5.3",
|
||||
"dotenv": "^10.0.0",
|
||||
"emoji-regex": "^9.2.0",
|
||||
"random-bunny": "^1.0.0",
|
||||
"vylbot-core": "^1.0.4"
|
||||
"jest": "^27.4.5",
|
||||
"jest-mock-extended": "^2.0.4",
|
||||
"random-bunny": "^2.0.0",
|
||||
"ts-jest": "^27.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.17.0"
|
||||
"@types/node": "^16.11.10",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
|
|
37
src/Register.ts
Normal file
37
src/Register.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { CoreClient } from "./client/client";
|
||||
import About from "./commands/about";
|
||||
import Ban from "./commands/ban";
|
||||
import Clear from "./commands/clear";
|
||||
import Evaluate from "./commands/eval";
|
||||
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 Unmute from "./commands/unmute";
|
||||
import Warn from "./commands/warn";
|
||||
import MemberEvents from "./events/MemberEvents";
|
||||
import MessageEvents from "./events/MessageEvents";
|
||||
|
||||
export default class Register {
|
||||
public static RegisterCommands(client: CoreClient) {
|
||||
client.RegisterCommand("about", new About());
|
||||
client.RegisterCommand("ban", new Ban());
|
||||
client.RegisterCommand("clear", new Clear());
|
||||
client.RegisterCommand("eval", new Evaluate());
|
||||
client.RegisterCommand("help", new Help());
|
||||
client.RegisterCommand("kick", new Kick());
|
||||
client.RegisterCommand("mute", new Mute());
|
||||
client.RegisterCommand("poll", new Poll());
|
||||
client.RegisterCommand("role", new Role());
|
||||
client.RegisterCommand("rules", new Rules());
|
||||
client.RegisterCommand("unmute", new Unmute());
|
||||
client.RegisterCommand("warn", new Warn());
|
||||
}
|
||||
|
||||
public static RegisterEvents(client: CoreClient) {
|
||||
client.RegisterEvent(new MemberEvents());
|
||||
client.RegisterEvent(new MessageEvents());
|
||||
}
|
||||
}
|
67
src/client/client.ts
Normal file
67
src/client/client.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { Client } from "discord.js";
|
||||
import * as dotenv from "dotenv";
|
||||
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 _commandItems: ICommandItem[];
|
||||
private _eventItems: IEventItem[];
|
||||
|
||||
private _events: Events;
|
||||
private _util: Util;
|
||||
|
||||
public get commandItems(): ICommandItem[] {
|
||||
return this._commandItems;
|
||||
}
|
||||
|
||||
public get eventItems(): IEventItem[] {
|
||||
return this._eventItems;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
dotenv.config();
|
||||
|
||||
this._commandItems = [];
|
||||
this._eventItems = [];
|
||||
|
||||
this._events = new Events();
|
||||
this._util = new Util();
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (!process.env.BOT_TOKEN) throw "BOT_TOKEN is not defined in .env";
|
||||
if (!process.env.BOT_PREFIX) throw "BOT_PREFIX is not defined in .env";
|
||||
if (!process.env.FOLDERS_COMMANDS) throw "FOLDERS_COMMANDS is not defined in .env";
|
||||
if (!process.env.FOLDERS_EVENTS) throw "FOLDERS_EVENTS is not defined in .env";
|
||||
|
||||
super.on("message", (message) => this._events.onMessage(message, this._commandItems));
|
||||
super.on("ready", this._events.onReady);
|
||||
|
||||
super.login(process.env.BOT_TOKEN);
|
||||
|
||||
this._util.loadEvents(this, this._eventItems);
|
||||
}
|
||||
|
||||
public RegisterCommand(name: string, command: Command) {
|
||||
const item: ICommandItem = {
|
||||
Name: name,
|
||||
Command: command,
|
||||
};
|
||||
|
||||
this._commandItems.push(item);
|
||||
}
|
||||
|
||||
public RegisterEvent(event: Event) {
|
||||
const item: IEventItem = {
|
||||
Event: event,
|
||||
};
|
||||
|
||||
this._eventItems.push(item);
|
||||
}
|
||||
}
|
76
src/client/events.ts
Normal file
76
src/client/events.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { Message } from "discord.js";
|
||||
import { IBaseResponse } from "../contracts/IBaseResponse";
|
||||
import ICommandItem from "../contracts/ICommandItem";
|
||||
import { Util } from "./util";
|
||||
|
||||
export interface IEventResponse extends IBaseResponse {
|
||||
context?: {
|
||||
prefix: string;
|
||||
name: string;
|
||||
args: string[];
|
||||
message: Message;
|
||||
}
|
||||
}
|
||||
|
||||
export class Events {
|
||||
private _util: Util;
|
||||
|
||||
constructor() {
|
||||
this._util = new Util();
|
||||
}
|
||||
|
||||
// Emit when a message is sent
|
||||
// Used to check for commands
|
||||
public onMessage(message: Message, commands: ICommandItem[]): IEventResponse {
|
||||
if (!message.guild) return {
|
||||
valid: false,
|
||||
message: "Message was not sent in a guild, ignoring.",
|
||||
};
|
||||
|
||||
if (message.author.bot) return {
|
||||
valid: false,
|
||||
message: "Message was sent by a bot, ignoring.",
|
||||
};
|
||||
|
||||
const prefix = process.env.BOT_PREFIX as string;
|
||||
|
||||
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 {
|
||||
valid: false,
|
||||
message: "Command name was not found",
|
||||
};
|
||||
|
||||
const res = this._util.loadCommand(name, args, message, commands);
|
||||
|
||||
if (!res.valid) {
|
||||
return {
|
||||
valid: false,
|
||||
message: res.message,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
context: {
|
||||
prefix: prefix,
|
||||
name: name,
|
||||
args: args,
|
||||
message: message,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message: "Message was not a command, ignoring.",
|
||||
}
|
||||
}
|
||||
|
||||
// Emit when bot is logged in and ready to use
|
||||
public onReady() {
|
||||
console.log("Ready");
|
||||
}
|
||||
}
|
102
src/client/util.ts
Normal file
102
src/client/util.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Required Components
|
||||
import { Client, Message } from "discord.js";
|
||||
import { readdirSync, existsSync } from "fs";
|
||||
import { IBaseResponse } from "../contracts/IBaseResponse";
|
||||
import { Command } from "../type/command";
|
||||
import { Event } from "../type/event";
|
||||
import { ICommandContext } from "../contracts/ICommandContext";
|
||||
import ICommandItem from "../contracts/ICommandItem";
|
||||
import IEventItem from "../contracts/IEventItem";
|
||||
|
||||
export interface IUtilResponse extends IBaseResponse {
|
||||
context?: {
|
||||
name: string;
|
||||
args: string[];
|
||||
message: Message;
|
||||
}
|
||||
}
|
||||
|
||||
// Util Class
|
||||
export class Util {
|
||||
public loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]): IUtilResponse {
|
||||
if (!message.member) return {
|
||||
valid: false,
|
||||
message: "Member is not part of message",
|
||||
};
|
||||
|
||||
const disabledCommands = process.env.COMMANDS_DISABLED?.split(',');
|
||||
|
||||
if (disabledCommands?.find(x => x == name)) {
|
||||
message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled.");
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message: "Command is disabled",
|
||||
};
|
||||
}
|
||||
|
||||
const folder = process.env.FOLDERS_COMMANDS;
|
||||
|
||||
const item = commands.find(x => x.Name == name);
|
||||
|
||||
if (!item) {
|
||||
message.reply('Command not found');
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message: "Command not found"
|
||||
};
|
||||
}
|
||||
|
||||
const requiredRoles = item.Command._roles;
|
||||
|
||||
for (const i in requiredRoles) {
|
||||
if (!message.member.roles.cache.find(role => role.name == requiredRoles[i])) {
|
||||
message.reply(`You require the \`${requiredRoles[i]}\` role to run this command`);
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message: `You require the \`${requiredRoles[i]}\` role to run this command`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: name,
|
||||
args: args,
|
||||
message: message
|
||||
};
|
||||
|
||||
item.Command.execute(context);
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
context: context
|
||||
}
|
||||
}
|
||||
|
||||
// Load the events
|
||||
loadEvents(client: Client, events: IEventItem[]): IUtilResponse {
|
||||
const folder = process.env.FOLDERS_EVENTS;
|
||||
|
||||
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('message', e.Event.message);
|
||||
client.on('messageDelete', e.Event.messageDelete);
|
||||
client.on('messageUpdate', e.Event.messageUpdate);
|
||||
client.on('ready', e.Event.ready);
|
||||
});
|
||||
|
||||
return {
|
||||
valid: true
|
||||
}
|
||||
}
|
||||
}
|
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 = [
|
||||
process.env.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?.member(targetUser);
|
||||
|
||||
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*"}` });
|
||||
|
||||
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 = [
|
||||
process.env.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]
|
||||
};
|
||||
}
|
||||
}
|
47
src/commands/eval.ts
Normal file
47
src/commands/eval.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
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 Evaluate extends Command {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
super._category = "Owner";
|
||||
}
|
||||
|
||||
public override execute(context: ICommandContext): ICommandReturnContext {
|
||||
if (context.message.author.id != process.env.BOT_OWNERID) {
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: []
|
||||
};
|
||||
}
|
||||
|
||||
const stmt = context.args;
|
||||
|
||||
console.log(`Eval Statement: ${stmt.join(" ")}`);
|
||||
|
||||
try {
|
||||
const result = eval(stmt.join(" "));
|
||||
|
||||
const embed = new PublicEmbed(context, "", result);
|
||||
embed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
catch (err: any) {
|
||||
const errorEmbed = new ErrorEmbed(context, err);
|
||||
errorEmbed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [errorEmbed]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
122
src/commands/help.ts
Normal file
122
src/commands/help.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
import { existsSync, readdirSync } from "fs";
|
||||
import { ICommandContext } from "../contracts/ICommandContext";
|
||||
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
|
||||
import PublicEmbed from "../helpers/embeds/PublicEmbed";
|
||||
import StringTools from "../helpers/StringTools";
|
||||
import ICommandReturnContext from "../contracts/ICommandReturnContext";
|
||||
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): ICommandReturnContext {
|
||||
if (context.args.length == 0) {
|
||||
return this.SendAll(context);
|
||||
} else {
|
||||
return this.SendSingle(context);
|
||||
}
|
||||
}
|
||||
|
||||
public SendAll(context: ICommandContext): ICommandReturnContext {
|
||||
const allCommands = this.GetAllCommandData();
|
||||
const cateogries = [...new Set(allCommands.map(x => x.Category!))];;
|
||||
|
||||
const embed = new PublicEmbed(context, "Commands", "");
|
||||
|
||||
cateogries.forEach(category => {
|
||||
let filtered = allCommands.filter(x => x.Category == category);
|
||||
|
||||
embed.addField(StringTools.Capitalise(category), filtered.flatMap(x => x.Name).join(", "));
|
||||
});
|
||||
|
||||
embed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [ embed ]
|
||||
};
|
||||
}
|
||||
|
||||
public SendSingle(context: ICommandContext): ICommandReturnContext {
|
||||
const command = this.GetCommandData(context.args[0]);
|
||||
|
||||
if (!command.Exists) {
|
||||
const errorEmbed = new ErrorEmbed(context, "Command does not exist");
|
||||
errorEmbed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [ errorEmbed ]
|
||||
};
|
||||
}
|
||||
|
||||
const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name!), "");
|
||||
embed.addField("Category", StringTools.Capitalise(command.Category!));
|
||||
embed.addField("Required Roles", StringTools.Capitalise(command.Roles!.join(", ")) || "*none*");
|
||||
|
||||
embed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [ embed ]
|
||||
};
|
||||
}
|
||||
|
||||
public GetAllCommandData(): ICommandData[] {
|
||||
const result: ICommandData[] = [];
|
||||
|
||||
const folder = process.env.FOLDERS_COMMANDS!;
|
||||
|
||||
const contents = readdirSync(`${process.cwd()}/${folder}`);
|
||||
|
||||
contents.forEach(name => {
|
||||
const file = require(`${process.cwd()}/${folder}/${name}`).default;
|
||||
const command = new file() as Command;
|
||||
|
||||
const data: ICommandData = {
|
||||
Exists: true,
|
||||
Name: name.replace(".ts", ""),
|
||||
Category: command._category || "none",
|
||||
Roles: command._roles,
|
||||
};
|
||||
|
||||
result.push(data);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public GetCommandData(name: string): ICommandData {
|
||||
const folder = process.env.FOLDERS_COMMANDS!;
|
||||
const path = `${process.cwd()}/${folder}/${name}.ts`;
|
||||
|
||||
if (!existsSync(path)) {
|
||||
return {
|
||||
Exists: false
|
||||
};
|
||||
}
|
||||
|
||||
const file = require(path).default;
|
||||
const command = new file() as Command;
|
||||
|
||||
const data: ICommandData = {
|
||||
Exists: true,
|
||||
Name: name,
|
||||
Category: command._category || "none",
|
||||
Roles: command._roles
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
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 = [
|
||||
process.env.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?.member(targetUser);
|
||||
|
||||
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*"}`);
|
||||
|
||||
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 = [
|
||||
process.env.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?.member(targetUser);
|
||||
|
||||
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*"}`);
|
||||
|
||||
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(embed);
|
||||
|
||||
description.forEach(async (value, index) => {
|
||||
await message.react(reactionEmojis[index]);
|
||||
});
|
||||
|
||||
if (context.message.deletable) {
|
||||
await context.message.delete({ reason: "Poll command" });
|
||||
}
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
}
|
99
src/commands/role.ts
Normal file
99
src/commands/role.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
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";
|
||||
|
||||
export default class Role extends Command {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
super._category = "General";
|
||||
}
|
||||
|
||||
public override async execute(context: ICommandContext) {
|
||||
const roles = process.env.COMMANDS_ROLE_ROLES!.split(',');
|
||||
|
||||
if (context.args.length == 0) {
|
||||
this.SendRolesList(context, roles);
|
||||
} else {
|
||||
await this.ToggleRole(context, roles);
|
||||
}
|
||||
}
|
||||
|
||||
public SendRolesList(context: ICommandContext, roles: String[]): ICommandReturnContext {
|
||||
const description = `Do ${process.env.BOT_PREFIX}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[0];
|
||||
|
||||
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]
|
||||
};
|
||||
}
|
||||
}
|
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 = [
|
||||
process.env.ROLES_MODERATOR!
|
||||
];
|
||||
}
|
||||
|
||||
public override execute(context: ICommandContext): ICommandReturnContext {
|
||||
if (!existsSync(`${process.cwd()}/${process.env.COMMANDS_RULES_FILE!}`)) {
|
||||
const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist");
|
||||
errorEmbed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [errorEmbed]
|
||||
};
|
||||
}
|
||||
|
||||
const rulesFile = readFileSync(`${process.cwd()}/${process.env.COMMANDS_RULES_FILE}`).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
|
||||
};
|
||||
}
|
||||
}
|
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 = [
|
||||
process.env.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?.member(targetUser);
|
||||
|
||||
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*"}`);
|
||||
|
||||
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 = [
|
||||
process.env.ROLES_MODERATOR!
|
||||
];
|
||||
}
|
||||
|
||||
public override execute(context: ICommandContext): 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?.member(user);
|
||||
|
||||
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);
|
||||
|
||||
logEmbed.SendToModLogsChannel();
|
||||
publicEmbed.SendToCurrentChannel();
|
||||
|
||||
return {
|
||||
commandContext: context,
|
||||
embeds: [logEmbed, publicEmbed]
|
||||
};
|
||||
}
|
||||
}
|
5
src/constants/ErrorMessages.ts
Normal file
5
src/constants/ErrorMessages.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
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";
|
||||
}
|
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;
|
||||
}
|
6
src/contracts/ICommandItem.ts
Normal file
6
src/contracts/ICommandItem.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { Command } from "../type/command";
|
||||
|
||||
export default interface ICommandItem {
|
||||
Name: string,
|
||||
Command: Command,
|
||||
}
|
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[]
|
||||
}
|
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 guildMemberAdd(member: GuildMember): IEventReturnContext {
|
||||
const embed = new EventEmbed(member.guild, "Member Joined");
|
||||
embed.AddUser("User", member.user, true);
|
||||
embed.addField("Created", member.user.createdAt);
|
||||
embed.setFooter(`Id: ${member.user.id}`);
|
||||
|
||||
embed.SendToMemberLogsChannel();
|
||||
|
||||
return {
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
|
||||
public override guildMemberRemove(member: GuildMember): IEventReturnContext {
|
||||
const embed = new EventEmbed(member.guild, "Member Left");
|
||||
embed.AddUser("User", member.user, true);
|
||||
embed.addField("Joined", member.joinedAt);
|
||||
embed.setFooter(`Id: ${member.user.id}`);
|
||||
|
||||
embed.SendToMemberLogsChannel();
|
||||
|
||||
return {
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
|
||||
public override guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember): IEventReturnContext {
|
||||
const handler = new GuildMemberUpdate(oldMember, newMember);
|
||||
|
||||
if (oldMember.nickname != newMember.nickname) { // Nickname change
|
||||
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 NicknameChanged(): 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(`Id: ${this.newMember.user.id}`);
|
||||
|
||||
embed.SendToMemberLogsChannel();
|
||||
|
||||
return {
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
}
|
71
src/events/MessageEvents.ts
Normal file
71
src/events/MessageEvents.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { Event } from "../type/event";
|
||||
import { Message } from "discord.js";
|
||||
import EventEmbed from "../helpers/embeds/EventEmbed";
|
||||
import IEventReturnContext from "../contracts/IEventReturnContext";
|
||||
|
||||
export default class MessageEvents extends Event {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public override messageDelete(message: Message): 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, true);
|
||||
embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``);
|
||||
|
||||
if (message.attachments.size > 0) {
|
||||
embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``);
|
||||
}
|
||||
|
||||
embed.SendToMessageLogsChannel();
|
||||
|
||||
return {
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
|
||||
public override messageUpdate(oldMessage: Message, newMessage: Message): 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, true);
|
||||
embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``);
|
||||
embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``);
|
||||
|
||||
embed.SendToMessageLogsChannel();
|
||||
|
||||
return {
|
||||
embeds: [embed]
|
||||
};
|
||||
}
|
||||
}
|
15
src/helpers/StringTools.ts
Normal file
15
src/helpers/StringTools.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
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(" ");
|
||||
}
|
||||
}
|
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(process.env.EMBED_COLOUR_ERROR!);
|
||||
super.setDescription(message);
|
||||
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public SendToCurrentChannel() {
|
||||
this.context.message.channel.send(this);
|
||||
}
|
||||
}
|
52
src/helpers/embeds/EventEmbed.ts
Normal file
52
src/helpers/embeds/EventEmbed.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { MessageEmbed, TextChannel, User, Guild } from "discord.js";
|
||||
|
||||
export default class EventEmbed extends MessageEmbed {
|
||||
public guild: Guild;
|
||||
|
||||
constructor(guild: Guild, title: string) {
|
||||
super();
|
||||
|
||||
super.setColor(process.env.EMBED_COLOUR!);
|
||||
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(this);
|
||||
}
|
||||
|
||||
public SendToMessageLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MESSAGE!)
|
||||
}
|
||||
|
||||
public SendToMemberLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MEMBER!)
|
||||
}
|
||||
|
||||
public SendToModLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MOD!)
|
||||
}
|
||||
}
|
60
src/helpers/embeds/LogEmbed.ts
Normal file
60
src/helpers/embeds/LogEmbed.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { MessageEmbed, TextChannel, User } from "discord.js";
|
||||
import ErrorMessages from "../../constants/ErrorMessages";
|
||||
import { ICommandContext } from "../../contracts/ICommandContext";
|
||||
import ErrorEmbed from "./ErrorEmbed";
|
||||
|
||||
export default class LogEmbed extends MessageEmbed {
|
||||
public context: ICommandContext;
|
||||
|
||||
constructor(context: ICommandContext, title: string) {
|
||||
super();
|
||||
|
||||
super.setColor(process.env.EMBED_COLOUR!);
|
||||
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(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(this);
|
||||
}
|
||||
|
||||
public SendToMessageLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MESSAGE!)
|
||||
}
|
||||
|
||||
public SendToMemberLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MEMBER!)
|
||||
}
|
||||
|
||||
public SendToModLogsChannel() {
|
||||
this.SendToChannel(process.env.CHANNELS_LOGS_MOD!)
|
||||
}
|
||||
}
|
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(process.env.EMBED_COLOUR!);
|
||||
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(this);
|
||||
}
|
||||
}
|
15
src/type/command.ts
Normal file
15
src/type/command.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { ICommandContext } from "../contracts/ICommandContext";
|
||||
|
||||
export class Command {
|
||||
public _roles: string[];
|
||||
|
||||
public _category?: string;
|
||||
|
||||
constructor() {
|
||||
this._roles = [];
|
||||
}
|
||||
|
||||
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, User, GuildMember, Message, PartialDMChannel, PartialGuildMember, PartialMessage } from "discord.js";
|
||||
|
||||
export class Event {
|
||||
public channelCreate(channel: Channel) {
|
||||
|
||||
}
|
||||
|
||||
public channelDelete(channel: Channel | PartialDMChannel) {
|
||||
|
||||
}
|
||||
|
||||
public channelUpdate(oldChannel: Channel, newChannel: Channel) {
|
||||
|
||||
}
|
||||
|
||||
public guildBanAdd(guild: Guild, user: User) {
|
||||
|
||||
}
|
||||
|
||||
public guildBanRemove(guild: Guild, user: User) {
|
||||
|
||||
}
|
||||
|
||||
public guildCreate(guild: Guild) {
|
||||
|
||||
}
|
||||
|
||||
public guildMemberAdd(member: GuildMember) {
|
||||
|
||||
}
|
||||
|
||||
public guildMemberRemove(member: GuildMember | PartialGuildMember) {
|
||||
|
||||
}
|
||||
|
||||
public guildMemberUpdate(oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) {
|
||||
|
||||
}
|
||||
|
||||
public message(message: Message) {
|
||||
|
||||
}
|
||||
|
||||
public messageDelete(message: Message | PartialMessage) {
|
||||
|
||||
}
|
||||
|
||||
public messageUpdate(oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage) {
|
||||
|
||||
}
|
||||
|
||||
public ready() {
|
||||
|
||||
}
|
||||
}
|
30
src/vylbot.ts
Normal file
30
src/vylbot.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { CoreClient } from "./client/client";
|
||||
import * as dotenv from "dotenv";
|
||||
import Register from "./Register";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const requiredConfigs = [
|
||||
"EMBED_COLOUR",
|
||||
"EMBED_COLOUR_ERROR",
|
||||
"ROLES_MODERATOR",
|
||||
"ROLES_MUTED",
|
||||
"CHANNELS_LOGS_MESSAGE",
|
||||
"CHANNELS_LOGS_MEMBER",
|
||||
"CHANNELS_LOGS_MOD",
|
||||
"COMMANDS_ROLE_ROLES",
|
||||
"COMMANDS_RULES_FILE"
|
||||
];
|
||||
|
||||
requiredConfigs.forEach(config => {
|
||||
if (!process.env[config]) {
|
||||
throw `${config} is required in .env`;
|
||||
}
|
||||
});
|
||||
|
||||
const client = new CoreClient();
|
||||
|
||||
Register.RegisterCommands(client);
|
||||
Register.RegisterEvents(client);
|
||||
|
||||
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"
|
||||
}
|
||||
]
|
167
tests/client/client.test.ts
Normal file
167
tests/client/client.test.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
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 { mock } from "jest-mock-extended";
|
||||
import { Event } from "../../src/type/event";
|
||||
|
||||
jest.mock("discord.js");
|
||||
jest.mock("dotenv");
|
||||
jest.mock("../../src/client/events");
|
||||
jest.mock("../../src/client/util");
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Constructor_ExpectSuccessfulInitialisation', () => {
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(coreClient).toBeInstanceOf(Client);
|
||||
expect(dotenv.config).toBeCalledTimes(1);
|
||||
expect(Events).toBeCalledTimes(1);
|
||||
expect(Util).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Start', () => {
|
||||
test('Given Env Is Valid, Expect Successful Start', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).not.toThrow();
|
||||
expect(coreClient.on).toBeCalledWith("message", expect.any(Function));
|
||||
expect(coreClient.on).toBeCalledWith("ready", expect.any(Function));
|
||||
});
|
||||
|
||||
test('Given BOT_TOKEN Is Null, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("BOT_TOKEN is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given BOT_TOKEN Is Empty, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: '',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("BOT_TOKEN is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given BOT_PREFIX Is Null, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("BOT_PREFIX is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given BOT_PREFIX Is Empty, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("BOT_PREFIX is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given FOLDERS_COMMANDS Is Null, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("FOLDERS_COMMANDS is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given FOLDERS_COMMANDS Is Empty, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: '',
|
||||
FOLDERS_EVENTS: 'events',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("FOLDERS_COMMANDS is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given FOLDERS_EVENTS Is Null, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("FOLDERS_EVENTS is not defined in .env");
|
||||
});
|
||||
|
||||
test('Given FOLDERS_EVENTS Is Empty, Expect Failure', () => {
|
||||
process.env = {
|
||||
BOT_TOKEN: 'TOKEN',
|
||||
BOT_PREFIX: '!',
|
||||
FOLDERS_COMMANDS: 'commands',
|
||||
FOLDERS_EVENTS: '',
|
||||
}
|
||||
|
||||
const coreClient = new CoreClient();
|
||||
|
||||
expect(() => coreClient.start()).toThrow("FOLDERS_EVENTS is not defined in .env");
|
||||
});
|
||||
});
|
||||
|
||||
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', () => {
|
||||
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 = util.loadCommand("test", [ "first" ], message, commands);
|
||||
|
||||
expect(result.valid).toBeTruthy();
|
||||
expect(cmd.execute).toBeCalled();
|
||||
});
|
||||
|
||||
test('Given Member Is Null, Expect Failed Result', () => {
|
||||
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 = 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', () => {
|
||||
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 = util.loadCommand("test", [ "first" ], message, commands);
|
||||
|
||||
expect(result.valid).toBeTruthy();
|
||||
expect(cmd.execute).toBeCalled();
|
||||
});
|
||||
|
||||
test('Given User Does Not Have Role, Expect Failed Result', () => {
|
||||
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 = 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', () => {
|
||||
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 = 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', () => {
|
||||
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 = 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', () => {
|
||||
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 = 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', () => {
|
||||
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 = 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();
|
||||
});
|
||||
});
|
549
tests/commands/kick.test.ts
Normal file
549
tests/commands/kick.test.ts
Normal file
|
@ -0,0 +1,549 @@
|
|||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { GuildMember, Message, TextChannel, User } from "discord.js";
|
||||
import Kick from "../../src/commands/kick";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: "Moderator"
|
||||
};
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
expect(kick._category).toBe('Moderation');
|
||||
expect(kick._roles.length).toBe(1);
|
||||
expect(kick._roles[0]).toBe('Moderator');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given user has permission, expect user to be kicked', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: true,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER", "Test", "Reason"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).toBeCalledTimes(1);
|
||||
expect(member.kick).toBeCalledWith('Moderator: AUTHORTAG, Reason: Test Reason');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Kicked');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedFieldUser = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedFieldUser.name).toBe('User');
|
||||
expect(logEmbedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedFieldModerator = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedFieldModerator.name).toBe('Moderator');
|
||||
expect(logEmbedFieldModerator.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('Test Reason');
|
||||
});
|
||||
|
||||
test('Given moderator did not supply a reason, expect default reason to be added', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: true,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).toBeCalledTimes(1);
|
||||
expect(member.kick).toBeCalledWith('Moderator: AUTHORTAG, Reason: *none*');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Kicked');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedFieldUser = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedFieldUser.name).toBe('User');
|
||||
expect(logEmbedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedFieldModerator = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedFieldModerator.name).toBe('Moderator');
|
||||
expect(logEmbedFieldModerator.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('*none*');
|
||||
});
|
||||
|
||||
test('Given target user is not found, expect user does not exist error', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: true,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER", "Test", "Reason"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).not.toBeCalled();
|
||||
expect(member.kick).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe(null);
|
||||
expect(embed.description).toBe('User does not exist');
|
||||
});
|
||||
|
||||
test('Given target member is not found, expect user is not in this server error', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: true,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
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 messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER", "Test", "Reason"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).not.toBeCalled();
|
||||
expect(member.kick).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe(null);
|
||||
expect(embed.description).toBe('User is not in this server');
|
||||
});
|
||||
|
||||
test('Given guild is not available, expect to stop', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: true,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER", "Test", "Reason"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
expect(logChannel.send).not.toBeCalled();
|
||||
expect(member.kick).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given client can not kick member, expect error', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
kickable: false,
|
||||
kick: jest.fn()
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
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: "kick",
|
||||
args: ["USER", "Test", "Reason"],
|
||||
message: message
|
||||
}
|
||||
|
||||
const kick = new Kick();
|
||||
|
||||
const result = await kick.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).not.toBeCalled();
|
||||
expect(member.kick).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe(null);
|
||||
expect(embed.description).toBe('Unable to do this action, am I missing permissions?');
|
||||
});
|
||||
});
|
815
tests/commands/mute.test.ts
Normal file
815
tests/commands/mute.test.ts
Normal file
|
@ -0,0 +1,815 @@
|
|||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { GuildMember, Message, Role, TextChannel, User } from "discord.js";
|
||||
import Mute from "../../src/commands/mute";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: 'Moderator'
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
expect(mute._category).toBe("Moderation");
|
||||
expect(mute._roles.length).toBe(1);
|
||||
expect(mute._roles[0]).toBe('Moderator');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given user has permission, expect user to be given muted role', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: Test Reason');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Muted');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedUserField = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedUserField.name).toBe('User');
|
||||
expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedUserField.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedModeratorField = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedModeratorField.name).toBe('Moderator');
|
||||
expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('Test Reason');
|
||||
|
||||
// Public Embed
|
||||
const publicEmbed = result.embeds[1];
|
||||
|
||||
expect(publicEmbed.title).toBe('');
|
||||
expect(publicEmbed.description).toBe('[object Object] has been muted');
|
||||
});
|
||||
|
||||
test('Given moderator did not supply a reason, expect default reason used', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(member.roles.add).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: *none*');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Muted');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedUserField = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedUserField.name).toBe('User');
|
||||
expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedUserField.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedModeratorField = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedModeratorField.name).toBe('Moderator');
|
||||
expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('*none*');
|
||||
|
||||
// Public Embed
|
||||
const publicEmbed = result.embeds[1];
|
||||
|
||||
expect(publicEmbed.title).toBe('');
|
||||
expect(publicEmbed.description).toBe('[object Object] has been muted');
|
||||
});
|
||||
|
||||
test('Given user did not mention a user, expect user not to exist', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).not.toBeCalled();
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('User does not exist');
|
||||
});
|
||||
|
||||
test('Given member can not be found from user, expect user to not be in server', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('User is not in this server');
|
||||
});
|
||||
|
||||
test('Given guild is unavailable, expect execution to stop', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: false,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given client can not manage user, expect insufficient permissions', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: false,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Unable to do this action, am I missing permissions?');
|
||||
});
|
||||
|
||||
test('Given muted role can not be found, expect role not found', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
add: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Mute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Unable to find role');
|
||||
});
|
||||
});
|
262
tests/commands/poll.test.ts
Normal file
262
tests/commands/poll.test.ts
Normal file
|
@ -0,0 +1,262 @@
|
|||
import { Message, MessageEmbed } from "discord.js";
|
||||
import Poll from "../../src/commands/poll";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
const poll = new Poll();
|
||||
|
||||
expect(poll._category).toBe('General');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given input is valid, expect poll to be generated', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: true
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: ['Test', 'title;', 'one;', 'two'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).toBeCalledTimes(1);
|
||||
expect(returnMessageReact).toBeCalledTimes(2);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Test title');
|
||||
expect(embed.description).toBe(':one: one\n:two: two');
|
||||
});
|
||||
|
||||
test('Given message is not deletable by client, expect it not to attempt deletion', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: false
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: ['Test', 'title;', 'one;', 'two'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).not.toBeCalled();
|
||||
expect(returnMessageReact).toBeCalledTimes(2);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Test title');
|
||||
expect(embed.description).toBe(':one: one\n:two: two');
|
||||
});
|
||||
|
||||
test('Given no arguments, expect error embed', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: true
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).not.toBeCalled();
|
||||
expect(returnMessageReact).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options');
|
||||
});
|
||||
|
||||
test('Given only 1 option, expect error embed', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: true
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: ['Test', 'title;', 'one'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).not.toBeCalled();
|
||||
expect(returnMessageReact).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options');
|
||||
});
|
||||
|
||||
test('Given 9 options, expect poll to be generated', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: true
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: ['Test', 'title;', 'one;', 'two;', 'three;', 'four;', 'five;', 'six;', 'seven;', 'eight;', 'nine'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).toBeCalledTimes(1);
|
||||
expect(returnMessageReact).toBeCalledTimes(9);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Test title');
|
||||
expect(embed.description).toBe(':one: one\n:two: two\n:three: three\n:four: four\n:five: five\n:six: six\n:seven: seven\n:eight: eight\n:nine: nine');
|
||||
});
|
||||
|
||||
test('Given 10 options, expect error embed', async () => {
|
||||
const returnMessageReact = jest.fn();
|
||||
|
||||
const returnMessage = {
|
||||
react: returnMessageReact
|
||||
} as unknown as Message;
|
||||
|
||||
const messageChannelSend = jest.fn()
|
||||
.mockReturnValue(returnMessage);
|
||||
const messageDelete = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
delete: messageDelete,
|
||||
deletable: true
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'poll',
|
||||
args: ['Test', 'title;', 'one;', 'two;', 'three;', 'four;', 'five;', 'six;', 'seven;', 'eight;', 'nine;', 'ten'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const poll = new Poll();
|
||||
|
||||
const result = await poll.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(messageDelete).not.toBeCalled();
|
||||
expect(returnMessageReact).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options');
|
||||
});
|
||||
});
|
411
tests/commands/role.test.ts
Normal file
411
tests/commands/role.test.ts
Normal file
|
@ -0,0 +1,411 @@
|
|||
import { GuildMemberRoleManager, Message, Role as DiscordRole } from "discord.js";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import Role from "../../src/commands/role";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties are set', () => {
|
||||
const role = new Role();
|
||||
|
||||
expect(role._category).toBe("General");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given no arguments were given, expect SendRolesList to be executed', async () => {
|
||||
process.env = {
|
||||
COMMANDS_ROLE_ROLES: 'One,Two'
|
||||
};
|
||||
|
||||
const message = {} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.SendRolesList = jest.fn();
|
||||
role.ToggleRole = jest.fn();
|
||||
|
||||
await role.execute(context);
|
||||
|
||||
expect(role.SendRolesList).toBeCalledWith(context, ['One', 'Two']);
|
||||
expect(role.ToggleRole).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('Given an argument was given, expect ToggleRole to be executed', async () => {
|
||||
process.env = {
|
||||
COMMANDS_ROLE_ROLES: 'One,Two'
|
||||
};
|
||||
|
||||
const message = {} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.SendRolesList = jest.fn();
|
||||
role.ToggleRole = jest.fn();
|
||||
|
||||
await role.execute(context);
|
||||
|
||||
expect(role.SendRolesList).not.toBeCalled();
|
||||
expect(role.ToggleRole).toBeCalledWith(context, ['One', 'Two']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendRolesList', () => {
|
||||
test('Expect embed with roles to be sent to the current channel', () => {
|
||||
process.env = {
|
||||
BOT_PREFIX: '!'
|
||||
};
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const roles = ['One', 'Two'];
|
||||
|
||||
const role = new Role();
|
||||
|
||||
const result = role.SendRolesList(context, roles);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Roles');
|
||||
expect(embed.description).toBe('Do !role <role> to get the role!\nOne\nTwo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ToggleRole', () => {
|
||||
test('Given role name is a valid role AND user does not have the role, expect role to be added', async () => {
|
||||
const discordRole = {} as unknown as DiscordRole;
|
||||
|
||||
const messageMemberRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(discordRole);
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageMemberRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const roles = ['One', 'Two'];
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.AddRole = jest.fn();
|
||||
role.RemoveRole = jest.fn();
|
||||
|
||||
const result = await role.ToggleRole(context, roles);
|
||||
|
||||
expect(messageMemberRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
expect(role.AddRole).toBeCalledWith(context, discordRole);
|
||||
expect(role.RemoveRole).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given role name is a valid role AND user has the role, expect role to be removed', async () => {
|
||||
const discordRole = {} as unknown as DiscordRole;
|
||||
|
||||
const messageMemberRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(discordRole);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(discordRole);
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageMemberRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const roles = ['One', 'Two'];
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.AddRole = jest.fn();
|
||||
role.RemoveRole = jest.fn();
|
||||
|
||||
const result = await role.ToggleRole(context, roles);
|
||||
|
||||
expect(messageMemberRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
expect(role.AddRole).not.toBeCalled();
|
||||
expect(role.RemoveRole).toBeCalledWith(context, discordRole);
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given role requested is not in the roles array, expect role not assignable error', async () => {
|
||||
const discordRole = {} as unknown as DiscordRole;
|
||||
|
||||
const messageMemberRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(discordRole);
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageMemberRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['Three'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const roles = ['One', 'Two'];
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.AddRole = jest.fn();
|
||||
role.RemoveRole = jest.fn();
|
||||
|
||||
const result = await role.ToggleRole(context, roles);
|
||||
|
||||
expect(messageMemberRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(role.AddRole).not.toBeCalled();
|
||||
expect(role.RemoveRole).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe("This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters");
|
||||
});
|
||||
|
||||
test('Given the role is not in the guild, expect error', async () => {
|
||||
const discordRole = {} as unknown as DiscordRole;
|
||||
|
||||
const messageMemberRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageMemberRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const roles = ['One', 'Two'];
|
||||
|
||||
const role = new Role();
|
||||
|
||||
role.AddRole = jest.fn();
|
||||
role.RemoveRole = jest.fn();
|
||||
|
||||
const result = await role.ToggleRole(context, roles);
|
||||
|
||||
expect(messageMemberRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(role.AddRole).not.toBeCalled();
|
||||
expect(role.RemoveRole).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe("The current server doesn't have this role. Please contact the server's moderators");
|
||||
});
|
||||
});
|
||||
|
||||
describe('AddRole', () => {
|
||||
test('Expect role to be added to user', async () => {
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const guildMemberRoleManager = mock<GuildMemberRoleManager>();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: guildMemberRoleManager
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const discordRole = {
|
||||
name: 'One'
|
||||
} as unknown as DiscordRole;
|
||||
|
||||
const role = new Role();
|
||||
|
||||
const result = await role.AddRole(context, discordRole);
|
||||
|
||||
expect(guildMemberRoleManager.add).toBeCalledWith(discordRole, "Toggled with role command");
|
||||
expect(messageChannelSend).toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('');
|
||||
expect(embed.description).toBe('Gave role: One');
|
||||
});
|
||||
});
|
||||
|
||||
describe('RemoveRole', () => {
|
||||
test('Expect role to be removed from user', async () => {
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const guildMemberRoleManager = mock<GuildMemberRoleManager>();
|
||||
|
||||
const message = {
|
||||
member: {
|
||||
roles: guildMemberRoleManager
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'role',
|
||||
args: ['One'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const discordRole = {
|
||||
name: 'One'
|
||||
} as unknown as DiscordRole;
|
||||
|
||||
const role = new Role();
|
||||
|
||||
const result = await role.RemoveRole(context, discordRole);
|
||||
|
||||
expect(guildMemberRoleManager.remove).toBeCalledWith(discordRole, "Toggled with role command");
|
||||
expect(messageChannelSend).toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('');
|
||||
expect(embed.description).toBe('Removed role: One');
|
||||
});
|
||||
});
|
106
tests/commands/rules.test.ts
Normal file
106
tests/commands/rules.test.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
import { Message } from "discord.js";
|
||||
import Rules from "../../src/commands/rules";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
const oldCwd = process.cwd();
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: "Moderator"
|
||||
};
|
||||
|
||||
const rules = new Rules();
|
||||
|
||||
expect(rules._category).toBe("Admin");
|
||||
expect(rules._roles.length).toBe(1);
|
||||
expect(rules._roles[0]).toBe("Moderator");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given rules exist, expect rules to be sent to current channel', () => {
|
||||
process.env = {
|
||||
COMMANDS_RULES_FILE: 'rules/rules.json'
|
||||
};
|
||||
|
||||
process.cwd = jest.fn()
|
||||
.mockReturnValue(`${oldCwd}/tests/_mocks`);
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'rules',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const rules = new Rules();
|
||||
|
||||
const result = rules.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(2);
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Header Embed
|
||||
const embedHeader = result.embeds[0];
|
||||
|
||||
expect(embedHeader.title).toBe("");
|
||||
expect(embedHeader.description).toBe("");
|
||||
expect(embedHeader.image?.url).toBe("IMAGEURL");
|
||||
expect(embedHeader.footer?.text).toBe("");
|
||||
|
||||
// Main Embed
|
||||
const embedMain = result.embeds[1];
|
||||
|
||||
expect(embedMain.title).toBe("TITLE 1");
|
||||
expect(embedMain.description).toBe("DESCRIPTION 1A\nDESCRIPTION 1B");
|
||||
expect(embedMain.image?.url).toBe("");
|
||||
expect(embedMain.footer?.text).toBe("FOOTER 1");
|
||||
});
|
||||
|
||||
test('Given rules file does not exist, expect does not exist error', () => {
|
||||
process.env = {
|
||||
COMMANDS_RULES_FILE: 'rules/none.json'
|
||||
};
|
||||
|
||||
process.cwd = jest.fn()
|
||||
.mockReturnValue(`${oldCwd}/tests/_mocks`);
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'rules',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const rules = new Rules();
|
||||
|
||||
const result = rules.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe("Rules file doesn't exist");
|
||||
});
|
||||
});
|
813
tests/commands/unmute.test.ts
Normal file
813
tests/commands/unmute.test.ts
Normal file
|
@ -0,0 +1,813 @@
|
|||
import { GuildMember, Message, Role, TextChannel, User } from "discord.js";
|
||||
import Unmute from "../../src/commands/unmute";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: 'Moderator'
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
expect(mute._category).toBe("Moderation");
|
||||
expect(mute._roles.length).toBe(1);
|
||||
expect(mute._roles[0]).toBe('Moderator');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given user has permission, expect user to be given muted role', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(member.roles.remove).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: Test Reason');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Unmuted');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedUserField = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedUserField.name).toBe('User');
|
||||
expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedUserField.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedModeratorField = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedModeratorField.name).toBe('Moderator');
|
||||
expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('Test Reason');
|
||||
|
||||
// Public Embed
|
||||
const publicEmbed = result.embeds[1];
|
||||
|
||||
expect(publicEmbed.title).toBe('');
|
||||
expect(publicEmbed.description).toBe('[object Object] has been unmuted');
|
||||
});
|
||||
|
||||
test('Given moderator did not supply a reason, expect default reason is used', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(member.roles.remove).toBeCalledWith(role, 'Moderator: AUTHORTAG, Reason: *none*');
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
// Log Embed
|
||||
const logEmbed = result.embeds[0];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Unmuted');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
|
||||
// Log Embed -> User Field
|
||||
const logEmbedUserField = logEmbed.fields[0];
|
||||
|
||||
expect(logEmbedUserField.name).toBe('User');
|
||||
expect(logEmbedUserField.value).toBe('[object Object] `USERTAG`');
|
||||
expect(logEmbedUserField.inline).toBeTruthy();
|
||||
|
||||
// Log Embed -> Moderator Field
|
||||
const logEmbedModeratorField = logEmbed.fields[1];
|
||||
|
||||
expect(logEmbedModeratorField.name).toBe('Moderator');
|
||||
expect(logEmbedModeratorField.value).toBe('[object Object] `AUTHORTAG`');
|
||||
|
||||
// Log Embed -> Reason Field
|
||||
const logEmbedFieldReason = logEmbed.fields[2];
|
||||
|
||||
expect(logEmbedFieldReason.name).toBe('Reason');
|
||||
expect(logEmbedFieldReason.value).toBe('*none*');
|
||||
|
||||
// Public Embed
|
||||
const publicEmbed = result.embeds[1];
|
||||
|
||||
expect(publicEmbed.title).toBe('');
|
||||
expect(publicEmbed.description).toBe('[object Object] has been unmuted');
|
||||
});
|
||||
|
||||
test('Given user did not mention a user, expect user not to exist', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).not.toBeCalled();
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('User does not exist');
|
||||
});
|
||||
|
||||
test('Given member can not be found from user, expect user to not be in server', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('User is not in this server');
|
||||
});
|
||||
|
||||
test('Given guild is unavailable, expect execution to stop', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: false,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given client can not manage user, expect insufficient permissions', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: false,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockImplementation((callback): Role | undefined => {
|
||||
const result = callback(role);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return role;
|
||||
});
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).not.toBeCalled();
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Unable to do this action, am I missing permissions?');
|
||||
});
|
||||
|
||||
test('Given muted role can not be found, expect role not found', async () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs',
|
||||
ROLES_MUTED: 'Muted'
|
||||
};
|
||||
|
||||
const user = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageAuthor = {
|
||||
tag: 'AUTHORTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const member = {
|
||||
manageable: true,
|
||||
roles: {
|
||||
remove: jest.fn()
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const role = {
|
||||
name: 'Muted'
|
||||
} as unknown as Role;
|
||||
|
||||
const logChannel = {
|
||||
name: 'mod-logs',
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const messageMentionsUsersFirst = jest.fn()
|
||||
.mockReturnValue(user);
|
||||
const messageGuildMember = jest.fn()
|
||||
.mockReturnValue(member);
|
||||
const messageGuildRolesCacheFind = jest.fn()
|
||||
.mockReturnValue(undefined);
|
||||
const messageChannelSend = jest.fn();
|
||||
const messageGuildChannelsCacheFind = jest.fn()
|
||||
.mockImplementation((callback): TextChannel | undefined => {
|
||||
const result = callback(logChannel);
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return logChannel;
|
||||
});
|
||||
|
||||
const message = {
|
||||
mentions: {
|
||||
users: {
|
||||
first: messageMentionsUsersFirst
|
||||
}
|
||||
},
|
||||
guild: {
|
||||
member: messageGuildMember,
|
||||
available: true,
|
||||
roles: {
|
||||
cache: {
|
||||
find: messageGuildRolesCacheFind
|
||||
}
|
||||
},
|
||||
channels: {
|
||||
cache: {
|
||||
find: messageGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: messageAuthor
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'mute',
|
||||
args: ['USER', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const mute = new Unmute();
|
||||
|
||||
const result = await mute.execute(context);
|
||||
|
||||
expect(messageMentionsUsersFirst).toBeCalledTimes(1);
|
||||
expect(messageGuildMember).toBeCalledWith(user);
|
||||
expect(messageGuildRolesCacheFind).toBeCalledTimes(1);
|
||||
expect(messageGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Error Embed
|
||||
const errorEmbed = result.embeds[0];
|
||||
|
||||
expect(errorEmbed.description).toBe('Unable to find role');
|
||||
});
|
||||
});
|
485
tests/commands/warn.test.ts
Normal file
485
tests/commands/warn.test.ts
Normal file
|
@ -0,0 +1,485 @@
|
|||
import { GuildMember, Message, TextChannel, User } from "discord.js";
|
||||
import Warn from "../../src/commands/warn";
|
||||
import { ICommandContext } from "../../src/contracts/ICommandContext";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect values to be set', () => {
|
||||
process.env.ROLES_MODERATOR = 'Moderator';
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
expect(warn._category).toBe('Moderation');
|
||||
expect(warn._roles.length).toBe(1);
|
||||
expect(warn._roles[0]).toBe('Moderator');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute', () => {
|
||||
test('Given user has permission, expect user to be warnned', async () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: 'Moderator',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
};
|
||||
|
||||
const mentionedUser = {
|
||||
displayAvatarURL: jest.fn(),
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
const mentionedMember = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
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 = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.execute(context);
|
||||
|
||||
expect(result.embeds.length).toBe(2);
|
||||
|
||||
const logEmbed = result.embeds[0];
|
||||
const publicEmbed = result.embeds[1];
|
||||
|
||||
expect(logEmbed.title).toBe('Member Warned');
|
||||
expect(publicEmbed.title).toBe("");
|
||||
expect(publicEmbed.description).toBe('[object Object] has been warned');
|
||||
expect(logEmbed.fields.length).toBe(3);
|
||||
expect(publicEmbed.fields.length).toBe(1);
|
||||
expect(publicEmbed.fields[0].name).toBe('Reason');
|
||||
expect(publicEmbed.fields[0].value).toBe('Test Reason');
|
||||
});
|
||||
|
||||
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 = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.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 user is not mentioned, expect error embed to be sent', async () => {
|
||||
process.env = {
|
||||
ROLES_MODERATOR: 'Moderator',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
};
|
||||
|
||||
const mentionedMember = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).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 = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.execute(context);
|
||||
|
||||
expect(messageChannelSend).toBeCalledTimes(1);
|
||||
expect(logChannel.send).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 = {
|
||||
warnnable: true,
|
||||
warn: 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: 'warn',
|
||||
args: ['warn', 'Test', 'Reason'],
|
||||
message: message
|
||||
};
|
||||
|
||||
const warn = new Warn();
|
||||
|
||||
const result = await warn.execute(context);
|
||||
|
||||
expect(messageChannelSend).not.toBeCalled();
|
||||
expect(logChannel.send).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
});
|
164
tests/events/MemberEvents.test.ts
Normal file
164
tests/events/MemberEvents.test.ts
Normal file
|
@ -0,0 +1,164 @@
|
|||
import { GuildMember, TextChannel, User } from "discord.js";
|
||||
import MemberEvents from "../../src/events/MemberEvents";
|
||||
import GuildMemberUpdate from "../../src/events/MemberEvents/GuildMemberUpdate";
|
||||
|
||||
describe('GuildMemberAdd', () => {
|
||||
test('When event is fired, expect embed to be sent to logs channel', () => {
|
||||
const currentDate = new Date();
|
||||
|
||||
const textChannel = {
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const userDisplayAvatarURL = jest.fn();
|
||||
|
||||
const guildUser = {
|
||||
tag: 'USERTAG',
|
||||
createdAt: currentDate,
|
||||
id: 'USERID',
|
||||
displayAvatarURL: userDisplayAvatarURL
|
||||
} as unknown as User;
|
||||
|
||||
const guildMember = {
|
||||
user: guildUser,
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const memberEvents = new MemberEvents();
|
||||
|
||||
const result = memberEvents.guildMemberAdd(guildMember);
|
||||
|
||||
expect(textChannel.send).toBeCalledTimes(1);
|
||||
expect(userDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe("Member Joined");
|
||||
expect(embed.footer?.text).toBe("Id: USERID");
|
||||
expect(embed.fields.length).toBe(2);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe("User");
|
||||
expect(embedFieldUser.value).toBe("[object Object] `USERTAG`");
|
||||
expect(embedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Created Field
|
||||
const embedFieldCreated = embed.fields[1];
|
||||
|
||||
expect(embedFieldCreated.name).toBe("Created");
|
||||
expect(embedFieldCreated.value).toBe(currentDate.toString());
|
||||
});
|
||||
});
|
||||
|
||||
describe('GuildMemberRemove', () => {
|
||||
test('When event is fired, expect embed to be sent to logs channel', () => {
|
||||
const currentDate = new Date();
|
||||
|
||||
const textChannel = {
|
||||
send: jest.fn()
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const userDisplayAvatarURL = jest.fn();
|
||||
|
||||
const guildUser = {
|
||||
tag: 'USERTAG',
|
||||
createdAt: currentDate,
|
||||
id: 'USERID',
|
||||
displayAvatarURL: userDisplayAvatarURL
|
||||
} as unknown as User;
|
||||
|
||||
const guildMember = {
|
||||
user: guildUser,
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
joinedAt: currentDate
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const memberEvents = new MemberEvents();
|
||||
|
||||
const result = memberEvents.guildMemberRemove(guildMember);
|
||||
|
||||
expect(textChannel.send).toBeCalledTimes(1);
|
||||
expect(userDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe("Member Left");
|
||||
expect(embed.footer?.text).toBe("Id: USERID");
|
||||
expect(embed.fields.length).toBe(2);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe("User");
|
||||
expect(embedFieldUser.value).toBe("[object Object] `USERTAG`");
|
||||
expect(embedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Joined Field
|
||||
const embedFieldJoined = embed.fields[1];
|
||||
|
||||
expect(embedFieldJoined.name).toBe("Joined");
|
||||
expect(embedFieldJoined.value).toBe(currentDate.toString());
|
||||
});
|
||||
});
|
||||
|
||||
describe('GuildMemberUpdate', () => {
|
||||
test('Given nicknames are the same, expect NicknameChanged NOT to be called', () => {
|
||||
const member = {
|
||||
nickname: 'member'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const nicknameChanged = jest.fn();
|
||||
|
||||
GuildMemberUpdate.prototype.NicknameChanged = nicknameChanged;
|
||||
|
||||
const memberEvents = new MemberEvents();
|
||||
|
||||
const result = memberEvents.guildMemberUpdate(member, member);
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
expect(nicknameChanged).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('Given nicknames are the different, expect NicknameChanged to be called', () => {
|
||||
const oldMember = {
|
||||
nickname: 'oldMember'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const newMember = {
|
||||
nickname: 'newMember'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const nicknameChanged = jest.fn();
|
||||
|
||||
GuildMemberUpdate.prototype.NicknameChanged = nicknameChanged;
|
||||
|
||||
const memberEvents = new MemberEvents();
|
||||
|
||||
const result = memberEvents.guildMemberUpdate(oldMember, newMember);
|
||||
|
||||
expect(result.embeds.length).toBe(0);
|
||||
expect(nicknameChanged).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
235
tests/events/MemberEvents/GuildMemberUpdate.test.ts
Normal file
235
tests/events/MemberEvents/GuildMemberUpdate.test.ts
Normal file
|
@ -0,0 +1,235 @@
|
|||
import { GuildMember, TextChannel } from "discord.js";
|
||||
import GuildMemberUpdate from "../../../src/events/MemberEvents/GuildMemberUpdate";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties are set', () => {
|
||||
const oldMember = {
|
||||
nickname: 'Old Nickname'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const newMember = {
|
||||
nickname: 'New Nickname'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember);
|
||||
|
||||
expect(guildMemberUpdate.oldMember).toBe(oldMember);
|
||||
expect(guildMemberUpdate.newMember).toBe(newMember);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NicknameChanged', () => {
|
||||
test('Given nickname has changed from one to another, expect embed to be sent with both', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const memberUserDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMember = {
|
||||
nickname: 'Old Nickname'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const newMember = {
|
||||
nickname: 'New Nickname',
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
user: {
|
||||
tag: 'USERTAG',
|
||||
id: 'USERID',
|
||||
displayAvatarURL: memberUserDisplayAvatarURL
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember);
|
||||
|
||||
const result = guildMemberUpdate.NicknameChanged();
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(memberUserDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Nickname Changed');
|
||||
expect(embed.footer?.text).toBe('Id: USERID');
|
||||
expect(embed.fields.length).toBe(3);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[1];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('Old Nickname');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[2];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('New Nickname');
|
||||
});
|
||||
|
||||
test('Given old nickname was null, expect embed to say old nickname was none', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const memberUserDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMember = {} as unknown as GuildMember;
|
||||
|
||||
const newMember = {
|
||||
nickname: 'New Nickname',
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
user: {
|
||||
tag: 'USERTAG',
|
||||
id: 'USERID',
|
||||
displayAvatarURL: memberUserDisplayAvatarURL
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember);
|
||||
|
||||
const result = guildMemberUpdate.NicknameChanged();
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(memberUserDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Nickname Changed');
|
||||
expect(embed.footer?.text).toBe('Id: USERID');
|
||||
expect(embed.fields.length).toBe(3);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[1];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('*none*');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[2];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('New Nickname');
|
||||
});
|
||||
|
||||
test('Given new nickname was null, expect embed to say new nickname was none', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const memberUserDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMember = {
|
||||
nickname: 'Old Nickname'
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const newMember = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
user: {
|
||||
tag: 'USERTAG',
|
||||
id: 'USERID',
|
||||
displayAvatarURL: memberUserDisplayAvatarURL
|
||||
}
|
||||
} as unknown as GuildMember;
|
||||
|
||||
const guildMemberUpdate = new GuildMemberUpdate(oldMember, newMember);
|
||||
|
||||
const result = guildMemberUpdate.NicknameChanged();
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(memberUserDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Nickname Changed');
|
||||
expect(embed.footer?.text).toBe('Id: USERID');
|
||||
expect(embed.fields.length).toBe(3);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[1];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('Old Nickname');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[2];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('*none*');
|
||||
});
|
||||
});
|
648
tests/events/MessageEvents.test.ts
Normal file
648
tests/events/MessageEvents.test.ts
Normal file
|
@ -0,0 +1,648 @@
|
|||
import { Collection, Message, MessageAttachment, TextChannel } from "discord.js";
|
||||
import MessageEvents from "../../src/events/MessageEvents";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('MessageDelete', () => {
|
||||
test('Given message was in a guild AND user was NOT a bot, expect message deleted embed to be sent', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const messageAttachments = new Collection<string, MessageAttachment>([
|
||||
[
|
||||
"0",
|
||||
{
|
||||
url: 'image0.png'
|
||||
} as unknown as MessageAttachment
|
||||
],
|
||||
[
|
||||
"1",
|
||||
{
|
||||
url: 'image1.png'
|
||||
} as unknown as MessageAttachment
|
||||
]
|
||||
]);
|
||||
|
||||
const message = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
channel: {},
|
||||
content: 'Message Content',
|
||||
attachments: messageAttachments
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageDelete(message);
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Message Deleted');
|
||||
expect(embed.fields.length).toBe(4);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
|
||||
// Embed -> Channel Field
|
||||
const embedFieldChannel = embed.fields[1];
|
||||
|
||||
expect(embedFieldChannel.name).toBe('Channel');
|
||||
expect(embedFieldChannel.value).toBe('[object Object]');
|
||||
|
||||
// Embed -> Content Field
|
||||
const embedFieldContent = embed.fields[2];
|
||||
|
||||
expect(embedFieldContent.name).toBe('Content');
|
||||
expect(embedFieldContent.value).toBe('```Message Content```');
|
||||
|
||||
// Embed -> Attachments Field
|
||||
const embedFieldAttachments = embed.fields[3];
|
||||
|
||||
expect(embedFieldAttachments.name).toBe('Attachments');
|
||||
expect(embedFieldAttachments.value).toBe('```image0.png\nimage1.png```');
|
||||
});
|
||||
|
||||
test('Given message was not sent in a guild, expect execution stopped', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const messageAttachments = new Collection<string, MessageAttachment>([
|
||||
[
|
||||
"0",
|
||||
{
|
||||
url: 'image0.png'
|
||||
} as unknown as MessageAttachment
|
||||
],
|
||||
[
|
||||
"1",
|
||||
{
|
||||
url: 'image1.png'
|
||||
} as unknown as MessageAttachment
|
||||
]
|
||||
]);
|
||||
|
||||
const message = {
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
channel: {},
|
||||
content: 'Message Content',
|
||||
attachments: messageAttachments
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageDelete(message);
|
||||
|
||||
expect(channelSend).not.toBeCalled();
|
||||
expect(memberGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageAuthorDisplayAvatarURL).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given author is a bot, expect execution stopped', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const messageAttachments = new Collection<string, MessageAttachment>([
|
||||
[
|
||||
"0",
|
||||
{
|
||||
url: 'image0.png'
|
||||
} as unknown as MessageAttachment
|
||||
],
|
||||
[
|
||||
"1",
|
||||
{
|
||||
url: 'image1.png'
|
||||
} as unknown as MessageAttachment
|
||||
]
|
||||
]);
|
||||
|
||||
const message = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: true,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
channel: {},
|
||||
content: 'Message Content',
|
||||
attachments: messageAttachments
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageDelete(message);
|
||||
|
||||
expect(channelSend).not.toBeCalled();
|
||||
expect(memberGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageAuthorDisplayAvatarURL).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given message does not contain any attachments, expect attachments field to be omitted', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const messageAttachments = new Collection<string, MessageAttachment>([]);
|
||||
|
||||
const message = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
channel: {},
|
||||
content: 'Message Content',
|
||||
attachments: messageAttachments
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageDelete(message);
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Message Deleted');
|
||||
expect(embed.fields.length).toBe(3);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
|
||||
// Embed -> Channel Field
|
||||
const embedFieldChannel = embed.fields[1];
|
||||
|
||||
expect(embedFieldChannel.name).toBe('Channel');
|
||||
expect(embedFieldChannel.value).toBe('[object Object]');
|
||||
|
||||
// Embed -> Content Field
|
||||
const embedFieldContent = embed.fields[2];
|
||||
|
||||
expect(embedFieldContent.name).toBe('Content');
|
||||
expect(embedFieldContent.value).toBe('```Message Content```');
|
||||
});
|
||||
});
|
||||
|
||||
describe('MessageUpdate', () => {
|
||||
test('Given message is in a guild AND user is not a bot AND the content has actually changed, e xpect log embed to be sent', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {
|
||||
content: 'Old Message'
|
||||
} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
content: 'New Message',
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Message Edited');
|
||||
expect(embed.fields.length).toBe(4);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
expect(embedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Channel Field
|
||||
const embedFieldChannel = embed.fields[1];
|
||||
|
||||
expect(embedFieldChannel.name).toBe('Channel');
|
||||
expect(embedFieldChannel.value).toBe('[object Object]');
|
||||
expect(embedFieldChannel.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[2];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('```Old Message```');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[3];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('```New Message```');
|
||||
});
|
||||
|
||||
test('Given message was not in a guild, expect execution stopped', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {
|
||||
content: 'Old Message'
|
||||
} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
content: 'New Message',
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).not.toBeCalled();
|
||||
expect(memberGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageAuthorDisplayAvatarURL).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given author is a bot, expect execution stopped', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {
|
||||
content: 'Old Message'
|
||||
} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: true,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
content: 'New Message',
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).not.toBeCalled();
|
||||
expect(memberGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageAuthorDisplayAvatarURL).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given the message contents are the same, expect execution stopped', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {
|
||||
content: 'Message'
|
||||
} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
content: 'Message',
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).not.toBeCalled();
|
||||
expect(memberGuildChannelsCacheFind).not.toBeCalled();
|
||||
expect(messageAuthorDisplayAvatarURL).not.toBeCalled();
|
||||
expect(result.embeds.length).toBe(0);
|
||||
});
|
||||
|
||||
test('Given Old Message did not have a content, expect field to account for this', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
content: 'New Message',
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Message Edited');
|
||||
expect(embed.fields.length).toBe(4);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
expect(embedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Channel Field
|
||||
const embedFieldChannel = embed.fields[1];
|
||||
|
||||
expect(embedFieldChannel.name).toBe('Channel');
|
||||
expect(embedFieldChannel.value).toBe('[object Object]');
|
||||
expect(embedFieldChannel.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[2];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('```*none*```');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[3];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('```New Message```');
|
||||
});
|
||||
|
||||
test('Given New Message does not have a content, expect field to account for this', () => {
|
||||
process.env = {
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const textChannel = {
|
||||
name: 'mod-logs',
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const memberGuildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(textChannel);
|
||||
const messageAuthorDisplayAvatarURL = jest.fn();
|
||||
|
||||
const oldMessage = {
|
||||
content: 'Old Message'
|
||||
} as unknown as Message;
|
||||
|
||||
const newMessage = {
|
||||
guild: {
|
||||
channels: {
|
||||
cache: {
|
||||
find: memberGuildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
},
|
||||
author: {
|
||||
bot: false,
|
||||
displayAvatarURL: messageAuthorDisplayAvatarURL,
|
||||
tag: 'USERTAG'
|
||||
},
|
||||
channel: {},
|
||||
} as unknown as Message;
|
||||
|
||||
const messageEvents = new MessageEvents();
|
||||
|
||||
const result = messageEvents.messageUpdate(oldMessage, newMessage);
|
||||
|
||||
expect(channelSend).toBeCalledTimes(1);
|
||||
expect(memberGuildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(messageAuthorDisplayAvatarURL).toBeCalledTimes(1);
|
||||
expect(result.embeds.length).toBe(1);
|
||||
|
||||
// Embed
|
||||
const embed = result.embeds[0];
|
||||
|
||||
expect(embed.title).toBe('Message Edited');
|
||||
expect(embed.fields.length).toBe(4);
|
||||
|
||||
// Embed -> User Field
|
||||
const embedFieldUser = embed.fields[0];
|
||||
|
||||
expect(embedFieldUser.name).toBe('User');
|
||||
expect(embedFieldUser.value).toBe('[object Object] `USERTAG`');
|
||||
expect(embedFieldUser.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Channel Field
|
||||
const embedFieldChannel = embed.fields[1];
|
||||
|
||||
expect(embedFieldChannel.name).toBe('Channel');
|
||||
expect(embedFieldChannel.value).toBe('[object Object]');
|
||||
expect(embedFieldChannel.inline).toBeTruthy();
|
||||
|
||||
// Embed -> Before Field
|
||||
const embedFieldBefore = embed.fields[2];
|
||||
|
||||
expect(embedFieldBefore.name).toBe('Before');
|
||||
expect(embedFieldBefore.value).toBe('```Old Message```');
|
||||
|
||||
// Embed -> After Field
|
||||
const embedFieldAfter = embed.fields[3];
|
||||
|
||||
expect(embedFieldAfter.name).toBe('After');
|
||||
expect(embedFieldAfter.value).toBe('```*none*```');
|
||||
});
|
||||
});
|
11
tests/helpers/StringTools.test.ts
Normal file
11
tests/helpers/StringTools.test.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import StringTools from "../../src/helpers/StringTools";
|
||||
|
||||
describe('Capitalise', () => {
|
||||
test('Expect sentence to be captilised', () => {
|
||||
const inputString = 'the big brown fox jumps over the lazy dog';
|
||||
|
||||
const result = StringTools.Capitalise(inputString);
|
||||
|
||||
expect(result).toBe('The Big Brown Fox Jumps Over The Lazy Dog');
|
||||
});
|
||||
});
|
57
tests/helpers/embeds/ErrorEmbed.test.ts
Normal file
57
tests/helpers/embeds/ErrorEmbed.test.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { Message } from "discord.js";
|
||||
import { ICommandContext } from "../../../src/contracts/ICommandContext";
|
||||
import ErrorEmbed from "../../../src/helpers/embeds/ErrorEmbed";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR_ERROR: '0xd52803'
|
||||
}
|
||||
|
||||
const message = {} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new ErrorEmbed(context, 'Error Message');
|
||||
|
||||
expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal
|
||||
expect(errorEmbed.description).toBe('Error Message');
|
||||
expect(errorEmbed.context).toBe(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToCurrentChannel', () => {
|
||||
test('Expect embed to be sent to the current channel in context', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR_ERROR: '0xd52803'
|
||||
}
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new ErrorEmbed(context, 'Error Message');
|
||||
|
||||
errorEmbed.SendToCurrentChannel();
|
||||
|
||||
expect(messageChannelSend).toBeCalledWith(errorEmbed);
|
||||
});
|
||||
});
|
222
tests/helpers/embeds/EventEmbed.test.ts
Normal file
222
tests/helpers/embeds/EventEmbed.test.ts
Normal file
|
@ -0,0 +1,222 @@
|
|||
import { Guild, Message, TextChannel, User } from "discord.js";
|
||||
import { ICommandContext } from "../../../src/contracts/ICommandContext";
|
||||
import EventEmbed from "../../../src/helpers/embeds/EventEmbed";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal
|
||||
expect(errorEmbed.title).toBe('Event Message');
|
||||
expect(errorEmbed.guild).toBe(guild);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AddUser', () => {
|
||||
test('Given setThumbnail is false, add field WITHOUT user thumbnail', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const addField = jest.fn();
|
||||
const setThumbnail = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const user = {
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.addField = addField;
|
||||
errorEmbed.setThumbnail = setThumbnail;
|
||||
|
||||
errorEmbed.AddUser('User', user);
|
||||
|
||||
expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true);
|
||||
expect(setThumbnail).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('Given setThumbnail is true, add field WITH user thumbnail', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const addField = jest.fn();
|
||||
const setThumbnail = jest.fn();
|
||||
const displayAvatarURL = jest.fn()
|
||||
.mockReturnValue('image0.png');
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const user = {
|
||||
tag: 'USERTAG',
|
||||
displayAvatarURL: displayAvatarURL
|
||||
} as unknown as User;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.addField = addField;
|
||||
errorEmbed.setThumbnail = setThumbnail;
|
||||
|
||||
errorEmbed.AddUser('User', user, true);
|
||||
|
||||
expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true);
|
||||
expect(setThumbnail).toBeCalledWith('image0.png');
|
||||
expect(displayAvatarURL).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToChannel', () => {
|
||||
test('Given channel can be found, expect embed to be sent to that channel', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const channel = {
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const guildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(channel);
|
||||
|
||||
const guild = {
|
||||
channels: {
|
||||
cache: {
|
||||
find: guildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
} as unknown as Guild;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel('channel-name');
|
||||
|
||||
expect(guildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(channelSend).toBeCalledWith(errorEmbed);
|
||||
});
|
||||
|
||||
test('Given channel can NOT be found, expect error logged', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const guildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
|
||||
const guild = {
|
||||
channels: {
|
||||
cache: {
|
||||
find: guildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
} as unknown as Guild;
|
||||
|
||||
console.error = jest.fn();
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel('channel-name');
|
||||
|
||||
expect(guildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(console.error).toBeCalledWith('Unable to find channel channel-name');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToMessageLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MESSAGE as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToMessageLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('message-logs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToMemberLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MEMBER as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToMemberLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('member-logs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToModLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MOD as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const errorEmbed = new EventEmbed(guild, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToModLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('mod-logs');
|
||||
});
|
||||
});
|
331
tests/helpers/embeds/LogEmbed.test.ts
Normal file
331
tests/helpers/embeds/LogEmbed.test.ts
Normal file
|
@ -0,0 +1,331 @@
|
|||
import { Guild, Message, TextChannel, User } from "discord.js";
|
||||
import { ICommandContext } from "../../../src/contracts/ICommandContext";
|
||||
import LogEmbed from "../../../src/helpers/embeds/LogEmbed";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Log Message');
|
||||
|
||||
expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal
|
||||
expect(errorEmbed.title).toBe('Log Message');
|
||||
expect(errorEmbed.context).toBe(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AddUser', () => {
|
||||
test('Given setThumbnail is false, add field WITHOUT user thumbnail', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const addField = jest.fn();
|
||||
const setThumbnail = jest.fn();
|
||||
|
||||
const user = {
|
||||
tag: 'USERTAG'
|
||||
} as unknown as User;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: user
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.addField = addField;
|
||||
errorEmbed.setThumbnail = setThumbnail;
|
||||
|
||||
errorEmbed.AddUser('User', user);
|
||||
|
||||
expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true);
|
||||
expect(setThumbnail).not.toBeCalled();
|
||||
});
|
||||
|
||||
test('Given setThumbnail is true, add field WITH user thumbnail', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const addField = jest.fn();
|
||||
const setThumbnail = jest.fn();
|
||||
const displayAvatarURL = jest.fn()
|
||||
.mockReturnValue('image0.png');
|
||||
|
||||
const user = {
|
||||
tag: 'USERTAG',
|
||||
displayAvatarURL: displayAvatarURL
|
||||
} as unknown as User;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
author: user
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.addField = addField;
|
||||
errorEmbed.setThumbnail = setThumbnail;
|
||||
|
||||
errorEmbed.AddUser('User', user, true);
|
||||
|
||||
expect(addField).toBeCalledWith('User', '[object Object] `USERTAG`', true);
|
||||
expect(setThumbnail).toBeCalledWith('image0.png');
|
||||
expect(displayAvatarURL).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToChannel', () => {
|
||||
test('Given channel can be found, expect embed to be sent to that channel', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const channelSend = jest.fn();
|
||||
|
||||
const channel = {
|
||||
send: channelSend
|
||||
} as unknown as TextChannel;
|
||||
|
||||
const guildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(channel);
|
||||
|
||||
const guild = {
|
||||
channels: {
|
||||
cache: {
|
||||
find: guildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
} as unknown as Guild;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
guild: guild
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel('channel-name');
|
||||
|
||||
expect(guildChannelsCacheFind).toBeCalledTimes(1);
|
||||
expect(channelSend).toBeCalledWith(errorEmbed);
|
||||
});
|
||||
|
||||
test('Given channel can NOT be found, expect error logged', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const guildChannelsCacheFind = jest.fn()
|
||||
.mockReturnValue(null);
|
||||
|
||||
const guild = {
|
||||
channels: {
|
||||
cache: {
|
||||
find: guildChannelsCacheFind
|
||||
}
|
||||
}
|
||||
} as unknown as Guild;
|
||||
|
||||
console.error = jest.fn();
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
},
|
||||
guild: guild
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel('channel-name');
|
||||
|
||||
expect(guildChannelsCacheFind).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToMessageLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MESSAGE as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToMessageLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('message-logs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToMemberLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MEMBER as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToMemberLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('member-logs');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToModLogsChannel', () => {
|
||||
describe('Expect SendToChannel caleld with CHANNELS_LOGS_MOD as parameter', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const sendToChannel = jest.fn();
|
||||
|
||||
const guild = {} as unknown as Guild;
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new LogEmbed(context, 'Event Message');
|
||||
|
||||
errorEmbed.SendToChannel = sendToChannel;
|
||||
|
||||
errorEmbed.SendToModLogsChannel();
|
||||
|
||||
expect(sendToChannel).toBeCalledWith('mod-logs');
|
||||
});
|
||||
});
|
67
tests/helpers/embeds/PublicEmbed.test.ts
Normal file
67
tests/helpers/embeds/PublicEmbed.test.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { Guild, Message, TextChannel, User } from "discord.js";
|
||||
import { ICommandContext } from "../../../src/contracts/ICommandContext";
|
||||
import PublicEmbed from "../../../src/helpers/embeds/PublicEmbed";
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {};
|
||||
});
|
||||
|
||||
describe('Constructor', () => {
|
||||
test('Expect properties to be set', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR: '0xd52803',
|
||||
CHANNELS_LOGS_MESSAGE: 'message-logs',
|
||||
CHANNELS_LOGS_MEMBER: 'member-logs',
|
||||
CHANNELS_LOGS_MOD: 'mod-logs'
|
||||
}
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new PublicEmbed(context, 'Log Message', 'Log Description');
|
||||
|
||||
expect(errorEmbed.color?.toString()).toBe('13969411'); // 0xd52803 in decimal
|
||||
expect(errorEmbed.title).toBe('Log Message');
|
||||
expect(errorEmbed.description).toBe('Log Description');
|
||||
expect(errorEmbed.context).toBe(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SendToCurrentChannel', () => {
|
||||
test('Expect embed to be sent to the current channel in context', () => {
|
||||
process.env = {
|
||||
EMBED_COLOUR_ERROR: '0xd52803'
|
||||
}
|
||||
|
||||
const messageChannelSend = jest.fn();
|
||||
|
||||
const message = {
|
||||
channel: {
|
||||
send: messageChannelSend
|
||||
}
|
||||
} as unknown as Message;
|
||||
|
||||
const context: ICommandContext = {
|
||||
name: 'command',
|
||||
args: [],
|
||||
message: message
|
||||
};
|
||||
|
||||
const errorEmbed = new PublicEmbed(context, 'Message', 'Description');
|
||||
|
||||
errorEmbed.SendToCurrentChannel();
|
||||
|
||||
expect(messageChannelSend).toBeCalledWith(errorEmbed);
|
||||
});
|
||||
});
|
78
tsconfig.json
Normal file
78
tsconfig.json
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"include": [
|
||||
"./src",
|
||||
],
|
||||
"exclude": [
|
||||
"./tests"
|
||||
]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
const vylbot = require('vylbot-core');
|
||||
const config = require('./config.json');
|
||||
|
||||
const client = new vylbot.client(config);
|
||||
client.start();
|
Loading…
Reference in a new issue