Compare commits

...

86 commits
v1.0.5 ... main

Author SHA1 Message Date
Ethan Lane 523ec0a343 v2.0.3 2021-12-02 15:32:29 +00:00
Ethan Lane 6bc0cf3d6a
Update tests to match event changes 2021-12-02 15:32:09 +00:00
Vylpes 7861829f5d
Merge pull request #68 from Vylpes/bug/67-type-error
Fix issue where event files existing would crash the bot
2021-12-02 15:30:08 +00:00
Ethan Lane c526423607
Fix issue where event files existing would crash the bot 2021-12-02 15:28:36 +00:00
Ethan Lane 6d030737fc
Bump version 2021-11-25 18:33:16 +00:00
Ethan Lane acde8b0ccf
Add typings 2021-11-25 18:32:58 +00:00
Ethan Lane 7c85aec971 v2.0.1 2021-10-11 16:22:18 +01:00
Vylpes 8e9db75a30
Merge pull request #56 from Vylpes/dependabot/npm_and_yarn/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5
2021-10-11 16:21:20 +01:00
Vylpes 5676efd969
Merge pull request #57 from Vylpes/dependabot/npm_and_yarn/ansi-regex-5.0.1
Bump ansi-regex from 5.0.0 to 5.0.1
2021-10-11 16:20:25 +01:00
Ethan Lane 16ac225a44 v2.0.0 2021-10-04 19:51:21 +01:00
dependabot[bot] e302f2ee4d
Bump ansi-regex from 5.0.0 to 5.0.1
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 18:45:45 +00:00
dependabot[bot] f6d57a1fda
Bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-04 18:45:44 +00:00
Vylpes c88a15187e
Merge pull request #55 from Vylpes/develop
v2.0
2021-10-04 19:45:15 +01:00
Ethan Lane 1a8baecfa1
Improvements 2021-10-04 19:43:53 +01:00
Vylpes d3f9fc4fa0
Merge pull request #53 from Vylpes/feature/VBC-52
VBC-52 Use export default rather than class of the same name
2021-09-29 17:53:21 +01:00
Ethan Lane 5ce0960606
Remove useless async code 2021-09-29 17:52:21 +01:00
Ethan Lane 799b04e936
Update tests 2021-09-26 14:32:56 +01:00
Ethan Lane 3a9f1965ae
Update documentation 2021-09-26 14:13:09 +01:00
Ethan Lane 00a057792b
Make command loader use the default class 2021-09-26 14:12:21 +01:00
Vylpes 1b1933a9ce
Merge pull request #50 from Vylpes/feature/3-disable-commands
Feature/3 disable commands
2021-09-24 19:00:08 +01:00
Ethan Lane b10ac37007
Update tests 2021-09-24 18:52:12 +01:00
Ethan Lane 3392e6f031
Add disabled command message reply 2021-09-24 18:46:49 +01:00
Ethan Lane a3230cad20
Add tests 2021-09-22 19:59:40 +01:00
Ethan Lane 4e1c418246
Add ability to disable commands 2021-09-22 19:54:49 +01:00
Vylpes 10fe4501b5
Merge pull request #44 from Vylpes/feature/tests
Tests and CI
2021-08-21 16:00:28 +01:00
Ethan Lane 7e623b4b64 Update command names, add describe categories 2021-08-21 15:33:14 +01:00
Ethan Lane bb848dcec3 Improvements 2021-08-21 15:20:40 +01:00
Ethan Lane 044140d58d Remove workflows 2021-08-20 16:13:02 +01:00
Ethan Lane 19e7539e53 Remove linting workflow 2021-08-20 16:10:06 +01:00
Ethan Lane c3bcf76aac Add event mocks 2021-08-20 16:09:33 +01:00
Ethan Lane 8ee5c62481 Add github workflows 2021-08-20 16:06:59 +01:00
Ethan Lane 1b036609a9 Add ESLint 2021-08-20 15:58:41 +01:00
Ethan Lane f11f3954a1 Improvements to Tests 2021-08-20 15:53:34 +01:00
Ethan Lane 338514ba1e Util tests, rewrite tests 2021-08-20 15:42:46 +01:00
Ethan Lane 2d2eaadea7 User role tests 2021-08-20 14:59:41 +01:00
Ethan Lane 569c22f1f4 More tests 2021-08-19 17:27:47 +01:00
Ethan Lane 1044c57e66 First util.ts test 2021-08-19 17:21:54 +01:00
Ethan Lane a0a93a4737 Remove unused code 2021-08-18 21:27:44 +01:00
Ethan Lane e6464d22cb Event tests 2021-08-18 21:11:49 +01:00
Ethan Lane 49d3d9c449
Add 2 more tests to events 2021-08-16 19:11:42 +01:00
Ethan Lane da96bbefa5
Add start of event tests 2021-08-12 20:18:09 +01:00
Ethan Lane 99e375633b Add client tests 2021-07-30 15:49:12 +01:00
Vylpes abe94ccc72
Merge pull request #42 from Vylpes/feature/ts
Feature/ts
2021-07-24 12:57:28 +01:00
Ethan Lane a9400a95f2 Fix typo 2021-07-22 17:54:46 +01:00
Ethan Lane 8313e560b4 Set a response to false when it should be 2021-07-22 17:54:12 +01:00
Ethan Lane 9579ca837f Add ready event 2021-07-22 17:52:36 +01:00
Ethan Lane a5123d6f79 Update documentation 2021-07-20 17:55:31 +01:00
Vylpes e9112ee00e Fix events not working 2021-07-18 15:29:09 +01:00
Vylpes 6e700df0de Add CommandContext 2021-07-18 14:14:32 +01:00
Ethan Lane 23858f72fc Merge branch 'develop' into feature/ts 2021-07-16 17:25:40 +01:00
Ethan Lane 3100953f2e Clean up 2021-07-14 14:39:48 +01:00
Ethan Lane 29f80fba5c Rewrite in TypeScript 2021-07-14 13:55:36 +01:00
Ethan Lane 7870e35599 Swap to Yarn 2021-07-13 22:49:55 +01:00
Ethan Lane 33b05d9db6 Merge branch 'main' into develop 2021-07-03 18:26:38 +01:00
Ethan Lane bec0e4871b Merge in dependabot/npm_and_yarn/ws-7.4.6 2021-06-10 09:14:32 +01:00
dependabot[bot] 1e578728c7
Bump ws from 7.3.1 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.3.1 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.3.1...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-31 02:58:03 +00:00
Vylpes c816f182b2 Update util to check if folder exists 2021-02-07 14:12:56 +00:00
Ethan Lane bebaf30d16 Fixed some issues 2021-02-01 17:49:08 +00:00
Vylpes c418ba4d84 Merge branch 'feature/VBC-16' into 'develop'
Tests

See merge request Vylpes/vylbot-core!30
2021-01-16 19:06:35 +00:00
Vylpes 80e9daa8dc Tests 2021-01-16 19:06:35 +00:00
Vylpes 7840bc9be8 Merge branch 'feature/config-file' into 'develop'
Command configs are now in their own file

See merge request Vylpes/vylbot-core!29
2020-12-23 22:05:23 +00:00
Vylpes 005de0302e Command configs are now in their own file 2020-12-23 22:05:23 +00:00
Vylpes a7fc0e2013 Change requiredConfigs to configs 2020-11-28 10:39:44 +00:00
Vylpes 4a28665048
Merge pull request #22 from GetGravitySoft/bug/category-field
Fix category field being empty crashing the bot
2020-11-08 17:54:59 +00:00
Vylpes b519537072 Fix category field being empty crashing the bot 2020-11-08 17:49:41 +00:00
Vylpes 96dba8c84b
Merge pull request #21 from GetGravitySoft/docs/email
Update documentation with a contact email
2020-11-08 17:02:25 +00:00
vylpes e4d87650c4 Adjust links 2020-11-07 21:23:14 +00:00
vylpes 03a0f6209b Update documentation with a contact email 2020-11-07 11:22:43 +00:00
vylpes cdb1cf9b53 Updated documentation 2020-11-07 11:09:13 +00:00
Vylpes 91ff0f0648
Update package.json 2020-11-03 18:09:47 +00:00
Vylpes 4928820e08 Update readme about vylbot essentials 2020-11-03 13:50:55 +00:00
vylpes ebae6d3336 Add funding to package.json 2020-11-01 11:22:03 +00:00
vylpes b37dcf136d Update CONTRIBUTING.md to match the config repo 2020-11-01 11:20:26 +00:00
Vylpes e50630e4f4
Merge pull request #12 from Vylpes/bug/commands-run-anyone
Fixed bug which didn't let commands to run by anyone if no user restriction was set
2020-11-01 11:12:12 +00:00
vylpes c1786d1f18 Fix bug which wouldn't let anyone to run a command 2020-11-01 11:09:20 +00:00
Vylpes 45cd269248
Merge pull request #10 from Vylpes/feature/comments
Code Comments
2020-10-30 23:06:59 +00:00
Vylpes a8aabf99d8 Fix Hound Violations 2020-10-30 23:06:17 +00:00
Vylpes de2d589504 Fix Hound Violations 2020-10-30 23:03:19 +00:00
vylpes 2ec2e81853 Commented util.js 2020-10-30 18:34:51 +00:00
vylpes c76532db32 Comment client.js, and event.js files 2020-10-28 23:02:43 +00:00
vylpes 75b0f426da Add docs link to readme 2020-10-28 22:25:56 +00:00
Vylpes e2974e1a0d
Merge pull request #4 from Vylpes/feature/command-user-only
Limit commands to specific users
2020-10-28 18:08:01 +00:00
vylpes 80122c9bba Fix hound violations 2020-10-28 17:54:40 +00:00
vylpes d8860212d2 Fix hound violations 2020-10-28 17:53:56 +00:00
Vylpes 44f2f63499 Add documentation about user-specific command settings 2020-10-26 20:33:10 +00:00
Vylpes fd61bbf448 Added the ability to make the bot only run for specific users 2020-10-26 20:29:14 +00:00
38 changed files with 4829 additions and 800 deletions

16
.env.template Normal file
View file

@ -0,0 +1,16 @@
# 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!
FOLDERS_COMMANDS=commands
FOLDERS_EVENTS=events
COMMANDS_DISABLED=
COMMANDS_DISABLED_MESSAGE=This command is disabled.

2
.eslintignore Normal file
View file

@ -0,0 +1,2 @@
node_modules
dist

19
.eslintrc Normal file
View file

@ -0,0 +1,19 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"globals": {
"exports": "writable",
"module": "writable",
"require": "writable",
"process": "writable",
"console": "writable"
}
}

9
.gitignore vendored
View file

@ -106,8 +106,13 @@ dist
# VylBot-Core Testing Files
commands/
events/
bot.js
config.json
/bot.ts
!tests/__mocks/commands
!tests/__mocks/events
# Linux Environment Files
*.swp
# macOS Environment Files
.DS_Store

17
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,17 @@
image: node
stages:
- lint
- test
eslint:
stage: lint
script:
- npm install
- npm run lint
jest:
stage: test
script:
- npm install
- npm test

View file

@ -1,4 +1,4 @@
bot.js
config.json
.env
bot.ts
commands/
events/

View file

@ -61,7 +61,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
ethan@vylpes.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View file

@ -12,6 +12,225 @@ This project and everyone participating in it is governed by the VylBot Core Cod
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below.
You can ask a question about the project by emailing us at `ethan@vylpes.com`.
You can ask a question about the project in the `#development` channel in the [Discord Server](https://discord.gg/UyAhAVp).
## What you should know
### Javascript and Node
VylBot Core uses [NodeJS](https://nodejs.org/), and therefore Javascript, as its runtime. You should know how to use this.
### DiscordJS
VylBot Core uses [DiscordJS](https://discord.js.org/) as its framework. Although you could contribute without much knowledge of this if you know NodeJS well, however it is recommended to know this.
## Conventions
There are a few conventions that have developed over time for this project. When you create a pull request a check will be ran making sure that your code follows these conventions.
We won't accept pull requests unless these checks pass. If yours fail, simply fix what the bot says until it passes and then get a repo member to review your code.
The rules for the code is based upon [Vylpes' Config Repo](https://github.com/vylpes/Config)
* Variable names should use **Camel Case**
* Functions should put **braces on the same line**
* **No comma dangle**, i.e. having a commma after the last item in an object
* **Arrow body style** should have braces around the body only when needed
* **Arrow parameters** should have brackets around them only when needed
* **Arrow spacing** should have a space around the arrow (' => ')
* **No var** should be used, instead use either let or const when appropriate
## How You Can Contribute
### Reporting Bugs
This section guides you through submitting a bug report for VylBot Core. Following these guidelines helps maintainers and the community understand your report. reproduce the behaviour, and find related reports.
When you are creating a bug report, please include as many details as possible.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before Submitting A Bug Report
* **Perform a search** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
#### How You Can Submit A (Good) Bug Report
Bugs are tracked as GitHub issues. After you've determined the bug you're reporting hasn't got a pre-existing **open** issue already, create an issue and provide information from below.
* **Use a clear and descriptive title** for the issue to indentify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started VylBot Core (if you're using your own instance), which command exactly you used, and the output which the bot replied with. If its your own instance, provide information on what the terminal output said, if any.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pastable snippets, which you use in those examples. If you're providing snippets in the issue, use Markdown code blocks.
* **Describe the behaviour you observed after following the steps** and point out what exactly is the problem with that behaviour.
* **Explain which behaviour you expected to see instead and why.**
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem.
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
Provide more context by answering these questions:
* **Did the problem start happening recently** (e.g. after updating to a new version of VylBot Core) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of VylBot Core?** What's the most recently version in which the problem doesn't happen? You can download older versions of VylBot Core from the releases page.
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of VylBot Core are you using?** You can get the exact version by running the `about` command.
* **Do you have any custom commands added to the commands folder?** If you do, provide information on what the command does, and a link to the command file if it exists.
> **Note:** We do not provide any support on issues caused by custom commands. You are welcome to create an issue if you believe the issue is to do with the base code, but if the error is to do with that custom command we are unable to fix that, you will need to contact the author of that command.
* **What's the name and version of the OS you're using?**
* **Are you running VylBot Core in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
* **What version of node do you have installed?** You can get this version by running the `node -v` command in your terminal.
* **What does your `config.json` file look like?**
> **Note:** remember to **not** give out your bot tokens which are inside of the `config.json` file. If you're giving a copy of your configuration remember to delete the tokens from the string and leave the empty.
* **Are you running the bot in live or dev mode?**
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for VylBot Core, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
When you are creating an enhancement suggestion, please include as many details as possible. Fill out the suggestion with the steps that you imagine you would take if the feature you're requesting existed.
#### Before Submitting an Enhancement Suggestion
* **Check if the feature already exists.** Make sure to check on the latest version and if you can get the desired behaviour using the config options inside of `config.json`
* **Perform a search** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion?
Enhancement suggestions are tracked as GitHub issues. After you've determined the feature doesn't already exist or been suggested before, create an issue on that repository and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps.** Include copy/pastable snippets which you use in those examples, as Markdown code blocks.
* **Describe the current behaviour** and **explain which behaviour you expected to see instead** and why.
* **Include screnshots and animated GIFs** which help you demonstrate the steps or point out the part of VylBot Core which the suggestion is related to.
* **Explain why this enhancement would be useful** to most VylBot Core users and isn't something that can or should be implemented as a custom command.
* **List some other bots where this enhancement exists.**
* **Specify which version of VylBot Core you're using.** You can get the exact version by running the `about` command.
* **Specify the name and version of the OS you're using.**
### Your First Code Contribution
Unsure where to begin contributing to VylBot Core? You can start by looking through these `good first` and `help wanted` issues:
* [Good first issue](https://github.com/vylpes/vylbot-core/labels/good%20first%20issue) - issues which should only require a few lines of code, and a test or two.
* [Help wanted](https://github.com/vylpes/vylbot-core/labels/help%20wanted) - issues which should be a bit more involved than `good first` issues.
#### Prerequisites
In order to download necessary tools, clone the repository, and install dependencies via `yarn` you need network access.
You'll need the following tools:
* Git
* NodeJS
Install and build all of the dependencies using `yarn`
```bash
cd vylbot-core
yarn install
cp .env.template .env
```
And then use your text editor of choice to fill in the `.env` file.
#### Build and Run
If you want to understand how VylBot Core works or want to debug an issue, you'll want to get the source, build it, and run the tool locally.
First, fork the VylBot Core repository so that you can make a pull request. Then, clone your fork locally:
```bash
git clone https://github.com/<your-github-account>/vylbot-core.git
```
Occasionally, you will want to merge changes in the upstream repository (the official code repo) with your fork.
```bash
cd vylbot-core
git checkout master
git pull https://github.com/vylpes/vylbot-core.git master
```
Manage any merge conflicts, commit them, and then push them to your fork.
Go into `VylBot Core` and start the bot in dev mode with `npm run dev` or `node bot dev`
> **Note:** If you have `nodemon` installed on your system you can use this during development so it auto restarts when you make code changes. Instead of the commands above you can then run `nodemon bot dev`.
#### Pull Requests
The process described here has several goals:
* Maintain VylBot Core's quality
* Fix problems that are important to users
* Engage the community in working toward the best possible VylBot Core
* Enable a sustainable system for VylBot Core's maintainers to review contributions
Please follow these steps to have your contribution considered by maintainers:
* You mention the issue id which this pull request aims to fix
* After you submit your pull request, verify that all status checks are passing.
> **Note**: If a check fails the pull request it is important that you go and fix these issues, or let us know that you no longer want to work on this issue by commenting on the pull request. Doing this will give you a better chance of having your pull request merged.
* When the checks have passed a maintainer will review your code and ask for any improvements or questions, and will merge it if they are satisifed.
While the prerequesites above must be satisifed prior to having your pull reuqest accepted, the reviewer(s) may ask you to complete additional design ork, tests, or other changes before your pull request can be ultimately accepted.
#### JavaScript Styleguide
All JavaScript code is linted with `eslint`.
* Prefer camelcase for variable names
* Prefer braces `{` to be on the same line
* Prefer no comma `,` dangle
* Prefer arrow function bodies to have brances `{}` only when needed
* Prefer arrow function parameters to have brackets `()` only when needed
* Prefer arrow function arrows `=>` to have a space before and after it
* Prefer `let` and `const` over `var`
* Prefer template strings over string concatenation
* Prefer comma at the end of the line
As well as eslint's recommended defaults.
Example
```ts
// hello.ts
import { Command, ICommandContext } from "vylbot-core";
export class hello extends Command {
constructor() {
super();
}
public override execute(context: ICommandContext) {
context.message.reply("Hello");
}
}
```
# Contributing to VylBot Core
First off, thanks for taking the time to contribute!
The following is a set of guidelines for contributing to VylBot Core. These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request.
## Code of Conduct
This project and everyone participating in it is governed by the VylBot Core Code of Conduct. By participating, you are expected to uphold this code.
## Questions about VylBot Core
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below.
You can ask a question about the project in the `#development` channel in the [Discord Server](https://discord.gg/UyAhAVp).
## What you should know
@ -39,6 +258,8 @@ The rules for the code is based upon [Vylpes' Config Repo](https://github.com/vy
* **Arrow parameters** should have brackets around them only when needed
* **Arrow spacing** should have a space around the arrow (' => ')
* **No var** should be used, instead use either let or const when appropriate
* **Template Strings** should be used over string concatenation
* **Comma Style** should have the commas separating items in a list at the end of the line
## How You Can Contribute
@ -193,6 +414,8 @@ All JavaScript code is linted with `eslint`.
* Prefer arrow function parameters to have brackets `()` only when needed
* Prefer arrow function arrows `=>` to have a space before and after it
* Prefer `let` and `const` over `var`
* Prefer template strngs over string concatenation
* Prefer commas separating items in a list to be at the end of the line
As well as eslint's recommended defaults.
@ -202,218 +425,14 @@ Example
function ban (member) {
let reason = "Example reason";
member.ban(reason).then(() => {
// handle then here
}).catch(err => {
// handle error here
});
}# Contributing to VylBot Core
First off, thanks for taking the time to contribute!
The following is a set of guidelines for contributing to VylBot Core. These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request.
## Code of Conduct
This project and everyone participating in it is governed by the VylBot Core Code of Conduct. By participating, you are expected to uphold this code.
## Questions about VylBot Core
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below.
You can ask a question about the project in the `#development` channel in the [Discord Server](https://discord.gg/UyAhAVp).
## What you should know
### Javascript and Node
VylBot Core uses [NodeJS](https://nodejs.org/), and therefore Javascript, as its runtime. You should know how to use this.
### DiscordJS
VylBot Core uses [DiscordJS](https://discord.js.org/) as its framework. Although you could contribute without much knowledge of this if you know NodeJS well, however it is recommended to know this.
## Conventions
There are a few conventions that have developed over time for this project. When you create a pull request a check will be ran making sure that your code follows these conventions.
We won't accept pull requests unless these checks pass. If yours fail, simply fix what the bot says until it passes and then get a repo member to review your code.
The rules for the code is based upon [Vylpes' Config Repo](https://github.com/vylpes/config)
* Variable names should use **Camel Case**
* Functions should put **braces on the same line**
* **No comma dangle**, i.e. having a commma after the last item in an object
* **Arrow body style** should have braces around the body only when needed
* **Arrow parameters** should have brackets around them only when needed
* **Arrow spacing** should have a space around the arrow (' => ')
* **No var** should be used, instead use either let or const when appropriate
## How You Can Contribute
### Reporting Bugs
This section guides you through submitting a bug report for VylBot Core. Following these guidelines helps maintainers and the community understand your report. reproduce the behaviour, and find related reports.
When you are creating a bug report, please include as many details as possible.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before Submitting A Bug Report
* **Perform a search** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
#### How You Can Submit A (Good) Bug Report
Bugs are tracked as GitHub issues. After you've determined the bug you're reporting hasn't got a pre-existing **open** issue already, create an issue and provide information from below.
* **Use a clear and descriptive title** for the issue to indentify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started VylBot Core (if you're using your own instance), which command exactly you used, and the output which the bot replied with. If its your own instance, provide information on what the terminal output said, if any.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pastable snippets, which you use in those examples. If you're providing snippets in the issue, use Markdown code blocks.
* **Describe the behaviour you observed after following the steps** and point out what exactly is the problem with that behaviour.
* **Explain which behaviour you expected to see instead and why.**
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem.
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
Provide more context by answering these questions:
* **Did the problem start happening recently** (e.g. after updating to a new version of VylBot Core) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of VylBot Core?** What's the most recently version in which the problem doesn't happen? You can download older versions of VylBot Core from the releases page.
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of VylBot Core are you using?** You can get the exact version by running the `about` command.
* **Do you have any custom commands added to the commands folder?** If you do, provide information on what the command does, and a link to the command file if it exists.
> **Note:** We do not provide any support on issues caused by custom commands. You are welcome to create an issue if you believe the issue is to do with the base code, but if the error is to do with that custom command we are unable to fix that, you will need to contact the author of that command.
* **What's the name and version of the OS you're using?**
* **Are you running VylBot Core in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
* **What version of node do you have installed?** You can get this version by running the `node -v` command in your terminal.
* **What does your `config.json` file look like?**
> **Note:** remember to **not** give out your bot tokens which are inside of the `config.json` file. If you're giving a copy of your configuration remember to delete the tokens from the string and leave the empty.
* **Are you running the bot in live or dev mode?**
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for VylBot Core, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
When you are creating an enhancement suggestion, please include as many details as possible. Fill out the suggestion with the steps that you imagine you would take if the feature you're requesting existed.
#### Before Submitting an Enhancement Suggestion
* **Check if the feature already exists.** Make sure to check on the latest version and if you can get the desired behaviour using the config options inside of `config.json`
* **Perform a search** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion?
Enhancement suggestions are tracked as GitHub issues. After you've determined the feature doesn't already exist or been suggested before, create an issue on that repository and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps.** Include copy/pastable snippets which you use in those examples, as Markdown code blocks.
* **Describe the current behaviour** and **explain which behaviour you expected to see instead** and why.
* **Include screnshots and animated GIFs** which help you demonstrate the steps or point out the part of VylBot Core which the suggestion is related to.
* **Explain why this enhancement would be useful** to most VylBot Core users and isn't something that can or should be implemented as a custom command.
* **List some other bots where this enhancement exists.**
* **Specify which version of VylBot Core you're using.** You can get the exact version by running the `about` command.
* **Specify the name and version of the OS you're using.**
### Your First Code Contribution
Unsure where to begin contributing to VylBot Core? You can start by looking through these `good first` and `help wanted` issues:
* [Good first issue](https://github.com/GetGravitySoft/VylBot Core/labels/good%20first%20issue) - issues which should only require a few lines of code, and a test or two.
* [Help wanted](https://github.com/GetGravitySoft/VylBot Core/labels/help%20wanted) - issues which should be a bit more involved than `good first` issues.
#### Prerequisites
In order to download necessary tools, clone the repository, and install dependencies via `npm` you need network access.
You'll need the following tools:
* Git
* NodeJS
Install and build all of the dependencies using `npm`
```bash
cd VylBot Core
npm install
cp config.json.template config.json
```
And then use your text editor of choice to fill in the `config.json` file.
#### Build and Run
If you want to understand how VylBot Core works or want to debug an issue, you'll want to get the source, build it, and run the tool locally.
First, fork the VylBot Core repository so that you can make a pull request. Then, clone your fork locally:
```bash
git clone https://github.com/<your-github-account>/VylBot Core.git
```
Occasionally, you will want to merge changes in the upstream repository (the official code repo) with your fork.
```bash
cd VylBot Core
git checkout master
git pull https://github.com/getgravitysoft/VylBot Core.git master
```
Manage any merge conflicts, commit them, and then push them to your fork.
Go into `VylBot Core` and start the bot in dev mode with `npm run dev` or `node bot dev`
> **Note:** If you have `nodemon` installed on your system you can use this during development so it auto restarts when you make code changes. Instead of the commands above you can then run `nodemon bot dev`.
#### Pull Requests
The process described here has several goals:
* Maintain VylBot Core's quality
* Fix problems that are important to users
* Engage the community in working toward the best possible VylBot Core
* Enable a sustainable system for VylBot Core's maintainers to review contributions
Please follow these steps to have your contribution considered by maintainers:
* You mention the issue id which this pull request aims to fix
* After you submit your pull request, verify that all status checks are passing.
> **Note**: If a check fails the pull request it is important that you go and fix these issues, or let us know that you no longer want to work on this issue by commenting on the pull request. Doing this will give you a better chance of having your pull request merged.
* When the checks have passed a maintainer will review your code and ask for any improvements or questions, and will merge it if they are satisifed.
While the prerequesites above must be satisifed prior to having your pull reuqest accepted, the reviewer(s) may ask you to complete additional design ork, tests, or other changes before your pull request can be ultimately accepted.
#### JavaScript Styleguide
All JavaScript code is linted with `eslint`.
* Prefer camelcase for variable names
* Prefer braces `{` to be on the same line
* Prefer no comma `,` dangle
* Prefer arrow function bodies to have brances `{}` only when needed
* Prefer arrow function parameters to have brackets `()` only when needed
* Prefer arrow function arrows `=>` to have a space before and after it
* Prefer `let` and `const` over `var`
As well as eslint's recommended defaults.
Example
```js
function ban (member) {
let reason = "Example reason";
let args = [
"one",
"two"
];
member.ban(reason).then(() => {
// handle then here
}).catch(err => {
// handle error here
});
}
}

109
README.md
View file

@ -4,41 +4,106 @@ Discord bot client based upon Discord.js
## Installation
Download the latest version from the [releases page](https://github.com/Vylpes/vylbot-core/releases).
Download the latest version from the [releases page](https://gitlab.vylpes.com/Vylpes/vylbot-core/-/releases).
Copy the config template file and fill in the strings.
Copy the config template file and fill in the values.
```json
{
"token": "",
"prefix": "",
"commands": [
""
],
"events": [
""
]
}
```env
BOT_TOKEN={TOKEN}
BOT_PREFIX=v!
FOLDERS_COMMANDS=commands
FOLDERS_EVENTS=events
COMMANDS_DISABLED=
COMMANDS_DISABLED_MESSAGE=This command is disabled.
```
* **Token:** Your bot's token
* **Prefix** The command prefix
* **Commands:** An array of the folders which contain your commands
* **Events:** An array of the folders which contain your events
* **BOT_TOKEN:** Your bot's token, replace {TOKEN} with your bot token.
* **BOT_PREFIX:** The command prefix.
* **FOLDERS_COMMANDS:** The folder which contains your commands.
* **FOLDERS_EVENTS:** The folder which contains your events.
* **COMMANDS_DISABLED:** List of command file names that won't run, separated by commas.
* **COMMANDS_DISABLED_MESSAGE:** The message which is replied to the user who tries to run a disabled command.
Make sure that you **DO NOT** put your .env file into VCS!
## Usage
Implement the client using something like:
```js
const vylbot = require('vylbot-core');
const config = require('config.json');
```ts
// bot.ts
const client = new vylbot.client(config);
import { CoreClient } from "vylbot-core";
const client = new CoreClient();
client.start();
```
See the `docs` folder for more information on how to use vylbot-core
### Writing Commands
The code below will reply to the user with 'PONG' when they type {PREFIX}ping
```ts
// Ping.ts
import { Command, ICommandContext } from "vylbot-core";
export default class Ping extends Command {
constructor() {
super();
this._roles = [ "Moderator" ];
this._category = "General";
}
public override execute(context: ICommandContext) {
context.message.reply('PONG');
}
}
```
* **roles**: An array containing what roles the user needs in order to run the command.
* **category**: The category the role is part of, useful for categorising commands together in a help command.
The `context` parameter contains the following:
* **name**: The command name
* **args**: An array of arguments supplied with the command
* **message**: The Discord Message object for the command message sent
### Writing Events
The code below will log to the console 'Member Joined: {USERNAME}' when a member joins a server the bot is in
```ts
// Moderation.ts
import { Event } from "vylbot-core";
import { GuildMember } from "discord.js";
export class Moderation extends Event {
public override guildMemberAdd(member: GuildMember) {
console.log(`Member Joined: ${member.tag}`);
}
}
```
The following events are supported:
* channelCreate(channel: Channel)
* channelDelete(channel: Channel | PartialDMChannel)
* channelUpdate(oldChannel: Channel, newChannel: Channel)
* guildBanAdd(guild: Guild, user: User)
* guildBanRemove(guild: Guild, user: User)
* guildCreate(guild: Guild)
* guildMemberAdd(member: GuildMember)
* guildMemberRemove(member: GuildMember | PartialGuildMember)
* guildMemberUpdate(oldMember: GuildMember | PartialGuildMember, newMember: GuildMember)
* message(message: Message)
* messageDelete(message: Message | PartialMessage)
* messageUpdate(oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage)
* ready()
All parameters are supplied from discord.js
## Contributing

View file

@ -1,132 +0,0 @@
# VylBot Core Documentation
Welcome to the VylBot Core documentation. In this file we will explain how to setup and use VylBot Core in your project.
## Contents
1. Initial Setup
2. Configuring the client
3. Creating a command
4. Handling an event
## 1. Initial Setup
To setup the package, download it from `npm`
```bash
cd <project-folder>
npm install vylbot-core
```
Add to your .js file
```js
const vylbot = require('vylbot-core');
const config = require('config.json');
const client = new vylbot.client(config);
client.start();
```
## 2. Configuring the client
When creating a new `vylbot.client` object you need to pass through some json configuration. This can be either from a .json file or directly written.
> **Note:** We recommend using a .json file and adding it to your `.gitignore` file to prevent accidentally publishing your bot token.
An example configuration:
```json
{
"token": "<YOUR-BOT-TOKEN>",
"prefix": "!",
"commands": [
"commands"
],
"events": [
"events"
]
}
```
Make sure the folders that you set for `commands` and `events` exist and are at your project's current working directory.
## 3. Creating a command
A basic command goes as follows:
```js
// commands/test.js
const { command } = require('vylbot-core');
class test extends command {
constructor() {
super("test");
super.description = "Test description";
super.category = "general";
super.requiredConfigs = "link";
super.roles = "Moderator";
super.roles = "Admin";
}
test(context) {
context.message.channel.send("Hello there, " + context.config.link);
}
}
module.exports = test;
```
1. You create a class and export it using `module.exports` and make it extend the vylbot's `command` class.
2. In the `constructor()` you need to call `super(run)`, replacing `run` with a string of the name of the method which will run when the command runs.
3. Call any of the `super` variables to give it some description, the current ones you can use are: `description`, `category`, `usage`
4. If you want the command to only be allowed to run by people with a role, set the role name in `super.roles`
> **Note:** You can set more than one role to be required by setting `super.roles` again.
5. If you want the command to require a variable in the config, set the name in `super.requiredConfigs`
> **Note:** You can set more than one role to be required by setting `super.requiredConfigs` again.
6. Create a method using the name you set in `super(run)`, with the `context` as its parameter.
The `context` parameter will be a JSON object of:
```json
{
"command": "<Command Name>",
"arguments": "<Command Arguments>",
"client": "<Bot Client Object",
"message": "Discord.js Message Object",
"config": "The command's config (if any)"
}
```
The command's config will be stored under the command's name, for example, if a command called `test` has set `super.requiredConfigs = "link"`, it will be set in the config as `test.link`.
## 4. Handling an event
A basic event handling goes as follows:
```js
// events/message.js
const { event } = require('vylbot-core');
class message extends event {
constructor() {
super("message");
}
message(message) {
console.log("Message received: " + message.content);
}
}
module.exports = message;
```
1. You create a class and export it using `module.exports` and make it extend the vylbot's `event` class.
2. In the `constructor()` you need to call `super(run)`, replacing `run` with a string of the name of the method which will run when the event gets triggered.
3. Create a method using the name you set in `super(run)`, with the parameters being as per your event's paramaters in the discord.js documentation.
> **Note:** The name of the event file will determine what event it will be triggered on. For example, if you want to have an event trigger everytime a message is sent, put into your event folder a file called `message.js` and follow the steps above.

5
jest.config.js Normal file
View file

@ -0,0 +1,5 @@
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

107
package-lock.json generated
View file

@ -1,107 +0,0 @@
{
"name": "vylbot-core",
"version": "1.0.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@discordjs/collection": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
"integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
},
"@discordjs/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"discord.js": {
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.3.1.tgz",
"integrity": "sha512-mSFyV/mbvzH12UXdS4zadmeUf8IMQOo/YdunubG1wWt1xjWvtaJz/s9CGsFD2B5pTw1W/LXxxUbrQjIZ/xlUdw==",
"requires": {
"@discordjs/collection": "^0.1.6",
"@discordjs/form-data": "^3.0.1",
"abort-controller": "^3.0.0",
"node-fetch": "^2.6.0",
"prism-media": "^1.2.2",
"setimmediate": "^1.0.5",
"tweetnacl": "^1.0.3",
"ws": "^7.3.1"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"prism-media": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz",
"integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw=="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"ws": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
"integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw=="
}
}
}

View file

@ -1,15 +1,21 @@
{
"name": "vylbot-core",
"version": "1.0.5",
"version": "2.0.3",
"description": "A discord client based upon discord.js",
"main": "./src/index",
"main": "./dist/index",
"typings": "./dist",
"scripts": {
"test": "echo \"No Tests Specified\""
"build": "tsc",
"test": "jest",
"lint": "eslint .",
"publish": "np"
},
"author": "Vylpes",
"license": "MIT",
"funding": "https://ko-fi.com/vylpes",
"dependencies": {
"discord.js": "^12.3.1"
"discord.js": "^12.3.1",
"dotenv": "^10.0.0"
},
"bugs": "https://github.com/vylpes/vylbot-core/issues",
"homepage": "https://github.com/vylpes/vylbot-core",
@ -18,5 +24,15 @@
"bot",
"client"
],
"repository": "github:vylpes/vylbot-core"
"repository": "github:vylpes/vylbot-core",
"devDependencies": {
"@types/jest": "^26.0.24",
"@types/node": "^16.3.2",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"eslint": "^7.32.0",
"jest": "^27.0.6",
"ts-jest": "^27.0.4",
"typescript": "^4.3.5"
}
}

View file

@ -1,43 +0,0 @@
const { Client } = require('discord.js');
const { existsSync } = require('fs');
const events = require('./events');
const util = require('./util');
class client extends Client {
constructor(config) {
super();
this.config = config;
this.events = new events();
this.util = new util(this);
}
start() {
super.on("message", this.events.message);
super.on("ready", this.events.ready);
super.login(this._config.token);
this.util.loadEvents();
}
// Config
get config() {
return this._config;
}
set config(config) {
// Validate the config
if (this._config) throw "Config has already been set";
if (typeof config != "object") throw "Config is not a JSON object";
if (typeof config.token != "string") throw "Token is not a string";
if (typeof config.prefix != "string") throw "Prefix is not a string";
if (typeof config.commands != "object") throw "Commands is not a string";
if (typeof config.events != "object") throw "Events is not a string";
this._config = config;
}
}
module.exports = client;

32
src/client/client.ts Normal file
View file

@ -0,0 +1,32 @@
import { Client } from "discord.js";
import * as dotenv from "dotenv";
import { Events } from "./events";
import { Util } from "./util";
export class CoreClient extends Client {
private _events: Events;
private _util: Util;
constructor() {
super();
dotenv.config();
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", this._events.onMessage);
super.on("ready", this._events.onReady);
super.login(process.env.BOT_TOKEN);
this._util.loadEvents(this);
}
}

View file

@ -1,21 +0,0 @@
class event {
message(message) {
if (!message.guild) return;
if (message.author.bot) return;
let prefix = this.config.prefix;
if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) {
let args = message.content.substring(prefix.length).split(" ");
let name = args.shift();
this.util.loadCommand(name, args, message);
}
}
ready() {
console.log("Ready");
}
}
module.exports = event;

75
src/client/events.ts Normal file
View file

@ -0,0 +1,75 @@
import { Message } from "discord.js";
import { IBaseResponse } from "../contracts/IBaseResponse";
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): 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);
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");
}
}

View file

@ -1,66 +0,0 @@
const { stat, readdirSync } = require('fs');
const { config } = require('process');
class util {
constructor(client) {
this._client = client;
}
loadCommand(name, args, message) {
for (let c = 0; c < this._client.config.commands.length; c++) {
let folder = this._client.config.commands[c];
stat(`${process.cwd()}/${folder}/${name}.js`, (err, stat) => {
if (err == null) {
let commandFile = require(`${process.cwd()}/${folder}/${name}.js`);
let command = new commandFile();
let requiredConfigs = command.requiredConfigs;
for (let i = 0; i < requiredConfigs.length; i++) {
if (!this._client.config[name]) throw `${commandFile.name} requires ${requiredConfigs[i]} in it's configuration`;
if (!this._client.config[name][requiredConfigs[i]]) throw `${commandFile.name} requires ${requiredConfigs[i]} in it's configuration`;
}
let requiredRoles = command.roles;
for (let i = 0; i < requiredRoles.length; i++) {
if (!message.member.roles.cache.find(role => role.name == requiredRoles[i])) {
message.reply(`You require the \`${requiredRoles[i]}\` role to run this command`);
return;
}
}
command[command.run]({
"command": name,
"arguments": args,
"client": this._client,
"message": message,
"config": config
});
} else if (err.code === 'ENOENT') {
// FILE DOESN'T EXIST
}
});
}
}
loadEvents() {
for (let e = 0; e < this._client.config.events.length; e++) {
let folder = this._client.config.events[e];
let eventFiles = readdirSync(`${process.cwd()}/${folder}/`);
for (let i = 0; i < eventFiles.length; i++) {
let eventName = eventFiles[i].split('.')[0];
let file = require(`${process.cwd()}/${folder}/${eventName}.js`);
let event = new file;
this._client.on(eventName, event[event.run]);
}
}
}
}
module.exports = util;

131
src/client/util.ts Normal file
View file

@ -0,0 +1,131 @@
// 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";
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): 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;
if (existsSync(`${process.cwd()}/${folder}/`)) {
if (existsSync(`${process.cwd()}/${folder}/${name}.ts`)) {
const commandFile = require(`${process.cwd()}/${folder}/${name}.ts`).default;
const command = new commandFile() as Command;
const requiredRoles = command._roles;
if (!command._category) command._category = "none";
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,
}
// Run the command and pass the command context with it
command.execute(context);
return {
valid: true,
context: {
name: name,
args: args,
message: message,
}
}
} else {
return {
valid: false,
message: "File does not exist",
}
}
} else {
return {
valid: false,
message: "Command folder does not exist",
}
}
}
// Load the events
loadEvents(client: Client): IUtilResponse {
const folder = process.env.FOLDERS_EVENTS;
if (existsSync(`${process.cwd()}/${folder}/`)) {
const eventFiles = readdirSync(`${process.cwd()}/${folder}/`);
for (let i = 0; i < eventFiles.length; i++) {
if (eventFiles[i].includes('.ts')) {
const eventName = eventFiles[i].split('.')[0];
const file = require(`${process.cwd()}/${folder}/${eventName}.ts`).default;
const event = new file() as Event;
// Load events
client.on('channelCreate', event.channelCreate);
client.on('channelDelete', event.channelDelete);
client.on('channelUpdate', event.channelUpdate);
client.on('guildBanAdd', event.guildBanAdd);
client.on('guildBanRemove', event.guildBanRemove);
client.on('guildCreate', event.guildCreate);
client.on('guildMemberAdd', event.guildMemberAdd);
client.on('guildMemberRemove', event.guildMemberRemove);
client.on('guildMemberUpdate', event.guildMemberUpdate);
client.on('message', event.message);
client.on('messageDelete', event.messageDelete);
client.on('messageUpdate', event.messageUpdate);
client.on('ready', event.ready);
}
}
return {
valid: true,
}
} else {
return {
valid: false,
message: "Event folder does not exist",
}
}
}
}

View file

@ -0,0 +1,4 @@
export interface IBaseResponse {
valid: boolean;
message?: string;
}

View file

@ -0,0 +1,7 @@
import { Message } from "discord.js";
export interface ICommandContext {
name: string;
args: string[];
message: Message;
}

View file

@ -1,7 +0,0 @@
const discord = require('discord.js');
module.exports = {
client: require('./client/client'),
command: require('./type/command'),
event: require('./type/event')
}

5
src/index.ts Normal file
View file

@ -0,0 +1,5 @@
export { CoreClient } from "./client/client";
export { Command } from "./type/command";
export { Event } from "./type/event";
export { ICommandContext } from "./contracts/ICommandContext";

107
src/package-lock.json generated
View file

@ -1,107 +0,0 @@
{
"name": "vylbot-core",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@discordjs/collection": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
"integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
},
"@discordjs/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"discord.js": {
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.3.1.tgz",
"integrity": "sha512-mSFyV/mbvzH12UXdS4zadmeUf8IMQOo/YdunubG1wWt1xjWvtaJz/s9CGsFD2B5pTw1W/LXxxUbrQjIZ/xlUdw==",
"requires": {
"@discordjs/collection": "^0.1.6",
"@discordjs/form-data": "^3.0.1",
"abort-controller": "^3.0.0",
"node-fetch": "^2.6.0",
"prism-media": "^1.2.2",
"setimmediate": "^1.0.5",
"tweetnacl": "^1.0.3",
"ws": "^7.3.1"
}
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"prism-media": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz",
"integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw=="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"ws": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
"integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw=="
}
}
}

View file

@ -1,14 +0,0 @@
{
"name": "vylbot-core",
"version": "1.0.0",
"description": "A discord client based upon discord.js",
"main": "./src/index",
"scripts": {
"test": "echo \"No Tests Specified\""
},
"author": "Vylpes",
"license": "ISC",
"dependencies": {
"discord.js": "^12.3.1"
}
}

View file

@ -1,55 +0,0 @@
class command {
constructor(run) {
this.run = run;
this._roles = [];
this._requiredConfigs = [];
}
// Description
get description() {
return this._description;
}
set description(description) {
this._description = description;
}
// Category
get category() {
return this._category;
}
set category(category) {
this._category = category;
}
// Usage
get usage() {
return this._usage;
}
set usage(usage) {
this._usage = usage;
}
// Roles
get roles() {
return this._roles;
}
set roles(role) {
this._roles.push(role);
}
// Config
get requiredConfigs() {
return this._requiredConfigs;
}
set requiredConfigs(conf) {
this._requiredConfigs.push(conf);
}
}
module.exports = command;

16
src/type/command.ts Normal file
View file

@ -0,0 +1,16 @@
import { Message } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext";
export class Command {
public _roles: string[];
public _category?: string;
constructor() {
this._roles = [];
}
public execute(context: ICommandContext) {
}
}

View file

@ -1,7 +0,0 @@
class event {
constructor(run) {
this.run = run;
}
}
module.exports = event;

55
src/type/event.ts Normal file
View 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() {
}
}

View file

@ -0,0 +1,7 @@
import { Command } from "../../../src/type/command";
export default class noCategory extends Command {
constructor() {
super();
}
}

View file

@ -0,0 +1,8 @@
import { Command } from "../../../src/type/command";
export default class normal extends Command {
constructor() {
super();
this._category = "General";
}
}

View file

@ -0,0 +1,8 @@
import { Command } from "../../../src/type/command";
export default class roles extends Command {
constructor() {
super();
this._roles = [ "Moderator" ];
}
}

View file

@ -0,0 +1,5 @@
import { Event } from "../../../src/type/event";
export default class normal extends Event {
public override channelCreate() {}
}

139
tests/client/client.test.ts Normal file
View file

@ -0,0 +1,139 @@
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";
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");
});
});

185
tests/client/events.test.ts Normal file
View file

@ -0,0 +1,185 @@
import { Events } from "../../src/client/events";
import { Message, Client, TextChannel, Guild, SnowflakeUtil, DMChannel } from "discord.js";
import { Util } from "../../src/client/util";
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 events = new Events();
const result = await events.onMessage(message);
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 events = new Events();
const result = await events.onMessage(message);
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 events = new Events();
const result = await events.onMessage(message);
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 events = new Events();
const result = await events.onMessage(message);
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 events = new Events();
const result = await events.onMessage(message);
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 events = new Events();
const result = await events.onMessage(message);
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");
});
});

421
tests/client/util.test.ts Normal file
View file

@ -0,0 +1,421 @@
import { Util } from "../../src/client/util";
import { Client, Guild, Message, Role, SnowflakeUtil, TextChannel, User } from "discord.js";
import fs from "fs";
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',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeTruthy();
});
test('Given Member Is Null, Expect Failed Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: null
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Member is not part of message");
});
test('Given Folder Does Not Exist, Expect Failed Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(false);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Command folder does not exist");
});
test('Given File Does Not Exist, Expect Failed Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValueOnce(true)
.mockReturnValue(false);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("File does not exist");
});
test('Given User Does Have Role, Expect Successful Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("roles", [ "first" ], message);
expect(result.valid).toBeTruthy();
});
test('Given User Does Not Have Role, Expect Failed Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(false),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("roles", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("You require the `Moderator` role to run this command");
});
test('Given Command Category Is Null, Expect Successful Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("noCategory", [ "first" ], message);
expect(result.valid).toBeTruthy();
});
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: 'normal',
COMMANDS_DISABLED_MESSAGE: 'disabled',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const messageReply = jest.spyOn(message, 'reply');
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Command is disabled");
expect(messageReply).toBeCalledWith("disabled");
});
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: 'normal',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const messageReply = jest.spyOn(message, 'reply');
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Command is disabled");
expect(messageReply).toBeCalledWith("This command is disabled.");
});
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: 'anything',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeTruthy();
});
test('Given a different command is disabled with this one, Expect command to not fire', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
COMMANDS_DISABLED: 'normal,anything,',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
const message = {
member: {
roles: {
cache: {
find: jest.fn().mockReturnValue(true),
}
},
},
reply: jest.fn(),
} as unknown as Message;
const util = new Util();
const result = util.loadCommand("normal", [ "first" ], message);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Command is disabled");
});
});
describe('LoadEvents', () => {
test('Given Events Are Loaded, Expect Successful Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
fs.readdirSync = jest.fn().mockReturnValue(["normal.ts"]);
const client = {
on: jest.fn(),
} as unknown as Client;
const util = new Util();
const result = util.loadEvents(client);
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',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(true);
fs.readdirSync = jest.fn().mockReturnValue(["normal"]);
const client = {
on: jest.fn(),
} as unknown as Client;
const util = new Util();
const result = util.loadEvents(client);
const clientOn = jest.spyOn(client, 'on');
expect(result.valid).toBeTruthy();
expect(clientOn).toBeCalledTimes(0);
});
test('Given Event Folder Does Not Exist, Expect Failed Result', () => {
process.env = {
BOT_TOKEN: 'TOKEN',
BOT_PREFIX: '!',
FOLDERS_COMMANDS: 'commands',
FOLDERS_EVENTS: 'events',
}
process.cwd = jest.fn().mockReturnValue("../../tests/__mocks");
fs.existsSync = jest.fn().mockReturnValue(false);
fs.readdirSync = jest.fn().mockReturnValue(["normal.ts"]);
const client = {
on: jest.fn(),
} as unknown as Client;
const util = new Util();
const result = util.loadEvents(client);
expect(result.valid).toBeFalsy();
expect(result.message).toBe("Event folder does not exist");
});
});

78
tsconfig.json Normal file
View 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"
]
}

3248
yarn.lock Normal file

File diff suppressed because it is too large Load diff