mirror of
https://github.com/chenasraf/venom.git
synced 2026-05-17 17:28:08 +00:00
refactor: improvements to codebase
This commit is contained in:
@@ -15,4 +15,4 @@ indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
insert_final_newline = true
|
||||
41
.eslintignore
Normal file
41
.eslintignore
Normal file
@@ -0,0 +1,41 @@
|
||||
.eslintrc.js
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
**/test-results/**
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Project specific
|
||||
.env
|
||||
.vscode/
|
||||
**/logs/**
|
||||
59
.eslintrc.js
Normal file
59
.eslintrc.js
Normal file
@@ -0,0 +1,59 @@
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: [
|
||||
"@typescript-eslint",
|
||||
"eslint-comments",
|
||||
"jest",
|
||||
"promise",
|
||||
"unicorn",
|
||||
],
|
||||
extends: [
|
||||
"airbnb-typescript",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:eslint-comments/recommended",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:promise/recommended",
|
||||
"plugin:unicorn/recommended",
|
||||
"prettier",
|
||||
"prettier/react",
|
||||
"prettier/@typescript-eslint",
|
||||
],
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
// Too restrictive, writing ugly code to defend against a very unlikely scenario: https://eslint.org/docs/rules/no-prototype-builtins
|
||||
"no-prototype-builtins": "off",
|
||||
// https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
// Too restrictive: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/destructuring-assignment.md
|
||||
"react/destructuring-assignment": "off",
|
||||
// No jsx extension: https://github.com/facebook/create-react-app/issues/87#issuecomment-234627904
|
||||
"react/jsx-filename-extension": "off",
|
||||
// Use function hoisting to improve code readability
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{ functions: false, classes: true, variables: true },
|
||||
],
|
||||
// Makes no sense to allow type inferrence for expression parameters, but require typing the response
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error",
|
||||
{ allowExpressions: true, allowTypedFunctionExpressions: true },
|
||||
],
|
||||
"@typescript-eslint/no-use-before-define": [
|
||||
"error",
|
||||
{ functions: false, classes: true, variables: true, typedefs: true },
|
||||
],
|
||||
// Common abbreviations are known and readable
|
||||
"unicorn/prevent-abbreviations": "off",
|
||||
"unicorn/filename-case": "off",
|
||||
"unicorn/no-fn-reference-in-iterator": "off",
|
||||
"no-underscore-dangle": ["error", { "allowAfterThis": true }]
|
||||
},
|
||||
}
|
||||
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
Issue tracker is **ONLY** used for reporting bugs. New features should be discussed on our slack channel. Please use [stackoverflow](https://stackoverflow.com) for supporting issues.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- Tell us what should happen -->
|
||||
|
||||
## Current Behavior
|
||||
<!--- Tell us what happens instead of the expected behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context (Environment)
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Detailed Description
|
||||
<!--- Provide a detailed description of the change or addition you are proposing -->
|
||||
|
||||
## Possible Implementation
|
||||
<!--- Not obligatory, but suggest an idea for implementing addition or change -->
|
||||
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,14 +2,15 @@
|
||||
|
||||
<!-- PR Context:
|
||||
A quick overview of the "why" of this PR to help reviewers
|
||||
If applicable, provide links to GitHub tickets
|
||||
If applicable, provide links to GitHub issues
|
||||
-->
|
||||
[GitHub Ticket](https://github.com/jondeaves/venom/issues/XXX)
|
||||
[GitHub Issue](https://github.com/jondeaves/venom/issues/XXX)
|
||||
|
||||
## Description
|
||||
|
||||
<!-- Description of PR:
|
||||
List of bullet points and screenshots are appreciated
|
||||
List of bullet points are appreciated
|
||||
If adding new features to the bot then screenshots of the interactions with your own test bot would be very helpful
|
||||
If applicable, have you thought of updating existing docs or
|
||||
create a new one?
|
||||
-->
|
||||
|
||||
16
.markdownlint.yml
Normal file
16
.markdownlint.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
default: true
|
||||
|
||||
# Some services use a linebreak-sensitive renderer, e.g. GitHub comment and BitBucket
|
||||
line-length: false
|
||||
|
||||
# This project needs to be able to document a code block with a space
|
||||
no-space-in-code: false
|
||||
|
||||
no-trailing-punctuation:
|
||||
# Allow headings to end with question mark for FAQ
|
||||
punctuation: ".,;:"
|
||||
|
||||
# Prettier handles these:
|
||||
list-marker-space: false
|
||||
no-multiple-blanks: false
|
||||
39
.markdownlintignore
Normal file
39
.markdownlintignore
Normal file
@@ -0,0 +1,39 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
**/test-results/**
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Project specific
|
||||
.env
|
||||
.vscode/
|
||||
**/logs/**
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120
|
||||
}
|
||||
74
CODE-OF-CONDUCT.md
Normal file
74
CODE-OF-CONDUCT.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at hello@jondeaves.me. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [https://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: https://contributor-covenant.org
|
||||
[version]: https://contributor-covenant.org/version/1/4/
|
||||
198
CONTRIBUTING.md
Normal file
198
CONTRIBUTING.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Contributing to Venom
|
||||
|
||||
First off, thanks for taking the time to contribute! ❤️
|
||||
|
||||
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [I Have a Question](#i-have-a-question)
|
||||
- [I Want To Contribute](#i-want-to-contribute)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Suggesting Enhancements](#suggesting-enhancements)
|
||||
- [Your First Code Contribution](#your-first-code-contribution)
|
||||
- [Improving The Documentation](#improving-the-documentation)
|
||||
- [Styleguides](#styleguides)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Join The Project Team](#join-the-project-team)
|
||||
|
||||
## I Have a Question
|
||||
|
||||
Before you ask a question, it is best to search for existing [Issues](https://github.com/jondeaves/venom/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
|
||||
|
||||
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||
|
||||
- Open an [Issue](https://github.com/jondeaves/venom/issues/new).
|
||||
- Provide as much context as you can about what you're running into.
|
||||
- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.
|
||||
- Add the "question" label
|
||||
|
||||
We will then take care of the issue as soon as possible.
|
||||
|
||||
<!--
|
||||
You might want to create a separate issue tag for questions and include it in this description. People should then tag their issues accordingly.
|
||||
|
||||
Depending on how large the project is, you may want to outsource the questioning, e.g. to Stack Overflow or Gitter. You may add additional contact and information possibilities:
|
||||
- IRC
|
||||
- Slack
|
||||
- Gitter
|
||||
- Stack Overflow tag
|
||||
- Blog
|
||||
- FAQ
|
||||
- Roadmap
|
||||
- E-Mail List
|
||||
- Forum
|
||||
-->
|
||||
|
||||
## I Want To Contribute
|
||||
|
||||
### Legal Notice
|
||||
|
||||
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
#### Before Submitting a Bug Report
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](README.md).
|
||||
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/jondeaves/venom/issues?q=label%3Abug).
|
||||
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
|
||||
- Collect information about the bug:
|
||||
- Stack trace (Traceback)
|
||||
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||
- Possibly your input and the output
|
||||
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
|
||||
|
||||
#### How Do I Submit a Good Bug Report?
|
||||
|
||||
> You must never report security related issues, vulnerabilities or bugs to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to hello@jondeaves.me.
|
||||
|
||||
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
|
||||
|
||||
- Open an [Issue](https://github.com/jondeaves/venom/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to label the issue.)
|
||||
- Fill in as much of the template as possible. In particular;
|
||||
- Explain the behavior you would expect and the actual behavior.
|
||||
- Please provide as much context as possible and describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
|
||||
- Provide the information you collected in the previous section.
|
||||
|
||||
Once it's filed:
|
||||
|
||||
- The project team will triage the issue accordingly.
|
||||
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-detail`. Bugs with the `needs-detail` tag will not be addressed until they have more information added.
|
||||
- If the team is able to reproduce the issue, it will be moved onto the project board and will be picked up by a project member.
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for Venom, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
|
||||
|
||||
#### Before Submitting an Enhancement
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Read the [documentation](README.md) carefully and find out if the functionality is already covered, maybe by an individual configuration.
|
||||
- Perform a [search](https://github.com/jondeaves/venom/issues) 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.
|
||||
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
|
||||
|
||||
#### How Do I Submit a Good Enhancement Suggestion?
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](https://github.com/jondeaves/venom/issues).
|
||||
|
||||
- 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.
|
||||
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
|
||||
- **Explain why this enhancement would be useful** to most Venom users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
There are a few steps a developer can take to make contributing to this project go more smoothly.
|
||||
|
||||
- Given this project is a Discord bot it means that testing changes can be difficult without the right tools set up and so the first step to contributing would be to [setup your own test environments](docs/development/environments.md).
|
||||
- The project requires Node (We recommend the latest LTS) and Yarn.
|
||||
- You can reference the [README.md](README.md) for steps to get the project running.
|
||||
|
||||
#### VSCode
|
||||
|
||||
If you are using VSCode then we recommend installing the ESLint and Prettier plugins and you can use the below configuration in `.vscode/settings.json` to have Prettier auto-format code for you.
|
||||
|
||||
```json
|
||||
{
|
||||
"eslint.packageManager": "yarn",
|
||||
"javascript.format.enable": false,
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
```
|
||||
|
||||
### Improving The Documentation
|
||||
|
||||
If your change adds new features, configuration options or anything else that may require relevant documentation then it is important that those updates are done as part of your work. It is important that anything that goes into the main branch is complete, documented and usable by others.
|
||||
|
||||
## Styleguides
|
||||
|
||||
We follow [Angular's Commit Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#)
|
||||
|
||||
### Commit Message Format
|
||||
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
```txt
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
### Revert
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert:`, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
### Type
|
||||
|
||||
Must be one of the following:
|
||||
|
||||
- **feat**: A new feature
|
||||
- **fix**: A bug fix
|
||||
- **docs**: Documentation only changes
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
- **perf**: A code change that improves performance
|
||||
- **test**: Adding missing tests
|
||||
- **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||
generation
|
||||
- **misc**: Changes that don't quite fit into other categories but are worth mentioning in the CHANGELOG - use sparingly.
|
||||
|
||||
### Subject
|
||||
|
||||
The subject contains succinct description of the change:
|
||||
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- don't capitalize first letter
|
||||
- no dot (.) at the end
|
||||
|
||||
### Body
|
||||
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Footer
|
||||
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
|
||||
## Attribution
|
||||
|
||||
This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
40
README.md
40
README.md
@@ -5,21 +5,39 @@ This is the Discord bot for Creation Asylum.
|
||||
## Development
|
||||
|
||||
- Requires [NodeJS](https://nodejs.org/), recommend at least the latest LTS version.
|
||||
- Run `yarn` or `npm install` to install dependencies
|
||||
- Requires [Yarn](https://classic.yarnpkg.com/lang/en/), recommend latest stable 1.x
|
||||
- Run `yarn` to install dependencies
|
||||
|
||||
### VSCode
|
||||
|
||||
For VSCode install the following plugins;
|
||||
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
|
||||
You wil also want to use the below configuration in `.vscode/settings.json` to have Prettier auto-format code for you.
|
||||
|
||||
```json
|
||||
{
|
||||
"eslint.packageManager": "yarn",
|
||||
"javascript.format.enable": false,
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
At a minimum you need to provide the Discord bots Token, which can be found on the Bot tab of a Discord application. See table below for possible values.
|
||||
At a minimum you need to provide the Discord bots Token (which can be found on the Bot tab of a Discord application), MONGODB_URI and MONGODB_DB_NAME values. See table below for possible values.
|
||||
|
||||
| key | description | example |
|
||||
|-------------------|-------------|---------|
|
||||
| BOT_TRIGGER | Prefix of message to let bot know you are speaking to it
|
||||
| DISCORD_BOT_TOKEN | Discord bots Token
|
||||
| NODE_ENV | What environment the bot is running in | `production`, `development` or `test` |
|
||||
| LOG_LEVEL | What level of logs should be displayed in console | `error`, `warn`, `info`, `verbose`, `debug` or `silly` |
|
||||
| MONGODB_URI | Full connection string for MongoDB database, include db_name if user is scoped to single database | mongodb://user:password@localhost:27017/venom_db |
|
||||
| MONGODB_DB_NAME | The name of the database to use for this project | venom_db |
|
||||
| key | description | example |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
||||
| BOT_TRIGGER | Prefix of message to let bot know you are speaking to it |
|
||||
| DISCORD_BOT_TOKEN | Discord bots Token |
|
||||
| NODE_ENV | What environment the bot is running in | `production`, `development` or `test` |
|
||||
| LOG_LEVEL | What level of logs should be displayed in console | `error`, `warn`, `info`, `verbose`, `debug` or `silly` |
|
||||
| MONGODB_URI | Full connection string for MongoDB database, include db_name if user is scoped to single database | mongodb://user:password@localhost:27017/venom_db |
|
||||
| MONGODB_DB_NAME | The name of the database to use for this project | venom_db |
|
||||
|
||||
### Bot commands
|
||||
|
||||
To add a command you create a Typescript file in `src/bot/commands/[filename].ts` and ensure it implements the \src/bot/commands/ICommand.ts` interface. You can see the other files in this directory for implementation examples. Also ensure you export this file in `src/bot/commands/index.ts`.
|
||||
To add a command you create a Typescript file in `src/bot/commands/[filename].ts` and ensure it implements the `src/bot/commands/ICommand.ts` interface. You can see the other files in this directory for implementation examples. Also ensure you export this file in `src/bot/commands/index.ts`.
|
||||
|
||||
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
};
|
||||
41
docs/development/environments.md
Normal file
41
docs/development/environments.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Developer Environments
|
||||
|
||||
Due to the nature of developing a Discord bot it can be helpful to have your own versions of certain systems used by the bot, in order to manually verify your changes.
|
||||
|
||||
## Discord Test Bot
|
||||
|
||||
Creation your own Bot will be critical to test any new changes made to how the bot is interacted with and indeed testing that most things work.
|
||||
|
||||
### Creation a Bot
|
||||
|
||||
- Visit the [Discord developer page](https://discord.com/developers/applications)
|
||||
- Click "New Application" in the top right
|
||||
- Name your Application, we like to use the format `VenomBotTest[Your name or initials]`, this helps identify the bot easily.
|
||||
- Once you hit create you will be presented with your application page. Take a note of the `CLIENT ID` that is found just next to your Applications Icon.
|
||||
- Setting an Icon isn't necessary but can be helpful to identify it.
|
||||
- In the left navigation select the "Bot" tab and then select "Add Bot" on the right of the new content.
|
||||
- Confirm you wish to add a bot.
|
||||
- Disable the "Public Bot" flag, given this is a test bot you don't want others finding it.
|
||||
- Copy the Bot token that can be found next to the Bots icon (This will be used in the environment variables ). You can just click the "Copy" button.
|
||||
- Scroll to the bottom of this page and enable "Permissions" for your bot, at this time only Text Permissions are needed and these are;
|
||||
- Send Messages
|
||||
- [You can add additional permissions if the feature you are development would require this]
|
||||
|
||||
### Adding your bot to a server
|
||||
|
||||
The best way to test new work is to add the test bot you created above to a different Discord server, possibly even a private server just for this purpose. Once you have a server that you are able to invite bots to then [follow this guide](https://discordjs.guide/preparations/adding-your-bot-to-servers.html) to add your bot. You will need the `CLIENT ID` from before.
|
||||
|
||||
## MongoDB Instance
|
||||
|
||||
The bot uses MongoDB to store it's data, as a consequence of this you will need to have your own instance of MongoDB if you wish to run the bot yourself. If you don't want to install an instance on your own machine then we can recommend [mLab](https://mlab.com/), as this will be as close to the production version as possible.
|
||||
|
||||
## Final thoughts
|
||||
|
||||
Once the above has been completed you can then run your bot locally, ensuring you set the values in `src/.env` to match those provided throughtout the steps above. You may also wish to set the following values.
|
||||
|
||||
- Set `BOT_TRIGGER` to `venom test ` or something similarly unique. Ensuring you don't confuse your bot with another.
|
||||
- Set `LOG_LEVEL` to `verbose` to see all possible log information.
|
||||
- Set `ENVIRONMENT` to `development`.
|
||||
- As mentioned above, set `DISCORD_BOT_TOKEN` to the value taken from the "Bot" tab of the Discord developer Application.
|
||||
- Set `MONGODB_URI` to the value of either your local Mongo instance or mLab. The format looks like; `mongodb://[username]:[password]@[host]:[port]/[db_name]`
|
||||
- Set `MONGODB_DB_NAME` to the database name you are using locally, matching the same value in the previous environment variable.
|
||||
3
extensions.json
Normal file
3
extensions.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
}
|
||||
50
package.json
50
package.json
@@ -13,7 +13,15 @@
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"start": "node dist/main.js",
|
||||
"start:dev": "nodemon"
|
||||
"start:dev": "nodemon",
|
||||
"lint": "run-p lint:code lint:markdown",
|
||||
"lint:code": "eslint src/**/*.{ts,js}",
|
||||
"lint:markdown": "markdownlint **/*.md",
|
||||
"format": "prettier \"**/*.ts\" --ignore-path ./.prettierignore --write",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"release:patch": "standard-version --release-as patch",
|
||||
"release:minor": "standard-version --release-as minor",
|
||||
"release:major": "standard-version --release-as major"
|
||||
},
|
||||
"dependencies": {
|
||||
"discord.js": "~12.2.0",
|
||||
@@ -24,17 +32,51 @@
|
||||
"winston": "~3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^9.1.1",
|
||||
"@types/dotenv": "~8.2.0",
|
||||
"@types/mongodb": "~3.5.25",
|
||||
"@types/node": "~14.0.27",
|
||||
"@types/winston": "~2.4.4",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
||||
"chalk": "^4.1.0",
|
||||
"commitlint": "^9.1.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-config-airbnb-typescript": "^9.0.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-formatter-pretty": "^4.0.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-jest": "^23.20.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.20.6",
|
||||
"eslint-plugin-unicorn": "^21.0.0",
|
||||
"husky": "^4.2.5",
|
||||
"lint-staged": "^10.2.11",
|
||||
"markdownlint-cli": "^0.23.2",
|
||||
"nodemon": "~2.0.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "~2.0.5",
|
||||
"standard-version": "^8.0.2",
|
||||
"ts-node": "~8.10.2",
|
||||
"tsconfig-paths": "~3.9.0",
|
||||
"tslint": "~6.1.3",
|
||||
"tslint-config-prettier": "~1.18.0",
|
||||
"tslint-react": "~5.0.0",
|
||||
"typescript": "~3.9.7"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
||||
"pre-commit": "./node_modules/.bin/lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js}": [
|
||||
"yarn format",
|
||||
"yarn lint:code",
|
||||
"yarn lint:markdown"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10 <=14",
|
||||
"yarn": ">=1.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
18
prettier.config.js
Normal file
18
prettier.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
endOfLine: 'lf',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: "all",
|
||||
printWidth: 120,
|
||||
overrides: [
|
||||
{
|
||||
files: ".editorconfig",
|
||||
options: { parser: "yaml" },
|
||||
},
|
||||
{
|
||||
files: ["CODE-OF-CONDUCT.md", "CONTRIBUTING.md", "LICENSE.md", "README.md"],
|
||||
options: { parser: "markdown" },
|
||||
},
|
||||
],
|
||||
}
|
||||
62
src/app.ts
62
src/app.ts
@@ -1,5 +1,5 @@
|
||||
import { exit } from 'process';
|
||||
import Discord from 'discord.js';
|
||||
import { exit } from 'process';
|
||||
|
||||
import container from './inversity.config';
|
||||
|
||||
@@ -7,12 +7,14 @@ import ConfigService from './core/services/config.service';
|
||||
import LoggerService from './core/services/logger.service';
|
||||
import MongoService from './core/services/mongo.service';
|
||||
|
||||
import ICommand from './bot/commands/ICommand';
|
||||
import rawCommands from './bot/commands';
|
||||
import ICommand from './bot/commands/ICommand';
|
||||
|
||||
export default class App {
|
||||
private _configService: ConfigService = container.resolve<ConfigService>(ConfigService);
|
||||
|
||||
private _loggerService: LoggerService = container.resolve<LoggerService>(LoggerService);
|
||||
|
||||
private _dbService: MongoService = container.resolve<MongoService>(MongoService);
|
||||
|
||||
private _discordClient: Discord.Client;
|
||||
@@ -20,15 +22,15 @@ export default class App {
|
||||
public async init(): Promise<void> {
|
||||
try {
|
||||
await this._dbService.connect();
|
||||
} catch (err) {
|
||||
this._loggerService.log('error', 'Cannot connect to database, exiting.');
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Cannot connect to database, exiting.', { error });
|
||||
exit(1);
|
||||
}
|
||||
|
||||
this._discordClient = new Discord.Client();
|
||||
const commandList = new Discord.Collection<string, ICommand>();
|
||||
|
||||
rawCommands.forEach(rawCommand => {
|
||||
rawCommands.forEach((rawCommand) => {
|
||||
commandList.set(rawCommand.name, rawCommand);
|
||||
});
|
||||
|
||||
@@ -38,7 +40,7 @@ export default class App {
|
||||
});
|
||||
|
||||
// Triggers on every message the bot can see
|
||||
this._discordClient.on('message', async message => {
|
||||
this._discordClient.on('message', async (message) => {
|
||||
const prefix = this._configService.get('BOT_TRIGGER');
|
||||
|
||||
// If the message either doesn't start with the prefix or was sent by a bot, exit early.
|
||||
@@ -46,44 +48,50 @@ export default class App {
|
||||
|
||||
const args = message.content.slice(prefix.length).trim().split(/ +/);
|
||||
const commandName = args.shift().toLowerCase();
|
||||
const command = commandList.get(commandName) || commandList.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
|
||||
const command =
|
||||
commandList.get(commandName) || commandList.find((cmd) => cmd.aliases && cmd.aliases.includes(commandName));
|
||||
|
||||
if (!command) {
|
||||
return message.reply('looks like I haven\'t learned that trick yet!');
|
||||
};
|
||||
|
||||
try {
|
||||
await command.execute(message, args, prefix, commandList, this._dbService);
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', error.message);
|
||||
message.reply('there was an error trying to follow that command!');
|
||||
message.reply("looks like I haven't learned that trick yet!");
|
||||
} else {
|
||||
try {
|
||||
await command.execute(message, args, prefix, commandList, this._dbService);
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', error.message);
|
||||
message.reply('there was an error trying to follow that command!');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._discordClient.on('guildMemberAdd', member => {
|
||||
this._discordClient.on('guildMemberAdd', (member) => {
|
||||
// base
|
||||
const greetings = ["Hello, {name}! CA greets you!", "Welcome to CA, {name}!", "Hi {name}! Welcome to CA!"];
|
||||
const greetings = ['Hello, {name}! CA greets you!', 'Welcome to CA, {name}!', 'Hi {name}! Welcome to CA!'];
|
||||
const greeting = greetings[Math.floor(Math.random() * greetings.length - 1)];
|
||||
// favor
|
||||
const flavors = [
|
||||
"As PROMISED, grab a free pie! Courtesy of {random}!",
|
||||
"The water is pure here! You should ask {random} for their water purified water for a sip!",
|
||||
"Home of the sane, the smart and {random}!"
|
||||
'As PROMISED, grab a free pie! Courtesy of {random}!',
|
||||
'The water is pure here! You should ask {random} for their water purified water for a sip!',
|
||||
'Home of the sane, the smart and {random}!',
|
||||
];
|
||||
const randomMember = member.guild.members.cache.random();
|
||||
const flavor = flavors[Math.floor(Math.random() * flavors.length - 1)];
|
||||
// result
|
||||
member.guild.systemChannel.send(greeting.replace('{name}', member.displayName) + " " + flavor.replace('{random}', randomMember.displayName));
|
||||
member.guild.systemChannel.send(
|
||||
`${greeting.replace('{name}', member.displayName)} ${flavor.replace('{random}', randomMember.displayName)}`,
|
||||
);
|
||||
});
|
||||
|
||||
this._discordClient.login(this._configService.get('DISCORD_BOT_TOKEN'))
|
||||
.catch((reason) => {
|
||||
this._loggerService.log('error', `Cannot initialise Discord client. Check the token: ${this._configService.get('DISCORD_BOT_TOKEN')}`);
|
||||
exit(1);
|
||||
});
|
||||
this._discordClient.login(this._configService.get('DISCORD_BOT_TOKEN')).catch((error) => {
|
||||
this._loggerService.log(
|
||||
'error',
|
||||
`Cannot initialise Discord client. Check the token: ${this._configService.get('DISCORD_BOT_TOKEN')}`,
|
||||
{ error },
|
||||
);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
public exit() {
|
||||
public exit(): void {
|
||||
this._dbService.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,38 @@ const command: ICommand = {
|
||||
aliases: ['eightball', 'magicball', 'ball', 'wisdomball'],
|
||||
description: 'Ask the magic eightball for advice!',
|
||||
async execute(message: Discord.Message, args: string[]) {
|
||||
if (args.length == 0)
|
||||
{
|
||||
message.reply('where\'s the question?');
|
||||
} else {
|
||||
const responses = ["as I see it, yes.", "err, ask again later.", "better not tell you now.", "that's hard to predict right now.",
|
||||
"concentrate... and ask again.", "don't count on it.", "it is certain.", "it is decidedly so, yes.", "most likely.", "no.",
|
||||
"likely not.", "my sources say no.", "hmm, outlook not so good.", "okay, outlook is good.", "not sure, ask again later.",
|
||||
"as dubealex commands it: maybe!", "googliano'd the answer and uh, it's a yes?", "careful, but yes",
|
||||
"signs point to a yes.", "very doubtful, very doubtful.", "without a doubt.","yes.", "yes - definitely.", "yeah, you can rely on it."];
|
||||
message.reply(responses[Math.floor(Math.random() * responses.length - 1)]);
|
||||
}
|
||||
if (args.length === 0) {
|
||||
return message.reply("where's the question?");
|
||||
}
|
||||
|
||||
const responses = [
|
||||
'as I see it, yes.',
|
||||
'err, ask again later.',
|
||||
'better not tell you now.',
|
||||
"that's hard to predict right now.",
|
||||
'concentrate... and ask again.',
|
||||
"don't count on it.",
|
||||
'it is certain.',
|
||||
'it is decidedly so, yes.',
|
||||
'most likely.',
|
||||
'no.',
|
||||
'likely not.',
|
||||
'my sources say no.',
|
||||
'hmm, outlook not so good.',
|
||||
'okay, outlook is good.',
|
||||
'not sure, ask again later.',
|
||||
'as dubealex commands it: maybe!',
|
||||
"googliano'd the answer and uh, it's a yes?",
|
||||
'careful, but yes',
|
||||
'signs point to a yes.',
|
||||
'very doubtful, very doubtful.',
|
||||
'without a doubt.',
|
||||
'yes.',
|
||||
'yes - definitely.',
|
||||
'yeah, you can rely on it.',
|
||||
];
|
||||
return message.reply(responses[Math.floor(Math.random() * responses.length - 1)]);
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
export default command;
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import Discord, { Collection } from 'discord.js';
|
||||
import MongoService from 'src/core/services/mongo.service';
|
||||
|
||||
import MongoService from '../../core/services/mongo.service';
|
||||
|
||||
export default interface ICommand {
|
||||
name: string;
|
||||
aliases?: string[];
|
||||
description: string;
|
||||
example?: string;
|
||||
execute: (message: Discord.Message, args: string[], prefix?: string, commands?: Collection<string, ICommand>, dbService?: MongoService) => Promise<any>,
|
||||
}
|
||||
execute: (
|
||||
message: Discord.Message,
|
||||
args: string[],
|
||||
prefix?: string,
|
||||
commands?: Collection<string, ICommand>,
|
||||
dbService?: MongoService,
|
||||
) => Promise<Discord.Message>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Discord, { Collection } from 'discord.js';
|
||||
|
||||
import ConfigService from '../../core/services/config.service';
|
||||
import MongoService from '../../core/services/mongo.service';
|
||||
import ConfigService from '../../core/services/config.service'
|
||||
|
||||
import container from '../../inversity.config';
|
||||
|
||||
@@ -12,16 +12,23 @@ const prefix = container.resolve<ConfigService>(ConfigService).get('BOT_TRIGGER'
|
||||
const command: ICommand = {
|
||||
name: 'addgreeting',
|
||||
aliases: ['ag'],
|
||||
description: 'Adds a string to the list greetings used when new users connect to server! Include `{name}` in your message to replace with the new users name.',
|
||||
description:
|
||||
'Adds a string to the list greetings used when new users connect to server! Include `{name}` in your message to replace with the new users name.',
|
||||
example: `\`${prefix}addgreeting Welcome to the club {name}\``,
|
||||
async execute(message: Discord.Message, args: string[], prefix?: string, commands?: Collection<string, ICommand>, dbService?: MongoService) {
|
||||
async execute(
|
||||
message: Discord.Message,
|
||||
args: string[],
|
||||
_prefix?: string,
|
||||
_commands?: Collection<string, ICommand>,
|
||||
dbService?: MongoService,
|
||||
) {
|
||||
// Only certain users can use this command
|
||||
// TODO: Better handling of permissions for commands in a generic way
|
||||
const permittedRoles = ['staff', 'mod', 'bot-devs']
|
||||
const isPermitted = message.member.roles.cache.some(r => permittedRoles.indexOf(r.name) !== -1);
|
||||
const permittedRoles = new Set(['staff', 'mod', 'bot-devs']);
|
||||
const isPermitted = message.member.roles.cache.some((r) => permittedRoles.has(r.name));
|
||||
|
||||
if (!isPermitted) {
|
||||
return message.author.send('Sorry but I can\'t let you add greetings!');
|
||||
return message.author.send("Sorry but I can't let you add greetings!");
|
||||
}
|
||||
|
||||
// Can't do much without a message
|
||||
@@ -39,11 +46,11 @@ const command: ICommand = {
|
||||
const result = await dbService.insert(message.author.id, 'greetings', [{ message: greetingStr }]);
|
||||
|
||||
if (!result) {
|
||||
message.author.send('Uh-oh! Couldn\'t add that greeting!');
|
||||
return message.author.send("Uh-oh! Couldn't add that greeting!");
|
||||
}
|
||||
|
||||
message.author.send('I\'ve added the greeting you told me about!');
|
||||
return message.author.send("I've added the greeting you told me about!");
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
export default command;
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
import Discord, { Collection } from 'discord.js';
|
||||
import ICommand from './ICommand';
|
||||
import ConfigService from '../../core/services/config.service'
|
||||
|
||||
import ConfigService from '../../core/services/config.service';
|
||||
import LoggerService from '../../core/services/logger.service';
|
||||
|
||||
import container from '../../inversity.config';
|
||||
|
||||
const prefix = container.resolve<ConfigService>(ConfigService).get('BOT_TRIGGER');
|
||||
const tmpPrefix = container.resolve<ConfigService>(ConfigService).get('BOT_TRIGGER');
|
||||
const loggerService = container.resolve<LoggerService>(LoggerService);
|
||||
|
||||
const command: ICommand = {
|
||||
name: 'help',
|
||||
aliases: ['commands'],
|
||||
example: `\`${prefix}help ping\``,
|
||||
example: `\`${tmpPrefix}help ping\``,
|
||||
description: 'Lists available commands!',
|
||||
async execute(message: Discord.Message, args: string[], prefix: string, commands: Collection<string, ICommand>) {
|
||||
const data = [];
|
||||
|
||||
if (!args.length) {
|
||||
if (!args || args.length === 0) {
|
||||
// Get for all commands
|
||||
data.push('here\'s a list of all my commands:\n');
|
||||
data.push("here's a list of all my commands:\n");
|
||||
|
||||
const cmds = commands.map(command => command.name);
|
||||
let cmd;
|
||||
cmds.forEach(element => {
|
||||
cmd = commands.get(element) || commands.find(c => c.aliases && c.aliases.includes(element));
|
||||
const cmds = commands.map((c) => c.name);
|
||||
cmds.forEach((element) => {
|
||||
const cmd = commands.get(element) || commands.find((c) => c.aliases && c.aliases.includes(element));
|
||||
let response = `\`${prefix}${cmd.name}\` `;
|
||||
if (cmd.description) {
|
||||
response += `**${cmd.description}** `;
|
||||
@@ -34,30 +38,31 @@ const command: ICommand = {
|
||||
} else {
|
||||
// Get description of single command
|
||||
const name = args[0].toLowerCase();
|
||||
const command = commands.get(name) || commands.find(c => c.aliases && c.aliases.includes(name));
|
||||
const cmd = commands.get(name) || commands.find((c) => c.aliases && c.aliases.includes(name));
|
||||
|
||||
if (!command) {
|
||||
message.reply('that\'s not a valid command!');
|
||||
if (!cmd) {
|
||||
message.reply("that's not a valid command!");
|
||||
} else {
|
||||
data.push(`**Name:** ${command.name}`);
|
||||
data.push(`**Name:** ${cmd.name}`);
|
||||
|
||||
if (command.aliases) data.push(`**Aliases:** ${command.aliases.join(', ')}`);
|
||||
if (command.description) data.push(`**Description:** ${command.description}`);
|
||||
if (cmd.aliases) {
|
||||
data.push(`**Aliases:** ${cmd.aliases.join(', ')}`);
|
||||
}
|
||||
|
||||
if (cmd.description) {
|
||||
data.push(`**Description:** ${cmd.description}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
//await message.author.send(data, { split: true });
|
||||
//if (message.channel.type === 'dm')
|
||||
// return;
|
||||
// message.reply('I\'ve sent you a DM with all my commands!');
|
||||
message.reply(data, { split: true })
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Could not send help DM to ${message.author.tag}.\n`, error);
|
||||
message.reply('it seems like I can\'t DM you! Do you have DMs disabled?');
|
||||
return message.reply(data, { split: true });
|
||||
} catch (error) {
|
||||
loggerService.log('error', `Could not send help DM to ${message.author.tag}.\n`, error);
|
||||
|
||||
return message.reply("it seems like I can't DM you! Do you have DMs disabled?");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
export default command;
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import magicball from './8ball';
|
||||
import addgreeting from './addgreeting';
|
||||
import help from './help';
|
||||
import ping from './ping';
|
||||
import see from './see';
|
||||
import magicball from './8ball';
|
||||
import addgreeting from './addgreeting'
|
||||
|
||||
export default [
|
||||
help,
|
||||
ping,
|
||||
see,
|
||||
magicball,
|
||||
addgreeting
|
||||
]
|
||||
export default [help, ping, see, magicball, addgreeting];
|
||||
|
||||
@@ -5,9 +5,9 @@ const command: ICommand = {
|
||||
name: 'ping',
|
||||
aliases: ['hello', 'hi'],
|
||||
description: 'Responds, kind of like telling you the bot is alive.',
|
||||
async execute(message: Discord.Message, args: string[]) {
|
||||
message.reply('Pong!');
|
||||
async execute(message: Discord.Message) {
|
||||
return message.reply('Pong!');
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
export default command;
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import Discord from 'discord.js';
|
||||
|
||||
import ICommand from './ICommand';
|
||||
import ConfigService from '../../core/services/config.service'
|
||||
import ConfigService from '../../core/services/config.service';
|
||||
import container from '../../inversity.config';
|
||||
|
||||
import ICommand from './ICommand';
|
||||
|
||||
const prefix = container.resolve<ConfigService>(ConfigService).get('BOT_TRIGGER');
|
||||
|
||||
const command: ICommand = {
|
||||
name: 'see',
|
||||
example: `\`${prefix}see\``,
|
||||
description: 'Sends a DM telling you information about your user on given server.',
|
||||
async execute(message: Discord.Message, args: string[]) {
|
||||
message.author.send(`Server: ${message.guild.name}\nYour username: ${message.author.username}\nYour ID: ${message.author.id}`);
|
||||
async execute(message: Discord.Message) {
|
||||
return message.author.send(
|
||||
`Server: ${message.guild.name}\nYour username: ${message.author.username}\nYour ID: ${message.author.id}`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
export default command;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import path from 'path';
|
||||
import dotenv from 'dotenv';
|
||||
import { injectable } from "inversify";
|
||||
import { injectable } from 'inversify';
|
||||
import path from 'path';
|
||||
|
||||
import Config from '../types/Config';
|
||||
import LogLevel from '../types/LogLevel';
|
||||
import Environment from '../types/Environment';
|
||||
import LogLevel from '../types/LogLevel';
|
||||
|
||||
@injectable()
|
||||
export default class ConfigService {
|
||||
@@ -21,12 +21,9 @@ export default class ConfigService {
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.load();
|
||||
this.setup();
|
||||
}
|
||||
|
||||
private load(): void {
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../', '.env') });
|
||||
|
||||
this.setup();
|
||||
}
|
||||
|
||||
private setup(): void {
|
||||
@@ -40,7 +37,7 @@ export default class ConfigService {
|
||||
};
|
||||
}
|
||||
|
||||
public get(key: keyof Config): any {
|
||||
public get(key: keyof Config): string {
|
||||
return this.config[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { injectable } from 'inversify';
|
||||
import path from 'path';
|
||||
import winston from 'winston';
|
||||
import { injectable } from 'inversify';
|
||||
|
||||
import LogLevel from '../types/LogLevel';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import container from '../../inversity.config';
|
||||
import LogLevel from '../types/LogLevel';
|
||||
|
||||
import ConfigService from './config.service';
|
||||
|
||||
@@ -18,7 +19,10 @@ export default class LoggerService {
|
||||
level: this._configService.get('LOG_LEVEL'),
|
||||
format: winston.format.json(),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: path.resolve(__dirname, '../../', 'logs', 'error.log'), level: 'error' }),
|
||||
new winston.transports.File({
|
||||
filename: path.resolve(__dirname, '../../', 'logs', 'error.log'),
|
||||
level: 'error',
|
||||
}),
|
||||
new winston.transports.Console({
|
||||
format: winston.format.simple(),
|
||||
}),
|
||||
@@ -26,7 +30,7 @@ export default class LoggerService {
|
||||
});
|
||||
}
|
||||
|
||||
log(level: LogLevel, message: string, payload?: object) {
|
||||
log(level: LogLevel, message: string, payload?: unknown): void {
|
||||
if (this._logger[level]) {
|
||||
this._logger.log(level, message, payload);
|
||||
} else {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import mongodb from 'mongodb';
|
||||
import { injectable } from "inversify";
|
||||
import assert from 'assert';
|
||||
import { injectable } from 'inversify';
|
||||
import mongodb, { Collection, CollectionInsertOneOptions } from 'mongodb';
|
||||
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import container from '../../inversity.config';
|
||||
|
||||
import ConfigService from './config.service';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import LoggerService from './logger.service';
|
||||
|
||||
@injectable()
|
||||
export default class MongoService {
|
||||
private _configService: ConfigService = container.resolve<ConfigService>(ConfigService);
|
||||
|
||||
private _loggerService: LoggerService = container.resolve<LoggerService>(LoggerService);
|
||||
|
||||
private _mongoClient: mongodb.MongoClient;
|
||||
|
||||
public _db: mongodb.Db;
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
@@ -34,7 +37,7 @@ export default class MongoService {
|
||||
});
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
public disconnect(): void {
|
||||
if (this._mongoClient && this._mongoClient.isConnected) {
|
||||
this._loggerService.log('info', 'Closing connection to MongoDB');
|
||||
this._mongoClient.close();
|
||||
@@ -51,7 +54,7 @@ export default class MongoService {
|
||||
*
|
||||
* @example findOne('123456', 'collectionName', { ident: 'generated-slug' })
|
||||
*/
|
||||
public async findOne(userId: string, collection: string, query: any) {
|
||||
public async findOne(userId: string, collection: string, query: unknown): Promise<Collection | boolean> {
|
||||
try {
|
||||
this.verifyConnection();
|
||||
|
||||
@@ -65,10 +68,10 @@ export default class MongoService {
|
||||
});
|
||||
|
||||
return resp;
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Could not find document', {
|
||||
userId,
|
||||
error: err,
|
||||
error,
|
||||
collection,
|
||||
query,
|
||||
});
|
||||
@@ -87,7 +90,7 @@ export default class MongoService {
|
||||
*
|
||||
* @example find('123456', 'collectionName', { key: 'value' })
|
||||
*/
|
||||
public async find(userId: string, collection: string, query: any) {
|
||||
public async find(userId: string, collection: string, query: unknown): Promise<Collection[]> {
|
||||
try {
|
||||
this.verifyConnection();
|
||||
|
||||
@@ -101,10 +104,10 @@ export default class MongoService {
|
||||
});
|
||||
|
||||
return resp;
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Could not find documents', {
|
||||
userId,
|
||||
error: err,
|
||||
error,
|
||||
collection,
|
||||
query,
|
||||
});
|
||||
@@ -123,7 +126,7 @@ export default class MongoService {
|
||||
*
|
||||
* @example `insert('123456', 'collectionName', [{ body: 'This is a document!' }])`
|
||||
*/
|
||||
public async insert(userId: string, collection: string, payload: any[]) {
|
||||
public async insert(userId: string, collection: string, payload: unknown[]): Promise<boolean> {
|
||||
try {
|
||||
this.verifyConnection();
|
||||
|
||||
@@ -137,16 +140,16 @@ export default class MongoService {
|
||||
userId,
|
||||
collection,
|
||||
payload,
|
||||
resp
|
||||
resp,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Could not insert documents to database', {
|
||||
userId,
|
||||
error: err,
|
||||
error,
|
||||
collection,
|
||||
payload
|
||||
payload,
|
||||
});
|
||||
|
||||
return false;
|
||||
@@ -164,7 +167,7 @@ export default class MongoService {
|
||||
*
|
||||
* @example `updateMany('123456', 'collectionName', { ident: 'generated-slug' }, { secondKey: 'new data'})`
|
||||
*/
|
||||
public async updateMany(userId: string, collection: string, query: any, payload: any) {
|
||||
public async updateMany(userId: string, collection: string, query: unknown, payload: unknown[]): Promise<boolean> {
|
||||
try {
|
||||
this.verifyConnection();
|
||||
|
||||
@@ -179,18 +182,20 @@ export default class MongoService {
|
||||
collection,
|
||||
query,
|
||||
payload,
|
||||
resp
|
||||
resp,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Could not update documents', {
|
||||
userId,
|
||||
collection,
|
||||
query,
|
||||
payload,
|
||||
err
|
||||
error,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +209,7 @@ export default class MongoService {
|
||||
*
|
||||
* @example `deleteMany('123456', 'collectionName', { ident: 'generated-slug' })`
|
||||
*/
|
||||
public async deleteMany(userId: string, collection: string, query: any) {
|
||||
public async deleteMany(userId: string, collection: string, query: unknown): Promise<boolean> {
|
||||
try {
|
||||
this.verifyConnection();
|
||||
|
||||
@@ -218,23 +223,23 @@ export default class MongoService {
|
||||
userId,
|
||||
collection,
|
||||
query,
|
||||
resp
|
||||
resp,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
this._loggerService.log('error', 'Could not delete documents', {
|
||||
userId,
|
||||
collection,
|
||||
query,
|
||||
err
|
||||
error,
|
||||
});
|
||||
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private verifyConnection() {
|
||||
private verifyConnection(): void {
|
||||
if (!this._mongoClient.isConnected) {
|
||||
this._loggerService.log('error', 'No database connection available');
|
||||
throw new Error('No database connection available');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import LogLevel from "./LogLevel";
|
||||
import Environment from "./Environment";
|
||||
import Environment from './Environment';
|
||||
import LogLevel from './LogLevel';
|
||||
|
||||
export default interface Config {
|
||||
BOT_TRIGGER: string;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
type Environment = 'production' | 'development' | 'test';
|
||||
|
||||
export default Environment;
|
||||
export default Environment;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
type LogLevel = 'error' | 'warn' | 'info' | 'verbose' | 'debug' | 'silly';
|
||||
|
||||
export default LogLevel;
|
||||
export default LogLevel;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Container } from "inversify";
|
||||
import { Container } from 'inversify';
|
||||
|
||||
import ConfigService from "./core/services/config.service";
|
||||
import LoggerService from "./core/services/logger.service";
|
||||
import MongoService from "./core/services/mongo.service";
|
||||
import ConfigService from './core/services/config.service';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import LoggerService from './core/services/logger.service';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import MongoService from './core/services/mongo.service';
|
||||
|
||||
const container = new Container();
|
||||
container.bind<ConfigService>(ConfigService).toSelf();
|
||||
container.bind<LoggerService>(LoggerService).toSelf();
|
||||
container.bind<MongoService>(MongoService).toSelf();
|
||||
|
||||
export default container;
|
||||
export default container;
|
||||
|
||||
22
src/main.ts
22
src/main.ts
@@ -1,31 +1,31 @@
|
||||
import { exit } from 'process';
|
||||
import 'reflect-metadata';
|
||||
|
||||
import App from './app';
|
||||
import { exit } from 'process';
|
||||
|
||||
const app = new App();
|
||||
|
||||
function exitHandler(): void {
|
||||
// Cleans up the application
|
||||
app.exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
app.init();
|
||||
|
||||
function exitHandler() {
|
||||
// Cleans up the application
|
||||
app.exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// do something when app is closing
|
||||
process.on('exit', exitHandler);
|
||||
|
||||
//catches ctrl+c event
|
||||
// catches ctrl+c event
|
||||
process.on('SIGINT', exitHandler);
|
||||
|
||||
// catches "kill pid" (for example: nodemon restart)
|
||||
process.on('SIGUSR1', exitHandler);
|
||||
process.on('SIGUSR2', exitHandler);
|
||||
|
||||
//catches uncaught exceptions
|
||||
// catches uncaught exceptions
|
||||
process.on('uncaughtException', exitHandler);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
16
tslint.json
16
tslint.json
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": ["tslint:latest", "tslint-react", "tslint-config-prettier"],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"interface-name": false,
|
||||
"semicolon": [true, "always"],
|
||||
"member-access": [false],
|
||||
"ordered-imports": [true],
|
||||
"no-console": [false],
|
||||
"no-var-requires": true,
|
||||
"quotemark": [true, "single", "jsx-double"],
|
||||
"no-implicit-dependencies": [true, "dev"]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
Reference in New Issue
Block a user