Full Code of 1Computer1/discord-akairo for AI

master 905f69382957 cached
92 files
340.5 KB
79.5k tokens
353 symbols
1 requests
Download .txt
Showing preview only (365K chars total). Download the full file or copy to clipboard to get everything.
Repository: 1Computer1/discord-akairo
Branch: master
Commit: 905f69382957
Files: 92
Total size: 340.5 KB

Directory structure:
gitextract_ns2ldn2j/

├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .jsdoc.json
├── .npmignore
├── .npmrc
├── .prettierignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs/
│   ├── arguments/
│   │   ├── arguments.md
│   │   ├── compose.md
│   │   ├── custom.md
│   │   ├── functions.md
│   │   ├── generators.md
│   │   ├── matches.md
│   │   ├── prompts.md
│   │   ├── prompts2.md
│   │   ├── types.md
│   │   └── unordered.md
│   ├── basics/
│   │   ├── commands.md
│   │   ├── inhibitors.md
│   │   ├── listeners.md
│   │   └── setup.md
│   ├── commands/
│   │   ├── commandutil.md
│   │   ├── conditional.md
│   │   ├── cooldowns.md
│   │   ├── permissions.md
│   │   ├── prefixes.md
│   │   ├── regex.md
│   │   └── restrictions.md
│   ├── general/
│   │   └── welcome.md
│   ├── index.yml
│   ├── inhibitors/
│   │   ├── inhibtypes.md
│   │   └── priority.md
│   ├── listeners/
│   │   └── emitters.md
│   ├── other/
│   │   ├── clientutil.md
│   │   ├── handlers.md
│   │   ├── handling.md
│   │   ├── mongoose.md
│   │   ├── providers.md
│   │   └── updating.md
│   └── snippets/
│       └── ping.md
├── package.json
├── src/
│   ├── index.d.ts
│   ├── index.js
│   ├── providers/
│   │   ├── MongooseProvider.js
│   │   ├── Provider.js
│   │   ├── SQLiteProvider.js
│   │   └── SequelizeProvider.js
│   ├── struct/
│   │   ├── AkairoClient.js
│   │   ├── AkairoHandler.js
│   │   ├── AkairoModule.js
│   │   ├── ClientUtil.js
│   │   ├── commands/
│   │   │   ├── Command.js
│   │   │   ├── CommandHandler.js
│   │   │   ├── CommandUtil.js
│   │   │   ├── ContentParser.js
│   │   │   ├── Flag.js
│   │   │   └── arguments/
│   │   │       ├── Argument.js
│   │   │       ├── ArgumentRunner.js
│   │   │       └── TypeResolver.js
│   │   ├── inhibitors/
│   │   │   ├── Inhibitor.js
│   │   │   └── InhibitorHandler.js
│   │   └── listeners/
│   │       ├── Listener.js
│   │       └── ListenerHandler.js
│   └── util/
│       ├── AkairoError.js
│       ├── Category.js
│       ├── Constants.js
│       └── Util.js
├── test/
│   ├── bot.js
│   ├── commands/
│   │   ├── args.js
│   │   ├── ayy.js
│   │   ├── condition.js
│   │   ├── condition.promise.js
│   │   ├── embed.js
│   │   ├── eval.js
│   │   ├── f.js
│   │   ├── generate.js
│   │   ├── lock.js
│   │   ├── p.js
│   │   ├── q.js
│   │   ├── separate.js
│   │   ├── sub.js
│   │   ├── test.js
│   │   ├── test2.js
│   │   └── unordered.js
│   ├── listeners/
│   │   ├── invalidMessage.js
│   │   └── message.js
│   └── struct/
│       └── TestClient.js
├── tsconfig.json
└── tslint.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.json
================================================
{
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 9
    },
    "env": {
        "es6": true,
        "node": true
    },
    "rules": {
        "no-await-in-loop": "off",
        "no-extra-parens": [
            "warn",
            "all",
            {
                "nestedBinaryExpressions": false
            }
        ],
        "no-template-curly-in-string": "error",
        "no-unsafe-negation": "error",
        "valid-jsdoc": [
            "error",
            {
                "requireReturn": true,
                "requireReturnDescription": false,
                "prefer": {
                    "return": "returns",
                    "arg": "param"
                },
                "preferType": {
                    "String": "string",
                    "Number": "number",
                    "Boolean": "boolean",
                    "object": "Object",
                    "function": "Function",
                    "array": "Array",
                    "date": "Date",
                    "error": "Error",
                    "null": "void"
                }
            }
        ],
        "accessor-pairs": "warn",
        "array-callback-return": "error",
        "complexity": [
            "warn",
            25
        ],
        "consistent-return": "error",
        "curly": [
            "error",
            "multi-line",
            "consistent"
        ],
        "dot-location": [
            "error",
            "property"
        ],
        "dot-notation": "error",
        "eqeqeq": [
            "error",
            "smart"
        ],
        "no-console": "warn",
        "no-empty-function": "error",
        "no-floating-decimal": "error",
        "no-implied-eval": "error",
        "no-invalid-this": "error",
        "no-lone-blocks": "error",
        "no-multi-spaces": "error",
        "no-new-func": "error",
        "no-new-wrappers": "error",
        "no-new": "error",
        "no-octal-escape": "error",
        "no-return-assign": "error",
        "no-return-await": "error",
        "no-self-compare": "error",
        "no-sequences": "error",
        "no-throw-literal": "error",
        "no-unmodified-loop-condition": "error",
        "no-unused-expressions": "error",
        "no-useless-call": "error",
        "no-useless-concat": "error",
        "no-useless-escape": "error",
        "no-useless-return": "error",
        "no-void": "error",
        "no-warning-comments": "warn",
        "require-await": "warn",
        "wrap-iife": "error",
        "yoda": "error",
        "no-label-var": "error",
        "no-shadow": "error",
        "no-undef-init": "error",
        "callback-return": "error",
        "handle-callback-err": "error",
        "no-mixed-requires": "error",
        "no-new-require": "error",
        "no-path-concat": "error",
        "array-bracket-spacing": "error",
        "block-spacing": "error",
        "brace-style": [
            "error",
            "1tbs",
            {
                "allowSingleLine": true
            }
        ],
        "capitalized-comments": [
            "error",
            "always",
            {
                "ignoreConsecutiveComments": true
            }
        ],
        "comma-dangle": [
            "error",
            "never"
        ],
        "comma-spacing": "error",
        "comma-style": "error",
        "computed-property-spacing": "error",
        "consistent-this": [
            "error",
            "$this"
        ],
        "eol-last": "error",
        "func-names": "error",
        "func-name-matching": "error",
        "func-style": [
            "error",
            "declaration",
            {
                "allowArrowFunctions": true
            }
        ],
        "indent": [
            "error",
            4,
            {
                "MemberExpression": 1
            }
        ],
        "key-spacing": "error",
        "keyword-spacing": "error",
        "max-depth": [
            "error",
            7
        ],
        "max-nested-callbacks": [
            "error",
            {
                "max": 4
            }
        ],
        "max-statements-per-line": [
            "error",
            {
                "max": 2
            }
        ],
        "new-cap": "error",
        "no-array-constructor": "error",
        "no-inline-comments": "error",
        "no-lonely-if": "error",
        "no-mixed-operators": "error",
        "no-multiple-empty-lines": [
            "error",
            {
                "max": 2,
                "maxEOF": 1,
                "maxBOF": 0
            }
        ],
        "no-new-object": "error",
        "no-spaced-func": "error",
        "no-trailing-spaces": "error",
        "no-unneeded-ternary": "error",
        "no-whitespace-before-property": "error",
        "object-curly-spacing": [
            "error",
            "always"
        ],
        "operator-assignment": "error",
        "operator-linebreak": [
            "error",
            "before"
        ],
        "padded-blocks": [
            "error",
            "never"
        ],
        "quote-props": [
            "error",
            "as-needed"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi-spacing": "error",
        "semi": "error",
        "space-before-blocks": "error",
        "space-before-function-paren": [
            "error",
            {
                "anonymous": "always",
                "named": "never",
                "asyncArrow": "always"
            }
        ],
        "space-in-parens": "error",
        "space-infix-ops": "error",
        "space-unary-ops": "error",
        "spaced-comment": "error",
        "unicode-bom": "error",
        "arrow-parens": [
            "error",
            "as-needed"
        ],
        "arrow-spacing": "error",
        "no-duplicate-imports": "error",
        "no-useless-computed-key": "error",
        "no-useless-constructor": "error",
        "prefer-const": "error",
        "prefer-arrow-callback": "error",
        "prefer-numeric-literals": "error",
        "prefer-rest-params": "error",
        "prefer-spread": "error",
        "prefer-template": "error",
        "rest-spread-spacing": "error",
        "template-curly-spacing": "error",
        "yield-star-spacing": "error"
    }
}

================================================
FILE: .gitattributes
================================================
* text=auto eol=lf


================================================
FILE: .gitignore
================================================
node_modules

.git
.vscode

test/auth.json
test/db.sqlite


================================================
FILE: .jsdoc.json
================================================
{
    "tags": {
        "allowUnknownTags": true,
        "dictionaries": ["jsdoc"]
    },
    "source": {
        "include": ["README.md", "src", "package.json"],
        "includePattern": ".js$",
        "excludePattern": "node_modules/"
    },
    "opts": {
        "encoding": "utf8",
        "private": true,
        "recurse": true,
        "sort": true
    }
}


================================================
FILE: .npmignore
================================================
# NPM
node_modules
.git
.vscode
.eslintrc.json
.gitattributes
.gitignore
.travis.yml
test/
docs/
tsconfig.json
tslint.json


================================================
FILE: .npmrc
================================================
package-lock=false


================================================
FILE: .prettierignore
================================================
*

================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
    - "12"


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 1Computer1

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.


================================================
FILE: README.md
================================================
<div align="center">
  <br />
  <p>
    <a href="https://discord-akairo.github.io"><img src="https://discord-akairo.github.io/static/logo.svg" width="546" alt="discord-akairo" /></a>
  </p>
  <br />
  <p>
    <a href="https://www.npmjs.com/package/discord-akairo"><img src="https://img.shields.io/npm/v/discord-akairo.svg?maxAge=3600" alt="NPM version" /></a>
    <a href="https://www.npmjs.com/package/discord-akairo"><img src="https://img.shields.io/npm/dt/discord-akairo.svg?maxAge=3600" alt="NPM downloads" /></a>
    <a href="https://travis-ci.org/discord-akairo/discord-akairo"><img src="https://travis-ci.org/discord-akairo/discord-akairo.svg" alt="Build status" /></a>
  </p>
  <p>
    <a href="https://nodei.co/npm/discord-akairo/"><img src="https://nodei.co/npm/discord-akairo.png?downloads=true" alt="npm installnfo" /></a>
  </p>
</div>

## Features

#### Completely modular commands, inhibitors, and listeners.

  - Reading files recursively from directories.
  - Adding, removing, and reloading modules.
  - Creating your own handlers and module types.

#### Flexible command handling and creation.

  - Command aliases.
  - Command throttling and cooldowns.
  - Client and user permission checks.
  - Running commands on edits and editing previous responses.
  - Multiple prefixes and mention prefixes.
  - Regular expression and conditional triggers.

#### Complex and highly customizable arguments.

  - Support for quoted arguments.
  - Arguments based on previous arguments.
  - Several ways to match arguments, such as flag arguments.
  - Casting input into certain types.
    - Simple types such as string, integer, float, url, date, etc.
    - Discord-related types such as user, member, message, etc.
    - Types that you can add yourself.
    - Asynchronous type casting.
  - Prompting for input for arguments.
    - Customizable prompts with embeds, files, etc.
    - Easily include dynamic data such as the incorrect input.
    - Infinite argument prompting.

#### Blocking and monitoring messages with inhibitors.

  - Run at various stages of command handling.
    - On all messages.
    - On messages that are from valid users.
    - On messages before commands.

#### Helpful events and modular listeners.

  - Events for handlers, such as loading modules.
  - Events for various stages of command handling.
  - Reloadable listeners to easily separate your event handling.

#### Useful utilities and database providers.

  - Resolvers for members, users, and others that can filter by name.
  - Shortcut methods for making embeds and collections.
  - Simple to use database providers.
    - Built-in support for `sqlite` and `sequelize`.
    - Works on entire table or single JSON column.
    - Caching data from databases.

## Installation

Requires Node 16.6.0+ and Discord.js v13.  

*discord-akairo*  
`npm install discord-akairo`

*discord.js*  
`npm install discord.js`

*sqlite (optional)*  
`npm install sqlite`

*sequelize (optional)*  
`npm install sequelize`

## Links

- [Website](https://discord-akairo.github.io)
- [Repository](https://github.com/discord-akairo/discord-akairo)  
- [Changelog](https://github.com/discord-akairo/discord-akairo/releases)
- [Discord](https://discord.gg/arTauDY)  

## Contributing

Open an issue or a pull request!  
Everyone is welcome to do so.  
Make sure to run `npm test` before committing.  


================================================
FILE: docs/arguments/arguments.md
================================================
# Basic Arguments

### Adding Numbers

Commands should also have some user input, in the form of arguments.  
In Akairo, arguments are the most complex things ever, so this tutorial will only go through the basics.  

Let's make a command that takes three numbers and adds them up.  

```js
const { Command } = require('discord-akairo');

class AddCommand extends Command {
    constructor() {
        super('add', {
            aliases: ['add']
        });
    }

    exec(message) {
        // This doesn't work!
        return message.reply(a + b + c);
    }
}

module.exports = AddCommand;
```

Now we will add arguments in the command options with the `args` option.  
This option must be an array of objects, containing info for parsing.  

```js
const { Command } = require('discord-akairo');

class AddCommand extends Command {
    constructor() {
        super('add', {
            aliases: ['add'],
            args: [
                {
                    id: 'numOne',
                    type: 'number',
                    default: 0
                },
                {
                    id: 'numTwo',
                    type: 'number',
                    default: 0
                },
                {
                    id: 'numThree',
                    type: 'number',
                    default: 0
                }
            ]
        });
    }

    exec(message, args) {
        const sum = args.numOne + args.numTwo + args.numThree;
        return message.reply(`The sum is ${sum}!`);
    }
}

module.exports = AddCommand;
```

Arguments must always have an `id`, it will be what you use to refer to them in `args`.  
The `type` options is optional, but since we want numbers, it is set to `number`.  
The `default` is what is used if there are no input or no number input.  

By default, arguments are able to be quoted (you can disable this by having the `quoted` option set to false).  
So, technically, this works (although it won't be an actual number input): `?add "hello world" 2 3`.  


================================================
FILE: docs/arguments/compose.md
================================================
# Composing Types

### Union Types

Akairo allows the creation of union types, where the input can match one of many types.  
You can import the `Argument` class, where there is the `Argument.union` static method.  

```js
{
    id: 'numOrName',
    type: Argument.union('integer', 'string')
}
```

The above argument will try matching using `integer` first, then `string`.  
So, it is recommended that you go from most to least specific types.  

### Product Types

A product type in Akairo casts the input to multiple types.  
The static method `Argument.product` lets us create one of these.  
The type will parse the input into an array containing the values respective to the given types.  

```js
{
    id: 'numAndName',
    type: Argument.product('integer', 'string')
}
```

The above argument will give an array where the first element was parsed using `integer`, and the second using `string`.  
If any of the types fail, the entire argument fails.  

### Validation

Extra validation can be done on the parsed value using `Argument.validate`.  
For numbers and things with a length or size, `Argument.range` is a convenient method as well.  

```js
{
    id: 'content',
    type: Argument.validate('string', (m, p, str) => str.length < 2000)
}
```

This argument ensures that the input is less than 2000 characters in length.  
If it is over 2000 characters, the input is considered invalid.  

```js
{
    id: 'number',
    type: Argument.range('number', 0, 100)
}
```

The `range` method ensures that the parsed value is within a certain range.  
Here, `number` will be between 0 and 100, exclusive.  
To make the upper bound inclusive, simply pass `true` to the 4th argument in the range function.  

### We're Going Functional

Types can be composed together using `Argument.compose`.  
For example, the result of `Argument.compose(type1, type2)` is a type that uses the first type, then the result of that is passed the second.  
A use case of this function is for preprocessing before casting:  

```js
{
    id: 'lowercaseChars',
    type: Argument.compose('lowercase', 'charCodes')
}
```

For more complicated types compositions and validations, it will be a lot easier to use type functions.  
See the [Using Functions](./functions.md) section for more information.  


================================================
FILE: docs/arguments/custom.md
================================================
# Custom Types

### New Type

We have to access the command handler's `TypeResolver` in order to add new types for our arguments.  
To add a new type:  

```js
this.commandHandler = new CommandHandler(this, { /* Options here */ });
this.commandHandler.resolver.addType('pokemon', (message, phrase) => {
    if (!phrase) return null;

    for (const pokemon of pokemonList) {
        if (pokemon.name.toLowerCase() === phrase.toLowerCase()) {
            return pokemon;
        }
    }

    return null;
});
```

We now have a new type called `pokemon` which we can use in a command!  
Simply do `type: 'pokemon'` for an argument and everything will work as expected.  

### With Message

Let's say we want to add a type that would get a role based on the input.  
This means we need access to the guild through the message.  
Good thing the first parameter is the message!  

```js
this.commandHandler.resolver.addType('colorRole', (message, phrase) => {
    if (!phrase) return null;

    const roles = {
        red: '225939226628194304',
        blue: '225939219841810432',
        green: '225939232512802816'
    };

    const role = message.guild.roles.cache.get(roles[phrase.toLowerCase()]);
    return role || null;
});
```

So now, using the type `colorRole`, we can get either red, blue, or green from the input and end up with corresponding role object!  

### Accessing Another Type

To get another type for use, you use the `type` method on `TypeResolver`.  
The following gives the `member` type and we can use as part of another type.  

```js
this.commandHandler.resolver.addType('moderator', (message, phrase) => {
    if (!phrase) return null;
    const memberType = this.commandHandler.resolver.type('member');
    const member = memberType(message, phrase);
    if (!member.roles.cache.has('222089067028807682')) return null;
    return member;
});
```


================================================
FILE: docs/arguments/functions.md
================================================
# Using Functions

### Dynamic Defaults

When you are doing default values for certain arguments, you could really only do what JavaScript has to offer: numbers, strings, etc.  
What if we want to use a default such as the author's username or the guild's owner?  
This is where you can use a function.  

```js
const { Command } = require('discord-akairo');

class HighestRoleCommand extends Command {
    constructor() {
        super('highestRole', {
            aliases: ['highestRole'],
            args: [
                {
                    id: 'member',
                    type: 'member',
                    default: message => message.member
                }
            ],
            channel: 'guild'
        });
    }

    exec(message, args) {
        return message.reply(args.member.roles.highest.name);
    }
}

module.exports = HighestRoleCommand;
```

The command above gives the name of the inputted member's highest role.  
If there were no member or an incorrect member provided, the `default` function is called, giving us the message member.  

### Dynamic Types

Let's go to using a function for types.  
Take a look at the roll command below.  

```js
const { Command } = require('discord-akairo');

class RollCommand extends Command {
    constructor() {
        super('roll', {
            aliases: ['roll'],
            args: [
                {
                    id: 'amount',
                    type: 'integer',
                    default: 100
                }
            ]
        });
    }

    exec(message, args) {
        const res = Math.floor(Math.random() * args.amount));
        return message.reply(`You rolled ${res}!`);
    }
}

module.exports = RollCommand;
```

Let's say we want to limit the user to between 1 and 100, so that there are no giant numbers.  
While we could do it in the execution function, let's stick it straight into the type as a function.  
This is much easier with a validation type (see [Composing Types](./compose.md)), but for the sake of example, let's do it anyways.  

```js
const { Command } = require('discord-akairo');

class RollCommand extends Command {
    constructor() {
        super('roll', {
            aliases: ['roll'],
            args: [
                {
                    id: 'amount',
                    type: (message, phrase) => {
                        if (!phrase || isNaN(phrase)) return null;
                        const num = parseInt(phrase);
                        if (num < 1 || num > 100) return null;
                        return num;
                    },
                    default: 100
                }
            ]
        });
    }

    exec(message, args) {
        const res = Math.floor(Math.random() * args.amount));
        return message.reply(`You rolled ${res}!`);
    }
}

module.exports = RollCommand;
```

The type function follows these steps:  

1. Check if there was input.
2. Check if input is not a number.
3. Parse input to an integer.
4. Check if the integer is out of bounds.
5. Return the integer.

Whenever a `null` or `undefined` value is returned, it means the type casting failed, and the default will be used.  
Otherwise, whatever you return is the result.  
Promise are awaited and the resolved value will go through the same process.  

### A Bit Further

Type functions can be used almost anywhere a type is expected.  
This includes the types builders in the [Composing Types](./compose.md) section.  
Take a look at this slightly exaggerated example for a type for a page argument:  

```js
args: [
    {
        id: 'page',
        type: Argument.compose(Argument.range('integer', 0, Infinity), n => n - 1)
    }
]
```

This casts the input to an integer, ensure it is at least 0, and decrements it by 1.  


================================================
FILE: docs/arguments/generators.md
================================================
# Generator Arguments

## Yield!

The most powerful aspect of Akairo's argument parsing is the fact that it is implemented using generators.  
With this, you can do things such as:
- Have an argument depend on the previous argument
- Branch your argument parsing
- Run an argument multiple times
- Inject code in between arguments
- And more!

To get started, take this command:  

```js
const { Command } = require('discord-akairo');

class GeneratorCommand extends Command {
    constructor() {
        super('generator', {
            aliases: ['generator']
        });
    }

    *args() {
        // Here!
    }

    exec(message, args) {
        // Do whatever.
    }
}

module.exports = GeneratorCommand;
```

Note that instead of an `args` object in the `super` call, we have a generator method, `*args`.  
We will focus on this method.  
(You can put it in the `super` call if you want, but it is cleaner this way.)  

To run an argument:  

```js
*args() {
    // Notice: no `id` necessary!
    // Also notice: `yield` must be used.
    const x = yield { type: 'integer' };
    const y = yield {
        type: 'integer',
        prompt: {
            // Everything you know still works.
        }
    };

    // When finished.
    return { x, y };
}
```

But more things are possible because you have access to all of JavaScript's syntax!  

```js
*args(message) {
    const x = yield { type: 'integer' };

    // Use previous arguments by referring to the identifier.
    const y = yield (x > 10 ? { type: 'integer' } : { type: 'string' });

    // Debug in between your arguments!
    console.log('debug', message.id, x, y);

    return { x, y };
}
```


================================================
FILE: docs/arguments/matches.md
================================================
# Matching Input

### Entire Content

Let's say you have a command that picks from a list inputted.  
Obviously, you won't know how many things there are.  
So, we need a different way of matching input instead of phrase by phrase.  

```js
const { Command } = require('discord-akairo');

class PickCommand extends Command {
    constructor() {
        super('pick', {
            aliases: ['pick'],
            args: [
                {
                    // Only takes one phrase!
                    id: 'items'
                }
            ]
        });
    }

    exec(message, args) {
        const picked = args.items; // ???
        return message.reply(`I picked ${picked}`);
    }
}

module.exports = PickCommand;
```

To remedy this, we will use the `match` option.  

```js
const { Command } = require('discord-akairo');

class PickCommand extends Command {
    constructor() {
        super('pick', {
            aliases: ['pick'],
            args: [
                {
                    id: 'items',
                    match: 'content'
                }
            ]
        });
    }

    exec(message, args) {
        const items = args.items.split('|');
        const picked = items[Math.floor(Math.random() * items.length)]
        return message.reply(`I picked ${picked.trim()}!`);
    }
}

module.exports = PickCommand;
```

Now, the entire content, excluding the prefix and command of course, is matched. 

### Flags

If you had a command with lots of argument that can be true or false, you may forget the order.  
This is where `flag` match comes in handy.  

Here is a command where the user can change the output with a flag:  

```js
const { Command } = require('discord-akairo');
const exampleAPI = require('example-api');

class StatsCommand extends Command {
    constructor() {
        super('stats', {
            aliases: ['stats'],
            args: [
                {
                    id: 'username'
                },
                {
                    id: 'advanced',
                    match: 'flag',
                    flag: '--advanced'
                }
            ]
        });
    }

    exec(message, args) {
        const user = exampleAPI.getUser(args.username);

        if (args.advanced) {
            return message.reply(user.advancedInfo);
        }

        return message.reply(user.basicInfo);
    }
}

module.exports = StatsCommand;
```

Now, if a user does `?stats 1Computer` they will get the `basicInfo`, but if they do `?stats 1Computer --advanced`, they will get the `advancedInfo`.  
It can be out of order too, so `?stats --advanced 1Computer` will work.  

### Option Flag

The above example shows `flag`, which does only a boolean value, there or not there.  
Here, we will use `option` for unordered input.  

Similar to the above example, but this time, we have many different possibilities.  

```js
const { Command } = require('discord-akairo');
const exampleAPI = require('example-api');

class StatsCommand extends Command {
    constructor() {
        super('stats', {
            aliases: ['stats'],
            args: [
                {
                    id: 'username'
                },
                {
                    id: 'color',
                    match: 'option',
                    flag: 'color:',
                    default: 'red'
                }
            ]
        });
    }

    exec(message, args) {
        const team = exampleAPI.getTeam(args.color);
        const user = team.getUser(args.username);
        
        return message.reply(user.info);
    }
}

module.exports = StatsCommand;
```

So now, all of these inputs can be valid:  

- `?stats 1Computer`
- `?stats 1Computer color:blue`
- `?stats color:green 1Computer`

It's also whitespace insensitive between the flag and the input:  

- `?stats 1Computer color: blue`
- `?stats color: green 1Computer`

If you would like to use multiple flags, you can use an array.  
So, if you did `prefix: ['color:', 'colour:']`, both will be valid for the user.  

Note that for both flag match type, you can have flags with whitespace or using an empty string.  
It will work, but will be extremely weird for the end users, so don't do it!  

### Separate

Let's say that we want our pick command to only work on numbers.    
This would mean having to deal with splitting then casting the types within the args!  
We can do this with a custom separator using `separator` option alongside the `separate` match.  

```js
const { Command } = require('discord-akairo');

class PickCommand extends Command {
    constructor() {
        super('pick', {
            aliases: ['pick'],
            separator: '|',
            args: [
                {
                    id: 'items',
                    match: 'separate',
                    type: 'number'
                }
            ]
        });
    }

    exec(message, args) {
        const picked = args.items[Math.floor(Math.random() * args.items.length)]
        return message.reply(`I picked ${picked} which is ${picked % 2 === 0 ? 'even' : 'odd'}!`);
    }
}

module.exports = PickCommand;
``` 

The `separate` match matches the phrases individually into an array where each element is type casted one by one.  
The `separator` option simply makes it so that all the input is separated via a certain character rather than by whitespace.  

Note that with the `separator` option, quotes will not work.  
Flags will also have to be contained individually:  

- `!foo a, --flag, c` recognizes `--flag`
- `!foo a, x --flag y, c` does not
- `!foo a, --option y, c` recognizes `--option`
- `!foo a, x --option y, c` does not

### Summary

Here are all the match types available in Akairo.  

- `phrase` (default) matches one by one (where a phrase is either a word or something in quotes).
- `rest` matches the rest of the content, minus things matched by `flag` and `option`.
- `separate` matches the same way as `rest`, but works on each phrase separately.
- `flag` matches a flag.
- `option` matches a flag with additional input.
- `content` matches the content.
- `text` matches the content, minus things matched by `flag` and `option`.
- `none` matches nothing at all.

The different match types have the following behavior with border whitespaces, quotes, and separators:  
- `phrase`, `separate`, and `option` do not preserve any of the three.  
- `rest`, `content`, `text` do preserve all three.  


================================================
FILE: docs/arguments/prompts.md
================================================
# Argument Prompting

### Please Try Again

You may notice prompting for arguments in other bots (Tatsumaki) or bot frameworks (Commando).  
Akairo has a flexible way for you to do them too!  
It allows you to set the following properties:  

- How many times the user can retry.
- How long they can stall the prompt for.
- The input to use to cancel a prompt (default is `cancel`).
- Whether or not the prompt is optional.
- The message to send on start, on retry, on timeout, on maximum retries, and on cancel.
    - There can be embeds or files too!
    - Or you can have no message at all!

Let's start with a basic prompt.  
We will be reusing this command:  

```js
const { Command } = require('discord-akairo');

class HighestRoleCommand extends Command {
    constructor() {
        super('highestRole', {
            aliases: ['highestRole'],
            args: [
                {
                    id: 'member',
                    type: 'member',
                    default: message => message.member
                }
            ],
            channel: 'guild'
        });
    }

    exec(message, args) {
        return message.reply(args.member.roles.highest.name);
    }
}

module.exports = HighestRoleCommand;
```

First, remove the `default`.  
Since prompting will have the user retry until it is finished, `default` won't do anything.  
Now, add the `prompt` property with the options you want.  

```js
const { Command } = require('discord-akairo');

class HighestRoleCommand extends Command {
    constructor() {
        super('highestRole', {
            aliases: ['highestRole'],
            args: [
                {
                    id: 'member',
                    type: 'member',
                    prompt: {
                        start: 'Who would you like to get the highest role of?',
                        retry: 'That\'s not a valid member! Try again.'
                    }
                }
            ],
            channel: 'guild'
        });
    }

    exec(message, args) {
        return message.reply(args.member.roles.highest.name);
    }
}

module.exports = HighestRoleCommand;
```

Simple as that, you have a prompt.  
Guess what, you can use a function for those messages too!  

```js
prompt: {
    start: message => `Hey ${message.author}, who would you like to get the highest role of?`,
    retry: message => `That\'s not a valid member! Try again, ${message.author}.`
}
```

More complex structures can also be returned as well.  
This includes embeds, attachments, anything that can be sent.  

```js
prompt: {
    start: message => {
        const embed = new MessageEmbed().setDescription('Please input a member!');
        const content = 'Please!';
        return { embed, content };
    }
}
```

### Cascading

Prompts can also "cascade" from three places: the command handler, then the command, then the argument.  
For the command handler or the command, we would set the `argumentDefaults` option.  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    argumentDefaults: {
        prompt: {
            timeout: 'Time ran out, command has been cancelled.',
            ended: 'Too many retries, command has been cancelled.',
            cancel: 'Command has been cancelled.',
            retries: 4,
            time: 30000
        }
    }
});
```

Those prompt options would now be applied to all prompts that do not have those options already.  
Or, with a command with similar arguments:  

```js
const { Command } = require('discord-akairo');

class AddCommand extends Command {
    constructor() {
        super('add', {
            aliases: ['add'],
            args: [
                {
                    id: 'numOne',
                    type: 'number',
                    prompt: true
                },
                {
                    id: 'numTwo',
                    type: 'number',
                    prompt: true
                }
                {
                    id: 'numThree',
                    type: 'number',
                    prompt: true
                }
            ],
            defaultPrompt: {
                start: 'Please input a number!',
                retry: 'Please input a number!'
            }
        });
    }

    exec(message, args) {
        const sum = args.numOne + args.numTwo + args.numThree;
        return message.reply(`The sum is ${sum}!`);
    }
}

module.exports = AddCommand;
```

Rather than repeating the text for all three arguments, there is a default prompt that applies to all three.  
Their `prompt` property still has to be truthy in order to actually prompt, of course.  

### Modifying

Prompts can then be modified with a modify function.  
It is most useful inside the `argumentDefaults` option, such as on the command handler.  

```js
argumentDefaults: {
    prompt: {
        modifyStart: (message, text) => `${text}\nType cancel to cancel this command.`,
        modifyRetry: (message, text) => `${text}\nType cancel to cancel this command.`,
        timeout: 'Time ran out, command has been cancelled.',
        ended: 'Too many retries, command has been cancelled.',
        cancel: 'Command has been cancelled.',
        retries: 4,
        time: 30000
    }
}
```

The options `modifyStart`, `modifyRetry`, etc. are used to modify those types of prompts.  
With the above options, all `start` and `retry` prompts will have "Type cancel to cancel this command." appended after it.  


================================================
FILE: docs/arguments/prompts2.md
================================================
# More Prompting

### Optional Prompts

Optional prompts are prompts that run if there was input, but the type casting failed.  
If there was no input, it would go on as normal.  

```js
const { Command } = require('discord-akairo');

class HighestRoleCommand extends Command {
    constructor() {
        super('highestRole', {
            aliases: ['highestRole'],
            args: [
                {
                    id: 'member',
                    type: 'member',
                    prompt: {
                        start: 'Who would you like to get the highest role of?',
                        retry: 'That\'s not a valid member! Try again.',
                        optional: true
                    },
                    default: message => message.member
                }
            ],
            channel: 'guild'
        });
    }

    exec(message, args) {
        return message.reply(args.member.roles.highest.name);
    }
}

module.exports = HighestRoleCommand;
```

With it, `default` is now used again.  

- `?highestRole` would give the name for the message author.
- `?highestRole 1Computer` would give the name for 1Computer.
- `?highestRole someone-non-existant` would start up the prompts.

### Infinite Prompts

Infinite prompts are prompts that go on and on until the user says stop.  
(You can customize the input, but by default it is `stop`.)  

```js
const { Command } = require('discord-akairo');

class PickCommand extends Command {
    constructor() {
        super('pick', {
            aliases: ['pick'],
            args: [
                {
                    id: 'items',
                    match: 'none',
                    prompt: {
                        start: [
                            'What items would you like to pick from?',
                            'Type them in separate messages.',
                            'Type `stop` when you are done.'
                        ],
                        infinite: true
                    }
                }
            ]
        });
    }

    exec(message, args) {
        const picked = args.items[Math.floor(Math.random() * args.items.length)];
        return message.reply(`I picked ${picked.trim()}!`);
    }
}

module.exports = PickCommand;
```

And with that, `args.items` is now an array of responses from the user.  
Note that the `none` match is used, meaning nothing is matched in the original message.  
Because this is an infinite prompt that goes across multiple messages, we don't want it to take input from the original message.  
If you wish to allow a hybrid of matching and prompting multiple phrases, try using `separate` match with infinite prompts.   


================================================
FILE: docs/arguments/types.md
================================================
# Argument Types

### Basic Types

As seen in the previous tutorials, there was the `type` option for type casting.  
You've only seen the type `number`, so here are the rest of them:  

- `string` (default)
    - This type does not do anything.
- `lowercase`
    - Transform input to all lowercase.
- `uppercase`
    - Transform input to all uppercase.
- `charCodes`
    - Transform the input to an array of char codes.
- `number`
    - Casts to a number.
- `integer`
    - Casts to a integer.
- `bigint`
    - Casts to a big integer.
- `url`
    - Parses to an URL object.
- `date`
    - Parses to a Date object.
- `color`
    - Parses a hex code to an color integer.
- `commandAlias`
    - Finds a command by alias.
- `command`
    - Finds a command by ID.
- `inhibitor`
    - Finds an inhibitor by ID.
- `listener`
    - Finds a listener by ID.

### Discord Types

Of course, since this is a framework for Discord.js, there are Discord-related types.  

- `user`
    - Resolves a user from the client's collection.
- `member`
    - Resolves a member from the guild's collection.
- `relevant`
    - Resolves a user from the relevant place.
    - Works in both guilds and DMs.
- `channel`
    - Resolves a channel from the guild's collection.
- `textChannel`
    - Resolves a text channel from the guild's collection.
- `voiceChannel`
    - Resolves a voice channel from the guild's collection.
- `role`
    - Resolves a role from the guild's collection.
- `emoji`
    - Resolves an emoji from the guild's collection.
- `guild`
    - Resolves a guild from the client's collection.

All of the above types also have plural forms.  
So if you do `users` instead of `user`, you will receive a Collection of resolved users.  
The types below are also Discord-related, but have no plural form.  

- `message`
    - Fetches a message from an ID within the channel.
- `guildMessage`
    - Fetches a message from an ID within the guild.
- `invite`
    - Fetches an invite from a link.
- `userMention`
    - Matches the user from a mention.
- `memberMention`
    - Matches the member from a mention.
- `channelMention`
    - Matches the channel from a mention.
- `roleMention`
    - Matches the role from a mention.
- `emojiMention`
    - Matches the emoji from a mention.

### Array Types

There are other ways to do type-casting instead of a string literal too.  
The first way is with an array:  

```js
const { Command } = require('discord-akairo');

class PokemonCommand extends Command {
    constructor() {
        super('pokemon', {
            aliases: ['pokemon'],
            args: [
                {
                    id: 'option',
                    type: ['grass', 'fire', 'water', 'electric'],
                    default: 'electric'
                }
            ]
        });
    }

    exec(message, args) {
        if (args.option === 'grass') return message.reply('bulbasaur');
        if (args.option === 'fire') return message.reply('charmander');
        if (args.option === 'water') return message.reply('squirtle');
        if (args.option === 'electric') return message.reply('pikachu');
    }
}

module.exports = PokemonCommand;
```

With the above, the user can only enter one of the entries in the array.  
It is also case-insensitive for input, but not for output.  
This means that if the array was `['GrasS', 'FIrE']` and the input was `grass`, you will get `GrasS`.  

You can also do aliases with the array type like so:  

```js
const { Command } = require('discord-akairo');

class PokemonCommand extends Command {
    constructor() {
        super('pokemon', {
            aliases: ['pokemon'],
            args: [
                {
                    id: 'option',
                    type: [
                        ['grass', 'leaf', 'green'],
                        ['fire', 'red'],
                        ['water', 'blue'],
                        ['electric', 'electricity', 'lightning', 'yellow']
                    ],
                    default: 'electric'
                }
            ]
        });
    }

    exec(message, args) {
        if (args.option === 'grass') return message.reply('bulbasaur');
        if (args.option === 'fire') return message.reply('charmander');
        if (args.option === 'water') return message.reply('squirtle');
        if (args.option === 'electric') return message.reply('pikachu');
    }
}

module.exports = PokemonCommand;
```

If the user inputs anything from the arrays, the first entry will be used.  
So, the input of `leaf` will give you `grass`, `blue` will give you `water`, etc.  

### Regex Types

You can also use a regular expression as a type.  

```js
const { Command } = require('discord-akairo');

class AskCommand extends Command {
    constructor() {
        super('ask', {
            aliases: ['ask'],
            args: [
                {
                    id: 'yesOrNo',
                    type: /^(yes|no)$/i
                }
            ]
        });
    }

    exec(message, args) {
        // {
        //   match: [...],
        //   matches: null
        // }
        console.log(args.yesOrNo);
    }
}

module.exports = AskCommand;
```

This will match `yes` or `no`, case-insensitive and `args.yesOrNo` will give you the result from `word.match(/^(yes|no)$/i`.  
If using a global regex, the `matches` property will be filled for the matches.   


================================================
FILE: docs/arguments/unordered.md
================================================
# Unordered Arguments

### Any Order!

Arguments can be made to be unordered.  
For example, if you want a command where the arguments are a role and a member in any order:  

```js
const { Command } = require('discord-akairo');

class AddRoleCommand extends Command {
    constructor() {
        super('addrole', {
            aliases: ['addrole'],
            args: [
                {
                    id: 'member',
                    type: 'member',
                    unordered: true
                },
                {
                    id: 'role',
                    type: 'role',
                    unordered: true
                }
            ],
            userPermissions: ['ADMINISTRATOR'],
            channel: 'guild'
        });
    }

    async exec(message, args) {
        await args.member.roles.add(args.role);
        return message.reply('Done!');
    }
}

module.exports = AddRoleCommand;
```

The above command would work as `!addrole member role` and `!addrole role member`.  
No phrase will be parsed twice, for example, if the first phrase matched as a member, the role argument will ignore the first phrase and start with the second.  

Only the match type `phrase` (which is by default) works with the `unordered` option.  
Other match types will ignore this behavior.  

To choose a index to be unordered from (e.g. from the second phrase onwards) use a number, e.g. `unordered: 1`.  
To choose specific indices to be unordered on, use an array, e.g. `unordered: [0, 1, 2]`.  

### With Defaults or Prompts

Unordered arguments have a slightly different behavior when used with a default value and/or a prompt.  

If an unordered argument has a default and nothing matches, the default is used.  
If there is a prompt and nothing matches:  
  - If the prompt is optional, the default value is used.  
  - If not, the prompt is started as if no input was given.  

So, if you do have a prompt, make sure the `optional` option is not used or else it will prompt not at all.  


================================================
FILE: docs/basics/commands.md
================================================
# Basic Commands

### The Command Handler

In Akairo, the hierachy is that there are handlers which contains modules.  
The handlers deals with loading modules and executing them.  
For commands, we will import and instantiate the `CommandHandler`.  

```js
const { AkairoClient, CommandHandler } = require('discord-akairo');

class MyClient extends AkairoClient {
    constructor() {
        super({
            ownerID: '123992700587343872', // or ['123992700587343872', '86890631690977280']
        }, {
            disableMentions: 'everyone'
        });

        this.commandHandler = new CommandHandler(this, {
            // Options for the command handler goes here.
        });
    }
}

const client = new MyClient();
client.login('TOKEN');
```

Now, for some options.  
The `directory` option tells the handler where the main set of commands modules are at.  
The `prefix` option is simply the prefixes you want to use, you can have multiple too!  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?' // or ['?', '!']
});
```

And now that the command handler has been setup, we simply have to tell it to load the modules.  

```js
this.commandHandler.loadAll();
```

### Ping Command

Our first order of business is to make a ping command.  
No bot is complete without one!  

We specified that the `directory` is in `./commands/`.  
So, go there, make a new file, and require Akairo.  

```js
const { Command } = require('discord-akairo');
```

Here is a basic ping command:  

```js
const { Command } = require('discord-akairo');

class PingCommand extends Command {
    constructor() {
        super('ping', {
           aliases: ['ping'] 
        });
    }
    
    exec(message) {
        return message.reply('Pong!');
    }
}

module.exports = PingCommand;
```

The first parameter of `super` is the unique ID of the command.  
It is not seen nor used by users, but you should keep it the same as one of the aliases.  

The second parameter is the options.  
The only option there right now are the aliases, which are the names of the command for the users to call.  
Note that the ID of the command is not an alias!  

The exec method is the execution function, ran when the command is called.  
You should try to always return a value such as a Promise with it, so that the framework can tell when a command finishes.  

If everything was done correctly, your command should now work!  
Because there are a lot of things that can be changed for commands, they will be explained further in other tutorials.  


================================================
FILE: docs/basics/inhibitors.md
================================================
# Basic Inhibitors

### Setup

Inhibitors are a way to monitor or block messages coming into the command handler.  
Because inhibitors are another kind of module, we need another kind of handler.  
To set it up, simply import and instantiate the `InhibitorHandler`, just like with the command handler.  

```js
const { AkairoClient, CommandHandler, InhibitorHandler } = require('discord-akairo');

class MyClient extends AkairoClient {
    constructor() {
        super({
            ownerID: '123992700587343872',
        }, {
            disableMentions: 'everyone'
        });

        this.commandHandler = new CommandHandler(this, {
            directory: './commands/',
            prefix: '?'
        });

        this.inhibitorHandler = new InhibitorHandler(this, {
            directory: './inhibitors/'
        });
    }
}

const client = new MyClient();
client.login('TOKEN');
```

Then, tell it to load all the modules.  
But, since inhibitors are a part of the command handling process, the command handler has to know about the inhibitor handler, so:  

```js
this.commandHandler.useInhibitorHandler(this.inhibitorHandler);
this.inhibitorHandler.loadAll();
```

### Blacklist

Create a folder named `inhibitors`, then a file there to make one.  

```js
const { Inhibitor } = require('discord-akairo');

class BlacklistInhibitor extends Inhibitor {
    constructor() {
        super('blacklist', {
            reason: 'blacklist'
        })
    }

    exec(message) {
        // He's a meanie!
        const blacklist = ['81440962496172032'];
        return blacklist.includes(message.author.id);
    }
}

module.exports = BlacklistInhibitor;
```

The first parameter in `super` is the unique ID of the inhibitor.  

The second parameter are the options.  
The option `reason` is what will get emitted to an event, but we can worry about that later.  

The exec method is ran on testing.  
It should return `true` in order to block the message.  
Promise are awaited and the resolved value will be checked.  


================================================
FILE: docs/basics/listeners.md
================================================
# Basic Listeners

### Setup

Listeners are a basic concept in Node.js.  
Problem is, you usually end up with loooooong files attaching listeners on your client.  
And plus, you can't reload them as easily!  

Let's add some listeners.  
You have to setup a `ListenerHandler` just like with commands and inhibitors.  

```js
const { AkairoClient, CommandHandler, InhibitorHandler, ListenerHandler } = require('discord-akairo');

class MyClient extends AkairoClient {
    constructor() {
        super({
            ownerID: '123992700587343872',
        }, {
            disableMentions: 'everyone'
        });

        this.commandHandler = new CommandHandler(this, {
            directory: './commands/',
            prefix: '?'
        });

        this.inhibitorHandler = new InhibitorHandler(this, {
            directory: './inhibitors/'
        });

        this.listenerHandler = new ListenerHandler(this, {
            directory: './listeners/'
        });
    }
}

const client = new MyClient();
client.login('TOKEN');
```

Then, tell it to load all the modules.  
The command handler may need to use the listener handler for some operations later on, so it should use it as well:  

```js
this.commandHandler.useListenerHandler(this.listenerHandler);
this.listenerHandler.loadAll();
```

### I'm Ready!

And now, we can make a listener!   
Let's start with a simple client `ready` event.  

```js
const { Listener } = require('discord-akairo');

class ReadyListener extends Listener {
    constructor() {
        super('ready', {
            emitter: 'client',
            event: 'ready'
        });
    }

    exec() {
        console.log('I\'m ready!');
    }
}

module.exports = ReadyListener;
```

The first parameter in `super` is the listener's unique ID.  

The second parameter are the options.  
First, we have the emitter's name.  
Then, we have the event we want to listen to.  

Then the exec method, whose parameters are the event's.  

### Custom Emitters

By default, the `client` emitter is the only one available.  
Handlers in Akairo are also EventEmitters, so we can have our listener handler listen to our handlers.  
Using `setEmitters`, we can set custom emitters:  

```js
this.listenerHandler.setEmitters({
    commandHandler: this.commandHandler,
    inhibitorHandler: this.inhibitorHandler,
    listenerHandler: this.listenerHandler
});
```

Note: You have to call `setEmitters` before `loadAll` or Akairo will not be able to resolve your emitters.

### Blocked Commands

Remember the `reason` for inhibitors in previous tutorial?  
They are emitted to the `messageBlocked` (anything with `pre` type or before) or `commandBlocked` (everything after) event by the command handler.  
Since we set the command handler to the key `commandHandler` up above, we have to use that as the `emitter` option.  

```js
const { Listener } = require('discord-akairo');

class CommandBlockedListener extends Listener {
    constructor() {
        super('commandBlocked', {
            emitter: 'commandHandler',
            event: 'commandBlocked'
        });
    }

    exec(message, command, reason) {
        console.log(`${message.author.username} was blocked from using ${command.id} because of ${reason}!`);
    }
}

module.exports = CommandBlockedListener;
```

And if you want your listeners to run only once, you add the option `type` with the value of `'once'`.  


================================================
FILE: docs/basics/setup.md
================================================
# Setting Up

### Installation

Before even doing anything else, you of course have to install the Discord.js and Akairo.  

`npm i discord.js`  
`npm i discord-akairo`  

If you feel like working with SQLite or Sequelize later, install them too.  

`npm i sqlite`  
`npm i sequelize`  

Once everything has been installed, your working directory should look something like this:  

```
mybot
|____ node_modules
      bot.js
```

### Main File

Inside `bot.js`, require `discord-akairo` and extend the `AkairoClient` class to customize your client.  
As your bot gets more complicated, you may want to separate this client class from your main file.  

```js
const { AkairoClient } = require('discord-akairo');

class MyClient extends AkairoClient {
    constructor() {
        super({
            // Options for Akairo go here.
        }, {
            // Options for discord.js goes here.
        });
    }
}

const client = new MyClient();
client.login('TOKEN');
```

There are some options you may want to setup first, for example, the owner of the bot.
If you would like to have multiple owners simply add those with an array.  
We want to use Discord.js's `disableMentions` option too.  

```js
const { AkairoClient } = require('discord-akairo');

class MyClient extends AkairoClient {
    constructor() {
        super({
            ownerID: '123992700587343872', // or ['123992700587343872', '86890631690977280']
        }, {
            disableMentions: 'everyone'
        });
    }
}

const client = new MyClient();
client.login('TOKEN');
```

Your bot should now login, and you are ready to make commands.  


================================================
FILE: docs/commands/commandutil.md
================================================
# CommandUtil

### Handling Edits

The CommandUtil class is a utility class for working with responses.  
In order to make it available, you must enable `commandUtil`.  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    handleEdits: true,
    commandUtil: true
});
```

Now, CommandUtil is available on messages with the property `util`.  
An instance is kept for each message that go through command handling, but they have a lifetime of 5 minutes from then.  
To keep them alive longer, set a larger time in milliseconds using the `commandUtilLifetime` option.  
Note that this can build up memory usage really fast on larger bots, so it is recommended you give it a reasonable lifetime.  

You can CommandUtil methods such as `send` in order to send responses.  
With `handleEdits` on, the `send` methods will edit responses accordingly.  
This works for prompts as well.  

```js
const { Command } = require('discord-akairo');

class HelloCommand extends Command {
    constructor() {
        super('hello', {
            aliases: ['hello']
        });
    }

    exec(message) {
        // Also available: util.reply()
        return message.util.send('Hello!');
    }
}

module.exports = HelloCommand;
```

As an example of what that means:  

- User sends `?ping` (message A).
- Bot replies with `Pong!` (message B).
- User edits message A to `?hello`.
- Bot edits message B to `Hello!`.

### Raw Input

CommandUtil can also be used to view the prefix, command alias, and arguments used.  
The format for command is almost always `<prefix><alias> <arguments>`.  
CommandUtil stores all three of that and more for you.  

```js
const { Command } = require('discord-akairo');

class HelloCommand extends Command {
    constructor() {
        super('hello', {
            aliases: ['hello', 'hi', 'konnichiha', 'bonjour', 'heyo']
        });
    }

    exec(message) {
        if (message.util.parsed.alias === 'konnichiha') {
            return message.util.send('こんにちは!');
        }

        if (message.util.parsed.alias === 'bonjour') {
            return message.util.send('Bonjour!');
        }

        return message.util.send('Hello!');
    }
}

module.exports = HelloCommand;
```

With that, you can see which alias was used by the user.  

You can see the prefix as well.  
For example, if you have two prefixes, `?` and `!`, `message.util.parsed.prefix` will be either `?` or `!`.  
The content can also be viewed, for example, in `!command xyz abc`, `message.util.parsed.content` would be `xyz abc`.  

CommandUtil, if enabled, is available on all messages just after built-in pre-inhibitors.  
This means an invalid input, e.g. `?not-a-command` will still be parsed with prefix of `?` and alias of `not-a-command`.  

### Stored Messages

If you set the command handler option `storeMessages` to true, CommandUtil instances will start storing messages from prompts.  
This means that prompts from the client as well as the user replies are stored within `message.util.messages`.  
See the prompting sections under Arguments for more information about prompts. 


================================================
FILE: docs/commands/conditional.md
================================================
# Conditional Commands

### Run Whenever

Conditional commands are commands that run if the following conditions are true:  
- The command was not invoked normally.
- The command's `condition` option is true.

Multiple conditional commands/regex commands can be triggered on one message.  

```js
const { Command } = require('discord-akairo');

class ComplimentCommand extends Command {
    constructor() {
        super('compliment', {
            category: 'random'
        });
    }

    condition(message) {
        return message.author.id === '126485019500871680';
    }

    exec(message) {
        return message.reply('You are a great person!');
    }
}

module.exports = ComplimentCommand;
```

This command, whenever a certain person sends any message, will execute.  


================================================
FILE: docs/commands/cooldowns.md
================================================
# Cooldowns

### No Spam!

Cooldowns are how you make sure that troublemakers don't spam your bot.  
Akairo allows you to set cooldowns in uses per milliseconds.  

```js
const { Command } = require('discord-akairo');
const exampleAPI = require('example-api');

class RequestCommand extends Command {
    constructor() {
        super('request', {
            aliases: ['request'],
            cooldown: 10000,
            ratelimit: 2
        });
    }

    async exec(message) {
        const info = await exampleAPI.fetchInfo();
        return message.reply(info);
    }
}

module.exports = RequestCommand;
```

`cooldown` is the amount of time a user would be in cooldown for.  
`ratelimit` is the amount of uses a user can do before they are denied usage.  

In simple terms, this means 2 uses every 10000 milliseconds.  

If you wish to set a default cooldown for all commands, the `defaultCooldown` option is available:  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    defaultCooldown: 1000
});
```

When someone uses a command while in cooldown, the event `cooldown` will be emitted on the command handler with the remaining time in milliseconds.  

### Ignoring Cooldown

By default, cooldowns are ignored by the client owners.  
This is actually done through the option `ignoreCooldown`.  
To change this, simply pass in an ID or an array of IDs:  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    defaultCooldown: 1000,
    ignoreCooldown: ['123992700587343872', '130175406673231873']
});
```

Note that you should pass the owner ID in as well, since it overrides the default.  
That is, unless you actually want to be ratelimited yourself.  
Also, a function could also be used to check who should be ignored.  


================================================
FILE: docs/commands/permissions.md
================================================
# Permissions

### Permission Flags

Some commands should only be used by someone with certain permissions.  
There are options to help you do this.  
The two options to use are `clientPermissions` and `userPermissions`.  

```js
const { Command } = require('discord-akairo');

class BanCommand extends Command {
    constructor() {
        super('ban', {
            aliases: ['ban'],
            args: [
                {
                    id: 'member',
                    type: 'member'
                }
            ],
            clientPermissions: ['BAN_MEMBERS'],
            userPermissions: ['BAN_MEMBERS'],
            channel: 'guild'
        });
    }

    async exec(message, args) {
        if (!args.member) {
            return message.reply('No member found with that name.');    
        }

        await args.member.ban();
        return message.reply(`${args.member} was banned!`);
    }
}

module.exports = BanCommand;
```

This now checks for the required permissions for the client, then the user.  
When blocked, it emits `missingPermissions` on the command handler.  
It will pass the message, command, either `client` or `user`, then the missing permissions.  

### Dynamic Permissions

Sometimes, you may want to check for a role instead of permission flags.  
This means you can use a function instead of an array!  
A function can be used on both `clientPermissions` and `userPermissions`.  

The return value is the `missing` parameter that is sent to the `missingPermissions` event.  
If the return value is null, then that means they're not missing anything.  

```js
const { Command } = require('discord-akairo');

class BanCommand extends Command {
    constructor() {
        super('ban', {
            aliases: ['ban'],
            args: [
                {
                    id: 'member',
                    type: 'member'
                }
            ],
            clientPermissions: ['BAN_MEMBERS'],
            channel: 'guild'
        });
    }

    userPermissions(message) {
        if (!message.member.roles.cache.some(role => role.name === 'Moderator')) {
            return 'Moderator';
        }

        return null;
    }

    async exec(message, args) {
        if (!args.member) {
            return message.reply('No member found with that name.');    
        }

        await args.member.ban();
        return message.reply(`${args.member} was banned!`);
    }
}

module.exports = BanCommand;
```


================================================
FILE: docs/commands/prefixes.md
================================================
# Prefixes and Aliases

### Mentioning

Sometimes people can forget or not know the prefix for your bot, so letting them use command with a mention is useful.  
This can be enabled with the `allowMention` option.  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    allowMention: true
});
```

Now both `?ping` and `@BOT ping` works!  

### Changeable Prefixes

A prefix can change based on the message.  
Use a function as the `prefix` option to do so.  
This is most useful with an actual database to back it up, so check out the [Using Providers](../other/providers.md) section.  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: msg => {
        // Get prefix here...
        return prefix;
    },
    allowMention: true
});
```

### Prefix Overrides

Prefix overrides are command-specific prefixes.  
To use them, simply add the `prefix` option.  

```js
const { Command } = require('discord-akairo');

class SecretCommand extends Command {
    constructor() {
        super('secret', {
            aliases: ['secret'],
            prefix: '???'
        });
    }

    exec(message) {
        return message.reply('Woah! How did you find this!?');
    }
}

module.exports = SecretCommand;
```

Now, if our prefix was `?`, `?secret` won't work, but `???secret` would.  
An array works too, so you can do `prefix: ['???', '??']` and both would work.  

### Automatic Aliases

To speed up your development, you can make command aliases automatically.  
For example, if you had a command alias that is two words, you might want both `command-name` and `commandname` to be valid.  
Use the `aliasReplacement` option, which takes a regular expression to make aliases:  

```js
this.commandHandler = new CommandHandler(this, {
    directory: './commands/',
    prefix: '?',
    aliasReplacement: /-/g,
    allowMention: true
});
```

The option is passed `/-/g` which means that all dashes are to be removed to make an alias.  
So now, in a command, you can pass `aliases: ['command-name']` and both `command-name` and `commandname` would be valid.  


================================================
FILE: docs/commands/regex.md
================================================
# Regex Commands

### Memes

Regex or regular expressions, is basically a way to match characters in a string.  
Regex commands are commands that run if the following conditions are true:  
- The command was not invoked normally.
- The command's `regex` matches the message.

Multiple regex commands/conditional commands can be triggered from one message.  

```js
const { Command } = require('discord-akairo');

class AyyCommand extends Command {
    constructor() {
        super('ayy', {
            regex: /^ayy$/i
        });
    }

    exec(message, args) {
        return message.reply('lmao');
    }
}

module.exports = AyyCommand;
```

This command will trigger on any message with the content `ayy`, case-insensitive.  
In `args`, the `match` property will be the result from `message.content.match(/^ayy$/i)`.  
The `matches` property will be the matches, if using a global regex.  

### As a Function

The `regex` option can also be a function.  

```js
const { Command } = require('discord-akairo');

class AyyCommand extends Command {
    constructor() {
        super('ayy', {
            category: 'random'
        });
    }

    regex(message) {
        // Do some code...
        return /^ayy$/i;
    }

    exec(message, args) {
        return message.reply('lmao');
    }
}

module.exports = AyyCommand;
```


================================================
FILE: docs/commands/restrictions.md
================================================
# Restrictions

### Channel Restrictions

If a command requires a guild to be used correctly, you can restrict it to a guild with one option.  

```js
const { Command } = require('discord-akairo');

class NicknameCommand extends Command {
    constructor() {
        super('nickname', {
            aliases: ['nickname']
        });
    }

    exec(message) {
        return message.reply(`Your nickname is ${message.member.nickname}.`);
    }
}

module.exports = NicknameCommand;
```

The above breaks in a DM, so let's add the `channel` option.  

```js
const { Command } = require('discord-akairo');

class NicknameCommand extends Command {
    constructor() {
        super('nickname', {
            aliases: ['nickname'],
            channel: 'guild'
        });
    }

    exec(message) {
        return message.reply(`Your nickname is ${message.member.nickname}.`);
    }
}

module.exports = NicknameCommand;
```

Everything is fixed and you can go on your way!  
As a bonus, this will emit `commandBlocked` on the command handler with the reason `guild` if someone tries to use it in a DM.  

### Owner Only

Remember the `ownerID` option in your client?  
Your commands can be owner-only, restricting them to be used by the owner(s).  
Simply add `ownerOnly`.  

```js
const { Command } = require('discord-akairo');

class TokenCommand extends Command {
    constructor() {
        super('token', {
            aliases: ['token'],
            ownerOnly: true,
            channel: 'dm'
        });
    }

    exec(message) {
        // Don't actually do this.
        return message.reply(this.client.token);
    }
}

module.exports = TokenCommand;
```

This will emit `commandBlocked` with the reason `owner` if someone else uses it.  


================================================
FILE: docs/general/welcome.md
================================================
<div align="center">
  <br />
  <p>
    <a href="https://discord-akairo.github.io"><img src="https://discord-akairo.github.io/static/logo.svg" width="546" alt="discord-akairo" /></a>
  </p>
  <br />
  <p>
    <a href="https://www.npmjs.com/package/discord-akairo"><img src="https://img.shields.io/npm/v/discord-akairo.svg?maxAge=3600" alt="NPM version" /></a>
    <a href="https://www.npmjs.com/package/discord-akairo"><img src="https://img.shields.io/npm/dt/discord-akairo.svg?maxAge=3600" alt="NPM downloads" /></a>
    <a href="https://travis-ci.org/discord-akairo/discord-akairo"><img src="https://travis-ci.org/discord-akairo/discord-akairo.svg" alt="Build status" /></a>
  </p>
  <p>
    <a href="https://nodei.co/npm/discord-akairo/"><img src="https://nodei.co/npm/discord-akairo.png?downloads=true" alt="npm installnfo" /></a>
  </p>
</div>

## Welcome!

You are currently looking at the discord-akairo v8 tutorials.

## Links

- [Website](https://discord-akairo.github.io)
- [Repository](https://github.com/discord-akairo/discord-akairo)  
- [Changelog](https://github.com/discord-akairo/discord-akairo/releases)
- [Discord](https://discord.gg/arTauDY)  


================================================
FILE: docs/index.yml
================================================
- name: General
  files:
    - name: Welcome
      path: welcome.md
- name: Basics
  files:
    - name: Setting Up
      path: setup.md
    - name: Basic Commands
      path: commands.md
    - name: Basic Inhibitors
      path: inhibitors.md
    - name: Basic Listeners
      path: listeners.md
- name: Commands
  files:
    - name: Restrictions
      path: restrictions.md
    - name: Permissions
      path: permissions.md
    - name: Cooldowns
      path: cooldowns.md
    - name: Regex Commands
      path: regex.md
    - name: Conditional Commands
      path: conditional.md
    - name: Prefixes and Aliases
      path: prefixes.md
    - name: CommandUtil
      path: commandutil.md
- name: Arguments
  files:
    - name: Basic Arguments
      path: arguments.md
    - name: Matching Input
      path: matches.md
    - name: Argument Types
      path: types.md
    - name: Using Functions
      path: functions.md
    - name: Composing Types
      path: compose.md
    - name: Custom Types
      path: custom.md
    - name: Argument Prompting
      path: prompts.md
    - name: More Prompting
      path: prompts2.md
    - name: Unordered Arguments
      path: unordered.md
    - name: Generator Arguments
      path: generators.md
- name: Inhibitors
  files:
    - name: Inhibitor Types
      path: inhibtypes.md
    - name: Inhibitor Priority
      path: priority.md
- name: Listeners
  files:
    - name: Custom Emitters
      path: emitters.md
- name: Other
  files:
    - name: Updating to v8
      path: updating.md
    - name: Handling Modules
      path: handling.md
    - name: Custom Handlers
      path: handlers.md
    - name: Using Providers
      path: providers.md
    - name: Using Mongoose Provider
      path: mongoose.md
    - name: ClientUtil
      path: clientutil.md
- name: Snippets
  files:
    - name: Ping Command
      path: ping.md


================================================
FILE: docs/inhibitors/inhibtypes.md
================================================
# Inhibitor Types

### More Coverage

Right now, your inhibitors only runs before a command.  
They do not actually run on all messages.  

To change that, change the `type` option.  

```js
const { Inhibitor } = require('discord-akairo');

class BlacklistInhibitor extends Inhibitor {
    constructor() {
        super('blacklist', {
            reason: 'blacklist',
            type: 'all'
        });
    }

    exec(message) {
        // Still a meanie!
        const blacklist = ['81440962496172032'];
        return blacklist.includes(message.author.id);
    }
}

module.exports = BlacklistInhibitor;
```

There are three types:  

- `all` is run on all messages.
- `pre` is run on messages not blocked by `all` and built-in inhibitors.
- `post` (the default) is run on messages before commands, not blocked by the previous.

The built-in inhibitors are:  

- `client` blocks the client (itself).
- `bot` blocks all other bots.
- `owner` blocks non-owners from using owner-only commands.
- `guild` blocks guild-only commands used in DMs.
- `dm` blocks DM-only commands used in guilds.

To make it easier to visualize, here is the order:  

- `all` type inhibitors.
- `client`, and `bot`.
- (commands sent when someone is in the middle of being prompted are blocked here)
- `pre` type inhibitors.
- `owner`, `guild`, and `dm`.
- (commands that have missing permissions are blocked here)
- `post` type inhibitors.
- (commands under cooldown are blocked here)


================================================
FILE: docs/inhibitors/priority.md
================================================
# Inhibitor Priority

### Me First!

Sometimes multiple inhibitors can block a message.  
For example, you may have an inhibitor for blacklisting within a server, and another for a global blacklist.  
By default, inhibitors are ordered by their load order, which is based on the filename.  
So, if you had named the inhibitors `blacklist.js` and `globalBlacklist.js`, the former would have a higher priority.  
Whenever both inhibitors block a message, the `commandBlocked` event would fire with the blacklist inhibitor's reason.  
If you want the global blacklist inhibitor's instead you can use the `priority` option.  

```js
const { Inhibitor } = require('discord-akairo');
const globalBlacklist = require('something');

class GlobalBlacklistInhibitor extends Inhibitor {
    constructor() {
        super('globalBlacklist', {
            reason: 'globalBlacklist',
            priority: 1
        });
    }

    exec(message) {
        return globalBlacklist.has(message.author.id);
    }
}

module.exports = BlacklistInhibitor;
```

By default, inhibitors have a priority of 0.  
By increasing it, it means that the inhibitor will now have priority over the others.  
So when two inhibitors block a message, the one with the higher priority will be used.  
If they have the same priority, then it is still by load order.  


================================================
FILE: docs/listeners/emitters.md
================================================
# Custom Emitters

### Watching Process

As shown in the first listener tutorial, we can have custom emitters.  
Listeners can run on more than Akairo-related things.  
To add a custom emitter, use the `setEmitters` method available on the listener handler.  

```js
this.listenerHandler.setEmitters({
    process: process,
    anything: youWant
});
```

Note: You have to call `setEmitters` before `load` or `loadAll` so that Akairo will be able to resolve your emitters.

The key will be the emitter's name, and the value is the emitter itself.  
Now, we can use a listener on the process:  

```js
const { Listener } = require('discord-akairo');

class UnhandledRejectionListener extends Listener {
    constructor() {
        super('unhandledRejection', {
            event: 'unhandledRejection',
            emitter: 'process'
        });
    }

    exec(error) {
        console.error(error);
    }
}

module.exports = UnhandledRejectionListener;
```


================================================
FILE: docs/other/clientutil.md
================================================
# ClientUtil

### Finding Things

ClientUtil is a class filled with utility methods.  
It is available on your client as `client.util`.  

There are three "groups" of resolver methods for finding or checking Discord-related things.  
They allow you to, for example, find a user named `1Computer` from an input of `comp`.  

- `resolve <thing>`
    - e.g. `resolveUser`, `resolveChannel`, etc.
    - Finds an Discord-related object from a collection of those objects.

- `resolve <things>`
    - e.g. `resolveUsers`, `resolveChannels`, etc.
    - Filters Discord-related objects from a collection of those objects.

- `check <thing>`
    - e.g. `checkUser`, `checkChannel`, etc.
    - Used for the above methods, checks if a string could be referring to the object.

### Other Methods

There are a bunch of other things you may find useful:  

- `embed`, `attachment`, and `collection`
    - Shortcuts for MessageEmbed, MessageAttachment, and Collection.
- `resolvePermissionNumber`
    - Converts a permission number to an array of permission names.


================================================
FILE: docs/other/handlers.md
================================================
# Custom Handlers

### And Custom Modules

Internally, Akairo's handlers all extends AkairoHandler, and all modules extends AkairoModule.  
So, you can create your own handlers and module types!  
Create a new class for your module.  

```js
const { AkairoModule } = require('discord-akairo');

class CustomModule extends AkairoModule {
    constructor(id, options = {}) {
        super(id, options);

        this.color = options.color || 'red';
    }

    exec() {
        throw new Error('Not implemented!');
    }
}

module.exports = CustomModule;
```

Note that the `exec` method you see in Command, Inhibitor, and Listener are not native to AkairoModule.  
They require you to actually create them within the module type, such as above.  
We throw an error there just in case you forget to implement it.  

Then, create a new class for your handler:  

```js
const { AkairoHandler } = require('discord-akairo');
const CustomModule = require('./CustomModule');

class CustomHandler extends AkairoHandler {
    constructor(client, options = {}) {
        super(client, {
            directory: options.directory,
            classToHandle: CustomModule
        });

        this.customOption = options.customOption || 'something';
    }
}

module.exports = CustomHandler;
```

For the handler, the `super()` takes the client, the directory for the handler, and the class of the module type we want to handle.  
Now we can add it to our client if we so desire:  

```js
const { AkairoClient } = require('discord-akairo');
const CustomHandler = require('./CustomHandler');

class MyClient extends AkairoClient {
    constructor() {
        super({
            ownerID: '123992700587343872',
        }, {
            disableMentions: 'everyone'
        });

        this.customHandler = new CustomHandler(this, {
            directory: './customs/'
        });

        this.customHandler.loadAll();
    }
}

module.exports = MyClient;
```

And the module:  

```js
const CustomModule = require('../CustomModule');

class CustomCustom extends CustomModule {
    constructor() {
        super('custom', {
            color: 'blue'
        });
    }

    exec() {
        console.log('I did something!');
    }
}

module.exports = CustomCustom;
```

Custom handlers and modules are can get much more complicated than this.  
However, it would be out of the scope of this tutorial, so if you want to go there, check out the source code on Github.  


================================================
FILE: docs/other/handling.md
================================================
# Handling Modules

### Categorizing

You can categorize a module with the `category` option.  

```js
const { Command } = require('discord-akairo');

class PingCommand extends Command {
    constructor() {
        super('ping', {
            aliases: ['ping'],
            category: 'stuff'
        });
    }

    exec(message) {
        return message.reply('Pong!');
    }
}

module.exports = PingCommand;
```

A new category will be created on the handler with the ID of `stuff`.  
By default, all modules are in the `default` category.  

### Reloading

Everything in Akairo is a module, and all modules are loaded by handlers.  
With that said, this means you can add, remove, or reload modules while the bot is running!  

Here is a basic command that reloads the inputted ID:  

```js
const { Command } = require('discord-akairo');

class ReloadCommand extends Command {
    constructor() {
        super('reload', {
            aliases: ['reload'],
            args: [
                {
                    id: 'commandID'
                }
            ],
            ownerOnly: true,
            category: 'owner'
        });
    }

    exec(message, args) {
        // `this` refers to the command object.
        this.handler.reload(args.commandID);
        return message.reply(`Reloaded command ${args.commandID}!`);
    }
}

module.exports = ReloadCommand;
```

Ways you can reload a module includes:  

- Individually:
    - `<AkairoHandler>.reload(moduleID)`
    - `<AkairoModule>.reload()`
- Many at once:
    - `<AkairoHandler>.reloadAll()`
    - `<Category>.reloadAll()`

### Removing and Adding

For removing, simply change all those `reload` to `remove`.  
To add a new module, you can use the `load` method.  
With `load`, you will need to specify a full filepath or a module class.  
If you load with a class, note that those cannot be reloaded.  


================================================
FILE: docs/other/mongoose.md
================================================
# Using Mongoose Provider

### Storing Prefixes

Let's implement per-guild prefixes.  
First, create a new MongooseProvider.

```js
// It is better to init mongoose in another file (eg. main.js)
// connect to database and then require this file (eg. bot.js)
const model = require('./path/to/model'); // see Model Example below

const { AkairoClient, MongooseProvider } = require('discord-akairo');

class CustomClient extends AkairoClient {
    constructor() {
        super({
            /* Options here */
        });

        // Mongoose Provider
        this.settings = new MongooseProvider(model);
    }
}
```

Before you can actually use the provider, you would have to run the `init` method.  
For example:

```js
class CustomClient extends AkairoClient {
    /* ... */
    async login(token) {
        await this.settings.init();
        return super.login(token);
    }
}
```

Now, the provider can be used like so:

```js
class CustomClient extends AkairoClient {
    constructor() {
        super({
            prefix: (message) => {
                if (message.guild) {
                    // The third param is the default.
                    return this.settings.get(message.guild.id, 'prefix', '!');
                }

                return '!';
            }
        });

        /* ... */
    }
}
```

Values can be set with the `set` method:

```js
const { Command } = require('discord-akairo');

class PrefixCommand extends Command {
    constructor() {
        super('prefix', {
            aliases: ['prefix'],
            category: 'stuff',
            args: [
                {
                    id: 'prefix',
                    default: '!'
                }
            ],
            channel: 'guild'
        });
    }

    async exec(message, args) {
        // The third param is the default.
        const oldPrefix = this.client.settings.get(message.guild.id, 'prefix', '!');

        await this.client.settings.set(message.guild.id, 'prefix', args.prefix);
        return message.reply(`Prefix changed from ${oldPrefix} to ${args.prefix}`);
    }
}

module.exports = PrefixCommand;
```

### Model Example

```js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const guildSchema = new Schema({
    id: {
        type: String,
        required: true
    },
    settings: {
        type: Object,
        required: true
    }
}, { minimize: false });

module.exports = mongoose.model('model', guildSchema);
```


================================================
FILE: docs/other/providers.md
================================================
# Using Providers

### Storing Prefixes

Akairo comes with SQLiteProvider and SequelizeProvider, optional utility classes for databases.  
Note that if you are doing something complicated with databases, you should use SQLite or Sequelize directly.  

Let's implement per-guild prefixes.  
First, create a new SQLiteProvider or SequelizeProvider.  

```js
const sqlite = require('sqlite');
const sequelize = require('sequelize');

const { AkairoClient, SQLiteProvider, SequelizeProvider } = require('discord-akairo');

class CustomClient extends AkairoClient {
    constructor() {
        super({
            /* Options here */
        });

        // With SQLite
        this.settings = new SQLiteProvider(sqlite.open('path/to/db.sqlite'), 'table_name', {
            idColumn: 'guild_id',
            dataColumn: 'settings'
        });

        // Or, with sequelize
        this.settings = new SequelizeProvider(/* Sequelize model here */, {
            idColumn: 'guild_id',
            dataColumn: 'settings'
        });
    }
}
```

The providers only handle one table at a time.  
Notice that you can set the `idColumn` and the `dataColumn`.  

The `idColumn` defaults to `id` and is the unique key for that table.  
The `dataColumn` is optional and will change the behavior of the provider in relation with the database.  

When the `dataColumn` is provided, the provider will parse a single column as JSON in order to set values.  
For Sequelize, remember to set that column's type to JSON or JSONB.  

When `dataColumn` is not provided, the provider will work on all columns of the table instead.  

Before you can actually use the provider, you would have to run the `init` method.  
For example:  

```js
class CustomClient extends AkairoClient {
    /* ... */
    async login(token) {
        await this.settings.init()
        return super.login(token);
    }
}
```

Now, the provider can be used like so:  

```js
class CustomClient extends AkairoClient {
    constructor() {
        super({
            prefix: message => {
                if (message.guild) {
                    // The third param is the default.
                    return this.settings.get(message.guild.id, 'prefix', '!');
                }

                return '!';
            }
        });

        /* ... */
    }
}
```

Values can be set with the `set` method:  

```js
const { Command } = require('discord-akairo');

class PrefixCommand extends Command {
    constructor() {
        super('prefix', {
            aliases: ['prefix'],
            category: 'stuff',
            args: [
                {
                    id: 'prefix',
                    default: '!'
                }
            ],
            channel: 'guild'
        });
    }

    async exec(message, args) {
        // The third param is the default.
        const oldPrefix = this.client.settings.get(message.guild.id, 'prefix', '!');

        await this.client.settings.set(message.guild.id, 'prefix', args.prefix);
        return message.reply(`Prefix changed from ${oldPrefix} to ${args.prefix}`);
    }
}

module.exports = PrefixCommand;
```


================================================
FILE: docs/other/updating.md
================================================
# Updating to v8

### Breaking Changes

This tutorial is for updating from Akairo v7 to v8.  
Many changes were introduced in v8 so hopefully this guide can help you fix them.  

Not only are there changes within the framework, there are also changes with Discord.js v12.  
You will have to update to Node 10 in order to use the libraries due to new JavaScript features.  

The suggestions below are not an exhaustive list.  
For a full changelog, see [here](https://github.com/discord-akairo/discord-akairo/releases).  

### Renames

Below are renames that will mostly be find-and-replace.  

##### General 

The ClientUtil method `fetchMemberFrom` has been renamed to `fetchMember`.  

##### Commands

The CommandHandler event `commandCooldown` has been renamed to `cooldown`.  

The Command option and property `channelRestriction` has been renamed to just `channel`.  
The Command option and property `trigger` has been renamed to `regex`.  

##### Arguments

The Argument option and property `prefix` has been renamed to `flag`.  
The Argument method `cast` has been renamed to `process`.  

Regex types in arguments e.g. `type: /^text$/` used to evaluate to an object with the property `match` and `groups`.  
This has been replaced with an object with the property `match` and `matches`.  

The match type `word` has been renamed to `phrase`.  
The match type `prefix` has been renamed to `option`.  

The TypeResolver property `handler` has been renamed to `commandHandler`.  

##### Listeners

The Listener option and property `eventName` has been renamed to just `event`.  

### Changes

Below are breaking changes that may require some more thought when fixing.  

##### General

The structure of the AkairoClient and the various handlers has been changed.  
To see what has changed, start at [Setting Up](../basics/setup.md).  

Before, in a type function of an arg when returning a Promise, it would only be a cast failure if the Promise rejected.  
Now, it will only be cast failure if the Promise resolves with `null` or `undefined`.  
This is to make async functions easier to use.  

Similarly, in inhibitors, a Promise rejection would be used to signify that the message was to be blocked.  
Now, with Promises, the Promise has to resolve with `true` to signify a block.  

The following methods are now properties:  

- `Argument#default`
- `Command#trigger`
- `CommandHandler#prefix`
- `CommandHandler#allowMention`

This means that you have to check if its a function before using it e.g.  
`typeof arg.default === 'function' ? arg.default(message) : arg.default`.  
This allows for checking if a value was set, such as the default value of an argument.  
Of course, if you already know that the property is or is not a function, then there is no need for changes.  

##### Commands

The events `commandStarted` and `commandFinished` have new parameters.  
`commandStarted` now passes `(message, command, args)` where `args` are the parsed args.  
`commandFinished` now passes `(message, command, args, returnValue)` where `returnValue` is what the command's exec function returned.  

The event `commandBlocked` is no longer fired when permissions checks are failed.  
Instead, a new event `missingPermissions` is fired.  
It will have the params `(message, command, type, missing)` where `type` could be either `client` or `user` and `missing` are the missing permissions.  

Regex commands used to pass in the values of `(message, match, groups, edited)`.  
Now it has been changed to `(message, args)`.  
The `args` object will contain `match` and `matches` property similar to a regex type.  

CommandHandler options `handleEdits` will no longer implicitly enable the `commandUtil` option.  
The `commandUtilLifetime` option also now defaults to 5 minutes.  

All the CommandUtil parse information such as `command`, `prefix`, `alias` etc. are moved to the `parsed` property.  

The `defaultPrompt` option has been changed to `argumentDefaults` which allow for more defaults.  
You can simply move your options into `argumentDefaults.prompt`.  

##### Arguments

Argument parsing now uses a new parser.  
Some behavior with whitespace and quotes may have changed.  

The argument type function used to have a special behavior for when `true` was returned.  
It would use the original user input phrase as the evaluated argument.  
Now, it simply is just `true`.  

Argument functions `this` binding has also been changed.  
They will now all point to the Argument instance rather than being inconsistent, where some would point to the command.  

The default value of the argument `default` option is now `null` rather than an empty string.  

Type functions previously would be passed `(phrase, message, args)`.  
They now pass `(message, phrase)`.  
The previous arguments can be accessed by using a generator for arguments.  

Previously, prompt functions would be passed `(message, args, retries)`.  
They now pass `(message, data)` where `data` is an object.  
You can get the retry count, among other properties, with `data.retries`.  
The previous arguments can be accessed by using a generator for arguments.  

The argument type `invite` used to just match an invite from a string.  
Now, it will attempt to fetch an Invite object.  

### Removals

All features deprecated in v7.5.x have been removed as well as some unexpected removals.  
Suggestions will be made for replacements.  

##### General

Loading modules by instances is now unsupported.  
This means you cannot do, for example, `module.exports = new Command(...)` or `handler.load(new Listener(...))`.  
Subsequently, this also means that the constructor for AkairoModule and related have also changed.  
The constructor is now `(id, options)` instead of `(id, exec, options)`.  
The `exec` method is now also not on the AkairoModule prototype and should be implemented where needed when extending.  

The AkairoHandler method `add` has been removed.  
Use the `load` with a full path instead.  

The AkairoHandler events `add` and `reload` have been removed.  
The `load` event will now take care of both.  
When reloaded, the `load` event will pass a second parameter which is a boolean, signifying that is was a reload.  

The ability to enable/disable modules have been removed, along with the events.  
It is recommended to implement this feature manually instead.  

Both ClientUtil prompt methods, `prompt` and `promptIn` were removed.  
Alternatives includes your own message collector, using `awaitMessages`, or using `Argument#collect`.  
An example of the `collect` method would be `new Argument(command, argOptions).collect(message)`.  

Selfbot support has been removed.  

##### Commands

The command option `split` is now removed.  
Instead, the `quoted` option is added, which can be true or false, defaulting to true.  

The `match` option on an argument can no longer be a function.  

In the command `exec` function as well as the `commandStarted` event and some other places, the `edited` parameter is removed.  
To see if the message was edited, you can check with `message.edited`.    

The `dynamic` and `dynamicInt` types were removed.  
Instead, use a union type e.g. `Argument.union('integer', 'string')`.  

##### SQLiteHandler

SQLiteHandler and related properties in AkairoClient have been removed completely.  
Alternatives include `SQLiteProvider` and `SequelizeProvider`.  
Or, you can make your own by extending the `Provider` class.  
For a guide on how to use the new providers, see [Using Providers](./providers,md).  

##### Other Removals

Other removals include the send aliases in CommandUtil, deprecated methods in ClientUtil, and some methods in AkairoClient.  
Most of them can now be found in Discord.js itself or implemented yourself if needed.  


================================================
FILE: docs/snippets/ping.md
================================================
# Ping Command

```js
const { Command } = require('discord-akairo');

class PingCommand extends Command {
    constructor() {
        super('ping', {
            aliases: ['ping', 'hello']
        });
    }

    async exec(message) {
        const sent = await message.util.reply('Pong!');
        const timeDiff = (sent.editedAt || sent.createdAt) - (message.editedAt || message.createdAt);
        return message.util.reply(
            'Pong!\n' +
            `🔂 **RTT**: ${timeDiff} ms\n` +
            `💟 **Heartbeat**: ${Math.round(this.client.ws.ping)} ms`
        );
    }
}

module.exports = PingCommand;
```


================================================
FILE: package.json
================================================
{
    "name": "discord-akairo",
    "version": "8.1.0",
    "description": "A highly customizable bot framework for Discord.js.",
    "main": "./src/index.js",
    "types": "./src/index.d.ts",
    "author": "1Computer",
    "license": "MIT",
    "keywords": [
        "discord",
        "discord-js",
        "discord.js",
        "framework",
        "bot",
        "client",
        "modular",
        "commands",
        "arguments"
    ],
    "dependencies": {},
    "devDependencies": {
        "@types/node": "^10.14.4",
        "discord.js-docgen": "github:discordjs/docgen",
        "eslint": "^5.16.0",
        "jsdoc": "^3.6.4",
        "tslint": "^5.15.0",
        "tslint-config-typings": "^0.3.1",
        "typescript": "^3.4.2"
    },
    "scripts": {
        "test": "npm run lint",
        "lint": "eslint ./src && tslint ./src/index.d.ts"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/discord-akairo/discord-akairo.git"
    },
    "bugs": {
        "url": "https://github.com/discord-akairo/discord-akairo/issues"
    },
    "homepage": "https://github.com/discord-akairo/discord-akairo"
}


================================================
FILE: src/index.d.ts
================================================
declare module 'discord-akairo' {
    import {
        BufferResolvable, Client, ClientOptions, Collection,
        Message, MessageAttachment, MessageEmbed,
        MessageEditOptions, MessageOptions, MessagePayload,
        User, UserResolvable, GuildMember,
        Channel, Role, Emoji, Guild, ReplyMessageOptions,
        PermissionResolvable, Snowflake
    } from 'discord.js';

    import { EventEmitter } from 'events';
    import { Stream } from 'stream';

    module 'discord.js' {
        export interface Message {
            util?: CommandUtil;
        }
    }

    export class AkairoError extends Error {
        public code: string;
    }

    export class AkairoClient extends Client {
        public constructor(options?: AkairoOptions & ClientOptions);
        public constructor(options: AkairoOptions, clientOptions: ClientOptions);

        public ownerID: Snowflake | Snowflake[];
        public util: ClientUtil;

        public isOwner(user: UserResolvable): boolean;
    }

    export class AkairoHandler extends EventEmitter {
        public constructor(client: AkairoClient, options: AkairoHandlerOptions);

        public automateCategories: boolean;
        public extensions: Set<string>;
        public categories: Collection<string, Category<string, AkairoModule>>;
        public classToHandle: Function;
        public client: AkairoClient;
        public directory: string;
        public loadFilter: LoadPredicate;
        public modules: Collection<string, AkairoModule>;

        public deregister(mod: AkairoModule): void;
        public findCategory(name: string): Category<string, AkairoModule>;
        public load(thing: string | Function, isReload?: boolean): AkairoModule;
        public loadAll(directory?: string, filter?: LoadPredicate): this;
        public register(mod: AkairoModule, filepath?: string): void;
        public reload(id: string): AkairoModule;
        public reloadAll(): this;
        public remove(id: string): AkairoModule;
        public removeAll(): this;
        public on(event: 'remove', listener: (mod: AkairoModule) => any): this;
        public on(event: 'load', listener: (mod: AkairoModule, isReload: boolean) => any): this;

        public static readdirRecursive(directory: string): string[];
    }

    export class AkairoModule {
        public constructor(id: string, options?: AkairoModuleOptions);

        public category: Category<string, AkairoModule>;
        public categoryID: string;
        public client: AkairoClient;
        public filepath: string;
        public handler: AkairoHandler;
        public id: string;

        public reload(): this;
        public remove(): this;
    }

    export class Argument {
        public constructor(command: Command, options: ArgumentOptions);

        public readonly client: AkairoClient;
        public command: Command;
        public default: DefaultValueSupplier | any;
        public description: string | any;
        public readonly handler: CommandHandler;
        public index?: number;
        public limit: number;
        public match: ArgumentMatch;
        public multipleFlags: boolean;
        public flag?: string | string[];
        public otherwise?: string | MessageOptions | OtherwiseContentSupplier;
        public prompt?: ArgumentPromptOptions | boolean;
        public type: ArgumentType | ArgumentTypeCaster;
        public unordered: boolean | number | number[];

        public allow(message: Message): boolean;
        public cast(message: Message, phrase: string): Promise<any>;
        public collect(message: Message, commandInput?: string): Promise<Flag | any>;
        public process(message: Message, phrase: string): Promise<any>;

        public static cast(type: ArgumentType | ArgumentTypeCaster, resolver: TypeResolver, message: Message, phrase: string): Promise<any>;
        public static compose(...types: (ArgumentType | ArgumentTypeCaster)[]): ArgumentTypeCaster;
        public static composeWithFailure(...types: (ArgumentType | ArgumentTypeCaster)[]): ArgumentTypeCaster;
        public static isFailure(value: any): value is null | undefined | Flag & { value: any };
        public static product(...types: (ArgumentType | ArgumentTypeCaster)[]): ArgumentTypeCaster;
        public static range(type: ArgumentType | ArgumentTypeCaster, min: number, max: number, inclusive?: boolean): ArgumentTypeCaster;
        public static tagged(type: ArgumentType | ArgumentTypeCaster, tag?: any): ArgumentTypeCaster;
        public static taggedUnion(...types: (ArgumentType | ArgumentTypeCaster)[]): ArgumentTypeCaster;
        public static taggedWithInput(type: ArgumentType | ArgumentTypeCaster, tag?: any): ArgumentTypeCaster;
        public static union(...types: (ArgumentType | ArgumentTypeCaster)[]): ArgumentTypeCaster;
        public static validate(type: ArgumentType | ArgumentTypeCaster, predicate: ParsedValuePredicate): ArgumentTypeCaster;
        public static withInput(type: ArgumentType | ArgumentTypeCaster): ArgumentTypeCaster;
    }

    export class Category<K, V> extends Collection<K, V> {
        public constructor(id: string, iterable?: Iterable<[K, V][]>);

        public id: string;

        public reloadAll(): this;
        public removeAll(): this;
    }

    export class ClientUtil {
        public constructor(client: AkairoClient);

        public readonly client: AkairoClient;

        public attachment(file: BufferResolvable | Stream, name?: string): MessageAttachment;
        public checkChannel(text: string, channel: Channel, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public checkEmoji(text: string, emoji: Emoji, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public checkGuild(text: string, guild: Guild, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public checkMember(text: string, member: GuildMember, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public checkRole(text: string, role: Role, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public checkUser(text: string, user: User, caseSensitive?: boolean, wholeWord?: boolean): boolean;
        public collection<K, V>(iterable?: Iterable<[K, V][]>): Collection<K, V>;
        public compareStreaming(oldMember: GuildMember, newMember: GuildMember): number;
        public embed(data?: object): MessageEmbed;
        public fetchMember(guild: Guild, id: string, cache?: boolean): Promise<GuildMember>;
        public resolveChannel(text: string, channels: Collection<Snowflake, Channel>, caseSensitive?: boolean, wholeWord?: boolean): Channel;
        public resolveChannels(text: string, channels: Collection<Snowflake, Channel>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, Channel>;
        public resolveEmoji(text: string, emojis: Collection<Snowflake, Emoji>, caseSensitive?: boolean, wholeWord?: boolean): Emoji;
        public resolveEmojis(text: string, emojis: Collection<Snowflake, Emoji>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, Emoji>;
        public resolveGuild(text: string, guilds: Collection<Snowflake, Guild>, caseSensitive?: boolean, wholeWord?: boolean): Guild;
        public resolveGuilds(text: string, guilds: Collection<Snowflake, Guild>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, Guild>;
        public resolveMember(text: string, members: Collection<Snowflake, GuildMember>, caseSensitive?: boolean, wholeWord?: boolean): GuildMember;
        public resolveMembers(text: string, members: Collection<Snowflake, GuildMember>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, GuildMember>;
        public resolvePermissionNumber(number: number): string[];
        public resolveRole(text: string, roles: Collection<Snowflake, Role>, caseSensitive?: boolean, wholeWord?: boolean): Role;
        public resolveRoles(text: string, roles: Collection<Snowflake, Role>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, Role>;
        public resolveUser(text: string, users: Collection<Snowflake, User>, caseSensitive?: boolean, wholeWord?: boolean): User;
        public resolveUsers(text: string, users: Collection<Snowflake, User>, caseSensitive?: boolean, wholeWord?: boolean): Collection<Snowflake, User>;
    }

    export class Command extends AkairoModule {
        public constructor(id: string, options?: CommandOptions);

        public aliases: string[];
        public argumentDefaults: DefaultArgumentOptions;
        public quoted: boolean;
        public category: Category<string, Command>;
        public channel?: string;
        public client: AkairoClient;
        public clientPermissions: PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;
        public cooldown?: number;
        public description: string | any;
        public editable: boolean;
        public filepath: string;
        public handler: CommandHandler;
        public id: string;
        public lock?: KeySupplier;
        public locker?: Set<string>;
        public ignoreCooldown?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        public ignorePermissions?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        public ownerOnly: boolean;
        public prefix?: string | string[] | PrefixSupplier;
        public ratelimit: number;
        public regex: RegExp | RegexSupplier;
        public typing: boolean;
        public userPermissions: PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;

        public before(message: Message): any;
        public condition(message: Message): boolean;
        public exec(message: Message, args: any): any;
        public parse(message: Message, content: string): Promise<Flag | any>;
        public reload(): this;
        public remove(): this;
    }

    export class CommandHandler extends AkairoHandler {
        public constructor(client: AkairoClient, options: CommandHandlerOptions);

        public aliasReplacement?: RegExp;
        public aliases: Collection<string, string>;
        public allowMention: boolean | MentionPrefixPredicate;
        public argumentDefaults: DefaultArgumentOptions;
        public blockBots: boolean;
        public blockClient: boolean;
        public categories: Collection<string, Category<string, Command>>;
        public classToHandle: typeof Command;
        public client: AkairoClient;
        public commandUtil: boolean;
        public commandUtilLifetime: number;
        public commandUtils: Collection<string, CommandUtil>;
        public commandUtilSweepInterval: number;
        public cooldowns: Collection<string, { [id: string]: CooldownData }>;
        public defaultCooldown: number;
        public directory: string;
        public fetchMembers: boolean;
        public handleEdits: boolean;
        public ignoreCooldown: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        public ignorePermissions: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        public inhibitorHandler?: InhibitorHandler;
        public modules: Collection<string, Command>;
        public prefix: string | string[] | PrefixSupplier;
        public prefixes: Collection<string | PrefixSupplier, Set<string>>;
        public prompts: Collection<string, Set<string>>;
        public resolver: TypeResolver;
        public storeMessage: boolean;

        public add(filename: string): Command;
        public addPrompt(channel: Channel, user: User): void;
        public deregister(command: Command): void;
        public emitError(err: Error, message: Message, command?: Command): void;
        public findCategory(name: string): Category<string, Command>;
        public findCommand(name: string): Command;
        public handle(message: Message): Promise<boolean | null>;
        public handleDirectCommand(message: Message, content: string, command: Command, ignore?: boolean): Promise<boolean | null>;
        public handleRegexAndConditionalCommands(message: Message): Promise<boolean>;
        public handleRegexCommands(message: Message): Promise<boolean>;
        public handleConditionalCommands(message: Message): Promise<boolean>;
        public hasPrompt(channel: Channel, user: User): boolean;
        public load(thing: string | Function, isReload?: boolean): Command;
        public loadAll(directory?: string, filter?: LoadPredicate): this;
        public parseCommand(message: Message): Promise<ParsedComponentData>;
        public parseCommandOverwrittenPrefixes(message: Message): Promise<ParsedComponentData>;
        public parseMultiplePrefixes(message: Message, prefixes: [string, Set<string> | null]): ParsedComponentData;
        public parseWithPrefix(message: Message, prefix: string, associatedCommands?: Set<string>): ParsedComponentData;
        public register(command: Command, filepath?: string): void;
        public reload(id: string): Command;
        public reloadAll(): this;
        public remove(id: string): Command;
        public removeAll(): this;
        public removePrompt(channel: Channel, user: User): void;
        public runAllTypeInhibitors(message: Message): Promise<boolean>;
        public runPermissionChecks(message: Message, command: Command): Promise<boolean>;
        public runPreTypeInhibitors(message: Message): Promise<boolean>;
        public runPostTypeInhibitors(message: Message, command: Command): Promise<boolean>;
        public runCooldowns(message: Message, command: Command): boolean;
        public runCommand(message: Message, command: Command, args: any): Promise<void>;
        public useInhibitorHandler(inhibitorHandler: InhibitorHandler): this;
        public useListenerHandler(ListenerHandler: ListenerHandler): this;
        public on(event: 'remove', listener: (command: Command) => any): this;
        public on(event: 'load', listener: (command: Command, isReload: boolean) => any): this;
        public on(event: 'commandBlocked', listener: (message: Message, command: Command, reason: string) => any): this;
        public on(event: 'commandBreakout', listener: (message: Message, command: Command, breakMessage: Message) => any): this;
        public on(event: 'commandCancelled', listener: (message: Message, command: Command, retryMessage?: Message) => any): this;
        public on(event: 'commandFinished', listener: (message: Message, command: Command, args: any, returnValue: any) => any): this;
        public on(event: 'commandLocked', listener: (message: Message, command: Command) => any): this;
        public on(event: 'commandStarted', listener: (message: Message, command: Command, args: any) => any): this;
        public on(event: 'cooldown', listener: (message: Message, command: Command, remaining: number) => any): this;
        public on(event: 'error', listener: (error: Error, message: Message, command?: Command) => any): this;
        public on(event: 'inPrompt' | 'messageInvalid', listener: (message: Message) => any): this;
        public on(event: 'messageBlocked', listener: (message: Message, reason: string) => any): this;
        public on(event: 'missingPermissions', listener: (message: Message, command: Command, type: 'client' | 'user', missing?: any) => any): this;
    }

    export class CommandUtil {
        public constructor(handler: CommandHandler, message: Message);

        public handler: CommandHandler;
        public lastResponse?: Message;
        public message: Message;
        public messages?: Collection<Snowflake, Message>;
        public parsed?: ParsedComponentData;
        public shouldEdit: boolean;

        public addMessage(message: Message | Message[]): Message | Message[];

        public edit(content: string | MessageEditOptions | MessagePayload): Promise<Message>;
        public reply(options: string | MessagePayload | ReplyMessageOptions): Promise<Message>;
        public send(options: string | MessagePayload | MessageOptions): Promise<Message>;
        public sendNew(options: string | MessagePayload | MessageOptions): Promise<Message>;

        public setEditable(state: boolean): this;
        public setLastResponse(message: Message | Message[]): Message;

        public static transformOptions(options?: string | MessageOptions): MessageOptions;
    }

    export class Flag {
        public constructor(type: string, data: object);

        public type: string;

        public static cancel(): Flag;
        public static continue(command: string, ignore?: boolean, rest?: string): Flag & { command: string, ignore: boolean, rest: string };
        public static retry(message: Message): Flag & { message: Message };
        public static fail(value: any): Flag & { value: any };
        public static is(value: any, type: 'cancel'): value is Flag;
        public static is(value: any, type: 'continue'): value is Flag & { command: string, ignore: boolean, rest: string };
        public static is(value: any, type: 'retry'): value is Flag & { message: Message };
        public static is(value: any, type: 'fail'): value is Flag & { value: any };
        public static is(value: any, type: string): value is Flag;
    }

    export class Inhibitor extends AkairoModule {
        public constructor(id: string, options?: InhibitorOptions);

        public category: Category<string, Inhibitor>;
        public client: AkairoClient;
        public filepath: string;
        public handler: InhibitorHandler;
        public id: string;
        public reason: string;
        public type: string;

        public exec(message: Message, command?: Command): boolean | Promise<boolean>;
        public reload(): this;
        public remove(): this;
    }

    export class InhibitorHandler extends AkairoHandler {
        public constructor(client: AkairoClient, options: AkairoHandlerOptions);

        public categories: Collection<string, Category<string, Inhibitor>>;
        public classToHandle: typeof Inhibitor;
        public client: AkairoClient;
        public directory: string;
        public modules: Collection<string, Inhibitor>;

        public deregister(inhibitor: Inhibitor): void;
        public findCategory(name: string): Category<string, Inhibitor>;
        public load(thing: string | Function): Inhibitor;
        public loadAll(directory?: string, filter?: LoadPredicate): this;
        public register(inhibitor: Inhibitor, filepath?: string): void;
        public reload(id: string): Inhibitor;
        public reloadAll(): this;
        public remove(id: string): Inhibitor;
        public removeAll(): this;
        public test(type: 'all' | 'pre' | 'post', message: Message, command?: Command): Promise<string | void>;
        public on(event: 'remove', listener: (inhibitor: Inhibitor) => any): this;
        public on(event: 'load', listener: (inhibitor: Inhibitor, isReload: boolean) => any): this;
    }

    export class Listener extends AkairoModule {
        public constructor(id: string, options?: ListenerOptions);

        public category: Category<string, Listener>;
        public client: AkairoClient;
        public emitter: string | EventEmitter;
        public event: string;
        public filepath: string;
        public handler: ListenerHandler;
        public type: string;

        public exec(...args: any[]): any;
        public reload(): this;
        public remove(): this;
    }

    export class ListenerHandler extends AkairoHandler {
        public constructor(client: AkairoClient, options: AkairoHandlerOptions);

        public categories: Collection<string, Category<string, Listener>>;
        public classToHandle: typeof Listener;
        public client: AkairoClient;
        public directory: string;
        public emitters: Collection<string, EventEmitter>;
        public modules: Collection<string, Listener>;

        public add(filename: string): Listener;
        public addToEmitter(id: string): Listener;
        public deregister(listener: Listener): void;
        public findCategory(name: string): Category<string, Listener>;
        public load(thing: string | Function): Listener;
        public loadAll(directory?: string, filter?: LoadPredicate): this;
        public register(listener: Listener, filepath?: string): void;
        public reload(id: string): Listener;
        public reloadAll(): this;
        public remove(id: string): Listener;
        public removeAll(): this;
        public removeFromEmitter(id: string): Listener;
        public setEmitters(emitters: { [x: string]: EventEmitter }): void;
        public on(event: 'remove', listener: (listener: Listener) => any): this;
        public on(event: 'load', listener: (listener: Listener, isReload: boolean) => any): this;
    }

    export abstract class Provider {
        public items: Collection<string, any>;

        public abstract clear(id: string): any;
        public abstract delete(id: string, key: string): any;
        public abstract get(id: string, key: string, defaultValue: any): any;
        public abstract init(): any;
        public abstract set(id: string, key: string, value: any): any;
    }

    export class SequelizeProvider extends Provider {
        public constructor(table: any, options?: ProviderOptions);

        public dataColumn?: string;
        public idColumn: string;
        public items: Collection<string, any>;
        public table: any;

        public clear(id: string): Promise<void>;
        public delete(id: string, key: string): Promise<boolean>;
        public get(id: string, key: string, defaultValue: any): any;
        public init(): Promise<void>;
        public set(id: string, key: string, value: any): Promise<boolean>;
    }

    export class SQLiteProvider extends Provider {
        public constructor(db: any | Promise<any>, tableName: string, options?: ProviderOptions);

        public dataColumn?: string;
        public db: any;
        public idColumn: string;
        public items: Collection<string, any>;
        public tableName: string;

        public clear(id: string): Promise<any>;
        public delete(id: string, key: string): Promise<any>;
        public get(id: string, key: string, defaultValue: any): any;
        public init(): Promise<void>;
        public set(id: string, key: string, value: any): Promise<any>;
    }

    export class MongooseProvider extends Provider {
        public constructor(model: any);

        public model: any;
        public items: Collection<string, any>;

        public clear(id: string): Promise<any>;
        public delete(id: string, key: string): Promise<any>;
        public get(id: string, key: string, defaultValue: any): any;
        public getDocument(id: string): any;
        public init(): Promise<void>;
        public set(id: string, key: string, value: any): Promise<any>;
    }

    export class TypeResolver {
        public constructor(handler: CommandHandler);

        public client: AkairoClient;
        public commandHandler: CommandHandler;
        public inhibitorHandler?: InhibitorHandler;
        public listenerHandler?: ListenerHandler;
        public types: Collection<string, ArgumentTypeCaster>;

        public addBuiltInTypes(): void;
        public addType(name: string, fn: ArgumentTypeCaster): this;
        public addTypes(types: { [x: string]: ArgumentTypeCaster }): this;
        public type(name: string): ArgumentTypeCaster;
    }

    export class Util {
        public static isEventEmitter(value: any): boolean;
        public static isPromise(value: any): boolean;
    }

    export interface AkairoHandlerOptions {
        automateCategories?: boolean;
        classToHandle?: Function;
        directory?: string;
        extensions?: string[] | Set<string>;
        loadFilter?: LoadPredicate;
    }

    export interface AkairoModuleOptions {
        category?: string;
    }

    export interface AkairoOptions {
        ownerID?: Snowflake | Snowflake[];
    }

    export interface DefaultArgumentOptions {
        prompt?: ArgumentPromptOptions;
        otherwise?: string | MessageOptions | OtherwiseContentSupplier;
        modifyOtherwise?: OtherwiseContentModifier;
    }

    export interface ArgumentOptions {
        default?: DefaultValueSupplier | any;
        description?: string;
        id?: string;
        index?: number;
        limit?: number;
        match?: ArgumentMatch;
        modifyOtherwise?: OtherwiseContentModifier;
        multipleFlags?: boolean;
        flag?: string | string[];
        otherwise?: string | MessageOptions | OtherwiseContentSupplier;
        prompt?: ArgumentPromptOptions | boolean;
        type?: ArgumentType | ArgumentTypeCaster;
        unordered?: boolean | number | number[];
    }

    export interface ArgumentPromptData {
        infinite: boolean;
        message: Message;
        retries: number;
        phrase: string;
        failure: void | (Flag & { value: any });
    }

    export interface ArgumentPromptOptions {
        breakout?: boolean;
        cancel?: string | MessageOptions | PromptContentSupplier;
        cancelWord?: string;
        ended?: string | MessageOptions | PromptContentSupplier;
        infinite?: boolean;
        limit?: number;
        modifyCancel?: PromptContentModifier;
        modifyEnded?: PromptContentModifier;
        modifyRetry?: PromptContentModifier;
        modifyStart?: PromptContentModifier;
        modifyTimeout?: PromptContentModifier;
        optional?: boolean;
        retries?: number;
        retry?: string | MessageOptions | PromptContentSupplier;
        start?: string | MessageOptions | PromptContentSupplier;
        stopWord?: string;
        time?: number;
        timeout?: string | MessageOptions | PromptContentSupplier;
    }

    export interface ArgumentRunnerState {
        index: number;
        phraseIndex: number;
        usedIndices: Set<number>;
    }

    export interface CommandOptions extends AkairoModuleOptions {
        aliases?: string[];
        args?: ArgumentOptions[] | ArgumentGenerator;
        argumentDefaults?: DefaultArgumentOptions;
        before?: BeforeAction;
        channel?: 'guild' | 'dm';
        clientPermissions?: PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;
        condition?: ExecutionPredicate;
        cooldown?: number;
        description?: string | any;
        editable?: boolean;
        flags?: string[];
        ignoreCooldown?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        ignorePermissions?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        lock?: KeySupplier | 'guild' | 'channel' | 'user';
        optionFlags?: string[];
        ownerOnly?: boolean;
        prefix?: string | string[] | PrefixSupplier;
        ratelimit?: number;
        regex?: RegExp | RegexSupplier;
        separator?: string;
        typing?: boolean;
        userPermissions?: PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;
        quoted?: boolean;
    }

    export interface CommandHandlerOptions extends AkairoHandlerOptions {
        aliasReplacement?: RegExp;
        allowMention?: boolean | MentionPrefixPredicate;
        argumentDefaults?: DefaultArgumentOptions;
        blockBots?: boolean;
        blockClient?: boolean;
        commandUtil?: boolean;
        commandUtilLifetime?: number;
        commandUtilSweepInterval?: number;
        defaultCooldown?: number;
        fetchMembers?: boolean;
        handleEdits?: boolean;
        ignoreCooldown?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        ignorePermissions?: Snowflake | Snowflake[] | IgnoreCheckPredicate;
        prefix?: string | string[] | PrefixSupplier;
        storeMessages?: boolean;
    }

    export interface ContentParserResult {
        all: StringData[];
        phrases: StringData[];
        flags: StringData[];
        optionFlags: StringData[];
    }

    export interface CooldownData {
        end: number;
        timer: NodeJS.Timer;
        uses: number;
    }

    export interface FailureData {
        phrase: string;
        failure: void | (Flag & { value: any });
    }

    export interface InhibitorOptions extends AkairoModuleOptions {
        reason?: string;
        type?: string;
        priority?: number;
    }

    export interface ListenerOptions extends AkairoModuleOptions {
        emitter: string | EventEmitter;
        event: string;
        type?: string;
    }

    export interface ParsedComponentData {
        afterPrefix?: string;
        alias?: string;
        command?: Command;
        content?: string;
        prefix?: string;
    }

    export interface ProviderOptions {
        dataColumn?: string;
        idColumn?: string;
    }

    export type StringData = {
        type: 'Phrase';
        value: string;
        raw: string;
    } | {
        type: 'Flag';
        key: string;
        raw: string;
    } | {
        type: 'OptionFlag',
        key: string;
        value: string;
        raw: string;
    };

    export type ArgumentMatch = 'phrase' | 'flag' | 'option' | 'rest' | 'separate' | 'text' | 'content' | 'restContent' | 'none';

    export type ArgumentType = 'string' | 'lowercase' | 'uppercase' | 'charCodes'
        | 'number' | 'integer' | 'bigint' | 'emojint'
        | 'url' | 'date' | 'color'
        | 'user' | 'users' | 'member' | 'members' | 'relevant' | 'relevants'
        | 'channel' | 'channels' | 'textChannel' | 'textChannels' | 'voiceChannel' | 'voiceChannels' | 'categoryChannel' | 'categoryChannels' | 'newsChannel' | 'newsChannels' | 'storeChannel' | 'storeChannels'
        | 'role' | 'roles' | 'emoji' | 'emojis' | 'guild' | 'guilds'
        | 'message' | 'guildMessage' | 'relevantMessage' | 'invite'
        | 'userMention' | 'memberMention' | 'channelMention' | 'roleMention' | 'emojiMention'
        | 'commandAlias' | 'command' | 'inhibitor' | 'listener'
        | (string | string[])[]
        | RegExp
        | string;

    export type ArgumentGenerator = (message: Message, parsed: ContentParserResult, state: ArgumentRunnerState) => IterableIterator<ArgumentOptions | Flag>;

    export type ArgumentTypeCaster = (message: Message, phrase: string) => any;

    export type BeforeAction = (message: Message) => any;

    export type DefaultValueSupplier = (message: Message, data: FailureData) => any;

    export type ExecutionPredicate = (message: Message) => boolean;

    export type IgnoreCheckPredicate = (message: Message, command: Command) => boolean;

    export type KeySupplier = (message: Message, args: any) => string;

    export type LoadPredicate = (filepath: string) => boolean;

    export type MentionPrefixPredicate = (message: Message) => boolean | Promise<boolean>;

    export type MissingPermissionSupplier = (message: Message) => Promise<any> | any;

    export type OtherwiseContentModifier = (message: Message, text: string, data: FailureData)
        => string | MessageOptions | Promise<string | MessageOptions>;

    export type OtherwiseContentSupplier = (message: Message, data: FailureData)
        => string | MessageOptions | Promise<string | MessageOptions>;

    export type ParsedValuePredicate = (message: Message, phrase: string, value: any) => boolean;

    export type PrefixSupplier = (message: Message) => string | string[] | Promise<string | string[]>;

    export type PromptContentModifier = (message: Message, text: string, data: ArgumentPromptData)
        => string | MessageOptions | Promise<string | MessageOptions>;

    export type PromptContentSupplier = (message: Message, data: ArgumentPromptData)
        => string | MessageOptions | Promise<string | MessageOptions>;

    export type RegexSupplier = (message: Message) => RegExp;

    export const Constants: {
        ArgumentMatches: {
            PHRASE: 'phrase';
            FLAG: 'flag';
            OPTION: 'option';
            REST: 'rest';
            SEPARATE: 'separate';
            TEXT: 'text';
            CONTENT: 'content';
            REST_CONTENT: 'restContent';
            NONE: 'none';
        };
        ArgumentTypes: {
            STRING: 'string';
            LOWERCASE: 'lowercase';
            UPPERCASE: 'uppercase';
            CHAR_CODES: 'charCodes';
            NUMBER: 'number';
            INTEGER: 'integer';
            BIGINT: 'bigint';
            EMOJINT: 'emojint';
            URL: 'url';
            DATE: 'date';
            COLOR: 'color';
            USER: 'user';
            USERS: 'users';
            MEMBER: 'member';
            MEMBERS: 'members';
            RELEVANT: 'relevant';
            RELEVANTS: 'relevants';
            CHANNEL: 'channel';
            CHANNELS: 'channels';
            TEXT_CHANNEL: 'textChannel';
            TEXT_CHANNELS: 'textChannels';
            VOICE_CHANNEL: 'voiceChannel';
            VOICE_CHANNELS: 'voiceChannels';
            CATEGORY_CHANNEL: 'categoryChannel';
            CATEGORY_CHANNELS: 'categoryChannels';
            NEWS_CHANNEL: 'newsChannel';
            NEWS_CHANNELS: 'newsChannels';
            STORE_CHANNEL: 'storeChannel';
            STORE_CHANNELS: 'storeChannels';
            ROLE: 'role';
            ROLES: 'roles';
            EMOJI: 'emoji';
            EMOJIS: 'emojis';
            GUILD: 'guild';
            GUILDS: 'guilds';
            MESSAGE: 'message';
            GUILD_MESSAGE: 'guildMessage';
            INVITE: 'invite';
            MEMBER_MENTION: 'memberMention';
            CHANNEL_MENTION: 'channelMention';
            ROLE_MENTION: 'roleMention';
            EMOJI_MENTION: 'emojiMention';
            COMMAND_ALIAS: 'commandAlias';
            COMMAND: 'command';
            INHIBITOR: 'inhibitor';
            LISTENER: 'listener';
        };
        AkairoHandlerEvents: {
            LOAD: 'load';
            REMOVE: 'remove';
        };
        CommandHandlerEvents: {
            MESSAGE_BLOCKED: 'messageBlocked';
            MESSAGE_INVALID: 'messageInvalid';
            COMMAND_BLOCKED: 'commandBlocked';
            COMMAND_STARTED: 'commandStarted';
            COMMAND_FINISHED: 'commandFinished';
            COMMAND_CANCELLED: 'commandCancelled';
            COMMAND_LOCKED: 'commandLocked';
            MISSING_PERMISSIONS: 'missingPermissions';
            COOLDOWN: 'cooldown';
            IN_PROMPT: 'inPrompt';
            ERROR: 'error';
        };
        BuiltInReasons: {
            CLIENT: 'client';
            BOT: 'bot';
            OWNER: 'owner';
            GUILD: 'guild';
            DM: 'dm';
        };
    };

    export const version: string;
}


================================================
FILE: src/index.js
================================================
module.exports = {
    // Core
    AkairoClient: require('./struct/AkairoClient'),
    AkairoHandler: require('./struct/AkairoHandler'),
    AkairoModule: require('./struct/AkairoModule'),
    ClientUtil: require('./struct/ClientUtil'),

    // Commands
    Command: require('./struct/commands/Command'),
    CommandHandler: require('./struct/commands/CommandHandler'),
    CommandUtil: require('./struct/commands/CommandUtil'),
    Flag: require('./struct/commands/Flag'),

    // Arguments
    Argument: require('./struct/commands/arguments/Argument'),
    TypeResolver: require('./struct/commands/arguments/TypeResolver'),

    // Inhibitors
    Inhibitor: require('./struct/inhibitors/Inhibitor'),
    InhibitorHandler: require('./struct/inhibitors/InhibitorHandler'),

    // Listeners
    Listener: require('./struct/listeners/Listener'),
    ListenerHandler: require('./struct/listeners/ListenerHandler'),

    // Providers
    Provider: require('./providers/Provider'),
    SequelizeProvider: require('./providers/SequelizeProvider'),
    SQLiteProvider: require('./providers/SQLiteProvider'),
    MongooseProvider: require('./providers/MongooseProvider'),

    // Utilities
    AkairoError: require('./util/AkairoError'),
    Category: require('./util/Category'),
    Constants: require('./util/Constants'),
    Util: require('./util/Util'),
    version: require('../package.json').version
};


================================================
FILE: src/providers/MongooseProvider.js
================================================
const Provider = require('./Provider');

/**
 * Provider using the `Mongoose` library.
 * @param {Model} model - A Mongoose model.
 * @extends {Provider}
 */
class MongooseProvider extends Provider {
    constructor(model) {
        super();

        /**
         * Mongoose model.
         * @type {Model}
         */
        this.model = model;
    }

    /**
     * Initializes the provider.
     * @returns {Promise<void>}
     */
    async init() {
        const guilds = await this.model.find();
        for (const i in guilds) {
            const guild = guilds[i];
            this.items.set(guild.id, guild.settings);
        }
    }

    /**
     * Gets a value.
     * @param {string} id - guildID.
     * @param {string} key - The key to get.
     * @param {any} [defaultValue] - Default value if not found or null.
     * @returns {any}
     */
    get(id, key, defaultValue) {
        if (this.items.has(id)) {
            const value = this.items.get(id)[key];
            return value == null ? defaultValue : value;
        }

        return defaultValue;
    }

    /**
     * Sets a value.
     * @param {string} id - guildID.
     * @param {string} key - The key to set.
     * @param {any} value - The value.
     * @returns {Promise<any>} - Mongoose query object|document
     */
    async set(id, key, value) {
        const data = this.items.get(id) || {};
        data[key] = value;
        this.items.set(id, data);

        const doc = await this.getDocument(id);
        doc.settings[key] = value;
        doc.markModified('settings');
        return doc.save();
    }

    /**
     * Deletes a value.
     * @param {string} id - guildID.
     * @param {string} key - The key to delete.
     * @returns {Promise<any>} - Mongoose query object|document
     */
    async delete(id, key) {
        const data = this.items.get(id) || {};
        delete data[key];

        const doc = await this.getDocument(id);
        delete doc.settings[key];
        doc.markModified('settings');
        return doc.save();
    }

    /**
     * Removes a document.
     * @param {string} id - GuildID.
     * @returns {Promise<void>}
     */
    async clear(id) {
        this.items.delete(id);
        const doc = await this.getDocument(id);
        if (doc) await doc.remove();
    }

    /**
     * Gets a document by guildID.
     * @param {string} id - guildID.
     * @returns {Promise<any>} - Mongoose query object|document
     */
    async getDocument(id) {
        const obj = await this.model.findOne({ id });
        if (!obj) {
            // eslint-disable-next-line new-cap
            const newDoc = await new this.model({ id, settings: {} }).save();
            return newDoc;
        }

        return obj;
    }
}

module.exports = MongooseProvider;


================================================
FILE: src/providers/Provider.js
================================================
const AkairoError = require('../util/AkairoError');
const { Collection } = require('discord.js');

/**
 * A provider for key-value storage.
 * Must be implemented.
 */
class Provider {
    constructor() {
        /**
         * Cached entries.
         * @type {Collection<string, Object>}
         */
        this.items = new Collection();
    }

    /**
     * Initializes the provider.
     * @abstract
     * @returns {any}
     */
    init() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'init');
    }

    /**
     * Gets a value.
     * @abstract
     * @param {string} id - ID of entry.
     * @param {string} key - The key to get.
     * @param {any} [defaultValue] - Default value if not found or null.
     * @returns {any}
     */
    get() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'get');
    }

    /**
     * Sets a value.
     * @abstract
     * @param {string} id - ID of entry.
     * @param {string} key - The key to set.
     * @param {any} value - The value.
     * @returns {any}
     */
    set() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'set');
    }

    /**
     * Deletes a value.
     * @abstract
     * @param {string} id - ID of entry.
     * @param {string} key - The key to delete.
     * @returns {any}
     */
    delete() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'delete');
    }

    /**
     * Clears an entry.
     * @abstract
     * @param {string} id - ID of entry.
     * @returns {any}
     */
    clear() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'clear');
    }
}

module.exports = Provider;

/**
 * Options to use for providers.
 * @typedef {Object} ProviderOptions
 * @prop {string} [idColumn='id'] - Column for the unique key, defaults to 'id'.
 * @prop {string} [dataColumn] - Column for JSON data.
 * If not provided, the provider will use all columns of the table.
 * If provided, only one column will be used, but it will be more flexible due to being parsed as JSON.
 * For Sequelize, note that the model has to specify the type of the column as JSON or JSONB.
 */


================================================
FILE: src/providers/SQLiteProvider.js
================================================
const Provider = require('./Provider');

/**
 * Provider using the `sqlite` library.
 * @param {Database|Promise<Database>} db - SQLite database from `sqlite`.
 * @param {string} tableName - Name of table to handle.
 * @param {ProviderOptions} [options={}] - Options to use.
 * @extends {Provider}
 */
class SQLiteProvider extends Provider {
    constructor(db, tableName, { idColumn = 'id', dataColumn } = {}) {
        super();

        /**
         * SQLite database.
         * @type {Database}
         */
        this.db = db;

        /**
         * Name of the table.
         * @type {string}
         */
        this.tableName = tableName;

        /**
         * Column for ID.
         * @type {string}
         */
        this.idColumn = idColumn;

        /**
         * Column for JSON data.
         * @type {?string}
         */
        this.dataColumn = dataColumn;
    }

    /**
     * Initializes the provider.
     * @returns {Promise<void>}
     */
    async init() {
        const db = await this.db;
        this.db = db;

        const rows = await this.db.all(`SELECT * FROM ${this.tableName}`);
        for (const row of rows) {
            this.items.set(row[this.idColumn], this.dataColumn ? JSON.parse(row[this.dataColumn]) : row);
        }
    }

    /**
     * Gets a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to get.
     * @param {any} [defaultValue] - Default value if not found or null.
     * @returns {any}
     */
    get(id, key, defaultValue) {
        if (this.items.has(id)) {
            const value = this.items.get(id)[key];
            return value == null ? defaultValue : value;
        }

        return defaultValue;
    }

    /**
     * Sets a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to set.
     * @param {any} value - The value.
     * @returns {Promise<Statement>}
     */
    set(id, key, value) {
        const data = this.items.get(id) || {};
        const exists = this.items.has(id);

        data[key] = value;
        this.items.set(id, data);

        if (this.dataColumn) {
            return this.db.run(exists
                ? `UPDATE ${this.tableName} SET ${this.dataColumn} = $value WHERE ${this.idColumn} = $id`
                : `INSERT INTO ${this.tableName} (${this.idColumn}, ${this.dataColumn}) VALUES ($id, $value)`, {
                $id: id,
                $value: JSON.stringify(data)
            });
        }

        return this.db.run(exists
            ? `UPDATE ${this.tableName} SET ${key} = $value WHERE ${this.idColumn} = $id`
            : `INSERT INTO ${this.tableName} (${this.idColumn}, ${key}) VALUES ($id, $value)`, {
            $id: id,
            $value: value
        });
    }

    /**
     * Deletes a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to delete.
     * @returns {Promise<Statement>}
     */
    delete(id, key) {
        const data = this.items.get(id) || {};
        delete data[key];

        if (this.dataColumn) {
            return this.db.run(`UPDATE ${this.tableName} SET ${this.dataColumn} = $value WHERE ${this.idColumn} = $id`, {
                $id: id,
                $value: JSON.stringify(data)
            });
        }

        return this.db.run(`UPDATE ${this.tableName} SET ${key} = $value WHERE ${this.idColumn} = $id`, {
            $id: id,
            $value: null
        });
    }

    /**
     * Clears an entry.
     * @param {string} id - ID of entry.
     * @returns {Promise<Statement>}
     */
    clear(id) {
        this.items.delete(id);
        return this.db.run(`DELETE FROM ${this.tableName} WHERE ${this.idColumn} = $id`, { $id: id });
    }
}

module.exports = SQLiteProvider;


================================================
FILE: src/providers/SequelizeProvider.js
================================================
const Provider = require('./Provider');

/**
 * Provider using the `sequelize` library.
 * @param {Model} table - A Sequelize model.
 * @param {ProviderOptions} [options={}] - Options to use.
 * @extends {Provider}
 */
class SequelizeProvider extends Provider {
    constructor(table, { idColumn = 'id', dataColumn } = {}) {
        super();

        /**
         * Sequelize model.
         * @type {Model}
         */
        this.table = table;

        /**
         * Column for ID.
         * @type {string}
         */
        this.idColumn = idColumn;

        /**
         * Column for JSON data.
         * @type {?string}
         */
        this.dataColumn = dataColumn;
    }

    /**
     * Initializes the provider.
     * @returns {Bluebird<void>}
     */
    async init() {
        const rows = await this.table.findAll();
        for (const row of rows) {
            this.items.set(row[this.idColumn], this.dataColumn ? row[this.dataColumn] : row);
        }
    }

    /**
     * Gets a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to get.
     * @param {any} [defaultValue] - Default value if not found or null.
     * @returns {any}
     */
    get(id, key, defaultValue) {
        if (this.items.has(id)) {
            const value = this.items.get(id)[key];
            return value == null ? defaultValue : value;
        }

        return defaultValue;
    }

    /**
     * Sets a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to set.
     * @param {any} value - The value.
     * @returns {Bluebird<boolean>}
     */
    set(id, key, value) {
        const data = this.items.get(id) || {};
        data[key] = value;
        this.items.set(id, data);

        if (this.dataColumn) {
            return this.table.upsert({
                [this.idColumn]: id,
                [this.dataColumn]: data
            });
        }

        return this.table.upsert({
            [this.idColumn]: id,
            [key]: value
        });
    }

    /**
     * Deletes a value.
     * @param {string} id - ID of entry.
     * @param {string} key - The key to delete.
     * @returns {Bluebird<boolean>}
     */
    delete(id, key) {
        const data = this.items.get(id) || {};
        delete data[key];

        if (this.dataColumn) {
            return this.table.upsert({
                [this.idColumn]: id,
                [this.dataColumn]: data
            });
        }

        return this.table.upsert({
            [this.idColumn]: id,
            [key]: null
        });
    }

    /**
     * Clears an entry.
     * @param {string} id - ID of entry.
     * @returns {Bluebird<void>}
     */
    clear(id) {
        this.items.delete(id);
        return this.table.destroy({ where: { [this.idColumn]: id } });
    }
}

module.exports = SequelizeProvider;


================================================
FILE: src/struct/AkairoClient.js
================================================
const { Client } = require('discord.js');
const ClientUtil = require('./ClientUtil');

/**
 * The Akairo framework client.
 * Creates the handlers and sets them up.
 * @param {AkairoOptions} [options={}] - Options for the client.
 * @param {ClientOptions} [clientOptions] - Options for Discord JS client.
 * If not specified, the previous options parameter is used instead.
 */
class AkairoClient extends Client {
    constructor(options = {}, clientOptions) {
        super(clientOptions || options);

        const { ownerID = '' } = options;

        /**
         * The ID of the owner(s).
         * @type {Snowflake|Snowflake[]}
         */
        this.ownerID = ownerID;

        /**
         * Utility methods.
         * @type {ClientUtil}
         */
        this.util = new ClientUtil(this);
    }

    /**
     * Checks if a user is the owner of this bot.
     * @param {UserResolvable} user - User to check.
     * @returns {boolean}
     */
    isOwner(user) {
        const id = this.users.resolveId(user);
        return Array.isArray(this.ownerID)
            ? this.ownerID.includes(id)
            : id === this.ownerID;
    }
}

module.exports = AkairoClient;

/**
 * Options for the client.
 * @typedef {Object} AkairoOptions
 * @prop {Snowflake|Snowflake[]} [ownerID=''] - Discord ID of the client owner(s).
 */


================================================
FILE: src/struct/AkairoHandler.js
================================================
const AkairoError = require('../util/AkairoError');
const { AkairoHandlerEvents } = require('../util/Constants');
const AkairoModule = require('./AkairoModule');
const Category = require('../util/Category');
const { Collection } = require('discord.js');
const EventEmitter = require('events');
const fs = require('fs');
const path = require('path');

/**
 * Base class for handling modules.
 * @param {AkairoClient} client - The Akairo client.
 * @param {AkairoHandlerOptions} options - Options for module loading and handling.
 * @extends {EventEmitter}
 */
class AkairoHandler extends EventEmitter {
    constructor(client, {
        directory,
        classToHandle = AkairoModule,
        extensions = ['.js', '.json', '.ts'],
        automateCategories = false,
        loadFilter = (() => true)
    }) {
        super();

        /**
         * The Akairo client.
         * @type {AkairoClient}
         */
        this.client = client;

        /**
         * The main directory to modules.
         * @type {string}
         */
        this.directory = directory;

        /**
         * Class to handle.
         * @type {Function}
         */
        this.classToHandle = classToHandle;

        /**
         * File extensions to load.
         * @type {Set<string>}
         */
        this.extensions = new Set(extensions);

        /**
         * Whether or not to automate category names.
         * @type {boolean}
         */
        this.automateCategories = Boolean(automateCategories);

        /**
         * Function that filters files when loading.
         * @type {LoadPredicate}
         */
        this.loadFilter = loadFilter;

        /**
         * Modules loaded, mapped by ID to AkairoModule.
         * @type {Collection<string, AkairoModule>}
         */
        this.modules = new Collection();

        /**
         * Categories, mapped by ID to Category.
         * @type {Collection<string, Category>}
         */
        this.categories = new Collection();
    }

    /**
     * Registers a module.
     * @param {AkairoModule} mod - Module to use.
     * @param {string} [filepath] - Filepath of module.
     * @returns {void}
     */
    register(mod, filepath) {
        mod.filepath = filepath;
        mod.client = this.client;
        mod.handler = this;
        this.modules.set(mod.id, mod);

        if (mod.categoryID === 'default' && this.automateCategories) {
            const dirs = path.dirname(filepath).split(path.sep);
            mod.categoryID = dirs[dirs.length - 1];
        }

        if (!this.categories.has(mod.categoryID)) {
            this.categories.set(mod.categoryID, new Category(mod.categoryID));
        }

        const category = this.categories.get(mod.categoryID);
        mod.category = category;
        category.set(mod.id, mod);
    }

    /**
     * Deregisters a module.
     * @param {AkairoModule} mod - Module to use.
     * @returns {void}
     */
    deregister(mod) {
        if (mod.filepath) delete require.cache[require.resolve(mod.filepath)];
        this.modules.delete(mod.id);
        mod.category.delete(mod.id);
    }

    /**
     * Loads a module, can be a module class or a filepath.
     * @param {string|Function} thing - Module class or path to module.
     * @param {boolean} [isReload=false] - Whether this is a reload or not.
     * @returns {AkairoModule}
     */
    load(thing, isReload = false) {
        const isClass = typeof thing === 'function';
        if (!isClass && !this.extensions.has(path.extname(thing))) return undefined;

        let mod = isClass
            ? thing
            : function findExport(m) {
                if (!m) return null;
                if (m.prototype instanceof this.classToHandle) return m;
                return m.default ? findExport.call(this, m.default) : null;
            }.call(this, require(thing));

        if (mod && mod.prototype instanceof this.classToHandle) {
            mod = new mod(this); // eslint-disable-line new-cap
        } else {
            if (!isClass) delete require.cache[require.resolve(thing)];
            return undefined;
        }

        if (this.modules.has(mod.id)) throw new AkairoError('ALREADY_LOADED', this.classToHandle.name, mod.id);

        this.register(mod, isClass ? null : thing);
        this.emit(AkairoHandlerEvents.LOAD, mod, isReload);
        return mod;
    }

    /**
     * Reads all modules from a directory and loads them.
     * @param {string} [directory] - Directory to load from.
     * Defaults to the directory passed in the constructor.
     * @param {LoadPredicate} [filter] - Filter for files, where true means it should be loaded.
     * Defaults to the filter passed in the constructor.
     * @returns {AkairoHandler}
     */
    loadAll(directory = this.directory, filter = this.loadFilter || (() => true)) {
        const filepaths = this.constructor.readdirRecursive(directory);
        for (let filepath of filepaths) {
            filepath = path.resolve(filepath);
            if (filter(filepath)) this.load(filepath);
        }

        return this;
    }

    /**
     * Removes a module.
     * @param {string} id - ID of the module.
     * @returns {AkairoModule}
     */
    remove(id) {
        const mod = this.modules.get(id.toString());
        if (!mod) throw new AkairoError('MODULE_NOT_FOUND', this.classToHandle.name, id);

        this.deregister(mod);

        this.emit(AkairoHandlerEvents.REMOVE, mod);
        return mod;
    }

    /**
     * Removes all modules.
     * @returns {AkairoHandler}
     */
    removeAll() {
        for (const m of Array.from(this.modules.values())) {
            if (m.filepath) this.remove(m.id);
        }

        return this;
    }

    /**
     * Reloads a module.
     * @param {string} id - ID of the module.
     * @returns {AkairoModule}
     */
    reload(id) {
        const mod = this.modules.get(id.toString());
        if (!mod) throw new AkairoError('MODULE_NOT_FOUND', this.classToHandle.name, id);
        if (!mod.filepath) throw new AkairoError('NOT_RELOADABLE', this.classToHandle.name, id);

        this.deregister(mod);

        const filepath = mod.filepath;
        const newMod = this.load(filepath, true);
        return newMod;
    }

    /**
     * Reloads all modules.
     * @returns {AkairoHandler}
     */
    reloadAll() {
        for (const m of Array.from(this.modules.values())) {
            if (m.filepath) this.reload(m.id);
        }

        return this;
    }

    /**
     * Finds a category by name.
     * @param {string} name - Name to find with.
     * @returns {Category}
     */
    findCategory(name) {
        return this.categories.find(category => {
            return category.id.toLowerCase() === name.toLowerCase();
        });
    }

    /**
     * Reads files recursively from a directory.
     * @param {string} directory - Directory to read.
     * @returns {string[]}
     */
    static readdirRecursive(directory) {
        const result = [];

        (function read(dir) {
            const files = fs.readdirSync(dir);

            for (const file of files) {
                const filepath = path.join(dir, file);

                if (fs.statSync(filepath).isDirectory()) {
                    read(filepath);
                } else {
                    result.push(filepath);
                }
            }
        }(directory));

        return result;
    }
}

module.exports = AkairoHandler;

/**
 * Emitted when a module is loaded.
 * @event AkairoHandler#load
 * @param {AkairoModule} mod - Module loaded.
 * @param {boolean} isReload - Whether or not this was a reload.
 */

/**
 * Emitted when a module is removed.
 * @event AkairoHandler#remove
 * @param {AkairoModule} mod - Module removed.
 */

/**
 * Options for module loading and handling.
 * @typedef {Object} AkairoHandlerOptions
 * @prop {string} [directory] - Directory to modules.
 * @prop {Function} [classToHandle=AkairoModule] - Only classes that extends this class can be handled.
 * @prop {string[]|Set<string>} [extensions] - File extensions to load.
 * By default this is .js, .json, and .ts files.
 * @prop {boolean} [automateCategories=false] - Whether or not to set each module's category to its parent directory name.
 * @prop {LoadPredicate} [loadFilter] - Filter for files to be loaded.
 * Can be set individually for each handler by overriding the `loadAll` method.
 */

/**
 * Function for filtering files when loading.
 * True means the file should be loaded.
 * @typedef {Function} LoadPredicate
 * @param {String} filepath - Filepath of file.
 * @returns {boolean}
*/


================================================
FILE: src/struct/AkairoModule.js
================================================
/**
 * Base class for a module.
 * @param {string} id - ID of module.
 * @param {AkairoModuleOptions} [options={}] - Options.
 */
class AkairoModule {
    constructor(id, { category = 'default' } = {}) {
        /**
         * ID of the module.
         * @type {string}
         */
        this.id = id;

        /**
         * ID of the category this belongs to.
         * @type {string}
         */
        this.categoryID = category;

        /**
         * Category this belongs to.
         * @type {Category}
         */
        this.category = null;

        /**
         * The filepath.
         * @type {string}
         */
        this.filepath = null;

        /**
         * The Akairo client.
         * @type {AkairoClient}
         */
        this.client = null;

        /**
         * The handler.
         * @type {AkairoHandler}
         */
        this.handler = null;
    }

    /**
     * Reloads the module.
     * @returns {AkairoModule}
     */
    reload() {
        return this.handler.reload(this.id);
    }

    /**
     * Removes the module.
     * @returns {AkairoModule}
     */
    remove() {
        return this.handler.remove(this.id);
    }

    /**
     * Returns the ID.
     * @returns {string}
     */
    toString() {
        return this.id;
    }
}

module.exports = AkairoModule;

/**
 * Options for module.
 * @typedef {Object} AkairoModuleOptions
 * @prop {string} [category='default'] - Category ID for organization purposes.
 */


================================================
FILE: src/struct/ClientUtil.js
================================================
const { Collection, MessageAttachment, MessageEmbed, Permissions } = require('discord.js');

/**
 * Client utilities to help with common tasks.
 * @param {AkairoClient} client - The client.
 */
class ClientUtil {
    constructor(client) {
        /**
         * The Akairo client.
         * @type {AkairoClient}
         */
        this.client = client;
    }

    /**
     * Resolves a user from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, User>} users - Collection of users to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {User}
     */
    resolveUser(text, users, caseSensitive = false, wholeWord = false) {
        return users.get(text) || users.find(user => this.checkUser(text, user, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple users from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, User>} users - Collection of users to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, User>}
     */
    resolveUsers(text, users, caseSensitive = false, wholeWord = false) {
        return users.filter(user => this.checkUser(text, user, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a user.
     * @param {string} text - Text to check.
     * @param {User} user - User to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkUser(text, user, caseSensitive = false, wholeWord = false) {
        if (user.id === text) return true;

        const reg = /<@!?(\d{17,19})>/;
        const match = text.match(reg);

        if (match && user.id === match[1]) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const username = caseSensitive ? user.username : user.username.toLowerCase();
        const discrim = user.discriminator;

        if (!wholeWord) {
            return username.includes(text)
            || (username.includes(text.split('#')[0]) && discrim.includes(text.split('#')[1]));
        }

        return username === text
        || (username === text.split('#')[0] && discrim === text.split('#')[1]);
    }

    /**
     * Resolves a member from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, GuildMember>} members - Collection of members to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {GuildMember}
     */
    resolveMember(text, members, caseSensitive = false, wholeWord = false) {
        return members.get(text) || members.find(member => this.checkMember(text, member, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple members from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, GuildMember>} members - Collection of members to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, GuildMember>}
     */
    resolveMembers(text, members, caseSensitive = false, wholeWord = false) {
        return members.filter(member => this.checkMember(text, member, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a member.
     * @param {string} text - Text to check.
     * @param {GuildMember} member - Member to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkMember(text, member, caseSensitive = false, wholeWord = false) {
        if (member.id === text) return true;

        const reg = /<@!?(\d{17,19})>/;
        const match = text.match(reg);

        if (match && member.id === match[1]) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const username = caseSensitive ? member.user.username : member.user.username.toLowerCase();
        const displayName = caseSensitive ? member.displayName : member.displayName.toLowerCase();
        const discrim = member.user.discriminator;

        if (!wholeWord) {
            return displayName.includes(text)
            || username.includes(text)
            || ((username.includes(text.split('#')[0]) || displayName.includes(text.split('#')[0])) && discrim.includes(text.split('#')[1]));
        }

        return displayName === text
        || username === text
        || ((username === text.split('#')[0] || displayName === text.split('#')[0]) && discrim === text.split('#')[1]);
    }

    /**
     * Resolves a channel from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Channel>} channels - Collection of channels to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Channel}
     */
    resolveChannel(text, channels, caseSensitive = false, wholeWord = false) {
        return channels.get(text) || channels.find(channel => this.checkChannel(text, channel, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple channels from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Channel>} channels - Collection of channels to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, Channel>}
     */
    resolveChannels(text, channels, caseSensitive = false, wholeWord = false) {
        return channels.filter(channel => this.checkChannel(text, channel, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a channel.
     * @param {string} text - Text to check.
     * @param {Channel} channel - Channel to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkChannel(text, channel, caseSensitive = false, wholeWord = false) {
        if (channel.id === text) return true;

        const reg = /<#(\d{17,19})>/;
        const match = text.match(reg);

        if (match && channel.id === match[1]) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const name = caseSensitive ? channel.name : channel.name.toLowerCase();

        if (!wholeWord) {
            return name.includes(text)
            || name.includes(text.replace(/^#/, ''));
        }

        return name === text
        || name === text.replace(/^#/, '');
    }

    /**
     * Resolves a role from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Role>} roles - Collection of roles to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Role}
     */
    resolveRole(text, roles, caseSensitive = false, wholeWord = false) {
        return roles.get(text) || roles.find(role => this.checkRole(text, role, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple roles from a string, such as an ID, a name, or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Role>} roles - Collection of roles to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, Role>}
     */
    resolveRoles(text, roles, caseSensitive = false, wholeWord = false) {
        return roles.filter(role => this.checkRole(text, role, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a role.
     * @param {string} text - Text to check.
     * @param {Role} role - Role to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkRole(text, role, caseSensitive = false, wholeWord = false) {
        if (role.id === text) return true;

        const reg = /<@&(\d{17,19})>/;
        const match = text.match(reg);

        if (match && role.id === match[1]) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const name = caseSensitive ? role.name : role.name.toLowerCase();

        if (!wholeWord) {
            return name.includes(text)
            || name.includes(text.replace(/^@/, ''));
        }

        return name === text
        || name === text.replace(/^@/, '');
    }

    /**
     * Resolves a custom emoji from a string, such as a name or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Emoji>} emojis - Collection of emojis to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Emoji}
     */
    resolveEmoji(text, emojis, caseSensitive = false, wholeWord = false) {
        return emojis.get(text) || emojis.find(emoji => this.checkEmoji(text, emoji, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple custom emojis from a string, such as a name or a mention.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Emoji>} emojis - Collection of emojis to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, Emoji>}
     */
    resolveEmojis(text, emojis, caseSensitive = false, wholeWord = false) {
        return emojis.filter(emoji => this.checkEmoji(text, emoji, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a emoji.
     * @param {string} text - Text to check.
     * @param {Emoji} emoji - Emoji to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkEmoji(text, emoji, caseSensitive = false, wholeWord = false) {
        if (emoji.id === text) return true;

        const reg = /<a?:[a-zA-Z0-9_]+:(\d{17,19})>/;
        const match = text.match(reg);

        if (match && emoji.id === match[1]) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const name = caseSensitive ? emoji.name : emoji.name.toLowerCase();

        if (!wholeWord) {
            return name.includes(text)
            || name.includes(text.replace(/:/, ''));
        }

        return name === text
        || name === text.replace(/:/, '');
    }

    /**
     * Resolves a guild from a string, such as an ID or a name.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Guild>} guilds - Collection of guilds to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Guild}
     */
    resolveGuild(text, guilds, caseSensitive = false, wholeWord = false) {
        return guilds.get(text) || guilds.find(guild => this.checkGuild(text, guild, caseSensitive, wholeWord));
    }

    /**
     * Resolves multiple guilds from a string, such as an ID or a name.
     * @param {string} text - Text to resolve.
     * @param {Collection<Snowflake, Guild>} guilds - Collection of guilds to find in.
     * @param {boolean} [caseSensitive=false] - Makes finding by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes finding by name match full word only.
     * @returns {Collection<Snowflake, Guild>}
     */
    resolveGuilds(text, guilds, caseSensitive = false, wholeWord = false) {
        return guilds.filter(guild => this.checkGuild(text, guild, caseSensitive, wholeWord));
    }

    /**
     * Checks if a string could be referring to a guild.
     * @param {string} text - Text to check.
     * @param {Guild} guild - Guild to check.
     * @param {boolean} [caseSensitive=false] - Makes checking by name case sensitive.
     * @param {boolean} [wholeWord=false] - Makes checking by name match full word only.
     * @returns {boolean}
     */
    checkGuild(text, guild, caseSensitive = false, wholeWord = false) {
        if (guild.id === text) return true;

        text = caseSensitive ? text : text.toLowerCase();
        const name = caseSensitive ? guild.name : guild.name.toLowerCase();

        if (!wholeWord) return name.includes(text);
        return name === text;
    }

    /**
     * Array of permission names.
     * @returns {string[]}
     */
    permissionNames() {
        return Object.keys(Permissions.FLAGS);
    }

    /**
     * Resolves a permission number and returns an array of permission names.
     * @param {number} number - The permissions number.
     * @returns {string[]}
     */
    resolvePermissionNumber(number) {
        const resolved = [];

        for (const key of Object.keys(Permissions.FLAGS)) {
            if (number & Permissions.FLAGS[key]) resolved.push(key);
        }

        return resolved;
    }

    /**
     * Compares two member objects presences and checks if they stopped or started a stream or not.
     * Returns `0`, `1`, or `2` for no change, stopped, or started.
     * @param {GuildMember} oldMember - The old member.
     * @param {GuildMember} newMember - The new member.
     * @returns {number}
     */
    compareStreaming(oldMember, newMember) {
        const s1 = oldMember.presence?.activities.find(c => c.type === 'STREAMING');
        const s2 = newMember.presence?.activities.find(c => c.type === 'STREAMING');
        if (s1 === s2) return 0;
        if (s1) return 1;
        if (s2) return 2;
        return 0;
    }

    /**
     * Combination of `<Client>.users.fetch()` and `<Guild>.members.fetch()`.
     * @param {Guild} guild - Guild to fetch in.
     * @param {string} id - ID of the user.
     * @param {boolean} cache - Whether or not to add to cache.
     * @returns {Promise<GuildMember>}
     */
    async fetchMember(guild, id, cache) {
        const user = await this.client.users.fetch(id, cache);
        return guild.members.fetch(user, cache);
    }

    /**
     * Makes a MessageEmbed.
     * @param {Object} [data] - Embed data.
     * @returns {MessageEmbed}
     */
    embed(data) {
        return new MessageEmbed(data);
    }

    /**
     * Makes a MessageAttachment.
     * @param {BufferResolvable|Stream} file - The file.
     * @param {string} [name] - The filename.
     * @returns {MessageAttachment}
     */
    attachment(file, name) {
        return new MessageAttachment(file, name);
    }

    /**
     * Makes a Collection.
     * @param {Iterable} [iterable] - Entries to fill with.
     * @returns {Collection}
     */
    collection(iterable) {
        return new Collection(iterable);
    }
}

module.exports = ClientUtil;


================================================
FILE: src/struct/commands/Command.js
================================================
const AkairoError = require('../../util/AkairoError');
const AkairoModule = require('../AkairoModule');
const Argument = require('./arguments/Argument');
const ArgumentRunner = require('./arguments/ArgumentRunner');
const ContentParser = require('./ContentParser');

/**
 * Represents a command.
 * @param {string} id - Command ID.
 * @param {CommandOptions} [options={}] - Options for the command.
 * @extends {AkairoModule}
 */
class Command extends AkairoModule {
    constructor(id, options = {}) {
        super(id, { category: options.category });

        const {
            aliases = [],
            args = this.args || [],
            quoted = true,
            separator,
            channel = null,
            ownerOnly = false,
            editable = true,
            typing = false,
            cooldown = null,
            ratelimit = 1,
            argumentDefaults = {},
            description = '',
            prefix = this.prefix,
            clientPermissions = this.clientPermissions,
            userPermissions = this.userPermissions,
            regex = this.regex,
            condition = this.condition || (() => false),
            before = this.before || (() => undefined),
            lock,
            ignoreCooldown,
            ignorePermissions,
            flags = [],
            optionFlags = []
        } = options;

        /**
         * Command names.
         * @type {string[]}
         */
        this.aliases = aliases;

        const { flagWords, optionFlagWords } = Array.isArray(args)
            ? ContentParser.getFlags(args)
            : { flagWords: flags, optionFlagWords: optionFlags };

        this.contentParser = new ContentParser({
            flagWords,
            optionFlagWords,
            quoted,
            separator
        });

        this.argumentRunner = new ArgumentRunner(this);
        this.argumentGenerator = Array.isArray(args)
            ? ArgumentRunner.fromArguments(args.map(arg => [arg.id, new Argument(this, arg)]))
            : args.bind(this);

        /**
         * Usable only in this channel type.
         * @type {?string}
         */
        this.channel = channel;

        /**
         * Usable only by the client owner.
         * @type {boolean}
         */
        this.ownerOnly = Boolean(ownerOnly);

        /**
         * Whether or not this command can be ran by an edit.
         * @type {boolean}
         */
        this.editable = Boolean(editable);

        /**
         * Whether or not to type during command execution.
         * @type {boolean}
         */
        this.typing = Boolean(typing);

        /**
         * Cooldown in milliseconds.
         * @type {?number}
         */
        this.cooldown = cooldown;

        /**
         * Uses allowed before cooldown.
         * @type {number}
         */
        this.ratelimit = ratelimit;

        /**
         * Default prompt options.
         * @type {DefaultArgumentOptions}
         */
        this.argumentDefaults = argumentDefaults;

        /**
         * Description of the command.
         * @type {string|any}
         */
        this.description = Array.isArray(description) ? description.join('\n') : description;

        /**
         * Command prefix overwrite.
         * @type {?string|string[]|PrefixSupplier}
         */
        this.prefix = typeof prefix === 'function' ? prefix.bind(this) : prefix;

        /**
         * Permissions required to run command by the client.
         * @type {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier}
         */
        this.clientPermissions = typeof clientPermissions === 'function' ? clientPermissions.bind(this) : clientPermissions;

        /**
         * Permissions required to run command by the user.
         * @type {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier}
         */
        this.userPermissions = typeof userPermissions === 'function' ? userPermissions.bind(this) : userPermissions;

        /**
         * The regex trigger for this command.
         * @type {RegExp|RegexSupplier}
         */
        this.regex = typeof regex === 'function' ? regex.bind(this) : regex;

        /**
         * Checks if the command should be ran by using an arbitrary condition.
         * @method
         * @param {Message} message - Message being handled.
         * @returns {boolean}
         */
        this.condition = condition.bind(this);

        /**
         * Runs before argument parsing and execution.
         * @method
         * @param {Message} message - Message being handled.
         * @returns {any}
         */
        this.before = before.bind(this);

        /**
         * The key supplier for the locker.
         * @type {?KeySupplier}
         */
        this.lock = lock;

        if (typeof lock === 'string') {
            this.lock = {
                guild: message => message.guild && message.guild.id,
                channel: message => message.channel.id,
                user: message => message.author.id
            }[lock];
        }

        if (this.lock) {
            /**
             * Stores the current locks.
             * @type {?Set<string>}
             */
            this.locker = new Set();
        }

        /**
         * ID of user(s) to ignore cooldown or a function to ignore.
         * @type {?Snowflake|Snowflake[]|IgnoreCheckPredicate}
         */
        this.ignoreCooldown = typeof ignoreCooldown === 'function' ? ignoreCooldown.bind(this) : ignoreCooldown;

        /**
         * ID of user(s) to ignore `userPermissions` checks or a function to ignore.
         * @type {?Snowflake|Snowflake[]|IgnoreCheckPredicate}
         */
        this.ignorePermissions = typeof ignorePermissions === 'function' ? ignorePermissions.bind(this) : ignorePermissions;

        /**
         * The ID of this command.
         * @name Command#id
         * @type {string}
         */

        /**
         * The command handler.
         * @name Command#handler
         * @type {CommandHandler}
         */
    }

    /**
     * Executes the command.
     * @abstract
     * @param {Message} message - Message that triggered the command.
     * @param {any} args - Evaluated arguments.
     * @returns {any}
     */
    exec() {
        throw new AkairoError('NOT_IMPLEMENTED', this.constructor.name, 'exec');
    }

    /**
     * Parses content using the command's arguments.
     * @param {Message} message - Message to use.
     * @param {string} content - String to parse.
     * @returns {Promise<Flag|any>}
     */
    parse(message, content) {
        const parsed = this.contentParser.parse(content);
        return this.argumentRunner.run(message, parsed, this.argumentGenerator);
    }

    /**
     * Reloads the command.
     * @method
     * @name Command#reload
     * @returns {Command}
     */

    /**
     * Removes the command.
     * @method
     * @name Command#remove
     * @returns {Command}
     */
}

module.exports = Command;

/**
 * Options to use for command execution behavior.
 * Also includes properties from AkairoModuleOptions.
 * @typedef {AkairoModuleOptions} CommandOptions
 * @prop {string[]} [aliases=[]] - Command names.
 * @prop {ArgumentOptions[]|ArgumentGenerator} [args=[]] - Argument options or generator.
 * @prop {boolean} [quoted=true] - Whether or not to consider quotes.
 * @prop {string} [separator] - Custom separator for argument input.
 * @prop {string[]} [flags=[]] - Flags to use when using an ArgumentGenerator.
 * @prop {string[]} [optionFlags=[]] - Option flags to use when using an ArgumentGenerator.
 * @prop {string} [channel] - Restricts channel to either 'guild' or 'dm'.
 * @prop {boolean} [ownerOnly=false] - Whether or not to allow client owner(s) only.
 * @prop {boolean} [typing=false] - Whether or not to type in channel during execution.
 * @prop {boolean} [editable=true] - Whether or not message edits will run this command.
 * @prop {number} [cooldown] - The command cooldown in milliseconds.
 * @prop {number} [ratelimit=1] - Amount of command uses allowed until cooldown.
 * @prop {string|string[]|PrefixSupplier} [prefix] - The prefix(es) to overwrite the global one for this command.
 * @prop {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier} [userPermissions] - Permissions required by the user to run this command.
 * @prop {PermissionResolvable|PermissionResolvable[]|MissingPermissionSupplier} [clientPermissions] - Permissions required by the client to run this command.
 * @prop {RegExp|RegexSupplier} [regex] - A regex to match in messages that are not directly commands.
 * The args object will have `match` and `matches` properties.
 * @prop {ExecutionPredicate} [condition] - Whether or not to run on messages that are not directly commands.
 * @prop {BeforeAction} [before] - Function to run before argument parsing and execution.
 * @prop {KeySupplier|string} [lock] - The key type or key generator for the locker. If lock is a string, it's expected one of 'guild', 'channel', or 'user'.
 * @prop {Snowflake|Snowflake[]|IgnoreCheckPredicate} [ignoreCooldown] - ID of user(s) to ignore cooldown or a function to ignore.
 * @prop {Snowflake|Snowflake[]|IgnoreCheckPredicate} [ignorePermissions] - ID of user(s) to ignore `userPermissions` checks or a function to ignore.
 * @prop {DefaultArgumentOptions} [argumentDefaults] - The default argument options.
 * @prop {string} [description=''] - Description of the command.
 */

/**
 * A function to run before argument parsing and execution.
 * @typedef {Function} BeforeAction
 * @param {Message} message - Message that triggered the command.
 * @returns {any}
 */

/**
 * A function used to supply the key for the locker.
 * @typedef {Function} KeySupplier
 * @param {Message} message - Message that triggered the command.
 * @param {any} args - Evaluated arguments.
 * @returns {string}
 */

/**
 * A function used to check if the command should run arbitrarily.
 * @typedef {Function} ExecutionPredicate
 * @param {Message} message - Message to check.
 * @returns {boolean}
 */

/**
 * A function used to check if a message has permissions for the command.
 * A non-null return value signifies the reason for missing permissions.
 * @typedef {Function} MissingPermissionSupplier
 * @param {Message} message - Message that triggered the command.
 * @returns {any}
 */

/**
 * A function used to return a regular expression.
 * @typedef {Function} RegexSupplier
 * @param {Message} message - Message to get regex for.
 * @returns {RegExp}
 */

/**
 * Generator for arguments.
 * When yielding argument options, that argument is ran and the result of the processing is given.
 * The last value when the generator is done is the resulting `args` for the command's `exec`.
 * @typedef {GeneratorFunction} ArgumentGenerator
 * @param {Message} message - Message that triggered the command.
 * @param {ContentParserResult} parsed - Parsed content.
 * @param {ArgumentRunnerState} state - Argument processing state.
 * @returns {IterableIterator<ArgumentOptions|Flag>}
 */


================================================
FILE: src/struct/commands/CommandHandler.js
================================================
const AkairoError = require('../../util/AkairoError');
const AkairoHandler = require('../AkairoHandler');
const { BuiltInReasons, CommandHandlerEvents } = require('../../util/Constants');
const { Collection } = require('discord.js');
const Command = require('./Command');
const CommandUtil = require('./CommandUtil');
const Flag = require('./Flag');
const { deepAssign, flatMap, intoArray, intoCallable, isPromise, prefixCompare } = require('../../util/Util');
const TypeResolver = require('./arguments/TypeResolver');

/**
 * Loads commands and handles messages.
 * @param {AkairoClient} client - The Akairo client.
 * @param {CommandHandlerOptions} options - Options.
 * @extends {AkairoHandler}
 */
class CommandHandler extends AkairoHandler {
    constructor(client, {
        directory,
        classToHandle = Command,
        extensions = ['.js', '.ts'],
        automateCategories,
        loadFilter,
        blockClient = true,
        blockBots = true,
        fetchMembers = false,
        handleEdits = false,
        storeMessages = false,
        commandUtil,
        commandUtilLifetime = 3e5,
        commandUtilSweepInterval = 3e5,
        defaultCooldown = 0,
        ignoreCooldown = client.ownerID,
        ignorePermissions = [],
        argumentDefaults = {},
        prefix = '!',
        allowMention = true,
        aliasReplacement
    } = {}) {
        if (!(classToHandle.prototype instanceof Command || classToHandle === Command)) {
            throw new AkairoError('INVALID_CLASS_TO_HANDLE', classToHandle.name, Command.name);
        }

        super(client, {
            directory,
            classToHandle,
            extensions,
            automateCategories,
            loadFilter
        });

        /**
         * The type resolver.
         * @type {TypeResolver}
         */
        this.resolver = new TypeResolver(this);

        /**
         * Collecion of command aliases.
         * @type {Collection<string, string>}
         */
        this.aliases = new Collection();

        /**
         * Regular expression to automatically make command aliases for.
         * @type {?RegExp}
         */
        this.aliasReplacement = aliasReplacement;

        /**
         * Collection of prefix overwrites to commands.
         * @type {Collection<string|PrefixSupplier, Set<string>>}
         */
        this.prefixes = new Collection();

        /**
         * Whether or not to block self.
         * @type {boolean}
         */
        this.blockClient = Boolean(blockClient);

        /**
         * Whether or not to block bots.
         * @type {boolean}
         */
        this.blockBots = Boolean(blockBots);

        /**
         * Whether or not members are fetched on each message author from a guild.
         * @type {boolean}
         */
        this.fetchMembers = Boolean(fetchMembers);

        /**
         * Whether or not edits are handled.
         * @type {boolean}
         */
        this.handleEdits = Boolean(handleEdits);

        /**
         * Whether or not to store messages in CommandUtil.
         * @type {boolean}
         */
        this.storeMessages = Boolean(storeMessages);

        /**
         * Whether or not `message.util` is assigned.
         * @type {boolean}
         */
        this.commandUtil = Boolean(commandUtil);
        if ((this.handleEdits || this.storeMessages) && !this.commandUtil) {
            throw new AkairoError('COMMAND_UTIL_EXPLICIT');
        }

        /**
         * Milliseconds a message should exist for before its command util instance is marked for removal.
         * @type {number}
         */
        this.commandUtilLifetime = commandUtilLifetime;

        /**
         * Time interval in milliseconds for sweeping command util instances.
         * @type {number}
         */
        this.commandUtilSweepInterval = commandUtilSweepInterval;
        if (this.commandUtilSweepInterval > 0) {
            setInterval(() => this.sweepCommandUtil(), this.commandUtilSweepInterval).unref();
        }

        /**
         * Collection of CommandUtils.
         * @type {Collection<string, CommandUtil>}
         */
        this.commandUtils = new Collection();

        /**
         * Collection of cooldowns.
         * <info>The elements in the collection are objects with user IDs as keys
         * and {@link CooldownData} objects as values</info>
         * @type {Collection<string, Object>}
         */
        this.cooldowns = new Collection();

        /**
         * Default cooldown for commands.
         * @type {number}
         */
        this.defaultCooldown = defaultCooldown;

        /**
         * ID of user(s) to ignore cooldown or a function to ignore.
         * @type {Snowflake|Snowflake[]|IgnoreCheckPredicate}
         */
        this.ignoreCooldown = typeof ignoreCooldown === 'function' ? ignoreCooldown.bind(this) : ignoreCooldown;

        /**
         * ID of user(s) to ignore `userPermissions` checks or a function to ignore.
         * @type {Snowflake|Snowflake[]|IgnoreCheckPredicate}
         */
        this.ignorePermissions = typeof ignorePermissions === 'function' ? ignorePermissions.bind(this) : ignorePermissions;

        /**
         * Collection of sets of ongoing argument prompts.
         * @type {Collection<string, Set<string>>}
         */
        this.prompts = new Collection();

        /**
         * Default argument options.
         * @type {DefaultArgumentOptions}
         */
        this.argumentDefaults = deepAssign({
            prompt: {
                start: '',
                retry: '',
                timeout: '',
                ended: '',
                cancel: '',
                retries: 1,
                time: 30000,
                cancelWord: 'cancel',
                stopWord: 'stop',
                optional: false,
                infinite: false,
                limit: Infinity,
                breakout: true
            }
        }, argumentDefaults);

        /**
         * The prefix(es) for command parsing.
         * @type {string|string[]|PrefixSupplier}
         */
        this.prefix = typeof prefix === 'function' ? prefix.bind(this) : prefix;

        /**
         * Whether or not mentions are allowed for prefixing.
         * @type {boolean|MentionPrefixPredicate}
         */
        this.allowMention = typeof allowMention === 'function' ? allowMention.bind(this) : Boolean(allowMention);

        /**
         * Inhibitor handler to use.
         * @type {?InhibitorHandler}
         */
        this.inhibitorHandler = null;

        /**
         * Directory to commands.
         * @name CommandHandler#directory
         * @type {string}
         */

        /**
         * Commands loaded, mapped by ID to Command.
         * @name CommandHandler#modules
         * @type {Collection<string, Command>}
         */

        this.setup();
    }

    setup() {
        this.client.once('ready', () => {
            this.client.on('messageCreate', async m => {
                if (m.partial) await m.fetch();
                this.handle(m);
            });

            if (this.handleEdits) {
                this.client.on('messageUpdate', async (o, m) => {
                    if (o.partial) await o.fetch();
                    if (m.partial) await m.fetch();
                    if (o.content === m.content) return;
                    if (this.handleEdits) this.handle(m);
                });
            }
        });
    }

    /**
     * Registers a module.
     * @param {Command} command - Module to use.
     * @param {string} [filepath] - Filepath of module.
     * @returns {void}
     */
    register(command, filepath) {
        super.register(command, filepath);

        for (let alias of command.aliases) {
            const conflict = this.aliases.get(alias.toLowerCase());
            if (conflict) throw new AkairoError('ALIAS_CONFLICT', alias, command.id, conflict);

            alias = alias.toLowerCase();
            this.aliases.set(alias, command.id);
            if (this.aliasReplacement) {
                const replacement = alias.replace(this.aliasReplacement, '');

                if (replacement !== alias) {
                    const replacementConflict = this.aliases.get(replacement);
                    if (replacementConflict) throw new AkairoError('ALIAS_CONFLICT', replacement, command.id, replacementConflict);
                    this.aliases.set(replacement, command.id);
                }
            }
        }

        if (command.prefix != null) {
            let newEntry = false;

            if (Array.isArray(command.prefix)) {
                for (const prefix of command.prefix) {
                    const prefixes = this.prefixes.get(prefix);
                    if (prefixes) {
                        prefixes.add(command.id);
                    } else {
                        this.prefixes.set(prefix, new Set([command.id]));
                        newEntry = true;
                    }
                }
            } else {
                const prefixes = this.prefixes.get(command.prefix);
                if (prefixes) {
                    prefixes.add(command.id);
                } else {
                    this.prefixes.set(command.prefix, new Set([command.id]));
                    newEntry = true;
                }
            }

            if (newEntry) {
                this.prefixes = this.prefixes.sort((aVal, bVal, aKey, bKey) => prefixCompare(aKey, bKey));
            }
        }
    }

    /**
     * Deregisters a module.
     * @param {Command} command - Module to use.
     * @returns {void}
     */
    deregister(command) {
        for (let alias of command.aliases) {
            alias = alias.toLowerCase();
            this.aliases.delete(alias);

            if (this.aliasReplacement) {
                const replacement = alias.replace(this.aliasReplacement, '');
                if (replacement !== alias) this.aliases.delete(replacement);
            }
        }

        if (command.prefix != null) {
            if (Array.isArray(command.prefix)) {
                for (const prefix of command.prefix) {
                    const prefixes = this.prefixes.get(prefix);
                    if (prefixes.size === 1) {
                        this.prefixes.delete(prefix);
                    } else {
                        prefixes.delete(prefix);
                    }
                }
            } else {
                const prefixes = this.prefixes.get(command.prefix);
                if (prefixes.size === 1) {
                    this.prefixes.delete(command.prefix);
                } else {
                    prefixes.delete(command.prefix);
                }
            }
        }

        super.deregister(command);
    }

    /**
     * Handles a message.
     * @param {Message} message - Message to handle.
     * @returns {Promise<?boolean>}
     */
    async handle(message) {
        tr
Download .txt
gitextract_ns2ldn2j/

├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .jsdoc.json
├── .npmignore
├── .npmrc
├── .prettierignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs/
│   ├── arguments/
│   │   ├── arguments.md
│   │   ├── compose.md
│   │   ├── custom.md
│   │   ├── functions.md
│   │   ├── generators.md
│   │   ├── matches.md
│   │   ├── prompts.md
│   │   ├── prompts2.md
│   │   ├── types.md
│   │   └── unordered.md
│   ├── basics/
│   │   ├── commands.md
│   │   ├── inhibitors.md
│   │   ├── listeners.md
│   │   └── setup.md
│   ├── commands/
│   │   ├── commandutil.md
│   │   ├── conditional.md
│   │   ├── cooldowns.md
│   │   ├── permissions.md
│   │   ├── prefixes.md
│   │   ├── regex.md
│   │   └── restrictions.md
│   ├── general/
│   │   └── welcome.md
│   ├── index.yml
│   ├── inhibitors/
│   │   ├── inhibtypes.md
│   │   └── priority.md
│   ├── listeners/
│   │   └── emitters.md
│   ├── other/
│   │   ├── clientutil.md
│   │   ├── handlers.md
│   │   ├── handling.md
│   │   ├── mongoose.md
│   │   ├── providers.md
│   │   └── updating.md
│   └── snippets/
│       └── ping.md
├── package.json
├── src/
│   ├── index.d.ts
│   ├── index.js
│   ├── providers/
│   │   ├── MongooseProvider.js
│   │   ├── Provider.js
│   │   ├── SQLiteProvider.js
│   │   └── SequelizeProvider.js
│   ├── struct/
│   │   ├── AkairoClient.js
│   │   ├── AkairoHandler.js
│   │   ├── AkairoModule.js
│   │   ├── ClientUtil.js
│   │   ├── commands/
│   │   │   ├── Command.js
│   │   │   ├── CommandHandler.js
│   │   │   ├── CommandUtil.js
│   │   │   ├── ContentParser.js
│   │   │   ├── Flag.js
│   │   │   └── arguments/
│   │   │       ├── Argument.js
│   │   │       ├── ArgumentRunner.js
│   │   │       └── TypeResolver.js
│   │   ├── inhibitors/
│   │   │   ├── Inhibitor.js
│   │   │   └── InhibitorHandler.js
│   │   └── listeners/
│   │       ├── Listener.js
│   │       └── ListenerHandler.js
│   └── util/
│       ├── AkairoError.js
│       ├── Category.js
│       ├── Constants.js
│       └── Util.js
├── test/
│   ├── bot.js
│   ├── commands/
│   │   ├── args.js
│   │   ├── ayy.js
│   │   ├── condition.js
│   │   ├── condition.promise.js
│   │   ├── embed.js
│   │   ├── eval.js
│   │   ├── f.js
│   │   ├── generate.js
│   │   ├── lock.js
│   │   ├── p.js
│   │   ├── q.js
│   │   ├── separate.js
│   │   ├── sub.js
│   │   ├── test.js
│   │   ├── test2.js
│   │   └── unordered.js
│   ├── listeners/
│   │   ├── invalidMessage.js
│   │   └── message.js
│   └── struct/
│       └── TestClient.js
├── tsconfig.json
└── tslint.json
Download .txt
SYMBOL INDEX (353 symbols across 43 files)

FILE: src/index.d.ts
  type Message (line 15) | interface Message {
  class AkairoError (line 20) | class AkairoError extends Error {
  class AkairoClient (line 24) | class AkairoClient extends Client {
  class AkairoHandler (line 34) | class AkairoHandler extends EventEmitter {
  class AkairoModule (line 61) | class AkairoModule {
  class Argument (line 75) | class Argument {
  class Category (line 112) | class Category<K, V> extends Collection<K, V> {
  class ClientUtil (line 121) | class ClientUtil {
  class Command (line 152) | class Command extends AkairoModule {
  class CommandHandler (line 187) | class CommandHandler extends AkairoHandler {
  class CommandUtil (line 265) | class CommandUtil {
  class Flag (line 288) | class Flag {
  class Inhibitor (line 304) | class Inhibitor extends AkairoModule {
  class InhibitorHandler (line 320) | class InhibitorHandler extends AkairoHandler {
  class Listener (line 343) | class Listener extends AkairoModule {
  class ListenerHandler (line 359) | class ListenerHandler extends AkairoHandler {
  class SequelizeProvider (line 396) | class SequelizeProvider extends Provider {
  class SQLiteProvider (line 411) | class SQLiteProvider extends Provider {
  class MongooseProvider (line 427) | class MongooseProvider extends Provider {
  class TypeResolver (line 441) | class TypeResolver {
  class Util (line 456) | class Util {
  type AkairoHandlerOptions (line 461) | interface AkairoHandlerOptions {
  type AkairoModuleOptions (line 469) | interface AkairoModuleOptions {
  type AkairoOptions (line 473) | interface AkairoOptions {
  type DefaultArgumentOptions (line 477) | interface DefaultArgumentOptions {
  type ArgumentOptions (line 483) | interface ArgumentOptions {
  type ArgumentPromptData (line 499) | interface ArgumentPromptData {
  type ArgumentPromptOptions (line 507) | interface ArgumentPromptOptions {
  type ArgumentRunnerState (line 528) | interface ArgumentRunnerState {
  type CommandOptions (line 534) | interface CommandOptions extends AkairoModuleOptions {
  type CommandHandlerOptions (line 560) | interface CommandHandlerOptions extends AkairoHandlerOptions {
  type ContentParserResult (line 578) | interface ContentParserResult {
  type CooldownData (line 585) | interface CooldownData {
  type FailureData (line 591) | interface FailureData {
  type InhibitorOptions (line 596) | interface InhibitorOptions extends AkairoModuleOptions {
  type ListenerOptions (line 602) | interface ListenerOptions extends AkairoModuleOptions {
  type ParsedComponentData (line 608) | interface ParsedComponentData {
  type ProviderOptions (line 616) | interface ProviderOptions {
  type StringData (line 621) | type StringData = {
  type ArgumentMatch (line 636) | type ArgumentMatch = 'phrase' | 'flag' | 'option' | 'rest' | 'separate' ...
  type ArgumentType (line 638) | type ArgumentType = 'string' | 'lowercase' | 'uppercase' | 'charCodes'
  type ArgumentGenerator (line 651) | type ArgumentGenerator = (message: Message, parsed: ContentParserResult,...
  type ArgumentTypeCaster (line 653) | type ArgumentTypeCaster = (message: Message, phrase: string) => any;
  type BeforeAction (line 655) | type BeforeAction = (message: Message) => any;
  type DefaultValueSupplier (line 657) | type DefaultValueSupplier = (message: Message, data: FailureData) => any;
  type ExecutionPredicate (line 659) | type ExecutionPredicate = (message: Message) => boolean;
  type IgnoreCheckPredicate (line 661) | type IgnoreCheckPredicate = (message: Message, command: Command) => bool...
  type KeySupplier (line 663) | type KeySupplier = (message: Message, args: any) => string;
  type LoadPredicate (line 665) | type LoadPredicate = (filepath: string) => boolean;
  type MentionPrefixPredicate (line 667) | type MentionPrefixPredicate = (message: Message) => boolean | Promise<bo...
  type MissingPermissionSupplier (line 669) | type MissingPermissionSupplier = (message: Message) => Promise<any> | any;
  type OtherwiseContentModifier (line 671) | type OtherwiseContentModifier = (message: Message, text: string, data: F...
  type OtherwiseContentSupplier (line 674) | type OtherwiseContentSupplier = (message: Message, data: FailureData)
  type ParsedValuePredicate (line 677) | type ParsedValuePredicate = (message: Message, phrase: string, value: an...
  type PrefixSupplier (line 679) | type PrefixSupplier = (message: Message) => string | string[] | Promise<...
  type PromptContentModifier (line 681) | type PromptContentModifier = (message: Message, text: string, data: Argu...
  type PromptContentSupplier (line 684) | type PromptContentSupplier = (message: Message, data: ArgumentPromptData)
  type RegexSupplier (line 687) | type RegexSupplier = (message: Message) => RegExp;

FILE: src/providers/MongooseProvider.js
  class MongooseProvider (line 8) | class MongooseProvider extends Provider {
    method constructor (line 9) | constructor(model) {
    method init (line 23) | async init() {
    method get (line 38) | get(id, key, defaultValue) {
    method set (line 54) | async set(id, key, value) {
    method delete (line 71) | async delete(id, key) {
    method clear (line 86) | async clear(id) {
    method getDocument (line 97) | async getDocument(id) {

FILE: src/providers/Provider.js
  class Provider (line 8) | class Provider {
    method constructor (line 9) | constructor() {
    method init (line 22) | init() {
    method get (line 34) | get() {
    method set (line 46) | set() {
    method delete (line 57) | delete() {
    method clear (line 67) | clear() {

FILE: src/providers/SQLiteProvider.js
  class SQLiteProvider (line 10) | class SQLiteProvider extends Provider {
    method constructor (line 11) | constructor(db, tableName, { idColumn = 'id', dataColumn } = {}) {
    method init (line 43) | async init() {
    method get (line 60) | get(id, key, defaultValue) {
    method set (line 76) | set(id, key, value) {
    method delete (line 106) | delete(id, key) {
    method clear (line 128) | clear(id) {

FILE: src/providers/SequelizeProvider.js
  class SequelizeProvider (line 9) | class SequelizeProvider extends Provider {
    method constructor (line 10) | constructor(table, { idColumn = 'id', dataColumn } = {}) {
    method init (line 36) | async init() {
    method get (line 50) | get(id, key, defaultValue) {
    method set (line 66) | set(id, key, value) {
    method delete (line 90) | delete(id, key) {
    method clear (line 112) | clear(id) {

FILE: src/struct/AkairoClient.js
  class AkairoClient (line 11) | class AkairoClient extends Client {
    method constructor (line 12) | constructor(options = {}, clientOptions) {
    method isOwner (line 35) | isOwner(user) {

FILE: src/struct/AkairoHandler.js
  class AkairoHandler (line 16) | class AkairoHandler extends EventEmitter {
    method constructor (line 17) | constructor(client, {
    method register (line 81) | register(mod, filepath) {
    method deregister (line 106) | deregister(mod) {
    method load (line 118) | load(thing, isReload = false) {
    method loadAll (line 152) | loadAll(directory = this.directory, filter = this.loadFilter || (() =>...
    method remove (line 167) | remove(id) {
    method removeAll (line 181) | removeAll() {
    method reload (line 194) | reload(id) {
    method reloadAll (line 210) | reloadAll() {
    method findCategory (line 223) | findCategory(name) {
    method readdirRecursive (line 234) | static readdirRecursive(directory) {

FILE: src/struct/AkairoModule.js
  class AkairoModule (line 6) | class AkairoModule {
    method constructor (line 7) | constructor(id, { category = 'default' } = {}) {
    method reload (line 49) | reload() {
    method remove (line 57) | remove() {
    method toString (line 65) | toString() {

FILE: src/struct/ClientUtil.js
  class ClientUtil (line 7) | class ClientUtil {
    method constructor (line 8) | constructor(client) {
    method resolveUser (line 24) | resolveUser(text, users, caseSensitive = false, wholeWord = false) {
    method resolveUsers (line 36) | resolveUsers(text, users, caseSensitive = false, wholeWord = false) {
    method checkUser (line 48) | checkUser(text, user, caseSensitive = false, wholeWord = false) {
    method resolveMember (line 77) | resolveMember(text, members, caseSensitive = false, wholeWord = false) {
    method resolveMembers (line 89) | resolveMembers(text, members, caseSensitive = false, wholeWord = false) {
    method checkMember (line 101) | checkMember(text, member, caseSensitive = false, wholeWord = false) {
    method resolveChannel (line 133) | resolveChannel(text, channels, caseSensitive = false, wholeWord = fals...
    method resolveChannels (line 145) | resolveChannels(text, channels, caseSensitive = false, wholeWord = fal...
    method checkChannel (line 157) | checkChannel(text, channel, caseSensitive = false, wholeWord = false) {
    method resolveRole (line 185) | resolveRole(text, roles, caseSensitive = false, wholeWord = false) {
    method resolveRoles (line 197) | resolveRoles(text, roles, caseSensitive = false, wholeWord = false) {
    method checkRole (line 209) | checkRole(text, role, caseSensitive = false, wholeWord = false) {
    method resolveEmoji (line 237) | resolveEmoji(text, emojis, caseSensitive = false, wholeWord = false) {
    method resolveEmojis (line 249) | resolveEmojis(text, emojis, caseSensitive = false, wholeWord = false) {
    method checkEmoji (line 261) | checkEmoji(text, emoji, caseSensitive = false, wholeWord = false) {
    method resolveGuild (line 289) | resolveGuild(text, guilds, caseSensitive = false, wholeWord = false) {
    method resolveGuilds (line 301) | resolveGuilds(text, guilds, caseSensitive = false, wholeWord = false) {
    method checkGuild (line 313) | checkGuild(text, guild, caseSensitive = false, wholeWord = false) {
    method permissionNames (line 327) | permissionNames() {
    method resolvePermissionNumber (line 336) | resolvePermissionNumber(number) {
    method compareStreaming (line 353) | compareStreaming(oldMember, newMember) {
    method fetchMember (line 369) | async fetchMember(guild, id, cache) {
    method embed (line 379) | embed(data) {
    method attachment (line 389) | attachment(file, name) {
    method collection (line 398) | collection(iterable) {

FILE: src/struct/commands/Command.js
  class Command (line 13) | class Command extends AkairoModule {
    method constructor (line 14) | constructor(id, options = {}) {
    method exec (line 207) | exec() {
    method parse (line 217) | parse(message, content) {

FILE: src/struct/commands/CommandHandler.js
  class CommandHandler (line 17) | class CommandHandler extends AkairoHandler {
    method constructor (line 18) | constructor(client, {
    method setup (line 223) | setup() {
    method register (line 247) | register(command, filepath) {
    method deregister (line 301) | deregister(command) {
    method handle (line 340) | async handle(message) {
    method handleDirectCommand (line 402) | async handleDirectCommand(message, content, command, ignore = false) {
    method handleRegexAndConditionalCommands (line 453) | async handleRegexAndConditionalCommands(message) {
    method handleRegexCommands (line 464) | async handleRegexCommands(message) {
    method handleConditionalCommands (line 518) | async handleConditionalCommands(message) {
    method runAllTypeInhibitors (line 560) | async runAllTypeInhibitors(message) {
    method runPreTypeInhibitors (line 585) | async runPreTypeInhibitors(message) {
    method runPostTypeInhibitors (line 605) | async runPostTypeInhibitors(message, command) {
    method runPermissionChecks (line 650) | async runPermissionChecks(message, command) {
    method runCooldowns (line 705) | runCooldowns(message, command) {
    method runCommand (line 761) | async runCommand(message, command, args) {
    method parseCommand (line 776) | async parseCommand(message) {
    method parseCommandOverwrittenPrefixes (line 793) | async parseCommandOverwrittenPrefixes(message) {
    method parseMultiplePrefixes (line 815) | parseMultiplePrefixes(message, pairs) {
    method parseWithPrefix (line 838) | parseWithPrefix(message, prefix, associatedCommands = null) {
    method emitError (line 873) | emitError(err, message, command) {
    method sweepCommandUtil (line 888) | sweepCommandUtil(lifetime = this.commandUtilLifetime) {
    method addPrompt (line 908) | addPrompt(channel, user) {
    method removePrompt (line 921) | removePrompt(channel, user) {
    method hasPrompt (line 934) | hasPrompt(channel, user) {
    method findCommand (line 945) | findCommand(name) {
    method useInhibitorHandler (line 954) | useInhibitorHandler(inhibitorHandler) {
    method useListenerHandler (line 966) | useListenerHandler(listenerHandler) {

FILE: src/struct/commands/CommandUtil.js
  class CommandUtil (line 8) | class CommandUtil {
    method constructor (line 9) | constructor(handler, message) {
    method setLastResponse (line 56) | setLastResponse(message) {
    method addMessage (line 71) | addMessage(message) {
    method setEditable (line 90) | setEditable(state) {
    method send (line 100) | async send(options) {
    method sendNew (line 118) | async sendNew(options) {
    method reply (line 130) | reply(options) {
    method edit (line 142) | edit(options) {
    method transformOptions (line 151) | static transformOptions(options) {

FILE: src/struct/commands/ContentParser.js
  class Tokenizer (line 56) | class Tokenizer {
    method constructor (line 57) | constructor(content, {
    method startsWith (line 74) | startsWith(str) {
    method match (line 78) | match(regex) {
    method slice (line 82) | slice(from, to) {
    method addToken (line 86) | addToken(type, value) {
    method advance (line 90) | advance(n) {
    method choice (line 94) | choice(...actions) {
    method tokenize (line 102) | tokenize() {
    method runOne (line 111) | runOne() {
    method runFlags (line 124) | runFlags() {
    method runOptionFlags (line 138) | runOptionFlags() {
    method runQuote (line 152) | runQuote() {
    method runOpenQuote (line 168) | runOpenQuote() {
    method runEndQuote (line 182) | runEndQuote() {
    method runSeparator (line 196) | runSeparator() {
    method runWord (line 206) | runWord() {
    method runWhitespace (line 241) | runWhitespace() {
  class Parser (line 253) | class Parser {
    method constructor (line 254) | constructor(tokens, { separated }) {
    method next (line 272) | next() {
    method lookaheadN (line 276) | lookaheadN(n, ...types) {
    method lookahead (line 280) | lookahead(...types) {
    method match (line 284) | match(...types) {
    method parse (line 293) | parse() {
    method runArgument (line 303) | runArgument() {
    method parseFlag (line 328) | parseFlag() {
    method parsePhrase (line 355) | parsePhrase() {
  class ContentParser (line 428) | class ContentParser {
    method constructor (line 429) | constructor({
    method parse (line 450) | parse(content) {
    method getFlags (line 466) | static getFlags(args) {

FILE: src/struct/commands/Flag.js
  class Flag (line 6) | class Flag {
    method constructor (line 7) | constructor(type, data = {}) {
    method cancel (line 16) | static cancel() {
    method retry (line 25) | static retry(message) {
    method fail (line 34) | static fail(value) {
    method continue (line 46) | static continue(command, ignore = false, rest = null) {
    method is (line 56) | static is(value, type) {

FILE: src/struct/commands/arguments/Argument.js
  class Argument (line 10) | class Argument {
    method constructor (line 11) | constructor(command, {
    method client (line 101) | get client() {
    method handler (line 109) | get handler() {
    method process (line 119) | async process(message, phrase) {
    method cast (line 193) | cast(message, phrase) {
    method collect (line 204) | async collect(message, commandInput = '', parsedInput = null) {
    method cast (line 355) | static async cast(type, resolver, message, phrase) {
    method union (line 409) | static union(...types) {
    method product (line 427) | static product(...types) {
    method validate (line 448) | static validate(type, predicate) {
    method range (line 466) | static range(type, min, max, inclusive = false) {
    method compose (line 487) | static compose(...types) {
    method composeWithFailure (line 506) | static composeWithFailure(...types) {
    method withInput (line 524) | static withInput(type) {
    method tagged (line 544) | static tagged(type, tag = type) {
    method taggedWithInput (line 566) | static taggedWithInput(type, tag = type) {
    method taggedUnion (line 585) | static taggedUnion(...types) {
    method isFailure (line 603) | static isFailure(value) {

FILE: src/struct/commands/arguments/ArgumentRunner.js
  class ArgumentRunner (line 11) | class ArgumentRunner {
    method constructor (line 12) | constructor(command) {
    method client (line 20) | get client() {
    method handler (line 28) | get handler() {
    method run (line 39) | async run(message, parsed, generator) {
    method runOne (line 82) | runOne(message, parsed, state, arg) {
    method runPhrase (line 111) | async runPhrase(message, parsed, state, arg) {
    method runRest (line 154) | async runRest(message, parsed, state, arg) {
    method runSeparate (line 173) | async runSeparate(message, parsed, state, arg) {
    method runFlag (line 211) | runFlag(message, parsed, state, arg) {
    method runOption (line 240) | async runOption(message, parsed, state, arg) {
    method runText (line 274) | runText(message, parsed, state, arg) {
    method runContent (line 288) | runContent(message, parsed, state, arg) {
    method runRestContent (line 302) | async runRestContent(message, parsed, state, arg) {
    method runNone (line 321) | runNone(message, parsed, state, arg) {
    method increaseIndex (line 332) | static increaseIndex(parsed, state, n = 1) {
    method isShortCircuit (line 347) | static isShortCircuit(value) {
    method fromArguments (line 356) | static fromArguments(args) {

FILE: src/struct/commands/arguments/TypeResolver.js
  class TypeResolver (line 10) | class TypeResolver {
    method constructor (line 11) | constructor(handler) {
    method addBuiltInTypes (line 49) | addBuiltInTypes() {
    method type (line 433) | type(name) {
    method addType (line 443) | addType(name, fn) {
    method addTypes (line 453) | addTypes(types) {

FILE: src/struct/inhibitors/Inhibitor.js
  class Inhibitor (line 10) | class Inhibitor extends AkairoModule {
    method constructor (line 11) | constructor(id, {
    method exec (line 59) | exec() {

FILE: src/struct/inhibitors/InhibitorHandler.js
  class InhibitorHandler (line 12) | class InhibitorHandler extends AkairoHandler {
    method constructor (line 13) | constructor(client, {
    method test (line 53) | async test(type, message, command) {

FILE: src/struct/listeners/Listener.js
  class Listener (line 10) | class Listener extends AkairoModule {
    method constructor (line 11) | constructor(id, {
    method exec (line 56) | exec() {

FILE: src/struct/listeners/ListenerHandler.js
  class ListenerHandler (line 13) | class ListenerHandler extends AkairoHandler {
    method constructor (line 14) | constructor(client, {
    method register (line 60) | register(listener, filepath) {
    method deregister (line 72) | deregister(listener) {
    method addToEmitter (line 82) | addToEmitter(id) {
    method removeFromEmitter (line 103) | removeFromEmitter(id) {
    method setEmitters (line 120) | setEmitters(emitters) {

FILE: src/util/AkairoError.js
  class AkairoError (line 28) | class AkairoError extends Error {
    method constructor (line 29) | constructor(key, ...args) {
    method name (line 39) | get name() {

FILE: src/util/Category.js
  class Category (line 9) | class Category extends Collection {
    method constructor (line 10) | constructor(id, iterable) {
    method reloadAll (line 24) | reloadAll() {
    method removeAll (line 36) | removeAll() {
    method toString (line 48) | toString() {

FILE: src/util/Util.js
  class Util (line 1) | class Util {
    method isPromise (line 2) | static isPromise(value) {
    method isEventEmitter (line 8) | static isEventEmitter(value) {
    method prefixCompare (line 14) | static prefixCompare(aKey, bKey) {
    method intoArray (line 26) | static intoArray(x) {
    method intoCallable (line 34) | static intoCallable(thing) {
    method flatMap (line 42) | static flatMap(xs, f) {
    method deepAssign (line 51) | static deepAssign(o1, ...os) {
    method choice (line 67) | static choice(...xs) {

FILE: test/commands/args.js
  class ArgsCommand (line 6) | class ArgsCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 50) | exec(message, args) {

FILE: test/commands/ayy.js
  class AyyCommand (line 3) | class AyyCommand extends Command {
    method constructor (line 4) | constructor() {
    method exec (line 10) | exec(message) {

FILE: test/commands/condition.js
  class ConditionalCommand (line 3) | class ConditionalCommand extends Command {
    method constructor (line 4) | constructor() {
    method condition (line 8) | condition(message) {
    method exec (line 12) | exec(message) {

FILE: test/commands/condition.promise.js
  class ConditionalPromiseCommand (line 3) | class ConditionalPromiseCommand extends Command {
    method constructor (line 4) | constructor() {
    method condition (line 8) | condition(message) {
    method exec (line 12) | exec(message) {

FILE: test/commands/embed.js
  class EmbedCommand (line 3) | class EmbedCommand extends Command {
    method constructor (line 4) | constructor() {
    method exec (line 26) | exec(message, args) {

FILE: test/commands/eval.js
  class EvalCommand (line 4) | class EvalCommand extends Command {
    method constructor (line 5) | constructor() {
    method exec (line 20) | async exec(message, { code }) {

FILE: test/commands/f.js
  class FCommand (line 6) | class FCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 29) | exec(message, args) {

FILE: test/commands/generate.js
  class GenerateCommand (line 6) | class GenerateCommand extends Command {
    method constructor (line 7) | constructor() {
    method args (line 13) | *args() {
    method exec (line 26) | exec(message, args) {

FILE: test/commands/lock.js
  class LockCommand (line 6) | class LockCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 14) | exec(message) {

FILE: test/commands/p.js
  class PCommand (line 6) | class PCommand extends Command {
    method constructor (line 7) | constructor() {
    method before (line 27) | before() {
    method exec (line 31) | exec(message, args) {

FILE: test/commands/q.js
  class QCommand (line 5) | class QCommand extends Command {
    method constructor (line 6) | constructor() {
    method exec (line 12) | exec(message) {

FILE: test/commands/separate.js
  class SeparateCommand (line 6) | class SeparateCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 24) | exec(message, args) {

FILE: test/commands/sub.js
  class SubCommand (line 6) | class SubCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 17) | exec(message, args) {

FILE: test/commands/test.js
  class TestCommand (line 6) | class TestCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 22) | exec(message, args) {

FILE: test/commands/test2.js
  class Test2Command (line 6) | class Test2Command extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 22) | exec(message, args) {

FILE: test/commands/unordered.js
  class UnorderedCommand (line 6) | class UnorderedCommand extends Command {
    method constructor (line 7) | constructor() {
    method exec (line 25) | exec(message, args) {

FILE: test/listeners/invalidMessage.js
  class InvalidMessageListener (line 5) | class InvalidMessageListener extends Listener {
    method constructor (line 6) | constructor() {
    method exec (line 14) | exec(msg) {

FILE: test/listeners/message.js
  class MessageListener (line 5) | class MessageListener extends Listener {
    method constructor (line 6) | constructor() {
    method exec (line 14) | exec(msg) {

FILE: test/struct/TestClient.js
  class TestClient (line 4) | class TestClient extends AkairoClient {
    method constructor (line 5) | constructor() {
    method setup (line 51) | setup() {
    method start (line 74) | async start(token) {
Condensed preview — 92 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (365K chars).
[
  {
    "path": ".eslintrc.json",
    "chars": 6372,
    "preview": "{\n    \"extends\": \"eslint:recommended\",\n    \"parserOptions\": {\n        \"ecmaVersion\": 9\n    },\n    \"env\": {\n        \"es6\""
  },
  {
    "path": ".gitattributes",
    "chars": 19,
    "preview": "* text=auto eol=lf\n"
  },
  {
    "path": ".gitignore",
    "chars": 58,
    "preview": "node_modules\n\n.git\n.vscode\n\ntest/auth.json\ntest/db.sqlite\n"
  },
  {
    "path": ".jsdoc.json",
    "chars": 368,
    "preview": "{\n    \"tags\": {\n        \"allowUnknownTags\": true,\n        \"dictionaries\": [\"jsdoc\"]\n    },\n    \"source\": {\n        \"incl"
  },
  {
    "path": ".npmignore",
    "chars": 123,
    "preview": "# NPM\nnode_modules\n.git\n.vscode\n.eslintrc.json\n.gitattributes\n.gitignore\n.travis.yml\ntest/\ndocs/\ntsconfig.json\ntslint.js"
  },
  {
    "path": ".npmrc",
    "chars": 19,
    "preview": "package-lock=false\n"
  },
  {
    "path": ".prettierignore",
    "chars": 1,
    "preview": "*"
  },
  {
    "path": ".travis.yml",
    "chars": 38,
    "preview": "language: node_js\nnode_js:\n    - \"12\"\n"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2020 1Computer1\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 3373,
    "preview": "<div align=\"center\">\n  <br />\n  <p>\n    <a href=\"https://discord-akairo.github.io\"><img src=\"https://discord-akairo.gith"
  },
  {
    "path": "docs/arguments/arguments.md",
    "chars": 2026,
    "preview": "# Basic Arguments\n\n### Adding Numbers\n\nCommands should also have some user input, in the form of arguments.  \nIn Akairo,"
  },
  {
    "path": "docs/arguments/compose.md",
    "chars": 2287,
    "preview": "# Composing Types\n\n### Union Types\n\nAkairo allows the creation of union types, where the input can match one of many typ"
  },
  {
    "path": "docs/arguments/custom.md",
    "chars": 1873,
    "preview": "# Custom Types\n\n### New Type\n\nWe have to access the command handler's `TypeResolver` in order to add new types for our a"
  },
  {
    "path": "docs/arguments/functions.md",
    "chars": 3771,
    "preview": "# Using Functions\n\n### Dynamic Defaults\n\nWhen you are doing default values for certain arguments, you could really only "
  },
  {
    "path": "docs/arguments/generators.md",
    "chars": 1665,
    "preview": "# Generator Arguments\n\n## Yield!\n\nThe most powerful aspect of Akairo's argument parsing is the fact that it is implement"
  },
  {
    "path": "docs/arguments/matches.md",
    "chars": 6425,
    "preview": "# Matching Input\n\n### Entire Content\n\nLet's say you have a command that picks from a list inputted.  \nObviously, you won"
  },
  {
    "path": "docs/arguments/prompts.md",
    "chars": 5502,
    "preview": "# Argument Prompting\n\n### Please Try Again\n\nYou may notice prompting for arguments in other bots (Tatsumaki) or bot fram"
  },
  {
    "path": "docs/arguments/prompts2.md",
    "chars": 2689,
    "preview": "# More Prompting\n\n### Optional Prompts\n\nOptional prompts are prompts that run if there was input, but the type casting f"
  },
  {
    "path": "docs/arguments/types.md",
    "chars": 5366,
    "preview": "# Argument Types\n\n### Basic Types\n\nAs seen in the previous tutorials, there was the `type` option for type casting.  \nYo"
  },
  {
    "path": "docs/arguments/unordered.md",
    "chars": 2015,
    "preview": "# Unordered Arguments\n\n### Any Order!\n\nArguments can be made to be unordered.  \nFor example, if you want a command where"
  },
  {
    "path": "docs/basics/commands.md",
    "chars": 2578,
    "preview": "# Basic Commands\n\n### The Command Handler\n\nIn Akairo, the hierachy is that there are handlers which contains modules.  \n"
  },
  {
    "path": "docs/basics/inhibitors.md",
    "chars": 2022,
    "preview": "# Basic Inhibitors\n\n### Setup\n\nInhibitors are a way to monitor or block messages coming into the command handler.  \nBeca"
  },
  {
    "path": "docs/basics/listeners.md",
    "chars": 3396,
    "preview": "# Basic Listeners\n\n### Setup\n\nListeners are a basic concept in Node.js.  \nProblem is, you usually end up with loooooong "
  },
  {
    "path": "docs/basics/setup.md",
    "chars": 1619,
    "preview": "# Setting Up\n\n### Installation\n\nBefore even doing anything else, you of course have to install the Discord.js and Akairo"
  },
  {
    "path": "docs/commands/commandutil.md",
    "chars": 3136,
    "preview": "# CommandUtil\n\n### Handling Edits\n\nThe CommandUtil class is a utility class for working with responses.  \nIn order to ma"
  },
  {
    "path": "docs/commands/conditional.md",
    "chars": 780,
    "preview": "# Conditional Commands\n\n### Run Whenever\n\nConditional commands are commands that run if the following conditions are tru"
  },
  {
    "path": "docs/commands/cooldowns.md",
    "chars": 1842,
    "preview": "# Cooldowns\n\n### No Spam!\n\nCooldowns are how you make sure that troublemakers don't spam your bot.  \nAkairo allows you t"
  },
  {
    "path": "docs/commands/permissions.md",
    "chars": 2459,
    "preview": "# Permissions\n\n### Permission Flags\n\nSome commands should only be used by someone with certain permissions.  \nThere are "
  },
  {
    "path": "docs/commands/prefixes.md",
    "chars": 2154,
    "preview": "# Prefixes and Aliases\n\n### Mentioning\n\nSometimes people can forget or not know the prefix for your bot, so letting them"
  },
  {
    "path": "docs/commands/regex.md",
    "chars": 1328,
    "preview": "# Regex Commands\n\n### Memes\n\nRegex or regular expressions, is basically a way to match characters in a string.  \nRegex c"
  },
  {
    "path": "docs/commands/restrictions.md",
    "chars": 1746,
    "preview": "# Restrictions\n\n### Channel Restrictions\n\nIf a command requires a guild to be used correctly, you can restrict it to a g"
  },
  {
    "path": "docs/general/welcome.md",
    "chars": 1164,
    "preview": "<div align=\"center\">\n  <br />\n  <p>\n    <a href=\"https://discord-akairo.github.io\"><img src=\"https://discord-akairo.gith"
  },
  {
    "path": "docs/index.yml",
    "chars": 1865,
    "preview": "- name: General\n  files:\n    - name: Welcome\n      path: welcome.md\n- name: Basics\n  files:\n    - name: Setting Up\n     "
  },
  {
    "path": "docs/inhibitors/inhibtypes.md",
    "chars": 1463,
    "preview": "# Inhibitor Types\n\n### More Coverage\n\nRight now, your inhibitors only runs before a command.  \nThey do not actually run "
  },
  {
    "path": "docs/inhibitors/priority.md",
    "chars": 1329,
    "preview": "# Inhibitor Priority\n\n### Me First!\n\nSometimes multiple inhibitors can block a message.  \nFor example, you may have an i"
  },
  {
    "path": "docs/listeners/emitters.md",
    "chars": 957,
    "preview": "# Custom Emitters\n\n### Watching Process\n\nAs shown in the first listener tutorial, we can have custom emitters.  \nListene"
  },
  {
    "path": "docs/other/clientutil.md",
    "chars": 1050,
    "preview": "# ClientUtil\n\n### Finding Things\n\nClientUtil is a class filled with utility methods.  \nIt is available on your client as"
  },
  {
    "path": "docs/other/handlers.md",
    "chars": 2447,
    "preview": "# Custom Handlers\n\n### And Custom Modules\n\nInternally, Akairo's handlers all extends AkairoHandler, and all modules exte"
  },
  {
    "path": "docs/other/handling.md",
    "chars": 1872,
    "preview": "# Handling Modules\n\n### Categorizing\n\nYou can categorize a module with the `category` option.  \n\n```js\nconst { Command }"
  },
  {
    "path": "docs/other/mongoose.md",
    "chars": 2466,
    "preview": "# Using Mongoose Provider\n\n### Storing Prefixes\n\nLet's implement per-guild prefixes.  \nFirst, create a new MongooseProvi"
  },
  {
    "path": "docs/other/providers.md",
    "chars": 3119,
    "preview": "# Using Providers\n\n### Storing Prefixes\n\nAkairo comes with SQLiteProvider and SequelizeProvider, optional utility classe"
  },
  {
    "path": "docs/other/updating.md",
    "chars": 7790,
    "preview": "# Updating to v8\n\n### Breaking Changes\n\nThis tutorial is for updating from Akairo v7 to v8.  \nMany changes were introduc"
  },
  {
    "path": "docs/snippets/ping.md",
    "chars": 618,
    "preview": "# Ping Command\n\n```js\nconst { Command } = require('discord-akairo');\n\nclass PingCommand extends Command {\n    constructo"
  },
  {
    "path": "package.json",
    "chars": 1146,
    "preview": "{\n    \"name\": \"discord-akairo\",\n    \"version\": \"8.1.0\",\n    \"description\": \"A highly customizable bot framework for Disc"
  },
  {
    "path": "src/index.d.ts",
    "chars": 34528,
    "preview": "declare module 'discord-akairo' {\n    import {\n        BufferResolvable, Client, ClientOptions, Collection,\n        Mess"
  },
  {
    "path": "src/index.js",
    "chars": 1402,
    "preview": "module.exports = {\n    // Core\n    AkairoClient: require('./struct/AkairoClient'),\n    AkairoHandler: require('./struct/"
  },
  {
    "path": "src/providers/MongooseProvider.js",
    "chars": 2782,
    "preview": "const Provider = require('./Provider');\n\n/**\n * Provider using the `Mongoose` library.\n * @param {Model} model - A Mongo"
  },
  {
    "path": "src/providers/Provider.js",
    "chars": 2177,
    "preview": "const AkairoError = require('../util/AkairoError');\nconst { Collection } = require('discord.js');\n\n/**\n * A provider for"
  },
  {
    "path": "src/providers/SQLiteProvider.js",
    "chars": 3763,
    "preview": "const Provider = require('./Provider');\n\n/**\n * Provider using the `sqlite` library.\n * @param {Database|Promise<Databas"
  },
  {
    "path": "src/providers/SequelizeProvider.js",
    "chars": 2866,
    "preview": "const Provider = require('./Provider');\n\n/**\n * Provider using the `sequelize` library.\n * @param {Model} table - A Sequ"
  },
  {
    "path": "src/struct/AkairoClient.js",
    "chars": 1334,
    "preview": "const { Client } = require('discord.js');\nconst ClientUtil = require('./ClientUtil');\n\n/**\n * The Akairo framework clien"
  },
  {
    "path": "src/struct/AkairoHandler.js",
    "chars": 8610,
    "preview": "const AkairoError = require('../util/AkairoError');\nconst { AkairoHandlerEvents } = require('../util/Constants');\nconst "
  },
  {
    "path": "src/struct/AkairoModule.js",
    "chars": 1478,
    "preview": "/**\n * Base class for a module.\n * @param {string} id - ID of module.\n * @param {AkairoModuleOptions} [options={}] - Opt"
  },
  {
    "path": "src/struct/ClientUtil.js",
    "chars": 16378,
    "preview": "const { Collection, MessageAttachment, MessageEmbed, Permissions } = require('discord.js');\n\n/**\n * Client utilities to "
  },
  {
    "path": "src/struct/commands/Command.js",
    "chars": 11080,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoModule = require('../AkairoModule');\nconst Argument ="
  },
  {
    "path": "src/struct/commands/CommandHandler.js",
    "chars": 40612,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoHandler = require('../AkairoHandler');\nconst { BuiltI"
  },
  {
    "path": "src/struct/commands/CommandUtil.js",
    "chars": 5114,
    "preview": "const { Collection } = require('discord.js');\n\n/**\n * Command utilities.\n * @param {CommandHandler} handler - The comman"
  },
  {
    "path": "src/struct/commands/ContentParser.js",
    "chars": 14620,
    "preview": "const { ArgumentMatches } = require('../../util/Constants');\n\n/*\n * Grammar:\n *\n * Arguments\n *  = (Argument (WS? Argume"
  },
  {
    "path": "src/struct/commands/Flag.js",
    "chars": 1757,
    "preview": "/**\n * Represents a special return value during commmand execution or argument parsing.\n * @param {string} type - Type o"
  },
  {
    "path": "src/struct/commands/arguments/Argument.js",
    "chars": 33596,
    "preview": "const { ArgumentMatches, ArgumentTypes } = require('../../../util/Constants');\nconst Flag = require('../Flag');\nconst { "
  },
  {
    "path": "src/struct/commands/arguments/ArgumentRunner.js",
    "chars": 12732,
    "preview": "const AkairoError = require('../../../util/AkairoError');\nconst Argument = require('./Argument');\nconst { ArgumentMatche"
  },
  {
    "path": "src/struct/commands/arguments/TypeResolver.js",
    "chars": 17256,
    "preview": "const { ArgumentTypes } = require('../../../util/Constants');\nconst { Collection } = require('discord.js');\nconst { URL "
  },
  {
    "path": "src/struct/inhibitors/Inhibitor.js",
    "chars": 2492,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoModule = require('../AkairoModule');\n\n/**\n * Represen"
  },
  {
    "path": "src/struct/inhibitors/InhibitorHandler.js",
    "chars": 4412,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoHandler = require('../AkairoHandler');\nconst Inhibito"
  },
  {
    "path": "src/struct/listeners/Listener.js",
    "chars": 1955,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoModule = require('../AkairoModule');\n\n/**\n * Represen"
  },
  {
    "path": "src/struct/listeners/ListenerHandler.js",
    "chars": 5646,
    "preview": "const AkairoError = require('../../util/AkairoError');\nconst AkairoHandler = require('../AkairoHandler');\nconst { Collec"
  },
  {
    "path": "src/util/AkairoError.js",
    "chars": 1724,
    "preview": "const Messages = {\n    // Module-related\n    FILE_NOT_FOUND: filename => `File '${filename}' not found`,\n    MODULE_NOT_"
  },
  {
    "path": "src/util/Category.js",
    "chars": 1038,
    "preview": "const { Collection } = require('discord.js');\n\n/**\n * A group of modules.\n * @param {string} id - ID of the category.\n *"
  },
  {
    "path": "src/util/Constants.js",
    "chars": 2502,
    "preview": "module.exports = {\n    ArgumentMatches: {\n        PHRASE: 'phrase',\n        FLAG: 'flag',\n        OPTION: 'option',\n    "
  },
  {
    "path": "src/util/Util.js",
    "chars": 1909,
    "preview": "class Util {\n    static isPromise(value) {\n        return value\n        && typeof value.then === 'function'\n        && t"
  },
  {
    "path": "test/bot.js",
    "chars": 244,
    "preview": "const TestClient = require('./struct/TestClient');\nconst client = new TestClient();\n\nconst { token } = require('./auth.j"
  },
  {
    "path": "test/commands/args.js",
    "chars": 1404,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\nconst util = require('util');\n\nclass ArgsCommand "
  },
  {
    "path": "test/commands/ayy.js",
    "chars": 261,
    "preview": "const { Command } = require('../..');\n\nclass AyyCommand extends Command {\n    constructor() {\n        super('ayy', {\n   "
  },
  {
    "path": "test/commands/condition.js",
    "chars": 352,
    "preview": "const { Command } = require('../../src');\n\nclass ConditionalCommand extends Command {\n    constructor() {\n        super("
  },
  {
    "path": "test/commands/condition.promise.js",
    "chars": 407,
    "preview": "const { Command } = require('../../src');\n\nclass ConditionalPromiseCommand extends Command {\n    constructor() {\n       "
  },
  {
    "path": "test/commands/embed.js",
    "chars": 989,
    "preview": "const { Command } = require('../..');\n\nclass EmbedCommand extends Command {\n    constructor() {\n        super('embed', {"
  },
  {
    "path": "test/commands/eval.js",
    "chars": 3296,
    "preview": "const { Command } = require('../..');\nconst util = require('util');\n\nclass EvalCommand extends Command {\n    constructor"
  },
  {
    "path": "test/commands/f.js",
    "chars": 865,
    "preview": "/* eslint-disable no-console */\n\nconst { Command, Flag } = require('../..');\nconst util = require('util');\n\nclass FComma"
  },
  {
    "path": "test/commands/generate.js",
    "chars": 629,
    "preview": "/* eslint-disable no-console */\n\nconst { Command, Flag } = require('../..');\nconst util = require('util');\n\nclass Genera"
  },
  {
    "path": "test/commands/lock.js",
    "chars": 484,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\nconst sleep = require('util').promisify(setTimeou"
  },
  {
    "path": "test/commands/p.js",
    "chars": 897,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\nconst util = require('util');\n\nclass PCommand ext"
  },
  {
    "path": "test/commands/q.js",
    "chars": 374,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\n\nclass QCommand extends Command {\n    constructor"
  },
  {
    "path": "test/commands/separate.js",
    "chars": 787,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\nconst util = require('util');\n\nclass SeparateComm"
  },
  {
    "path": "test/commands/sub.js",
    "chars": 449,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../../src');\nconst util = require('util');\n\nclass SubComma"
  },
  {
    "path": "test/commands/test.js",
    "chars": 727,
    "preview": "/* eslint-disable no-console */\n\nconst { Argument: { compose, range, union }, Command } = require('../..');\nconst util ="
  },
  {
    "path": "test/commands/test2.js",
    "chars": 727,
    "preview": "/* eslint-disable no-console */\n\nconst { Argument: { compose, range, union }, Command } = require('../..');\nconst util ="
  },
  {
    "path": "test/commands/unordered.js",
    "chars": 728,
    "preview": "/* eslint-disable no-console */\n\nconst { Command } = require('../..');\nconst util = require('util');\n\nclass UnorderedCom"
  },
  {
    "path": "test/listeners/invalidMessage.js",
    "chars": 413,
    "preview": "/* eslint-disable no-console */\n\nconst { Listener } = require('../..');\n\nclass InvalidMessageListener extends Listener {"
  },
  {
    "path": "test/listeners/message.js",
    "chars": 365,
    "preview": "/* eslint-disable no-console */\n\nconst { Listener } = require('../..');\n\nclass MessageListener extends Listener {\n    co"
  },
  {
    "path": "test/struct/TestClient.js",
    "chars": 2891,
    "preview": "const { AkairoClient, CommandHandler, InhibitorHandler, ListenerHandler, SQLiteProvider } = require('../../src/index');\n"
  },
  {
    "path": "tsconfig.json",
    "chars": 276,
    "preview": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"target\": \"es6\",\n        \"noImplicitAny\": true,\n       "
  },
  {
    "path": "tslint.json",
    "chars": 1894,
    "preview": "{\n    \"extends\": [\n        \"tslint-config-typings\"\n    ],\n    \"rules\": {\n        \"class-name\": true,\n        \"comment-fo"
  }
]

About this extraction

This page contains the full source code of the 1Computer1/discord-akairo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 92 files (340.5 KB), approximately 79.5k tokens, and a symbol index with 353 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!