mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 11:28:15 +00:00
style: format files
This commit is contained in:
+40
-14
@@ -1,43 +1,69 @@
|
|||||||
# Fundamental Design Goals
|
# Fundamental Design Goals
|
||||||
|
|
||||||
This document serves to outline the overall design goals of the project. Please see below a list of these fundamentals.
|
This document serves to outline the overall design goals of the project. Please
|
||||||
|
see below a list of these fundamentals.
|
||||||
|
|
||||||
## Do not allow anything Discord does not permit
|
## Do not allow anything Discord does not permit
|
||||||
Prevent any and all attempts of making user bots. If someone connects and the client.user is not a bot user then throw an error immediately.
|
|
||||||
|
Prevent any and all attempts of making user bots. If someone connects and the
|
||||||
|
client.user is not a bot user then throw an error immediately.
|
||||||
|
|
||||||
Do not support non-bot features like Group DMs or dm calls etc...
|
Do not support non-bot features like Group DMs or dm calls etc...
|
||||||
|
|
||||||
## Prettier Philosophy Regarding Options
|
## Prettier Philosophy Regarding Options
|
||||||
|
|
||||||
Avoid options/customizable whenever possible. Always enforce default values. Except in cases like intents where the user should be able to pick which intents to listen for.
|
Avoid options/customizable whenever possible. Always enforce default values.
|
||||||
|
Except in cases like intents where the user should be able to pick which intents
|
||||||
|
to listen for.
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
Permission checks should be done by the library! We can throw a custom error that shows which permissions are missing in order to run this request and save an API call. This will also prevent bots from getting banned due to Missing Access errors.
|
Permission checks should be done by the library! We can throw a custom error
|
||||||
|
that shows which permissions are missing in order to run this request and save
|
||||||
|
an API call. This will also prevent bots from getting banned due to Missing
|
||||||
|
Access errors.
|
||||||
|
|
||||||
Typescript 3.8 provides **TRUE** private props and methods that no one can access. We will use this to our advantage to truly make a proper API. This isn't a silly `_` to mark it as a private but the user should NEVER be able to access it no matter what.
|
Typescript 3.8 provides **TRUE** private props and methods that no one can
|
||||||
|
access. We will use this to our advantage to truly make a proper API. This isn't
|
||||||
|
a silly `_` to mark it as a private but the user should NEVER be able to access
|
||||||
|
it no matter what.
|
||||||
|
|
||||||
## Functional API
|
## Functional API
|
||||||
|
|
||||||
Events emitted by the client, for example the message creation event, should not emit a `Message` class instance, but instead a *POJO* (Plain Ol' JavaScript Object). This will overall make a cleaner and more performant API, while removing the headaches of extending built-in classes, and inheritance.
|
Events emitted by the client, for example the message creation event, should not
|
||||||
|
emit a `Message` class instance, but instead a _POJO_ (Plain Ol' JavaScript
|
||||||
|
Object). This will overall make a cleaner and more performant API, while
|
||||||
|
removing the headaches of extending built-in classes, and inheritance.
|
||||||
|
|
||||||
Use functions when possible instead of an event emitter to prevent emitter related memory leak issues or a number of other headaches that arise.
|
Use functions when possible instead of an event emitter to prevent emitter
|
||||||
|
related memory leak issues or a number of other headaches that arise.
|
||||||
|
|
||||||
TLDR: Avoid `classes` whenever possible. Avoid `loops` whenever possible(opt for iterations like .forEach, map reduce, some find etc...)
|
TLDR: Avoid `classes` whenever possible. Avoid `loops` whenever possible(opt for
|
||||||
|
iterations like .forEach, map reduce, some find etc...)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Use `/** Description here */` comments above all properties and methods to describe it so that VSC and other good IDE's with intellisense can pick it up and provide the documentation right inside the IDE preventing a developer from needing Discord API docs or even Deno documentation.
|
Use `/** Description here */` comments above all properties and methods to
|
||||||
|
describe it so that VSC and other good IDE's with intellisense can pick it up
|
||||||
|
and provide the documentation right inside the IDE preventing a developer from
|
||||||
|
needing Discord API docs or even Deno documentation.
|
||||||
|
|
||||||
We should have a step by step guide nonetheless but this is a POST v1 launch.
|
We should have a step by step guide nonetheless but this is a POST v1 launch. We
|
||||||
We should have a template repo to creating a boilerplate bot.
|
should have a template repo to creating a boilerplate bot.
|
||||||
|
|
||||||
## Backwards Compatibility BS
|
## Backwards Compatibility BS
|
||||||
|
|
||||||
Backwards compatibility is the death of code. It causes clutter and uglyness to pile up and makes developers lazier. There will be no such thing as backwards compatibility reasons in Discordeno. We will always support the latest and greatest of JS. The end! Users can fork the lib at any commit to keep older versions until they are ready to update.
|
Backwards compatibility is the death of code. It causes clutter and uglyness to
|
||||||
|
pile up and makes developers lazier. There will be no such thing as backwards
|
||||||
|
compatibility reasons in Discordeno. We will always support the latest and
|
||||||
|
greatest of JS. The end! Users can fork the lib at any commit to keep older
|
||||||
|
versions until they are ready to update.
|
||||||
|
|
||||||
That said, we don't expect many things to be changing drastically after v1. As you can imagine Typescript allows the latest and greatest of JS so we will be ahead of the curve for years to come.
|
That said, we don't expect many things to be changing drastically after v1. As
|
||||||
|
you can imagine Typescript allows the latest and greatest of JS so we will be
|
||||||
|
ahead of the curve for years to come.
|
||||||
|
|
||||||
## Style Guide
|
## Style Guide
|
||||||
|
|
||||||
Prettier is our style guide. No discussions around styling ever. The options are set and that is all. When you code let prettier handle the styling. PERIOD!
|
Prettier is our style guide. No discussions around styling ever. The options are
|
||||||
|
set and that is all. When you code let prettier handle the styling. PERIOD!
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
**Status**
|
**Status**
|
||||||
|
|
||||||
- [ ] Code changes have been tested against the Discord API, or there are no code changes
|
- [ ] Code changes have been tested against the Discord API, or there are no
|
||||||
|
code changes
|
||||||
|
|
||||||
**Semantic versioning classification:**
|
**Semantic versioning classification:**
|
||||||
|
|
||||||
- [ ] This PR changes the library's interface (methods or parameters added)
|
- [ ] This PR changes the library's interface (methods or parameters added)
|
||||||
- [ ] This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
|
- [ ] This PR includes breaking changes (methods removed or renamed,
|
||||||
- [ ] This PR **only** includes non-code changes, like changes to documentation, README, etc.
|
parameters moved or removed)
|
||||||
|
- [ ] This PR **only** includes non-code changes, like changes to documentation,
|
||||||
|
README, etc.
|
||||||
|
|||||||
@@ -6,16 +6,32 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
- **Secure & stable**: Discordeno is comparatively more stable than the other libraries. One of the greatest issues with almost every library is stability; types are outdated, less (or minimal) parity with the API, core maintainers have quit or no longer actively maintain the library, and whatnot. Discordeno, on the other hand, is actively maintained to ensure great performance and convenience. Discordeno internally checks all missing permissions before forwarding a request to the API so that the client does not get globally-banned by Discord.
|
- **Secure & stable**: Discordeno is comparatively more stable than the other
|
||||||
- **Efficient & lightweight**: Discordeno is simplistic and easy-to-use. Always prefer defaults that Discord recommends or the best configuration for the majority―if necessary, it is remarkably customizable, versatile, and efficient.
|
libraries. One of the greatest issues with almost every library is stability;
|
||||||
- **Functional API**: This will produce a cleaner and more performant code while removing the difficulties of extending built-in classes and inheritance. Avoid potential memory leaks or crashes because of too many listeners or other silly issues.
|
types are outdated, less (or minimal) parity with the API, core maintainers
|
||||||
|
have quit or no longer actively maintain the library, and whatnot. Discordeno,
|
||||||
|
on the other hand, is actively maintained to ensure great performance and
|
||||||
|
convenience. Discordeno internally checks all missing permissions before
|
||||||
|
forwarding a request to the API so that the client does not get
|
||||||
|
globally-banned by Discord.
|
||||||
|
- **Efficient & lightweight**: Discordeno is simplistic and easy-to-use. Always
|
||||||
|
prefer defaults that Discord recommends or the best configuration for the
|
||||||
|
majority―if necessary, it is remarkably customizable, versatile, and
|
||||||
|
efficient.
|
||||||
|
- **Functional API**: This will produce a cleaner and more performant code while
|
||||||
|
removing the difficulties of extending built-in classes and inheritance. Avoid
|
||||||
|
potential memory leaks or crashes because of too many listeners or other silly
|
||||||
|
issues.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Beginner Developers
|
### Beginner Developers
|
||||||
|
|
||||||
Don't worry a lot of developers start out coding their first projects as a Discord bot (I did 😉) and it is not so easy to do so. Discordeno is built considering all the issues with pre-existing libraries and issues that I had when I first started out coding bots.
|
Don't worry a lot of developers start out coding their first projects as a
|
||||||
If you are a beginner developer, you may check out these awesome official and unofficial boilerplates:
|
Discord bot (I did 😉) and it is not so easy to do so. Discordeno is built
|
||||||
|
considering all the issues with pre-existing libraries and issues that I had
|
||||||
|
when I first started out coding bots. If you are a beginner developer, you may
|
||||||
|
check out these awesome official and unofficial boilerplates:
|
||||||
|
|
||||||
- Official Discordeno Boilerplate
|
- Official Discordeno Boilerplate
|
||||||
- [GitHub](https://github.com/Skillz4Killz/Discordeno-bot-template)
|
- [GitHub](https://github.com/Skillz4Killz/Discordeno-bot-template)
|
||||||
@@ -56,7 +72,8 @@ startBot({
|
|||||||
|
|
||||||
We appreciate your help!
|
We appreciate your help!
|
||||||
|
|
||||||
Before contributing, please read the [Contributing Guide](https://github.com/discordeno/discordeno/blob/master/.github/CONTRIBUTING.md).
|
Before contributing, please read the
|
||||||
|
[Contributing Guide](https://github.com/discordeno/discordeno/blob/master/.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|||||||
+30
-8
@@ -6,9 +6,22 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
- **Secure & stable**: Discordeno is comparatively more stable than the other libraries. One of the greatest issues with almost every library is stability; types are outdated, less (or minimal) parity with the API, core maintainers have quit or no longer actively maintain the library, and whatnot. Discordeno, on the other hand, is actively maintained to ensure great performance and convenience. Discordeno internally checks all missing permissions before forwarding a request to the API so that the client does not get globally-banned by Discord.
|
- **Secure & stable**: Discordeno is comparatively more stable than the other
|
||||||
- **Efficient & lightweight**: Discordeno is simplistic and easy-to-use. Always prefer defaults that Discord recommends or the best configuration for the majority―if necessary, it is remarkably customizable, versatile, and efficient.
|
libraries. One of the greatest issues with almost every library is stability;
|
||||||
- **Functional API**: This will produce a cleaner and more performant code while removing the difficulties of extending built-in classes and inheritance. Avoid potential memory leaks or crashes because of too many listeners or other silly issues.
|
types are outdated, less (or minimal) parity with the API, core maintainers
|
||||||
|
have quit or no longer actively maintain the library, and whatnot. Discordeno,
|
||||||
|
on the other hand, is actively maintained to ensure great performance and
|
||||||
|
convenience. Discordeno internally checks all missing permissions before
|
||||||
|
forwarding a request to the API so that the client does not get
|
||||||
|
globally-banned by Discord.
|
||||||
|
- **Efficient & lightweight**: Discordeno is simplistic and easy-to-use. Always
|
||||||
|
prefer defaults that Discord recommends or the best configuration for the
|
||||||
|
majority―if necessary, it is remarkably customizable, versatile, and
|
||||||
|
efficient.
|
||||||
|
- **Functional API**: This will produce a cleaner and more performant code while
|
||||||
|
removing the difficulties of extending built-in classes and inheritance. Avoid
|
||||||
|
potential memory leaks or crashes because of too many listeners or other silly
|
||||||
|
issues.
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
@@ -18,17 +31,26 @@
|
|||||||
|
|
||||||
## Read me first...
|
## Read me first...
|
||||||
|
|
||||||
Discordeno is cool right? You could make the next big bot! Who knows, but before we get right into developing our Bot. We want to get started with learning the basics...
|
Discordeno is cool right? You could make the next big bot! Who knows, but before
|
||||||
|
we get right into developing our Bot. We want to get started with learning the
|
||||||
|
basics...
|
||||||
|
|
||||||
You've seen how amazing Discord Bots are built and functioned! So beginning with Discordeno always starts with learning the TypeScript and/or JavaScript programming languages first. Making a Discord bot with very little knowledge is possible, it can be a challenge! You may end up dealing with Console errors or just syntax typographical errors...
|
You've seen how amazing Discord Bots are built and functioned! So beginning with
|
||||||
|
Discordeno always starts with learning the TypeScript and/or JavaScript
|
||||||
|
programming languages first. Making a Discord bot with very little knowledge is
|
||||||
|
possible, it can be a challenge! You may end up dealing with Console errors or
|
||||||
|
just syntax typographical errors...
|
||||||
|
|
||||||
If you are new to Discordeno, TypeScript or JavaScript, here are some great resources:
|
If you are new to Discordeno, TypeScript or JavaScript, here are some great
|
||||||
|
resources:
|
||||||
|
|
||||||
- [Official TypeScript Documentation](https://www.typescriptlang.org/docs/home.html)
|
- [Official TypeScript Documentation](https://www.typescriptlang.org/docs/home.html)
|
||||||
- [JavaScript Documentation from Devdocs](https://devdocs.io/javascript/)
|
- [JavaScript Documentation from Devdocs](https://devdocs.io/javascript/)
|
||||||
- [Codecademy](https://www.codecademy.com/)
|
- [Codecademy](https://www.codecademy.com/)
|
||||||
- [TypeScript Deep Dive](https://basarat.gitbook.io/typescript/)
|
- [TypeScript Deep Dive](https://basarat.gitbook.io/typescript/)
|
||||||
- [Deno Crash Course by Traversy Media](https://www.youtube.com/watch?v=NHHhiqwcfRM)
|
- [Deno Crash Course by Traversy Media](https://www.youtube.com/watch?v=NHHhiqwcfRM)
|
||||||
- [TypeScript Crash Course by Traversy Media](https://www.youtube.com/watch?v=rAy_3SIqT-E)
|
- [TypeScript Crash Course by Traversy
|
||||||
|
Media](https://www.youtube.com/watch?v=rAy_3SIqT-E)
|
||||||
|
|
||||||
There is always more resources... Take your time and don't fret! Come back when you are ready, we can't wait to see what your Discordeno created bot does!
|
There is always more resources... Take your time and don't fret! Come back when
|
||||||
|
you are ready, we can't wait to see what your Discordeno created bot does!
|
||||||
|
|||||||
@@ -1,25 +1,41 @@
|
|||||||
# Command Arguments
|
# Command Arguments
|
||||||
|
|
||||||
Command Arguments is a really cool and powerful feature that will parse, validate and handle arguments for your commands. Sometimes, you want certain commands to have arguments provided. For example, in a `ban` command, you will need a `member` and an optional reason to ban them. Discordeno will handle this for you even before the code reaches your commands execution.
|
Command Arguments is a really cool and powerful feature that will parse,
|
||||||
|
validate and handle arguments for your commands. Sometimes, you want certain
|
||||||
|
commands to have arguments provided. For example, in a `ban` command, you will
|
||||||
|
need a `member` and an optional reason to ban them. Discordeno will handle this
|
||||||
|
for you even before the code reaches your commands execution.
|
||||||
|
|
||||||
## Built-In Arguments
|
## Built-In Arguments
|
||||||
|
|
||||||
Discordeno comes with the most useful command arguments already built for you. The command arguments can be found in the `src/arguments` folder.
|
Discordeno comes with the most useful command arguments already built for you.
|
||||||
|
The command arguments can be found in the `src/arguments` folder.
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- `string` A valid **1 word** string with atleast 1 character. This is the default argument type. If you don't provide a type, it will use string
|
|
||||||
- `...string` Sometimes you will want strings but with more than 1 word. For example, in our ban command we will need a longer reason argument. For that we use `...string`. Note, this should always be your FINAL argument.
|
|
||||||
- `boolean` When you want the user to provide `true` or `false`. This also supports `on` or `off`.
|
|
||||||
- `command` When you want a command name or an command alias.
|
|
||||||
- `duration` When you want the user to provide a string like `12d2h5m`. This will be converted to milliseconds for you.
|
|
||||||
- `guild` When you want a guild object and the user provides a guild id.
|
|
||||||
- `member` When you want a member object and the user can provide a member id, @mention or their username/nickname.
|
|
||||||
- `number` When you want a number.
|
|
||||||
- `role` When you want a role object. The user can provide the role id, mention or role name.
|
|
||||||
- `snowflake` When you just want a snowflake. A snowflake is an ID generation format from Twitter that Discord uses to make their IDs unique. These IDs do never change and are used to identify users, guilds, emojis, and more.
|
|
||||||
- `...snowflakes` When you want to check if the mentioned ID is a valid snowflake. Note that this is similar to ...string, it will take all coming arguments and check them. This should always be your FINAL argument.
|
|
||||||
- `subcommand` When your command has subcommands.
|
|
||||||
|
|
||||||
|
- `string` A valid **1 word** string with atleast 1 character. This is the
|
||||||
|
default argument type. If you don't provide a type, it will use string
|
||||||
|
- `...string` Sometimes you will want strings but with more than 1 word. For
|
||||||
|
example, in our ban command we will need a longer reason argument. For that we
|
||||||
|
use `...string`. Note, this should always be your FINAL argument.
|
||||||
|
- `boolean` When you want the user to provide `true` or `false`. This also
|
||||||
|
supports `on` or `off`.
|
||||||
|
- `command` When you want a command name or an command alias.
|
||||||
|
- `duration` When you want the user to provide a string like `12d2h5m`. This
|
||||||
|
will be converted to milliseconds for you.
|
||||||
|
- `guild` When you want a guild object and the user provides a guild id.
|
||||||
|
- `member` When you want a member object and the user can provide a member id,
|
||||||
|
@mention or their username/nickname.
|
||||||
|
- `number` When you want a number.
|
||||||
|
- `role` When you want a role object. The user can provide the role id, mention
|
||||||
|
or role name.
|
||||||
|
- `snowflake` When you just want a snowflake. A snowflake is an ID generation
|
||||||
|
format from Twitter that Discord uses to make their IDs unique. These IDs do
|
||||||
|
never change and are used to identify users, guilds, emojis, and more.
|
||||||
|
- `...snowflakes` When you want to check if the mentioned ID is a valid
|
||||||
|
snowflake. Note that this is similar to ...string, it will take all coming
|
||||||
|
arguments and check them. This should always be your FINAL argument.
|
||||||
|
- `subcommand` When your command has subcommands.
|
||||||
|
|
||||||
### Channels
|
### Channels
|
||||||
|
|
||||||
@@ -28,17 +44,19 @@ Discordeno comes with the most useful command arguments already built for you. T
|
|||||||
- `textchannel` Must be a text channel id or name or mention.
|
- `textchannel` Must be a text channel id or name or mention.
|
||||||
- `voicechannel` Must be a voice channel id or name or mention.
|
- `voicechannel` Must be a voice channel id or name or mention.
|
||||||
|
|
||||||
|
Any of these can be easily modified, in their files. Let's go ahead and try and
|
||||||
Any of these can be easily modified, in their files. Let's go ahead and try and modify one of the command arguments as an example.
|
modify one of the command arguments as an example.
|
||||||
|
|
||||||
## Modifying Existing Command Arguments
|
## Modifying Existing Command Arguments
|
||||||
|
|
||||||
Suppose you wanted to make it possible so that the boolean argument could accept other options as well. By default, it supports:
|
Suppose you wanted to make it possible so that the boolean argument could accept
|
||||||
|
other options as well. By default, it supports:
|
||||||
|
|
||||||
- `true` and `on` which will be true
|
- `true` and `on` which will be true
|
||||||
- `false` and `off` which will be false
|
- `false` and `off` which will be false
|
||||||
|
|
||||||
What if we also wanted to support, `yes` and `no`? Let's open up the `boolean.ts` file in the arguments folder and get started.
|
What if we also wanted to support, `yes` and `no`? Let's open up the
|
||||||
|
`boolean.ts` file in the arguments folder and get started.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -70,16 +88,23 @@ botCache.arguments.set("boolean", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome. You just managed to modify one of the existing command arguments. But what if we wanted to create our own custom command argument. Let's do that next.
|
Awesome. You just managed to modify one of the existing command arguments. But
|
||||||
|
what if we wanted to create our own custom command argument. Let's do that next.
|
||||||
|
|
||||||
## Creating A Command Argument
|
## Creating A Command Argument
|
||||||
|
|
||||||
Let's create a command argument for the purposes of this guide so we can learn how to create one. For example, let's say we wanted a command argument for urls.
|
Let's create a command argument for the purposes of this guide so we can learn
|
||||||
|
how to create one. For example, let's say we wanted a command argument for urls.
|
||||||
|
|
||||||
First, we need to update the **CommandArgument** interface in `src/types/commands.ts`.
|
First, we need to update the **CommandArgument** interface in
|
||||||
|
`src/types/commands.ts`.
|
||||||
|
|
||||||
- Add your argument type to the list of types in the interface. In this case we will add **"url"**. You can add it in any order here you like. The specific order does not matter.
|
- Add your argument type to the list of types in the interface. In this case we
|
||||||
- Once that's done, we can go and create the code for it. Now, lets create a new file in `src/arguments` folder called `url.ts` and paste the base snippet for a command argument below.
|
will add **"url"**. You can add it in any order here you like. The specific
|
||||||
|
order does not matter.
|
||||||
|
- Once that's done, we can go and create the code for it. Now, lets create a new
|
||||||
|
file in `src/arguments` folder called `url.ts` and paste the base snippet for
|
||||||
|
a command argument below.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -92,7 +117,8 @@ botCache.arguments.set("argumentname", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
First let's change the `argumentname` to be `url`. Then we can start adding the pseudo-code.
|
First let's change the `argumentname` to be `url`. Then we can start adding the
|
||||||
|
pseudo-code.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -105,7 +131,6 @@ botCache.arguments.set("url", {
|
|||||||
// The regex we will use to test if it's a valid url
|
// The regex we will use to test if it's a valid url
|
||||||
|
|
||||||
// Use the regex to test if it is a valid url
|
// Use the regex to test if it is a valid url
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -123,9 +148,10 @@ botCache.arguments.set("url", {
|
|||||||
if (!url) return;
|
if (!url) return;
|
||||||
|
|
||||||
// The regex we will use to test if it's a valid url
|
// The regex we will use to test if it's a valid url
|
||||||
const urlRegex = /^((https?):\/\/)?([w|W]{3}\.)*[a-zA-Z0-9\-\.]{3,}\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?$/
|
const urlRegex =
|
||||||
|
/^((https?):\/\/)?([w|W]{3}\.)*[a-zA-Z0-9\-\.]{3,}\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?$/;
|
||||||
// Use the regex to test if it is a valid url
|
// Use the regex to test if it is a valid url
|
||||||
const validURL = urlRegex.test(url)
|
const validURL = urlRegex.test(url);
|
||||||
if (!validURL) return;
|
if (!validURL) return;
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
@@ -133,16 +159,22 @@ botCache.arguments.set("url", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Feel free to use any other regex you prefer. There are so many URL regexes out there. Use the one that fits you best. [URL Regex Comparison](https://mathiasbynens.be/demo/url-regex)
|
Feel free to use any other regex you prefer. There are so many URL regexes out
|
||||||
|
there. Use the one that fits you best.
|
||||||
|
[URL Regex Comparison](https://mathiasbynens.be/demo/url-regex)
|
||||||
|
|
||||||
Awesome! You just created your very own command argument. Now let's check out how to use command arguments.
|
Awesome! You just created your very own command argument. Now let's check out
|
||||||
|
how to use command arguments.
|
||||||
|
|
||||||
## Using Command Arguments
|
## Using Command Arguments
|
||||||
|
|
||||||
When you create a command, you have the option to provide an array of command arguments using the `arguments` property on a command. A CommandArgument must follow the following rules.
|
When you create a command, you have the option to provide an array of command
|
||||||
|
arguments using the `arguments` property on a command. A CommandArgument must
|
||||||
|
follow the following rules.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
arguments: [
|
arguments:
|
||||||
|
[
|
||||||
// Each argument goes here
|
// Each argument goes here
|
||||||
{
|
{
|
||||||
// This is required. You can name it anything you like. This will be used when you want to access the properties. For example, `args.url` will be done to use this argument when the command code is written.
|
// This is required. You can name it anything you like. This will be used when you want to access the properties. For example, `args.url` will be done to use this argument when the command code is written.
|
||||||
@@ -160,11 +192,16 @@ arguments: [
|
|||||||
// This is optional BUT it is required if the type is subcommand. When you have a type of string or subcommand you can sometimes want very specific keywords like `add` or `remove`.
|
// This is optional BUT it is required if the type is subcommand. When you have a type of string or subcommand you can sometimes want very specific keywords like `add` or `remove`.
|
||||||
literals: ["add", "remove"],
|
literals: ["add", "remove"],
|
||||||
// This is optional. If a argument is not provided it can be given a default argument. Useful for a default subcommand if you wish.
|
// This is optional. If a argument is not provided it can be given a default argument. Useful for a default subcommand if you wish.
|
||||||
defaultValue: "my default value here"
|
defaultValue: "my default value here",
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
We already covered using the arguments in a command in our guide when we created the role command, so we can skip that here. To re-read that you can check it out [here](https://discordeno.mod.land/stepbystep/createcommand.html#arguments).
|
We already covered using the arguments in a command in our guide when we created
|
||||||
|
the role command, so we can skip that here. To re-read that you can check it out
|
||||||
|
[here](https://discordeno.mod.land/stepbystep/createcommand.html#arguments).
|
||||||
|
|
||||||
Command arguments are an extremely powerful feature that can help make creating bots a lot easier. Discordeno provides extremely flexible command arguments. As a side benefit, command arguments are designed to be hot reloadable from the reload command. 🎉
|
Command arguments are an extremely powerful feature that can help make creating
|
||||||
|
bots a lot easier. Discordeno provides extremely flexible command arguments. As
|
||||||
|
a side benefit, command arguments are designed to be hot reloadable from the
|
||||||
|
reload command. 🎉
|
||||||
|
|||||||
@@ -1,33 +1,56 @@
|
|||||||
# Customizing Discordeno
|
# Customizing Discordeno
|
||||||
|
|
||||||
This guide is only for extremely advanced developers. This feature is extremely powerful but comes with huge risks. When I was building Discordeno, there was always one goal I had in my mind that I wanted because when I used other libraries I always felt this was missing. I hated forking a library just to customize it as then it made staying up to date a much larger hassle. Discordeno was designed with customizability in mind. A vast majority of the library can be overridden allowing you to make it work however you would like for your bot.
|
This guide is only for extremely advanced developers. This feature is extremely
|
||||||
|
powerful but comes with huge risks. When I was building Discordeno, there was
|
||||||
|
always one goal I had in my mind that I wanted because when I used other
|
||||||
|
libraries I always felt this was missing. I hated forking a library just to
|
||||||
|
customize it as then it made staying up to date a much larger hassle. Discordeno
|
||||||
|
was designed with customizability in mind. A vast majority of the library can be
|
||||||
|
overridden allowing you to make it work however you would like for your bot.
|
||||||
|
|
||||||
## Customizing Structures
|
## Customizing Structures
|
||||||
|
|
||||||
Discordeno provides the ability to customize structures. You have the ability to override the function that creates the structures inside Discordeno. **THIS DOES NOT MODIFY THE PROTOTYPE!** I wanted to make sure that Discordeno handled this without encouraging bad practices such as prototype pollution which can make your bots inherintly unstable and unpredictable. Discordeno allows you to take control over the functions that create the structures.
|
Discordeno provides the ability to customize structures. You have the ability to
|
||||||
|
override the function that creates the structures inside Discordeno. **THIS DOES
|
||||||
|
NOT MODIFY THE PROTOTYPE!** I wanted to make sure that Discordeno handled this
|
||||||
|
without encouraging bad practices such as prototype pollution which can make
|
||||||
|
your bots inherintly unstable and unpredictable. Discordeno allows you to take
|
||||||
|
control over the functions that create the structures.
|
||||||
|
|
||||||
### Customizing Member Structure
|
### Customizing Member Structure
|
||||||
|
|
||||||
Let's take into consideration that you wanted to add some custom properties to the member structure. For instance, let's add a couple of properties that could help make working with the bot easier such as `member.tag`, `member.guild`, `member.avatarURL`. We are going to add these, simple as an example for the purpose of this guide. Please note, that these don't exist in the library itself because they take up quite a bit of RAM/memory since as your bot grows you can hit millions of members which adds up to GB of memory. Keeping this minimal can help allow your bots to grow larger without increasing the cost. However, if you are advanced enough and feel adding/customizing the structures is necessary then this feature allows you to do that.
|
Let's take into consideration that you wanted to add some custom properties to
|
||||||
|
the member structure. For instance, let's add a couple of properties that could
|
||||||
|
help make working with the bot easier such as `member.tag`, `member.guild`,
|
||||||
|
`member.avatarURL`. We are going to add these, simple as an example for the
|
||||||
|
purpose of this guide. Please note, that these don't exist in the library itself
|
||||||
|
because they take up quite a bit of RAM/memory since as your bot grows you can
|
||||||
|
hit millions of members which adds up to GB of memory. Keeping this minimal can
|
||||||
|
help allow your bots to grow larger without increasing the cost. However, if you
|
||||||
|
are advanced enough and feel adding/customizing the structures is necessary then
|
||||||
|
this feature allows you to do that.
|
||||||
|
|
||||||
|
To begin customizing, create a file in the structures folder called `member.ts`.
|
||||||
To begin customizing, create a file in the structures folder called `member.ts`. The name of the file is not important at all.
|
The name of the file is not important at all.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
async function createMember() {
|
async function createMember() {
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We start by declaring a function that will be run to create the structure. Once again the name here is not important. The function must take the same arguments that the internal function takes. In this case the createMember function takes 2 arguments. `data: MemberCreatePayload, guildID: string`
|
We start by declaring a function that will be run to create the structure. Once
|
||||||
|
again the name here is not important. The function must take the same arguments
|
||||||
|
that the internal function takes. In this case the createMember function takes 2
|
||||||
|
arguments. `data: MemberCreatePayload, guildID: string`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
async function createMember(data: MemberCreatePayload, guildID: string) {
|
async function createMember(data: MemberCreatePayload, guildID: string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The next step is to fill in this function. You can make this do whatever you want. My recommendation is to start by copying the current code from the internal libraries structure.
|
The next step is to fill in this function. You can make this do whatever you
|
||||||
|
want. My recommendation is to start by copying the current code from the
|
||||||
|
internal libraries structure.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
async function createMember(data: MemberCreatePayload, guildID: string) {
|
async function createMember(data: MemberCreatePayload, guildID: string) {
|
||||||
@@ -63,7 +86,8 @@ async function createMember(data: MemberCreatePayload, guildID: string) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we have a base to work with. We can now add a `tag`, `avatarURL`, `mention`, and `guild` properties to the member.
|
Now we have a base to work with. We can now add a `tag`, `avatarURL`, `mention`,
|
||||||
|
and `guild` properties to the member.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { rawAvatarURL } from "../../deps.ts";
|
import { rawAvatarURL } from "../../deps.ts";
|
||||||
@@ -84,20 +108,24 @@ async function createMember(data: MemberCreatePayload, guildID: string) {
|
|||||||
/** The guild object for where this member is located */
|
/** The guild object for where this member is located */
|
||||||
guild: await cacheHandler.get("guilds", guildID),
|
guild: await cacheHandler.get("guilds", guildID),
|
||||||
/** Easily mention the member */
|
/** Easily mention the member */
|
||||||
mention: `<@${data.user.id}>`
|
mention: `<@${data.user.id}>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we need to use this function and telling Discordeno to override the internal createMember function. To do this, we will modify the internal functions. This is where we reassign the value of the function.
|
Now we need to use this function and telling Discordeno to override the internal
|
||||||
|
createMember function. To do this, we will modify the internal functions. This
|
||||||
|
is where we reassign the value of the function.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
structures.createMember = createMember;
|
structures.createMember = createMember;
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome. Now, we have one more step to complete which is to declare these new properties on our typings for the member structure. At the bottom, of the file we will write the following code.
|
Awesome. Now, we have one more step to complete which is to declare these new
|
||||||
|
properties on our typings for the member structure. At the bottom, of the file
|
||||||
|
we will write the following code.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
declare module "../../deps.ts" {
|
declare module "../../deps.ts" {
|
||||||
@@ -110,8 +138,8 @@ declare module "../../deps.ts" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Important:** when you modify structures, it is important to restart the bot so it takes effect on all members that have already be constructed.
|
> **Important:** when you modify structures, it is important to restart the bot
|
||||||
|
> so it takes effect on all members that have already be constructed.
|
||||||
|
|
||||||
The code should look like this right now:
|
The code should look like this right now:
|
||||||
|
|
||||||
@@ -150,7 +178,7 @@ async function createMember(data: MemberCreatePayload, guildID: string) {
|
|||||||
/** The guild object for where this member is located */
|
/** The guild object for where this member is located */
|
||||||
guild: await cacheHandler.get("guilds", guildID),
|
guild: await cacheHandler.get("guilds", guildID),
|
||||||
/** Easily mention the member */
|
/** Easily mention the member */
|
||||||
mention: `<@${data.user.id}>`
|
mention: `<@${data.user.id}>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
return member;
|
return member;
|
||||||
@@ -170,13 +198,23 @@ declare module "../../deps.ts" {
|
|||||||
|
|
||||||
#### Removing Properties
|
#### Removing Properties
|
||||||
|
|
||||||
Now, that we have added the properties we want, we can discuss how to remove properties we don't want. Remember every property that exists on member can become very expensive as your bot grows.
|
Now, that we have added the properties we want, we can discuss how to remove
|
||||||
|
properties we don't want. Remember every property that exists on member can
|
||||||
|
become very expensive as your bot grows.
|
||||||
|
|
||||||
To do a little easy math, let's suppose we had 1,000,000 member objects. Each one of them could possibly store an avatar string which can take up about 32 bytes. Now that comes to a total of around 32MB for every 1 million members.
|
To do a little easy math, let's suppose we had 1,000,000 member objects. Each
|
||||||
|
one of them could possibly store an avatar string which can take up about 32
|
||||||
|
bytes. Now that comes to a total of around 32MB for every 1 million members.
|
||||||
|
|
||||||
Now let's go ahead and delete the `avatar`, `username` and `discriminator` strings since we already used them and don't really want them to be duplicated as we will never need their raw counterparts in our bot(Note: You may in your bot and this is why this is an advanced feature. Decide carefully what you wish to add or remove). In fact, for the purposes of this guide let's go a little crazy and remove everything we don't specifically want to have.
|
Now let's go ahead and delete the `avatar`, `username` and `discriminator`
|
||||||
|
strings since we already used them and don't really want them to be duplicated
|
||||||
|
as we will never need their raw counterparts in our bot(Note: You may in your
|
||||||
|
bot and this is why this is an advanced feature. Decide carefully what you wish
|
||||||
|
to add or remove). In fact, for the purposes of this guide let's go a little
|
||||||
|
crazy and remove everything we don't specifically want to have.
|
||||||
|
|
||||||
The following is the current base data available to us when we create the member.
|
The following is the current base data available to us when we create the
|
||||||
|
member.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
user: {
|
user: {
|
||||||
@@ -219,10 +257,16 @@ The following is the current base data available to us when we create the member
|
|||||||
mute: boolean;
|
mute: boolean;
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's just keep `nick`, `roles`, `joinedAt`, `bot` and `id` plus the new stuff we have added above.
|
Let's just keep `nick`, `roles`, `joinedAt`, `bot` and `id` plus the new stuff
|
||||||
|
we have added above.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { cacheHandlers, Guild, MemberCreatePayload, rawAvatarURL } from "../../deps.ts";
|
import {
|
||||||
|
cacheHandlers,
|
||||||
|
Guild,
|
||||||
|
MemberCreatePayload,
|
||||||
|
rawAvatarURL,
|
||||||
|
} from "../../deps.ts";
|
||||||
|
|
||||||
async function createMember(data: MemberCreatePayload, guildID: string) {
|
async function createMember(data: MemberCreatePayload, guildID: string) {
|
||||||
const {
|
const {
|
||||||
@@ -260,7 +304,11 @@ declare module "../../deps.ts" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You might be seeing an error on `structures.createMember`. This is happening because our new member structures is modifying/removing existing properties that the lib internally said it would have. To solve this, simply just add a ts-ignore above it as you know better than TS that the typings are being overwritten.
|
You might be seeing an error on `structures.createMember`. This is happening
|
||||||
|
because our new member structures is modifying/removing existing properties that
|
||||||
|
the lib internally said it would have. To solve this, simply just add a
|
||||||
|
ts-ignore above it as you know better than TS that the typings are being
|
||||||
|
overwritten.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -269,8 +317,11 @@ structures.createMember = createMember;
|
|||||||
|
|
||||||
## Custom Cache
|
## Custom Cache
|
||||||
|
|
||||||
One of the features that Discordeno has is the ability to customize the cache. In order to do this, we will simply override the cache handlers. For the purposes of this guide, we are going to use Redis for our custom cache solution. You can use anything you like, this guide purpose is just to teach the basics of how to customize. We will not be discussing how to use Redis.
|
One of the features that Discordeno has is the ability to customize the cache.
|
||||||
|
In order to do this, we will simply override the cache handlers. For the
|
||||||
|
purposes of this guide, we are going to use Redis for our custom cache solution.
|
||||||
|
You can use anything you like, this guide purpose is just to teach the basics of
|
||||||
|
how to customize. We will not be discussing how to use Redis.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { cacheHandlers } from "../../../deps.ts";
|
import { cacheHandlers } from "../../../deps.ts";
|
||||||
@@ -280,9 +331,14 @@ cacheHandlers.has = async function (table, key) {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This code now overrides the `has` method which will check from Redis. The basic concept is that the function `has` takes a table name and a key. The `has` method returns whether or not a value exists. You can do anything you like inside this function, like connecting to a database or any other solution you would like to use.
|
This code now overrides the `has` method which will check from Redis. The basic
|
||||||
|
concept is that the function `has` takes a table name and a key. The `has`
|
||||||
|
method returns whether or not a value exists. You can do anything you like
|
||||||
|
inside this function, like connecting to a database or any other solution you
|
||||||
|
would like to use.
|
||||||
|
|
||||||
Similarily, since we want to use Redis we would want to customize all the methods on the cacheHandlers. The current list of methods available are:
|
Similarily, since we want to use Redis we would want to customize all the
|
||||||
|
methods on the cacheHandlers. The current list of methods available are:
|
||||||
|
|
||||||
- has
|
- has
|
||||||
- get
|
- get
|
||||||
@@ -293,9 +349,20 @@ Similarily, since we want to use Redis we would want to customize all the method
|
|||||||
|
|
||||||
## Custom Gateway Payload Handling (Controllers)
|
## Custom Gateway Payload Handling (Controllers)
|
||||||
|
|
||||||
Controllers are one of the most powerful features of Discordeno. They allow you to take control of how Discordeno handles the Discord payloads from the gateway. When an event comes in, you can override and control how you want it to work. For example, if your bot does not use emojis at all, you could simply just take control over the GUILD_EMOJIS_UPDATE event and prevent anyone from caching any emojis at all. Another example, is if you are building a custom module/framework around Discordeno and you want to provide more custom events such as when someone boosts the server or some other custom event this is possible as well.
|
Controllers are one of the most powerful features of Discordeno. They allow you
|
||||||
|
to take control of how Discordeno handles the Discord payloads from the gateway.
|
||||||
|
When an event comes in, you can override and control how you want it to work.
|
||||||
|
For example, if your bot does not use emojis at all, you could simply just take
|
||||||
|
control over the GUILD_EMOJIS_UPDATE event and prevent anyone from caching any
|
||||||
|
emojis at all. Another example, is if you are building a custom module/framework
|
||||||
|
around Discordeno and you want to provide more custom events such as when
|
||||||
|
someone boosts the server or some other custom event this is possible as well.
|
||||||
|
|
||||||
Someone once asked if it was possible to make Discordeno, show the number of users currently typing in the server. He had managed to build this himself in his bot, but he wanted to do it inside the library itself. In order to keep Discordeno minimalistic and memory efficient I avoided adding this. So let's see how we could achieve this same thing with Controllers.
|
Someone once asked if it was possible to make Discordeno, show the number of
|
||||||
|
users currently typing in the server. He had managed to build this himself in
|
||||||
|
his bot, but he wanted to do it inside the library itself. In order to keep
|
||||||
|
Discordeno minimalistic and memory efficient I avoided adding this. So let's see
|
||||||
|
how we could achieve this same thing with Controllers.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {
|
import {
|
||||||
@@ -326,8 +393,18 @@ controllers.TYPING_START = function (data) {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Controllers are amazing in so many ways. This is just a basic example but it's true potential is only limited by your imagination. I would love to see what you all can create with controllers.
|
Controllers are amazing in so many ways. This is just a basic example but it's
|
||||||
|
true potential is only limited by your imagination. I would love to see what you
|
||||||
|
all can create with controllers.
|
||||||
|
|
||||||
Something worth noting about why Discordeno controllers are so amazing is that it allows you to never depend on me. When Discord releases something new, you don't need to wait for me to update the library to access it. Without controllers, if you wanted access to a feature you would need to wait for the library to be updated or have to fork it, modify it and modify your code for it. Then when the library does get updated, you need to switch back to it and modify your code again possibly to how the lib designed it. With controllers, you never have to fork or anything. Just take control!
|
Something worth noting about why Discordeno controllers are so amazing is that
|
||||||
|
it allows you to never depend on me. When Discord releases something new, you
|
||||||
|
don't need to wait for me to update the library to access it. Without
|
||||||
|
controllers, if you wanted access to a feature you would need to wait for the
|
||||||
|
library to be updated or have to fork it, modify it and modify your code for it.
|
||||||
|
Then when the library does get updated, you need to switch back to it and modify
|
||||||
|
your code again possibly to how the lib designed it. With controllers, you never
|
||||||
|
have to fork or anything. Just take control!
|
||||||
|
|
||||||
Controllers are extremely powerful. **Remember with great power comes great bugs!**
|
Controllers are extremely powerful. **Remember with great power comes great
|
||||||
|
bugs!**
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
# Docker Hosting
|
# Docker Hosting
|
||||||
|
|
||||||
Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code quickly, you can significantly reduce the delay between writing code and running it in production.
|
Docker is an open platform for developing, shipping, and running applications.
|
||||||
|
Docker enables you to separate your applications from your infrastructure so you
|
||||||
|
can deliver software quickly. With Docker, you can manage your infrastructure in
|
||||||
|
the same ways you manage your applications. By taking advantage of Docker’s
|
||||||
|
methodologies for shipping, testing, and deploying code quickly, you can
|
||||||
|
significantly reduce the delay between writing code and running it in
|
||||||
|
production.
|
||||||
|
|
||||||
Learn more [here](https://docs.docker.com/get-started/)
|
Learn more [here](https://docs.docker.com/get-started/)
|
||||||
|
|
||||||
### Installing Docker
|
### Installing Docker
|
||||||
|
|
||||||
Installing Docker is very simple and supported on nearly every major operating system! Just follow the instructions [here](https://docs.docker.com/get-docker/) to get started.
|
Installing Docker is very simple and supported on nearly every major operating
|
||||||
|
system! Just follow the instructions [here](https://docs.docker.com/get-docker/)
|
||||||
|
to get started.
|
||||||
|
|
||||||
### Building The Bot's Docker Image
|
### Building The Bot's Docker Image
|
||||||
|
|
||||||
Now it is time to build the image that will be running the bot! Simply run this command, replacing `IMAGE_NAME` with whatever you want to name the image. Make sure to run this command from the root of the bot directory! This command could take a while depending on how powerful your device is.
|
Now it is time to build the image that will be running the bot! Simply run this
|
||||||
|
command, replacing `IMAGE_NAME` with whatever you want to name the image. Make
|
||||||
|
sure to run this command from the root of the bot directory! This command could
|
||||||
|
take a while depending on how powerful your device is.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t IMAGE_NAME .
|
docker build -t IMAGE_NAME .
|
||||||
@@ -18,17 +29,24 @@ docker build -t IMAGE_NAME .
|
|||||||
|
|
||||||
### Running The Bot For The First Time
|
### Running The Bot For The First Time
|
||||||
|
|
||||||
With the bot's image built, it is time to create a container and run the bot for the first time! Remember to replace `IMAGE_NAME` with the same name you chose in the last step, and replace `CONTAINER_NAME` with what you want to name the container. (RECOMMENDATION: Name the container with the name of your bot)
|
With the bot's image built, it is time to create a container and run the bot for
|
||||||
|
the first time! Remember to replace `IMAGE_NAME` with the same name you chose in
|
||||||
|
the last step, and replace `CONTAINER_NAME` with what you want to name the
|
||||||
|
container. (RECOMMENDATION: Name the container with the name of your bot)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --init -v $PWD:/bot --name CONTAINER_NAME IMAGE_NAME
|
docker run -it --init -v $PWD:/bot --name CONTAINER_NAME IMAGE_NAME
|
||||||
```
|
```
|
||||||
|
|
||||||
This command should create a new container linked to the directory containing your bot's code. Once your bot finishes loading, you should be able to use the bot just like normal.
|
This command should create a new container linked to the directory containing
|
||||||
|
your bot's code. Once your bot finishes loading, you should be able to use the
|
||||||
|
bot just like normal.
|
||||||
|
|
||||||
### Starting, Stopping, And Restarting The Bot
|
### Starting, Stopping, And Restarting The Bot
|
||||||
|
|
||||||
Now that the container is created, you should only need three main commands to manage the bot. Remember to replace CONTAINER_NAME with whatever you chose in the last step.
|
Now that the container is created, you should only need three main commands to
|
||||||
|
manage the bot. Remember to replace CONTAINER_NAME with whatever you chose in
|
||||||
|
the last step.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker start CONTAINER_NAME
|
docker start CONTAINER_NAME
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
# Dynamic Command Creation!
|
# Dynamic Command Creation!
|
||||||
|
|
||||||
Discordeno has one of the coolest ways to create commands for advanced level developers. I call it Dynamic command creation. This allows you to basically create a bunch of commands that are similar instantly.
|
Discordeno has one of the coolest ways to create commands for advanced level
|
||||||
|
developers. I call it Dynamic command creation. This allows you to basically
|
||||||
|
create a bunch of commands that are similar instantly.
|
||||||
|
|
||||||
This is some advanced level super magical command creation skills. Discordeno gives you the power to dynamically create commands. What that means is you can write the code for one command but dynamically create like 50 commands using that same code.
|
This is some advanced level super magical command creation skills. Discordeno
|
||||||
|
gives you the power to dynamically create commands. What that means is you can
|
||||||
|
write the code for one command but dynamically create like 50 commands using
|
||||||
|
that same code.
|
||||||
|
|
||||||
## Nekos.Life API Commands
|
## Nekos.Life API Commands
|
||||||
|
|
||||||
A lot of bot's have entertainment commands. Usually they have to create like 100 different files and each file is it's own code. Some might just take the functionality and move it to a separate file and reuse that function in all those files.
|
A lot of bot's have entertainment commands. Usually they have to create like 100
|
||||||
|
different files and each file is it's own code. Some might just take the
|
||||||
|
functionality and move it to a separate file and reuse that function in all
|
||||||
|
those files.
|
||||||
|
|
||||||
In Discordeno, there is a better way. Dynamically create the commands based on the command name.
|
In Discordeno, there is a better way. Dynamically create the commands based on
|
||||||
|
the command name.
|
||||||
|
|
||||||
Let's create all the commands for the entire Nekos.Life API in a very short time.
|
Let's create all the commands for the entire Nekos.Life API in a very short
|
||||||
|
time.
|
||||||
|
|
||||||
- Create a file in the commands folder called `nekos.ts` which will create all our fun commands.
|
- Create a file in the commands folder called `nekos.ts` which will create all
|
||||||
|
our fun commands.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache, sendMessage } from "../../deps.ts";
|
import { botCache, sendMessage } from "../../deps.ts";
|
||||||
@@ -65,9 +76,12 @@ nekosEndpoints.forEach((endpoint) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** We have removed the endpoints that were leading to NSFW content. With them, we would just have created 68 different commands.
|
> **Note:** We have removed the endpoints that were leading to NSFW content.
|
||||||
|
> With them, we would just have created 68 different commands.
|
||||||
|
|
||||||
Take a minute to realize what just happened. This has made 29 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this.
|
Take a minute to realize what just happened. This has made 29 different unique
|
||||||
|
commands dynamically. In 1 file, using the same piece of code, we created so
|
||||||
|
many commands. You can easily add more commands to this.
|
||||||
|
|
||||||
**That ladies and gentleman is the power and magic of Discordeno!**
|
**That ladies and gentleman is the power and magic of Discordeno!**
|
||||||
|
|
||||||
@@ -79,10 +93,11 @@ If your still a little confused, don't worry. Let's break it down.
|
|||||||
const nekosEndpoints = [
|
const nekosEndpoints = [
|
||||||
{ name: "tickle", path: "/img/tickle", nsfw: false },
|
{ name: "tickle", path: "/img/tickle", nsfw: false },
|
||||||
// ...
|
// ...
|
||||||
]
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
This is just an array of all the endpoints on the API giving a name, a path, and a boolean to say if it is NSFW or not.
|
This is just an array of all the endpoints on the API giving a name, a path, and
|
||||||
|
a boolean to say if it is NSFW or not.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botCache.commands.set(endpoint.name, {
|
botCache.commands.set(endpoint.name, {
|
||||||
@@ -97,12 +112,19 @@ botCache.commands.set(endpoint.name, {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
This is the part where we are doing the magical stuff. So let's take a look at this a bit.
|
This is the part where we are doing the magical stuff. So let's take a look at
|
||||||
|
this a bit.
|
||||||
|
|
||||||
We ran a loop `nekosEndpoints.forEach()` on that array and for each item in that array, we created a command.
|
We ran a loop `nekosEndpoints.forEach()` on that array and for each item in that
|
||||||
|
array, we created a command.
|
||||||
|
|
||||||
For the command name, we used the `endpoint.name` property. For the `nsfw` property we used the `endpoint.nsfw` property. Since all of these commands, send a message that is a URL we simply required those permissions in all 29 of these commands.
|
For the command name, we used the `endpoint.name` property. For the `nsfw`
|
||||||
|
property we used the `endpoint.nsfw` property. Since all of these commands, send
|
||||||
|
a message that is a URL we simply required those permissions in all 29 of these
|
||||||
|
commands.
|
||||||
|
|
||||||
Then we simply write the code for the commands and it all just works. If you were to reload/restart your bot now with this. You will see that you have access to 68 new commands.
|
Then we simply write the code for the commands and it all just works. If you
|
||||||
|
were to reload/restart your bot now with this. You will see that you have access
|
||||||
|
to 68 new commands.
|
||||||
|
|
||||||
Take a minute and try out some of the commands and how they function.
|
Take a minute and try out some of the commands and how they function.
|
||||||
|
|||||||
@@ -1,24 +1,34 @@
|
|||||||
# Permission Levels!
|
# Permission Levels!
|
||||||
|
|
||||||
Permission levels is a really cool concept that will let you easily control which users can use your commands. For example, if you have commands like `warn` or `ban`, you only want users that are Moderators or Admins of a server to be able to use these commands. By default, a command is set so any member can use the command. To raise the requirement, you can use Permission Levels.
|
Permission levels is a really cool concept that will let you easily control
|
||||||
|
which users can use your commands. For example, if you have commands like `warn`
|
||||||
|
or `ban`, you only want users that are Moderators or Admins of a server to be
|
||||||
|
able to use these commands. By default, a command is set so any member can use
|
||||||
|
the command. To raise the requirement, you can use Permission Levels.
|
||||||
|
|
||||||
## Default Permission Levels
|
## Default Permission Levels
|
||||||
|
|
||||||
The default permission levels can be found in the `src/permissionLevels` folder.
|
The default permission levels can be found in the `src/permissionLevels` folder.
|
||||||
|
|
||||||
- `MEMBER` Anyone can use this command.
|
- `MEMBER` Anyone can use this command.
|
||||||
- `MODERATOR` User needs **MANAGE SERVER** permission in order to use this command
|
- `MODERATOR` User needs **MANAGE SERVER** permission in order to use this
|
||||||
|
command
|
||||||
- `ADMIN` User needs **ADMINISTRATOR** permission in order to use this command.
|
- `ADMIN` User needs **ADMINISTRATOR** permission in order to use this command.
|
||||||
- `SERVER_OWNER` Only a **SERVER OWNER** can use this command in their server.
|
- `SERVER_OWNER` Only a **SERVER OWNER** can use this command in their server.
|
||||||
- `BOT_SUPPORT` Requires the user ID be present in **configs.userIDs.botSupporters**
|
- `BOT_SUPPORT` Requires the user ID be present in
|
||||||
|
**configs.userIDs.botSupporters**
|
||||||
- `BOT_DEVS` Requires the user ID be present in **configs.userIDs.botDevs**
|
- `BOT_DEVS` Requires the user ID be present in **configs.userIDs.botDevs**
|
||||||
- `BOT_OWNERS` Requires the user ID be present in **configs.userIDs.botOwners**
|
- `BOT_OWNERS` Requires the user ID be present in **configs.userIDs.botOwners**
|
||||||
|
|
||||||
Any of these can be easily modified, in their files. Let's go ahead and try and modify one of the permission levels as an example.
|
Any of these can be easily modified, in their files. Let's go ahead and try and
|
||||||
|
modify one of the permission levels as an example.
|
||||||
|
|
||||||
## Modifying Existing Permission Levels
|
## Modifying Existing Permission Levels
|
||||||
|
|
||||||
If you have a bot support server, you might also have a role setup for your bot's support team. So to make life easier, it would be nice if someone with that role could use the command without having to modify the configs file every time.
|
If you have a bot support server, you might also have a role setup for your
|
||||||
|
bot's support team. So to make life easier, it would be nice if someone with
|
||||||
|
that role could use the command without having to modify the configs file every
|
||||||
|
time.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -39,7 +49,7 @@ botCache.permissionLevels.set(
|
|||||||
PermissionLevels.BOT_SUPPORT,
|
PermissionLevels.BOT_SUPPORT,
|
||||||
async (message) => {
|
async (message) => {
|
||||||
// If the user id exists in the configs allow the command
|
// If the user id exists in the configs allow the command
|
||||||
if (configs.userIDs.botSupporters.includes(message.author.id)) return true
|
if (configs.userIDs.botSupporters.includes(message.author.id)) return true;
|
||||||
|
|
||||||
// The users id was not in the configs, check if they have the role in bot server
|
// The users id was not in the configs, check if they have the role in bot server
|
||||||
|
|
||||||
@@ -59,29 +69,33 @@ botCache.permissionLevels.set(
|
|||||||
PermissionLevels.BOT_SUPPORT,
|
PermissionLevels.BOT_SUPPORT,
|
||||||
async (message) => {
|
async (message) => {
|
||||||
// If the user id exists in the configs allow the command
|
// If the user id exists in the configs allow the command
|
||||||
if (configs.userIDs.botSupporters.includes(message.author.id)) return true
|
if (configs.userIDs.botSupporters.includes(message.author.id)) return true;
|
||||||
|
|
||||||
// The users id was not in the configs, check if they have the role in bot server
|
// The users id was not in the configs, check if they have the role in bot server
|
||||||
|
|
||||||
// Get the bots support server
|
// Get the bots support server
|
||||||
const guild = cache.guilds.get(configs.supportServerID)
|
const guild = cache.guilds.get(configs.supportServerID);
|
||||||
if (!guild) return false
|
if (!guild) return false;
|
||||||
|
|
||||||
// If the user is not a member of the support server they can't be one of the support staff.
|
// If the user is not a member of the support server they can't be one of the support staff.
|
||||||
const member = guild.members.get(message.author.id)
|
const member = guild.members.get(message.author.id);
|
||||||
if (!member) return false;
|
if (!member) return false;
|
||||||
|
|
||||||
// If they have the role allow the command otherwise it will be false and block the command.
|
// If they have the role allow the command otherwise it will be false and block the command.
|
||||||
return member.roles.includes('BOT_SUPPORT_ROLE_ID_HERE')
|
return member.roles.includes("BOT_SUPPORT_ROLE_ID_HERE");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome. You just managed to modify one of the existing permission levels. But what if we wanted to create our own custom permission level. Let's do that next.
|
Awesome. You just managed to modify one of the existing permission levels. But
|
||||||
|
what if we wanted to create our own custom permission level. Let's do that next.
|
||||||
|
|
||||||
## Creating A Permission Level
|
## Creating A Permission Level
|
||||||
|
|
||||||
Let's create a permission level for the purposes of this guide so we can learn how to create one. For example, let's say we wanted a permission level for users that have boosted the server. We want it to check if the user has a role called Nitro Booster.
|
Let's create a permission level for the purposes of this guide so we can learn
|
||||||
|
how to create one. For example, let's say we wanted a permission level for users
|
||||||
|
that have boosted the server. We want it to check if the user has a role called
|
||||||
|
Nitro Booster.
|
||||||
|
|
||||||
First, we need to update the Permission Levels enum in `src/types/commands.ts`.
|
First, we need to update the Permission Levels enum in `src/types/commands.ts`.
|
||||||
|
|
||||||
@@ -97,7 +111,8 @@ export enum PermissionLevels {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's add a new one for a Nitro Booster role. You can add it in any order here you like. The specific order does not matter.
|
Let's add a new one for a Nitro Booster role. You can add it in any order here
|
||||||
|
you like. The specific order does not matter.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export enum PermissionLevels {
|
export enum PermissionLevels {
|
||||||
@@ -112,7 +127,9 @@ export enum PermissionLevels {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Once that's done, we can go and create the code for it. Now, lets create a new file in `permissionLevels` folder called `booster.ts` and paste the base snippet for a permission level below.
|
Once that's done, we can go and create the code for it. Now, lets create a new
|
||||||
|
file in `permissionLevels` folder called `booster.ts` and paste the base snippet
|
||||||
|
for a permission level below.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -123,14 +140,16 @@ botCache.permissionLevels.set(
|
|||||||
PermissionLevels,
|
PermissionLevels,
|
||||||
async (message) => {
|
async (message) => {
|
||||||
// Code goes here
|
// Code goes here
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE:** You will see an error in the `PermissionLevels,` line because we need to select one of the permission levels. In this case we want the NITRO_BOOSTER level we just created above.
|
> **NOTE:** You will see an error in the `PermissionLevels,` line because we
|
||||||
|
> need to select one of the permission levels. In this case we want the
|
||||||
|
> NITRO_BOOSTER level we just created above.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
PermissionLevels.NITRO_BOOSTER
|
PermissionLevels.NITRO_BOOSTER;
|
||||||
```
|
```
|
||||||
|
|
||||||
Next we can add the code in place.
|
Next we can add the code in place.
|
||||||
@@ -146,22 +165,26 @@ botCache.permissionLevels.set(
|
|||||||
const guild = message.guild();
|
const guild = message.guild();
|
||||||
if (!guild) return false;
|
if (!guild) return false;
|
||||||
|
|
||||||
const member = message.member()
|
const member = message.member();
|
||||||
if (!member) return false;
|
if (!member) return false;
|
||||||
|
|
||||||
const boosterRole = guild.roles.find(role => role.name.toLowerCase() === 'nitro booster')
|
const boosterRole = guild.roles.find((role) =>
|
||||||
|
role.name.toLowerCase() === "nitro booster"
|
||||||
|
);
|
||||||
if (!boosterRole) return false;
|
if (!boosterRole) return false;
|
||||||
|
|
||||||
return member.roles.includes(boosterRole.id)
|
return member.roles.includes(boosterRole.id);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome! You just created your very own permission level. Now let's check out how to use permission levels.
|
Awesome! You just created your very own permission level. Now let's check out
|
||||||
|
how to use permission levels.
|
||||||
|
|
||||||
## Using Permission Levels
|
## Using Permission Levels
|
||||||
|
|
||||||
There are two ways to use permission levels. You can provide an array of PermissionLevels or you can provide a custom function.
|
There are two ways to use permission levels. You can provide an array of
|
||||||
|
PermissionLevels or you can provide a custom function.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createCommand({
|
createCommand({
|
||||||
@@ -170,19 +193,23 @@ createCommand({
|
|||||||
botChannelPermissions: ["SEND_MESSAGES"],
|
botChannelPermissions: ["SEND_MESSAGES"],
|
||||||
```
|
```
|
||||||
|
|
||||||
As an example the `reload` command requires the user to be a bot owner to use this command.
|
As an example the `reload` command requires the user to be a bot owner to use
|
||||||
|
this command.
|
||||||
|
|
||||||
**ONLY ONE LEVEL IS NECESSARY TO ALLOW THE COMMAND.**
|
**ONLY ONE LEVEL IS NECESSARY TO ALLOW THE COMMAND.**
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
[PermissionLevels.MODERATOR, PermissionLevels.ADMIN]
|
[PermissionLevels.MODERATOR, PermissionLevels.ADMIN];
|
||||||
```
|
```
|
||||||
|
|
||||||
If you provide an array of permission levels, only one of these is necessary to run the command. For example, if the user passes MODERATOR, they do not need ADMIN to run it.
|
If you provide an array of permission levels, only one of these is necessary to
|
||||||
|
run the command. For example, if the user passes MODERATOR, they do not need
|
||||||
|
ADMIN to run it.
|
||||||
|
|
||||||
### Advanced Perm Levels
|
### Advanced Perm Levels
|
||||||
|
|
||||||
There is another way to use permission levels. You can provide a custom function that must return a boolean. For example,
|
There is another way to use permission levels. You can provide a custom function
|
||||||
|
that must return a boolean. For example,
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createCommand({
|
createCommand({
|
||||||
@@ -193,11 +220,13 @@ createCommand({
|
|||||||
botChannelPermissions: ["SEND_MESSAGES"],
|
botChannelPermissions: ["SEND_MESSAGES"],
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The function is able to take 3 arguments. `message`, `command` and `guild`.
|
The function is able to take 3 arguments. `message`, `command` and `guild`.
|
||||||
|
|
||||||
You can use an async function if you like as well.
|
You can use an async function if you like as well.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Permission levels are an extremely powerful feature that can help make creating bots a lot easier. Discordeno provides extremely flexible permission levels. As a side benefit, Permission Levels are designed to be hot reloadable from the reload command. 🎉
|
Permission levels are an extremely powerful feature that can help make creating
|
||||||
|
bots a lot easier. Discordeno provides extremely flexible permission levels. As
|
||||||
|
a side benefit, Permission Levels are designed to be hot reloadable from the
|
||||||
|
reload command. 🎉
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
# Subcommands Inside Subcommands!
|
# Subcommands Inside Subcommands!
|
||||||
|
|
||||||
One of the most powerful things about Discordeno is it's ability to have infinite subcommands. A command can have a subcommand which can have it's own subcommands... Mind not blown yet? Wait for it.... A `subcommand` is a full `command`! What that means is, every subcommand has the full power and flexibility of any command. BOOM! Mind blown!
|
One of the most powerful things about Discordeno is it's ability to have
|
||||||
|
infinite subcommands. A command can have a subcommand which can have it's own
|
||||||
|
subcommands... Mind not blown yet? Wait for it.... A `subcommand` is a full
|
||||||
|
`command`! What that means is, every subcommand has the full power and
|
||||||
|
flexibility of any command. BOOM! Mind blown!
|
||||||
|
|
||||||
A subcommand can have it's own settings for example, you can allow 1 subcommand inside a guild and the other in dm. You can allow 1 subcommand to be NSFW and the other to be SFW. You can have different permissions requirements for each. You can set custom permission levels for each so that certain subcommands could only be done by mods/admins. Each subcommand also has it's own arguments meaning you can require different things for each and have the best experience possible when making your bot.
|
A subcommand can have it's own settings for example, you can allow 1 subcommand
|
||||||
|
inside a guild and the other in dm. You can allow 1 subcommand to be NSFW and
|
||||||
|
the other to be SFW. You can have different permissions requirements for each.
|
||||||
|
You can set custom permission levels for each so that certain subcommands could
|
||||||
|
only be done by mods/admins. Each subcommand also has it's own arguments meaning
|
||||||
|
you can require different things for each and have the best experience possible
|
||||||
|
when making your bot.
|
||||||
|
|
||||||
## Basic Example
|
## Basic Example
|
||||||
|
|
||||||
Let's start by understanding the `prefix` command which uses subcommands. It is a good basic example, to help us understand how to create a subcommand.
|
Let's start by understanding the `prefix` command which uses subcommands. It is
|
||||||
|
a good basic example, to help us understand how to create a subcommand.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
import { PermissionLevels } from "../types/commands.ts";
|
import { PermissionLevels } from "../types/commands.ts";
|
||||||
import { sendResponse, sendEmbed, createSubcommand, createCommand } from "../utils/helpers.ts";
|
import {
|
||||||
|
createCommand,
|
||||||
|
createSubcommand,
|
||||||
|
sendEmbed,
|
||||||
|
sendResponse,
|
||||||
|
} from "../utils/helpers.ts";
|
||||||
import { parsePrefix } from "../monitors/commandHandler.ts";
|
import { parsePrefix } from "../monitors/commandHandler.ts";
|
||||||
import { Embed } from "../utils/Embed.ts";
|
import { Embed } from "../utils/Embed.ts";
|
||||||
|
|
||||||
@@ -75,7 +91,9 @@ createSubcommand("prefix", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's separate this to understand what is happening here. The first half of the code is the main command. This is like any other command. However, the important thing is in the arguments we requested a subcommand!
|
Let's separate this to understand what is happening here. The first half of the
|
||||||
|
code is the main command. This is like any other command. However, the important
|
||||||
|
thing is in the arguments we requested a subcommand!
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createCommand({
|
createCommand({
|
||||||
@@ -84,7 +102,7 @@ createCommand({
|
|||||||
{
|
{
|
||||||
name: "sub commmand",
|
name: "sub commmand",
|
||||||
type: "subcommand",
|
type: "subcommand",
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
guildOnly: true,
|
guildOnly: true,
|
||||||
@@ -141,26 +159,46 @@ createSubcommand("prefix", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
`createSubcommand` takes 2 arguments. The first is the command name and the second is a full Command object.
|
`createSubcommand` takes 2 arguments. The first is the command name and the
|
||||||
The command is just like any other command you will make. You can customize this as you like as you would any other.
|
second is a full Command object. The command is just like any other command you
|
||||||
|
will make. You can customize this as you like as you would any other.
|
||||||
|
|
||||||
What this does is it creates a subcommand in the `prefix` command. This subcommand will be called `set` so the user will have to type `!prefix set` in order to trigger this command. This subcommand also takes a required argument that will be a string. In this case, that means the user has to type something like `!prefix set $` to change their prefix.
|
What this does is it creates a subcommand in the `prefix` command. This
|
||||||
|
subcommand will be called `set` so the user will have to type `!prefix set` in
|
||||||
|
order to trigger this command. This subcommand also takes a required argument
|
||||||
|
that will be a string. In this case, that means the user has to type something
|
||||||
|
like `!prefix set $` to change their prefix.
|
||||||
|
|
||||||
*POP QUIZ TIME*
|
_POP QUIZ TIME_
|
||||||
|
|
||||||
Alright, that sounds good so far. Now let's take a step back. What would happen if the user typed `!prefix` without the `set` keyword? This would trigger the main commands execute. Did you notice that in the main command the subcommand argument has `required: false`. This makes it so that the user can execute a command without a subcommand.
|
Alright, that sounds good so far. Now let's take a step back. What would happen
|
||||||
|
if the user typed `!prefix` without the `set` keyword? This would trigger the
|
||||||
|
main commands execute. Did you notice that in the main command the subcommand
|
||||||
|
argument has `required: false`. This makes it so that the user can execute a
|
||||||
|
command without a subcommand.
|
||||||
|
|
||||||
## Default Subcommands
|
## Default Subcommands
|
||||||
|
|
||||||
On occassion you may want to trigger a default subcommand. This is very simple. In your subcommand argument, just set the defaultValue as the default subcommand you wish to trigger.
|
On occassion you may want to trigger a default subcommand. This is very simple.
|
||||||
|
In your subcommand argument, just set the defaultValue as the default subcommand
|
||||||
|
you wish to trigger.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
arguments: [
|
arguments:
|
||||||
{ name: 'subcommand', type: 'subcommand', defaultValue: 'set', required: false }
|
[
|
||||||
]
|
{
|
||||||
|
name: "subcommand",
|
||||||
|
type: "subcommand",
|
||||||
|
defaultValue: "set",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
If the user now typed `!prefix`, it would automatically execute the `prefix set` command. Using a default subcommand isn't so helpful in the prefix command but imagine other commands where you may want to list the items to the user. For example, if you had a reactiorole command.
|
If the user now typed `!prefix`, it would automatically execute the `prefix set`
|
||||||
|
command. Using a default subcommand isn't so helpful in the prefix command but
|
||||||
|
imagine other commands where you may want to list the items to the user. For
|
||||||
|
example, if you had a reactiorole command.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
!reactionroles create
|
!reactionroles create
|
||||||
@@ -169,13 +207,19 @@ If the user now typed `!prefix`, it would automatically execute the `prefix set`
|
|||||||
!reactionroles list
|
!reactionroles list
|
||||||
```
|
```
|
||||||
|
|
||||||
This is where the default subcommands can shine, making it easier for your users to trigger the commands they want.
|
This is where the default subcommands can shine, making it easier for your users
|
||||||
|
to trigger the commands they want.
|
||||||
|
|
||||||
## Subcommand In A Subcommand
|
## Subcommand In A Subcommand
|
||||||
|
|
||||||
Now, let's see how crazy we can get with this. What if we wanted a subcommand inside our subcommand. Is that possible? What did Bob The Builder always say? **YES WE CAN!!!**
|
Now, let's see how crazy we can get with this. What if we wanted a subcommand
|
||||||
|
inside our subcommand. Is that possible? What did Bob The Builder always say?
|
||||||
|
**YES WE CAN!!!**
|
||||||
|
|
||||||
This is really helpful when you are creating a `settings` command which can help users configure your bot. You might have a bot like mine with 300 different features and 1000 different settings. For this, you will want the power and flexibility of Discordeno!
|
This is really helpful when you are creating a `settings` command which can help
|
||||||
|
users configure your bot. You might have a bot like mine with 300 different
|
||||||
|
features and 1000 different settings. For this, you will want the power and
|
||||||
|
flexibility of Discordeno!
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
!settings feedback suggestions on
|
!settings feedback suggestions on
|
||||||
@@ -187,7 +231,8 @@ This is really helpful when you are creating a `settings` command which can help
|
|||||||
!settings gooodbye channel #channel
|
!settings gooodbye channel #channel
|
||||||
```
|
```
|
||||||
|
|
||||||
To create a subcommand inside a subcommand, `name` in the createSubcommand() function must be as follows:
|
To create a subcommand inside a subcommand, `name` in the createSubcommand()
|
||||||
|
function must be as follows:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createSubcommand("settings", { name: "feedback" })
|
createSubcommand("settings", { name: "feedback" })
|
||||||
@@ -201,18 +246,30 @@ createSubcommand("settings-goodbye", { name: "channel" })
|
|||||||
createSubcommand("settings-goodbye", { name: "message" })
|
createSubcommand("settings-goodbye", { name: "message" })
|
||||||
```
|
```
|
||||||
|
|
||||||
You can nest subcommands inside subcommands to provide the functionality to the user. Before, I was able to do this, my settings command was literally thousands of lines long and I had headaches just wanting to open it. Trying to handle 1000s of different settings was a huge pain. So now, I have subcommands inside subcommands. Why? Because you can **separate subcommands into separate files**!!! That's right, those thousands of lines of code now are modularized making maintainability much better. There are a lot less chances of bugs arising. Adding/removing/modifying settings is also incredibly simpler.
|
You can nest subcommands inside subcommands to provide the functionality to the
|
||||||
|
user. Before, I was able to do this, my settings command was literally thousands
|
||||||
|
of lines long and I had headaches just wanting to open it. Trying to handle
|
||||||
|
1000s of different settings was a huge pain. So now, I have subcommands inside
|
||||||
|
subcommands. Why? Because you can **separate subcommands into separate
|
||||||
|
files**!!! That's right, those thousands of lines of code now are modularized
|
||||||
|
making maintainability much better. There are a lot less chances of bugs
|
||||||
|
arising. Adding/removing/modifying settings is also incredibly simpler.
|
||||||
|
|
||||||
## Subcommands Also Have Aliases
|
## Subcommands Also Have Aliases
|
||||||
|
|
||||||
Another powerful part about subcommands is that they also have their own aliases. This means you can create different ways to trigger a subcommand. For example, earlier we saw a subcommand `suggestions` of another subcommand `feedback` of a command `settings`. What if we wanted to allow users to type `ideas` for `suggestions`? Let's add some aliases to the subcommand to see the magic!
|
Another powerful part about subcommands is that they also have their own
|
||||||
|
aliases. This means you can create different ways to trigger a subcommand. For
|
||||||
|
example, earlier we saw a subcommand `suggestions` of another subcommand
|
||||||
|
`feedback` of a command `settings`. What if we wanted to allow users to type
|
||||||
|
`ideas` for `suggestions`? Let's add some aliases to the subcommand to see the
|
||||||
|
magic!
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createSubcommand("settings-feedback", {
|
createSubcommand("settings-feedback", {
|
||||||
name: "suggestions",
|
name: "suggestions",
|
||||||
aliases: ["ideas", "sugg", "s"],
|
aliases: ["ideas", "sugg", "s"],
|
||||||
// The rest of the command stuff below here!
|
// The rest of the command stuff below here!
|
||||||
})
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+171
-102
@@ -2,99 +2,126 @@
|
|||||||
|
|
||||||
## Understanding The Goals of This Guide
|
## Understanding The Goals of This Guide
|
||||||
|
|
||||||
This guide is not intended to trash or hate on Discord.JS. In fact, Discord.JS is the most popular Node.JS library which is why most users wanting to use Discordeno come from Discord.JS. Today, I had a user ask me for a guide to convert a Discord.JS bot to Discordeno. That was the start of this guide.
|
This guide is not intended to trash or hate on Discord.JS. In fact, Discord.JS
|
||||||
|
is the most popular Node.JS library which is why most users wanting to use
|
||||||
|
Discordeno come from Discord.JS. Today, I had a user ask me for a guide to
|
||||||
|
convert a Discord.JS bot to Discordeno. That was the start of this guide.
|
||||||
|
|
||||||
## Finding A Open Source Bot
|
## Finding A Open Source Bot
|
||||||
|
|
||||||
For the purposes of this guide, I wanted to find a moderation bot that is totally open source to show an example of how to convert the bot to Discordeno. Trying to find one was not easy as most bot's were not using the latest Discord.JS version 12. Trying to find one that was using TypeScript made it even more difficult. My next best solution was to find a moderation bot that was recently updated(showing it is maintained or recently built). The best one I could find was [Zodiac Bot](https://github.com/Nukestye/Zodiac).
|
For the purposes of this guide, I wanted to find a moderation bot that is
|
||||||
|
totally open source to show an example of how to convert the bot to Discordeno.
|
||||||
|
Trying to find one was not easy as most bot's were not using the latest
|
||||||
|
Discord.JS version 12. Trying to find one that was using TypeScript made it even
|
||||||
|
more difficult. My next best solution was to find a moderation bot that was
|
||||||
|
recently updated(showing it is maintained or recently built). The best one I
|
||||||
|
could find was [Zodiac Bot](https://github.com/Nukestye/Zodiac).
|
||||||
|
|
||||||
For the purposes of this guide, I will be using the current [latest commit](https://github.com/Nukestye/Zodiac/tree/213891a38af1b7ecbd068b661ef9062ab58cc818)
|
For the purposes of this guide, I will be using the current
|
||||||
|
[latest commit](https://github.com/Nukestye/Zodiac/tree/213891a38af1b7ecbd068b661ef9062ab58cc818)
|
||||||
|
|
||||||
## Preparations
|
## Preparations
|
||||||
|
|
||||||
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template) I will name it Zodiac.
|
- First, create a Discordeno Bot using the
|
||||||
|
[Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template)
|
||||||
|
I will name it Zodiac.
|
||||||
|
|
||||||
- Then `git clone https://github.com/Skillz4Killz/Zodiac.git`
|
- Then `git clone https://github.com/Skillz4Killz/Zodiac.git`
|
||||||
|
|
||||||
Now that I had the repository cloned, I could begin. Note that although the bot we are converting is built in JavaScript, I converted all code to TypeScript in this Guide as Discordeno is designed to be the best lib for TypeScript developers.
|
Now that I had the repository cloned, I could begin. Note that although the bot
|
||||||
|
we are converting is built in JavaScript, I converted all code to TypeScript in
|
||||||
|
this Guide as Discordeno is designed to be the best lib for TypeScript
|
||||||
|
developers.
|
||||||
|
|
||||||
Time to get started!
|
Time to get started!
|
||||||
|
|
||||||
## Converting main.js (index file)
|
## Converting main.js (index file)
|
||||||
|
|
||||||
The first thing is to convert the `main.js` file which would be the app.js or index.js file. This is the file that is run to start your bot. In this case, the bot developer chose `main.js`. In Deno, the initial file is named `mod.ts` so we can go ahead and opt for the Deno pattern. Note: there is already a `mod.ts` file created and prebuilt entirely using the Generator.
|
The first thing is to convert the `main.js` file which would be the app.js or
|
||||||
|
index.js file. This is the file that is run to start your bot. In this case, the
|
||||||
|
bot developer chose `main.js`. In Deno, the initial file is named `mod.ts` so we
|
||||||
|
can go ahead and opt for the Deno pattern. Note: there is already a `mod.ts`
|
||||||
|
file created and prebuilt entirely using the Generator.
|
||||||
|
|
||||||
Current Discord.JS Code:
|
Current Discord.JS Code:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
/* Keeping this to shoutout/credit the original author <3
|
/* Keeping this to shoutout/credit the original author <3
|
||||||
* @author: nukestye
|
* @author: nukestye
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const config = require('./config.json')
|
const config = require("./config.json");
|
||||||
const fs = require('fs')
|
const fs = require("fs");
|
||||||
const log = console.log
|
const log = console.log;
|
||||||
|
|
||||||
// Setting up the way to get commands
|
// Setting up the way to get commands
|
||||||
const { CommandoClient } = require('discord.js-commando')
|
const { CommandoClient } = require("discord.js-commando");
|
||||||
const path = require('path')
|
const path = require("path");
|
||||||
|
|
||||||
// reading events
|
// reading events
|
||||||
fs.readdir('./src/events/', (err, files) => {
|
fs.readdir("./src/events/", (err, files) => {
|
||||||
if (err) return console.error(err)
|
if (err) return console.error(err);
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
const eventFunction = require(`./src/events/${file}`)
|
const eventFunction = require(`./src/events/${file}`);
|
||||||
if (eventFunction.disabled) return
|
if (eventFunction.disabled) return;
|
||||||
const event = eventFunction.event || file.split('.')[0]
|
const event = eventFunction.event || file.split(".")[0];
|
||||||
const emitter = (typeof eventFunction.emitter === 'string' ? client[eventFunction.emitter] : eventFunction.emitter) || client
|
const emitter = (typeof eventFunction.emitter === "string"
|
||||||
const { once } = eventFunction
|
? client[eventFunction.emitter]
|
||||||
|
: eventFunction.emitter) || client;
|
||||||
|
const { once } = eventFunction;
|
||||||
try {
|
try {
|
||||||
emitter[once ? 'once' : 'on'](event, (...args) => eventFunction.run(...args))
|
emitter[
|
||||||
|
once
|
||||||
|
? "once"
|
||||||
|
: "on"
|
||||||
|
](event, (...args) => eventFunction.run(...args));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error.stack)
|
console.error(error.stack);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const client = global.client = new CommandoClient({
|
const client = global.client = new CommandoClient({
|
||||||
commandPrefix: `${config.prefix}`,
|
commandPrefix: `${config.prefix}`,
|
||||||
owner: `${config.owner}`,
|
owner: `${config.owner}`,
|
||||||
invite: `${config.discord}`,
|
invite: `${config.discord}`,
|
||||||
unknownCommandResponse: false
|
unknownCommandResponse: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
// Registing the commands
|
// Registing the commands
|
||||||
client.registry
|
client.registry
|
||||||
.registerDefaultTypes()
|
.registerDefaultTypes()
|
||||||
// The different fields for cmds
|
// The different fields for cmds
|
||||||
.registerGroups([
|
.registerGroups([
|
||||||
['mod', 'Moderation Commands'],
|
["mod", "Moderation Commands"],
|
||||||
['public', 'Public Commands']
|
["public", "Public Commands"],
|
||||||
])
|
])
|
||||||
.registerDefaultGroups()
|
.registerDefaultGroups()
|
||||||
// Basic cmds can be disabled like {"cmd: false"}
|
// Basic cmds can be disabled like {"cmd: false"}
|
||||||
.registerDefaultCommands()
|
.registerDefaultCommands()
|
||||||
// commands in "/src/commands" will be counted
|
// commands in "/src/commands" will be counted
|
||||||
.registerCommandsIn(path.join(__dirname, '/src/commands'))
|
.registerCommandsIn(path.join(__dirname, "/src/commands"));
|
||||||
|
|
||||||
// list of activities that the bot goes through
|
// list of activities that the bot goes through
|
||||||
const activityArray = [`${config.prefix}help | `]
|
const activityArray = [`${config.prefix}help | `];
|
||||||
// Bot lanuch code
|
// Bot lanuch code
|
||||||
client.once('ready', () => {
|
client.once("ready", () => {
|
||||||
log(`Logged in as ${client.user.tag} in ${client.guilds.size} guild(s)!`)
|
log(`Logged in as ${client.user.tag} in ${client.guilds.size} guild(s)!`);
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const index = Math.floor(Math.random() * (activityArray.length)) // generates a random number between 1 and the length of the activities array list
|
const index = Math.floor(Math.random() * (activityArray.length)); // generates a random number between 1 and the length of the activities array list
|
||||||
client.user.setActivity(
|
client.user.setActivity(
|
||||||
activityArray[index],
|
activityArray[index],
|
||||||
{
|
{
|
||||||
type: 'PLAYING'
|
type: "PLAYING",
|
||||||
}) // sets bot"s activities to one of the phrases in the arraylist.
|
},
|
||||||
}, 5000) // updates every 10000ms = 10s
|
); // sets bot"s activities to one of the phrases in the arraylist.
|
||||||
})
|
}, 5000); // updates every 10000ms = 10s
|
||||||
|
});
|
||||||
// If an error print it out
|
// If an error print it out
|
||||||
client.on('error', console.error)
|
client.on("error", console.error);
|
||||||
|
|
||||||
// Login in using the token in config
|
// Login in using the token in config
|
||||||
client.login(config.env.TOKEN)
|
client.login(config.env.TOKEN);
|
||||||
```
|
```
|
||||||
|
|
||||||
Discordeno Version:
|
Discordeno Version:
|
||||||
@@ -146,18 +173,21 @@ startBot({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Something we haven't converted yet from the `main.js` files is the event listeners. To do that, we will open up the events folder and find the corresponding event or create it if necessary. In this case, we have the `ready` event and there is already a `ready.ts` file. We can just use that.
|
Something we haven't converted yet from the `main.js` files is the event
|
||||||
|
listeners. To do that, we will open up the events folder and find the
|
||||||
|
corresponding event or create it if necessary. In this case, we have the `ready`
|
||||||
|
event and there is already a `ready.ts` file. We can just use that.
|
||||||
|
|
||||||
In our `ready.ts` file we can add the `ready` event listener.
|
In our `ready.ts` file we can add the `ready` event listener.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {
|
import {
|
||||||
|
ActivityType,
|
||||||
botCache,
|
botCache,
|
||||||
cache,
|
cache,
|
||||||
|
chooseRandom,
|
||||||
editBotsStatus,
|
editBotsStatus,
|
||||||
StatusTypes,
|
StatusTypes,
|
||||||
ActivityType,
|
|
||||||
chooseRandom
|
|
||||||
} from "../../deps.ts";
|
} from "../../deps.ts";
|
||||||
import { registerTasks } from "./../utils/taskHelper.ts";
|
import { registerTasks } from "./../utils/taskHelper.ts";
|
||||||
|
|
||||||
@@ -165,7 +195,7 @@ botCache.eventHandlers.ready = function () {
|
|||||||
editBotsStatus(
|
editBotsStatus(
|
||||||
StatusTypes.DoNotDisturb,
|
StatusTypes.DoNotDisturb,
|
||||||
"Discordeno Best Lib",
|
"Discordeno Best Lib",
|
||||||
ActivityType.Game
|
ActivityType.Game,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(`Loaded ${botCache.arguments.size} Argument(s)`);
|
console.log(`Loaded ${botCache.arguments.size} Argument(s)`);
|
||||||
@@ -178,78 +208,92 @@ botCache.eventHandlers.ready = function () {
|
|||||||
registerTasks();
|
registerTasks();
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[READY] Bot is online and ready in ${cache.guilds.size} guild(s)!`
|
`[READY] Bot is online and ready in ${cache.guilds.size} guild(s)!`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// list of activities that the bot goes through
|
// list of activities that the bot goes through
|
||||||
const activityArray = [`${configs.prefix}help | `];
|
const activityArray = [`${configs.prefix}help | `];
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
editBotsStatus(StatusType.Online, chooseRandom(activityArray), ActivityType.Game)
|
editBotsStatus(
|
||||||
}, 5000)
|
StatusType.Online,
|
||||||
|
chooseRandom(activityArray),
|
||||||
|
ActivityType.Game,
|
||||||
|
);
|
||||||
|
}, 5000);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
To understand this code, we are setting a function to be run when the bot is `ready`. Then the bot will edit the bots status every 5 seconds. Notice, that Discordeno provides a nice clean util function to choose a random item from an array. You also have beautiful enums provided that prevent you from making any typos/mistakes.
|
To understand this code, we are setting a function to be run when the bot is
|
||||||
|
`ready`. Then the bot will edit the bots status every 5 seconds. Notice, that
|
||||||
|
Discordeno provides a nice clean util function to choose a random item from an
|
||||||
|
array. You also have beautiful enums provided that prevent you from making any
|
||||||
|
typos/mistakes.
|
||||||
|
|
||||||
We have now converted the entire `main.js` file, in a matter of seconds. The Discordeno official generator took care of the majority of workload and we just modified the `ready.ts` file.
|
We have now converted the entire `main.js` file, in a matter of seconds. The
|
||||||
|
Discordeno official generator took care of the majority of workload and we just
|
||||||
|
modified the `ready.ts` file.
|
||||||
|
|
||||||
`Note:` I did remove some generally well known "bad practices" such as global vars and such. Overall, you will see the functionality of the project will not change as we progress through this guide.
|
`Note:` I did remove some generally well known "bad practices" such as global
|
||||||
|
vars and such. Overall, you will see the functionality of the project will not
|
||||||
|
change as we progress through this guide.
|
||||||
|
|
||||||
## Converting Commands
|
## Converting Commands
|
||||||
|
|
||||||
The first command in the commands folder is the `addRole` command.
|
The first command in the commands folder is the `addRole` command.
|
||||||
|
|
||||||
This is the code from the bot:
|
This is the code from the bot:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// Getting the 'Command' features from Commando
|
// Getting the 'Command' features from Commando
|
||||||
const { Command } = require('discord.js-commando')
|
const { Command } = require("discord.js-commando");
|
||||||
|
|
||||||
// Code for the command
|
// Code for the command
|
||||||
module.exports = class addRoleCommand extends Command {
|
module.exports = class addRoleCommand extends Command {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
super(client, {
|
super(client, {
|
||||||
// name of the command, must be in lowercase
|
// name of the command, must be in lowercase
|
||||||
name: 'addrole',
|
name: "addrole",
|
||||||
// other ways to call the command, must be in lowercase
|
// other ways to call the command, must be in lowercase
|
||||||
aliases: ['role'],
|
aliases: ["role"],
|
||||||
// command group its part of
|
// command group its part of
|
||||||
group: 'mod',
|
group: "mod",
|
||||||
// name within the command group, must be in lowercase
|
// name within the command group, must be in lowercase
|
||||||
memberName: 'addrole',
|
memberName: "addrole",
|
||||||
// Is the description used for 'help' command
|
// Is the description used for 'help' command
|
||||||
description: 'Adds mentioned role to mentioned user.',
|
description: "Adds mentioned role to mentioned user.",
|
||||||
// Prevents it from being used in dms
|
// Prevents it from being used in dms
|
||||||
guildOnly: true,
|
guildOnly: true,
|
||||||
// Permissions, list found here > `discord.js.org/#/docs/main/11.5.1/class/Permissions?scrollTo=s-FLAGS`
|
// Permissions, list found here > `discord.js.org/#/docs/main/11.5.1/class/Permissions?scrollTo=s-FLAGS`
|
||||||
clientPermissions: ['ADMINISTRATOR', 'MANAGE_ROLES'],
|
clientPermissions: ["ADMINISTRATOR", "MANAGE_ROLES"],
|
||||||
userPermissions: ['MANAGE_ROLES'],
|
userPermissions: ["MANAGE_ROLES"],
|
||||||
// Prevents anyone other than owner to use the command
|
// Prevents anyone other than owner to use the command
|
||||||
ownerOnly: false
|
ownerOnly: false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run code goes here
|
// Run code goes here
|
||||||
run(message) {
|
run(message) {
|
||||||
const user = message.mentions.members.first()
|
const user = message.mentions.members.first();
|
||||||
const roleToAdd = message.mentions.roles.first()
|
const roleToAdd = message.mentions.roles.first();
|
||||||
|
|
||||||
// checking to see if the user has the role or not
|
// checking to see if the user has the role or not
|
||||||
if (!(user.roles.find(r => r.name === roleToAdd.name))) {
|
if (!(user.roles.find((r) => r.name === roleToAdd.name))) {
|
||||||
user.addRole(roleToAdd)
|
user.addRole(roleToAdd);
|
||||||
message.channel.send(`${user} has been given the role: ${roleToAdd.name}`)
|
message.channel.send(`${user} has been given the role: ${roleToAdd.name}`)
|
||||||
.then(msg => {
|
.then((msg) => {
|
||||||
msg.delete(5000)
|
msg.delete(5000);
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
message.channel.send(`${user} already has the role: ${roleToAdd.name}`)
|
message.channel.send(`${user} already has the role: ${roleToAdd.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.error(user, roleToAdd, message.member.roles.find(r => r.name === roleToAdd));
|
// console.error(user, roleToAdd, message.member.roles.find(r => r.name === roleToAdd));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This is how to do it with Discordeno:
|
This is how to do it with Discordeno:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createCommand } from "./../../utils/helpers.ts";
|
import { createCommand } from "./../../utils/helpers.ts";
|
||||||
|
|
||||||
@@ -265,93 +309,106 @@ createCommand({
|
|||||||
userServerPermissions: ["MANAGE_ROLES"],
|
userServerPermissions: ["MANAGE_ROLES"],
|
||||||
arguments: [
|
arguments: [
|
||||||
{ name: "member", type: "member" },
|
{ name: "member", type: "member" },
|
||||||
{ name: "role", type: "role" }
|
{ name: "role", type: "role" },
|
||||||
],
|
],
|
||||||
execute: (message, args) => {
|
execute: (message, args) => {
|
||||||
// checking to see if the user has the role or not
|
// checking to see if the user has the role or not
|
||||||
if (!args.member.roles.includes(args.role.id)) {
|
if (!args.member.roles.includes(args.role.id)) {
|
||||||
args.member.addRole(message.guildID, args.role.id)
|
args.member.addRole(message.guildID, args.role.id);
|
||||||
message.sendResponse(`${args.member.mention} has been given the role: ${args.role.name}`, 5);
|
message.sendResponse(
|
||||||
|
`${args.member.mention} has been given the role: ${args.role.name}`,
|
||||||
|
5,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
message.sendResponse(`${args.member.mention} already has the role: ${args.role.name}`)
|
message.sendResponse(
|
||||||
}
|
`${args.member.mention} already has the role: ${args.role.name}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Awesome, that is a full command converted from Discord.JS to Discordeno. See how easy it is! Let's convert one more command to see how to really take full advantage of Discordeno boilerplate and have something amazing.
|
Awesome, that is a full command converted from Discord.JS to Discordeno. See how
|
||||||
|
easy it is! Let's convert one more command to see how to really take full
|
||||||
|
advantage of Discordeno boilerplate and have something amazing.
|
||||||
|
|
||||||
Discord.JS Kick Command Version
|
Discord.JS Kick Command Version
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Getting the 'Command' features from Commando
|
// Getting the 'Command' features from Commando
|
||||||
const { Command } = require('discord.js-commando')
|
const { Command } = require("discord.js-commando");
|
||||||
const { RichEmbed } = require('discord.js')
|
const { RichEmbed } = require("discord.js");
|
||||||
const chalk = require('chalk')
|
const chalk = require("chalk");
|
||||||
const log = console.log
|
const log = console.log;
|
||||||
|
|
||||||
// Code for the command
|
// Code for the command
|
||||||
module.exports = class kickCommand extends Command {
|
module.exports = class kickCommand extends Command {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
super(client, {
|
super(client, {
|
||||||
// name of the command, must be in lowercase
|
// name of the command, must be in lowercase
|
||||||
name: 'kick',
|
name: "kick",
|
||||||
// other ways to call the command, must be in lowercase
|
// other ways to call the command, must be in lowercase
|
||||||
aliases: ['boot', 'tempban'],
|
aliases: ["boot", "tempban"],
|
||||||
// command group its part of
|
// command group its part of
|
||||||
group: 'mod',
|
group: "mod",
|
||||||
// name within the command group, must be in lowercase
|
// name within the command group, must be in lowercase
|
||||||
memberName: 'kick',
|
memberName: "kick",
|
||||||
// Is the description used for 'help' command
|
// Is the description used for 'help' command
|
||||||
description: 'Kick command.',
|
description: "Kick command.",
|
||||||
// adds cooldowns to the command
|
// adds cooldowns to the command
|
||||||
throttling: {
|
throttling: {
|
||||||
// usages in certain time x
|
// usages in certain time x
|
||||||
usages: 1,
|
usages: 1,
|
||||||
// the cooldown
|
// the cooldown
|
||||||
duration: 10
|
duration: 10,
|
||||||
},
|
},
|
||||||
// Prevents it from being used in dms
|
// Prevents it from being used in dms
|
||||||
guildOnly: true,
|
guildOnly: true,
|
||||||
// Permissions, list found here > `discord.js.org/#/docs/main/11.5.1/class/Permissions?scrollTo=s-FLAGS`
|
// Permissions, list found here > `discord.js.org/#/docs/main/11.5.1/class/Permissions?scrollTo=s-FLAGS`
|
||||||
clientPermissions: ['ADMINISTRATOR'],
|
clientPermissions: ["ADMINISTRATOR"],
|
||||||
userPermissions: ['KICK_MEMBERS'],
|
userPermissions: ["KICK_MEMBERS"],
|
||||||
// Prevents anyone other than owner to use the command
|
// Prevents anyone other than owner to use the command
|
||||||
ownerOnly: false
|
ownerOnly: false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run code goes here
|
// Run code goes here
|
||||||
run(message) {
|
run(message) {
|
||||||
const messageArry = message.content.split(' ')
|
const messageArry = message.content.split(" ");
|
||||||
const args = messageArry.slice(1)
|
const args = messageArry.slice(1);
|
||||||
|
|
||||||
const kUser = message.guild.member(message.mentions.users.first() || message.guild.get(args[0]))
|
const kUser = message.guild.member(
|
||||||
if (!kUser) return message.channel.send('User cannot be found!')
|
message.mentions.users.first() || message.guild.get(args[0]),
|
||||||
const kreason = args.join(' ').slice(22)
|
);
|
||||||
|
if (!kUser) return message.channel.send("User cannot be found!");
|
||||||
|
const kreason = args.join(" ").slice(22);
|
||||||
|
|
||||||
// setting up the embed for report/log
|
// setting up the embed for report/log
|
||||||
const kickEmbed = new RichEmbed()
|
const kickEmbed = new RichEmbed()
|
||||||
.setDescription(`Report: ${kUser} Kick`)
|
.setDescription(`Report: ${kUser} Kick`)
|
||||||
.addField('Reason >', `${kreason}`)
|
.addField("Reason >", `${kreason}`)
|
||||||
.addField('Time', message.createdAt)
|
.addField("Time", message.createdAt);
|
||||||
|
|
||||||
const reportchannel = message.guild.channels.find('name', 'report')
|
const reportchannel = message.guild.channels.find("name", "report");
|
||||||
if (!reportchannel) return message.channel.send('*`Report channel cannot be found!`*')
|
if (!reportchannel) {
|
||||||
|
return message.channel.send("*`Report channel cannot be found!`*");
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the message command
|
// Delete the message command
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
message.delete().catch(O_o => {})
|
message.delete().catch((O_o) => {});
|
||||||
// Kick the user with reason
|
// Kick the user with reason
|
||||||
message.guild.member(kUser).kick(kreason)
|
message.guild.member(kUser).kick(kreason);
|
||||||
// sends the kick report into log/report
|
// sends the kick report into log/report
|
||||||
reportchannel.send(kickEmbed)
|
reportchannel.send(kickEmbed);
|
||||||
// Logs the kick into the terminal
|
// Logs the kick into the terminal
|
||||||
log(chalk.red('KICK', chalk.underline.bgBlue(kUser) + '!'))
|
log(chalk.red("KICK", chalk.underline.bgBlue(kUser) + "!"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Discordeno Version
|
Discordeno Version
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createCommand } from "./../../utils/helpers.ts";
|
import { createCommand } from "./../../utils/helpers.ts";
|
||||||
|
|
||||||
@@ -418,8 +475,20 @@ interface KickArgs {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's take a minute and explain the differences here. The first thing you will probably notice is different is the `arguments` property. Discordeno provides the `arguments` property because it provides argument handling/parsing/validating internally. You don't need to be splitting the message content or going through and validating it yourself. All you do is tell Discordeno that you want a member and a reason. It will do the magic and hard work to get you that data before you even run the command. You just do `args.member` and you have access to the full member object. There are a lot more powerful aspects to Discordeno like arguments. Keep diving in and you will find all the wonderful tools available to give you the best developer experience possible.
|
Let's take a minute and explain the differences here. The first thing you will
|
||||||
|
probably notice is different is the `arguments` property. Discordeno provides
|
||||||
|
the `arguments` property because it provides argument
|
||||||
|
handling/parsing/validating internally. You don't need to be splitting the
|
||||||
|
message content or going through and validating it yourself. All you do is tell
|
||||||
|
Discordeno that you want a member and a reason. It will do the magic and hard
|
||||||
|
work to get you that data before you even run the command. You just do
|
||||||
|
`args.member` and you have access to the full member object. There are a lot
|
||||||
|
more powerful aspects to Discordeno like arguments. Keep diving in and you will
|
||||||
|
find all the wonderful tools available to give you the best developer experience
|
||||||
|
possible.
|
||||||
|
|
||||||
### Need More Examples/Help
|
### Need More Examples/Help
|
||||||
|
|
||||||
If you still need more help converting other aspects of your bot please contact me at [Discord](https://discord.com/invite/5vBgXk3UcZ). I will continue adding more examples to this guide as more people request them.
|
If you still need more help converting other aspects of your bot please contact
|
||||||
|
me at [Discord](https://discord.com/invite/5vBgXk3UcZ). I will continue adding
|
||||||
|
more examples to this guide as more people request them.
|
||||||
|
|||||||
+88
-17
@@ -2,54 +2,123 @@
|
|||||||
|
|
||||||
## Does Discordeno Support TypeScript?
|
## Does Discordeno Support TypeScript?
|
||||||
|
|
||||||
Discordeno provides first class support for TypeScript! Since Deno provides support for TypeScript, that also comes into Discordeno. This means you don't need to compile TypeScript before you use it. However, this isn't really why Discordeno is the best library for TypeScript developers. When I developed this library, I was experimenting with a lot of different things and one of them was automated typings.
|
Discordeno provides first class support for TypeScript! Since Deno provides
|
||||||
|
support for TypeScript, that also comes into Discordeno. This means you don't
|
||||||
|
need to compile TypeScript before you use it. However, this isn't really why
|
||||||
|
Discordeno is the best library for TypeScript developers. When I developed this
|
||||||
|
library, I was experimenting with a lot of different things and one of them was
|
||||||
|
automated typings.
|
||||||
|
|
||||||
Whenever I used other libraries, I was always seeing typings being inaccurate or problematic. This is because in any Discord API library, the majority is not used by the library itself so TypeScript doesn't warn the library developers. This makes it extremely likely that those typings become inaccurate or out of date because of simple mistakes like forgetting to update typings. Sometimes libraries will add a property and forget to add that on their typings. This makes it usable for JavaScript developers but not for TypeScript devs. For TypeScript developers, typings are everything! Discordeno treats typings as part of it's code! A breaking change in typings is a breaking change for the library!
|
Whenever I used other libraries, I was always seeing typings being inaccurate or
|
||||||
|
problematic. This is because in any Discord API library, the majority is not
|
||||||
|
used by the library itself so TypeScript doesn't warn the library developers.
|
||||||
|
This makes it extremely likely that those typings become inaccurate or out of
|
||||||
|
date because of simple mistakes like forgetting to update typings. Sometimes
|
||||||
|
libraries will add a property and forget to add that on their typings. This
|
||||||
|
makes it usable for JavaScript developers but not for TypeScript devs. For
|
||||||
|
TypeScript developers, typings are everything! Discordeno treats typings as part
|
||||||
|
of it's code! A breaking change in typings is a breaking change for the library!
|
||||||
|
|
||||||
## How Stable Is Discordeno?
|
## How Stable Is Discordeno?
|
||||||
|
|
||||||
One of the biggest issues with almost every library(I have used) is stability. None of the libraries gave much love and attention to TypeScript developers the way it deserves. Sometimes TypeScript projects would break because breaking changes to typings did not make a MAJOR bump so TypeScript bots in production would break. Sometimes I was personally maintaing the typings because no one else was for that lib. Some libs were pre 1.0 and didn't even have a stable branch/version where I would not have to worry about breaking changes.
|
One of the biggest issues with almost every library(I have used) is stability.
|
||||||
|
None of the libraries gave much love and attention to TypeScript developers the
|
||||||
|
way it deserves. Sometimes TypeScript projects would break because breaking
|
||||||
|
changes to typings did not make a MAJOR bump so TypeScript bots in production
|
||||||
|
would break. Sometimes I was personally maintaing the typings because no one
|
||||||
|
else was for that lib. Some libs were pre 1.0 and didn't even have a stable
|
||||||
|
branch/version where I would not have to worry about breaking changes.
|
||||||
|
|
||||||
This is why I made it one of my foundational goals of this library to have the best stability for TypeScript developers. No matter how small, a breaking change is a breaking change when it affects the public API. I could care less if we end up at version 500. Being afraid to bump a MAJOR because it's a small change or a typing change is a terrible decision as a library maintainer and destroys the experience for end users.
|
This is why I made it one of my foundational goals of this library to have the
|
||||||
|
best stability for TypeScript developers. No matter how small, a breaking change
|
||||||
|
is a breaking change when it affects the public API. I could care less if we end
|
||||||
|
up at version 500. Being afraid to bump a MAJOR because it's a small change or a
|
||||||
|
typing change is a terrible decision as a library maintainer and destroys the
|
||||||
|
experience for end users.
|
||||||
|
|
||||||
## Why Doesn't Discordeno Use Classes or EventEmitter?
|
## Why Doesn't Discordeno Use Classes or EventEmitter?
|
||||||
|
|
||||||
This is a design decision for the lib itself. You can still use class if you want on your bot. In fact, I hope someone makes a framework/boilerplate for this lib one day using classes so that devs have a choice on which style they prefer. Without trying to write an entire thesis statement on the reasons why I avoided Classes in this lib, I will just link to the best resources I believe help explain it.
|
This is a design decision for the lib itself. You can still use class if you
|
||||||
|
want on your bot. In fact, I hope someone makes a framework/boilerplate for this
|
||||||
|
lib one day using classes so that devs have a choice on which style they prefer.
|
||||||
|
Without trying to write an entire thesis statement on the reasons why I avoided
|
||||||
|
Classes in this lib, I will just link to the best resources I believe help
|
||||||
|
explain it.
|
||||||
|
|
||||||
- [Really good article](https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/)
|
- [Really good article](https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/)
|
||||||
- [Lecture by one of the developers who makes JavaScript](https://www.youtube.com/watch?v=PSGEjv3Tqo0)
|
- [Lecture by one of the developers who makes
|
||||||
|
JavaScript](https://www.youtube.com/watch?v=PSGEjv3Tqo0)
|
||||||
|
|
||||||
In regards to EventEmitter, I believe a functional event API was a much better choice. EventEmitter at it's core is simply just functions that run when a certain event is emitted. In Discordeno, that function is executed instead of emitting some event to trigger that function.
|
In regards to EventEmitter, I believe a functional event API was a much better
|
||||||
|
choice. EventEmitter at it's core is simply just functions that run when a
|
||||||
|
certain event is emitted. In Discordeno, that function is executed instead of
|
||||||
|
emitting some event to trigger that function.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// EventEmitter Example
|
// EventEmitter Example
|
||||||
EventEmitter.emit('guildCreate', guild);
|
EventEmitter.emit("guildCreate", guild);
|
||||||
// Discordeno Example
|
// Discordeno Example
|
||||||
eventHandlers.guildCreate?.(guild);
|
eventHandlers.guildCreate?.(guild);
|
||||||
```
|
```
|
||||||
There isn't really any difference especially for users when they use it. One bad thing about EventEmitter is that if misused it can easily cause memory leaks. It is very easy to open yourself up to these memory leak issues. It has happened to me when I started coding as well. This is why I wanted Discordeno's implementation to help devs avoid the issues I had. It prevents anyone from having this as a potential issue. Another issue with EventEmitter is trying to update the code in those functions without having to deal with headaches left and right of removing and adding listeners. You don't need to worry about binding or not binding events. They are just pure functions
|
|
||||||
|
|
||||||
In Discordeno, this is extremely simple, you just simply give it the new event handlers.
|
There isn't really any difference especially for users when they use it. One bad
|
||||||
|
thing about EventEmitter is that if misused it can easily cause memory leaks. It
|
||||||
|
is very easy to open yourself up to these memory leak issues. It has happened to
|
||||||
|
me when I started coding as well. This is why I wanted Discordeno's
|
||||||
|
implementation to help devs avoid the issues I had. It prevents anyone from
|
||||||
|
having this as a potential issue. Another issue with EventEmitter is trying to
|
||||||
|
update the code in those functions without having to deal with headaches left
|
||||||
|
and right of removing and adding listeners. You don't need to worry about
|
||||||
|
binding or not binding events. They are just pure functions
|
||||||
|
|
||||||
|
In Discordeno, this is extremely simple, you just simply give it the new event
|
||||||
|
handlers.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
updateEventHandlers(newEventHandlers)
|
updateEventHandlers(newEventHandlers);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Why Do You Have A Class for Collection If Classes Are Bad?
|
## Why Do You Have A Class for Collection If Classes Are Bad?
|
||||||
|
|
||||||
The Collection class is an exception in the library where a class was allowed. This is because Collection extends Map. The Map class is provided by JavaScript itself and is extremely fast. You can perform millions of operations a second with a Map. Maps are too useful to avoid and don't have downsides like EventEmitters do. The Collection class simply adds on other functionality that Discordeno users felt they needed. Although I am against using classes whenever possible, I am also a big supporter of providing the best developer experience.
|
The Collection class is an exception in the library where a class was allowed.
|
||||||
|
This is because Collection extends Map. The Map class is provided by JavaScript
|
||||||
|
itself and is extremely fast. You can perform millions of operations a second
|
||||||
|
with a Map. Maps are too useful to avoid and don't have downsides like
|
||||||
|
EventEmitters do. The Collection class simply adds on other functionality that
|
||||||
|
Discordeno users felt they needed. Although I am against using classes whenever
|
||||||
|
possible, I am also a big supporter of providing the best developer experience.
|
||||||
|
|
||||||
## Why Are there no options in Discordeno?
|
## Why Are there no options in Discordeno?
|
||||||
|
|
||||||
Discordeno is not a library that handles code in the exact way every person wants it to. It is opinionated. Discordeno defaults to the Discord recommended options or the best options for majority of developers needs. For example, there is no option of fetching all members startup. This is a practice that Discord does not recommend or want users doing. By default, we don't support stuff like this. In Discordeno, we follow Discords recommended solution and it just works internally. The End! No fuss! No Muss! Just good stuff!
|
Discordeno is not a library that handles code in the exact way every person
|
||||||
|
wants it to. It is opinionated. Discordeno defaults to the Discord recommended
|
||||||
|
options or the best options for majority of developers needs. For example, there
|
||||||
|
is no option of fetching all members startup. This is a practice that Discord
|
||||||
|
does not recommend or want users doing. By default, we don't support stuff like
|
||||||
|
this. In Discordeno, we follow Discords recommended solution and it just works
|
||||||
|
internally. The End! No fuss! No Muss! Just good stuff!
|
||||||
|
|
||||||
Now, I understand that there are times when it's necessary to be able to customize this and fetch them all. If you are advanced enough to need these options, you should be able to simply do it yourself. For most users, this is just an unnecessary option. The main module should remain minimalistic and easy to use for 99% of users.
|
Now, I understand that there are times when it's necessary to be able to
|
||||||
|
customize this and fetch them all. If you are advanced enough to need these
|
||||||
|
options, you should be able to simply do it yourself. For most users, this is
|
||||||
|
just an unnecessary option. The main module should remain minimalistic and easy
|
||||||
|
to use for 99% of users.
|
||||||
|
|
||||||
## Why Do I See errors Like "MISSING_VIEW_CHANNEL" or "BOTS_HIGHEST_ROLE_TOO_LOW"?
|
## Why Do I See errors Like "MISSING_VIEW_CHANNEL" or "BOTS_HIGHEST_ROLE_TOO_LOW"?
|
||||||
|
|
||||||
Discordeno is the only library(that I have used), that has built in permission handling. A lot of bots get automatically banned by Discord because they forget to handle permissions. When bots don't check permissions and continue to send requests to the API, this leads to bots being banned. I have tried to request adding this feature into libraries but they were reluctant to do so because it would require the devs to maintain the library whenever an update was made by Discord.
|
Discordeno is the only library(that I have used), that has built in permission
|
||||||
|
handling. A lot of bots get automatically banned by Discord because they forget
|
||||||
|
to handle permissions. When bots don't check permissions and continue to send
|
||||||
|
requests to the API, this leads to bots being banned. I have tried to request
|
||||||
|
adding this feature into libraries but they were reluctant to do so because it
|
||||||
|
would require the devs to maintain the library whenever an update was made by
|
||||||
|
Discord.
|
||||||
|
|
||||||
Discordeno provides you specific keywords that you can use to send a clean response to the end user of your choosing. I have even seen some bots have hundreds of thousands of Missing Permission or Missing Access errors because libraries don't handle it. IMO, this is a crucial part of any good library as much as it is to handle rate limiting.
|
Discordeno provides you specific keywords that you can use to send a clean
|
||||||
|
response to the end user of your choosing. I have even seen some bots have
|
||||||
|
hundreds of thousands of Missing Permission or Missing Access errors because
|
||||||
|
libraries don't handle it. IMO, this is a crucial part of any good library as
|
||||||
|
much as it is to handle rate limiting.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Errors, Message } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
import { Errors, Message } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
@@ -57,7 +126,9 @@ import { Errors, Message } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
|||||||
export function handleCommandError(message: Message, type: Errors) {
|
export function handleCommandError(message: Message, type: Errors) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Errors.MISSING_MANAGE_NICKNAMES:
|
case Errors.MISSING_MANAGE_NICKNAMES:
|
||||||
return message.channel.sendMessage("The bot does not have the necessary permission to manage/edit other user's nicknames. Grant the **MANAGE_NICKNAME** permission to the bot and try again.");
|
return message.channel.sendMessage(
|
||||||
|
"The bot does not have the necessary permission to manage/edit other user's nicknames. Grant the **MANAGE_NICKNAME** permission to the bot and try again.",
|
||||||
|
);
|
||||||
case Errors.MISSING_MANAGE_ROLES:
|
case Errors.MISSING_MANAGE_ROLES:
|
||||||
// Note: i18n is not part of the library. This is just an example of how you could use i18n for custom error responses.
|
// Note: i18n is not part of the library. This is just an example of how you could use i18n for custom error responses.
|
||||||
return message.channel.sendMessage(i18n.translate(type));
|
return message.channel.sendMessage(i18n.translate(type));
|
||||||
|
|||||||
+38
-15
@@ -1,12 +1,17 @@
|
|||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
Discordeno aims for a simple, easy and stress-free interaction with the Discord API. Always supporting the latest version to ensure stability, consistency and the best developer experience.
|
Discordeno aims for a simple, easy and stress-free interaction with the Discord
|
||||||
|
API. Always supporting the latest version to ensure stability, consistency and
|
||||||
|
the best developer experience.
|
||||||
|
|
||||||
This website serves as the purpose for introducing Discordeno to developers. The full documentation for all the functions and methods can be visited by clicking the link below:
|
This website serves as the purpose for introducing Discordeno to developers. The
|
||||||
|
full documentation for all the functions and methods can be visited by clicking
|
||||||
|
the link below:
|
||||||
|
|
||||||
[View Documentation on Deno](https://doc.deno.land/https/deno.land/x/discordeno/mod.ts)
|
[View Documentation on Deno](https://doc.deno.land/https/deno.land/x/discordeno/mod.ts)
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
- [GitHub Repository](https://github.com/discordeno/discordeno)
|
- [GitHub Repository](https://github.com/discordeno/discordeno)
|
||||||
- [Deno Page](https://deno.land/x/discordeno)
|
- [Deno Page](https://deno.land/x/discordeno)
|
||||||
- [Website](https://discordeno.mod.land)
|
- [Website](https://discordeno.mod.land)
|
||||||
@@ -19,28 +24,42 @@ This website serves as the purpose for introducing Discordeno to developers. The
|
|||||||
|
|
||||||
Plenty of guides are available on how to create a Discord Bot Application.
|
Plenty of guides are available on how to create a Discord Bot Application.
|
||||||
|
|
||||||
1. [Creating an Application](https://discord.com/developers/applications) on the Developer Portal, name something cool and pick a sweet icon!
|
1. [Creating an Application](https://discord.com/developers/applications) on the
|
||||||
2. After creating an application. Save the **Client ID.** Thats the unique identifier for a Discord Bot.
|
Developer Portal, name something cool and pick a sweet icon!
|
||||||
3. Now, go and create a bot by clicking the **Bot** tab. You will see a **Token** section and thats the Discord Bot's token. **Make sure you don't share that token with anyone!!!**
|
2. After creating an application. Save the **Client ID.** Thats the unique
|
||||||
4. Invite the bot to the server, you can use the **[Discord Permissions Calculator](https://discordapi.com/permissions.html#0)** for creating the invite link with custom permissions. By default, `0` means no permissions and `8` means Administrator.
|
identifier for a Discord Bot.
|
||||||
|
3. Now, go and create a bot by clicking the **Bot** tab. You will see a
|
||||||
|
**Token** section and thats the Discord Bot's token. **Make sure you don't
|
||||||
|
share that token with anyone!!!**
|
||||||
|
4. Invite the bot to the server, you can use the
|
||||||
|
**[Discord Permissions Calculator](https://discordapi.com/permissions.html#0)**
|
||||||
|
for creating the invite link with custom permissions. By default, `0` means
|
||||||
|
no permissions and `8` means Administrator.
|
||||||
|
|
||||||
Now you've created an Application but it will need some code in order for it to be online. Thats when Discordeno comes in handy!
|
Now you've created an Application but it will need some code in order for it to
|
||||||
|
be online. Thats when Discordeno comes in handy!
|
||||||
|
|
||||||
> Make sure you store your tokens in a file that is NOT deployed by adding it to the .gitignore file. **Don't share your bot token with anybody.**
|
> Make sure you store your tokens in a file that is NOT deployed by adding it to
|
||||||
|
> the .gitignore file. **Don't share your bot token with anybody.**
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can install Discordeno by importing:
|
You can install Discordeno by importing:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { startBot } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
import { startBot } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
Starting with Discordeno is very simple, you can start from scratch without any boilerplates/frameworks: Add this snippet of code into a new TypeScript file:
|
Starting with Discordeno is very simple, you can start from scratch without any
|
||||||
|
boilerplates/frameworks: Add this snippet of code into a new TypeScript file:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { startBot, Intents } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
import {
|
||||||
|
Intents,
|
||||||
|
startBot,
|
||||||
|
} from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||||
import config from "./config.ts";
|
import config from "./config.ts";
|
||||||
|
|
||||||
startBot({
|
startBot({
|
||||||
@@ -54,17 +73,21 @@ startBot({
|
|||||||
if (message.content === "!ping") {
|
if (message.content === "!ping") {
|
||||||
message.reply("Pong");
|
message.reply("Pong");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
Below you will find youtube playlists that display channels using Discordeno for their tutorials.
|
Below you will find youtube playlists that display channels using Discordeno for
|
||||||
|
their tutorials.
|
||||||
|
|
||||||
Web-Mystery Tutorials:
|
Web-Mystery Tutorials:
|
||||||
- [Making a Discord bot with Deno and Discordeno](https://web-mystery.com/articles/making-discord-bot-deno-and-discordeno)
|
|
||||||
- [Running a Discord bot written in Deno in Docker](https://web-mystery.com/articles/running-discord-bot-written-deno-docker)
|
- [Making a Discord bot with Deno and
|
||||||
|
Discordeno](https://web-mystery.com/articles/making-discord-bot-deno-and-discordeno)
|
||||||
|
- [Running a Discord bot written in Deno in
|
||||||
|
Docker](https://web-mystery.com/articles/running-discord-bot-written-deno-docker)
|
||||||
- YouTube Tutorials:
|
- YouTube Tutorials:
|
||||||
- [Discordeno Bot Tutorials YouTube series](https://youtu.be/rIph9-BGsuQ)
|
- [Discordeno Bot Tutorials YouTube series](https://youtu.be/rIph9-BGsuQ)
|
||||||
|
|||||||
@@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
## Understanding The Goals of This Guide
|
## Understanding The Goals of This Guide
|
||||||
|
|
||||||
This guide is a step by step simple walkthrough meant for beginner developers but also can be quite useful for advanced developers to learn about Discordeno.
|
This guide is a step by step simple walkthrough meant for beginner developers
|
||||||
|
but also can be quite useful for advanced developers to learn about Discordeno.
|
||||||
|
|
||||||
## Who Made Discordeno?
|
## Who Made Discordeno?
|
||||||
|
|
||||||
Skillz4Killz. However, there are a lot of other contributors to the project as well. Join the discord server to meet and learn more about the wonderful team!
|
Skillz4Killz. However, there are a lot of other contributors to the project as
|
||||||
|
well. Join the discord server to meet and learn more about the wonderful team!
|
||||||
|
|
||||||
## Why You Should Use Discordeno?
|
## Why You Should Use Discordeno?
|
||||||
|
|
||||||
Discordeno provides you all the tools that you need to make bot development really easy. All the tools are already made for you to just simply copy and use for yourself. We will go through each and every tool in other sections of this guide.
|
Discordeno provides you all the tools that you need to make bot development
|
||||||
|
really easy. All the tools are already made for you to just simply copy and use
|
||||||
|
for yourself. We will go through each and every tool in other sections of this
|
||||||
|
guide.
|
||||||
|
|
||||||
As the old saying goes, the best way to learn to ride a bicycle is to actually try riding a bicycle. So let's try out Discordeno.
|
As the old saying goes, the best way to learn to ride a bicycle is to actually
|
||||||
|
try riding a bicycle. So let's try out Discordeno.
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
# Creating The Bot!
|
# Creating The Bot!
|
||||||
|
|
||||||
Discordeno will help make Discord bot development much easier. Don't worry, as you go through this guide it will make a lot more sense.
|
Discordeno will help make Discord bot development much easier. Don't worry, as
|
||||||
|
you go through this guide it will make a lot more sense.
|
||||||
|
|
||||||
## Creating The Bot!
|
## Creating The Bot!
|
||||||
|
|
||||||
> This guide is going to assume you already have the basic requirements to make a bot ready. This includes github, git, a code editor like Visual Studio Code. If you don't have these yet please prepare them first before going forward.
|
> This guide is going to assume you already have the basic requirements to make
|
||||||
|
> a bot ready. This includes github, git, a code editor like Visual Studio Code.
|
||||||
|
> If you don't have these yet please prepare them first before going forward.
|
||||||
|
|
||||||
- First, create a Discordeno Bot using the [Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template/generate). Give it any name you like. For the purpose of this guide we will call it, Stargate.
|
- First, create a Discordeno Bot using the
|
||||||
|
[Generator Boilerplate](https://github.com/discordeno/discordeno-bot-template/generate).
|
||||||
|
Give it any name you like. For the purpose of this guide we will call it,
|
||||||
|
Stargate.
|
||||||
|
|
||||||
- Then `git clone https://github.com/Skillz4Killz/Stargate.git` Replace **Stargate** with the name you chose.
|
- Then `git clone https://github.com/Skillz4Killz/Stargate.git` Replace
|
||||||
|
**Stargate** with the name you chose.
|
||||||
- When that is done, go ahead and open up the folder with VSC.
|
- When that is done, go ahead and open up the folder with VSC.
|
||||||
- Create a new file called `configs.ts`. Open the `configs.example.ts` file and copy everything over.
|
- Create a new file called `configs.ts`. Open the `configs.example.ts` file and
|
||||||
|
copy everything over.
|
||||||
|
|
||||||
Let's take a minute to review all the options we have available to us.
|
Let's take a minute to review all the options we have available to us.
|
||||||
|
|
||||||
### Configs File
|
### Configs File
|
||||||
|
|
||||||
The `configs.ts` file is where you will keep all your secret info you don't want to share with anyone else. As long as `.gitignore` file is ignoring configs.ts your configurations will be kept private!
|
The `configs.ts` file is where you will keep all your secret info you don't want
|
||||||
|
to share with anyone else. As long as `.gitignore` file is ignoring configs.ts
|
||||||
|
your configurations will be kept private!
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// Step 1: Remove the `.example` from this file name so it is called `configs.ts`
|
// Step 1: Remove the `.example` from this file name so it is called `configs.ts`
|
||||||
@@ -45,7 +55,7 @@ export const configs = {
|
|||||||
// When a translation is missing this is the channel you will be alerted in.
|
// When a translation is missing this is the channel you will be alerted in.
|
||||||
missingTranslation: "",
|
missingTranslation: "",
|
||||||
// When an error occurs, we will try and log it to this channel
|
// When an error occurs, we will try and log it to this channel
|
||||||
errorChannelID: ""
|
errorChannelID: "",
|
||||||
},
|
},
|
||||||
// These are the role ids that will enable some functionality.
|
// These are the role ids that will enable some functionality.
|
||||||
roleIDs: {
|
roleIDs: {
|
||||||
@@ -59,68 +69,95 @@ export const configs = {
|
|||||||
// The user ids for the other devs on your team
|
// The user ids for the other devs on your team
|
||||||
botDevs: [],
|
botDevs: [],
|
||||||
// The user ids who have complete 100% access to your bot
|
// The user ids who have complete 100% access to your bot
|
||||||
botOwners: []
|
botOwners: [],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Token
|
#### Token
|
||||||
|
|
||||||
First, add your bot token. This is **required** for the bot to start. Review the [instructions](https://discordeno.mod.land/gettingstarted.html#creating-your-first-discord-bot-application) if you have not made your token yet.
|
First, add your bot token. This is **required** for the bot to start. Review the
|
||||||
|
[instructions](https://discordeno.mod.land/gettingstarted.html#creating-your-first-discord-bot-application)
|
||||||
|
if you have not made your token yet.
|
||||||
|
|
||||||
#### Prefix
|
#### Prefix
|
||||||
|
|
||||||
The prefix is where you will set the default prefix for your bot. Don't worry, every server will be able to choose their own prefix but we need a default prefix to start.
|
The prefix is where you will set the default prefix for your bot. Don't worry,
|
||||||
|
every server will be able to choose their own prefix but we need a default
|
||||||
|
prefix to start.
|
||||||
|
|
||||||
#### Bot Lists Tokens
|
#### Bot Lists Tokens
|
||||||
|
|
||||||
This section of the file is so you can easily have your bot's statistics updated on all the bot lists out there to help you grow your bot. The code is already written to handle this but you will need to do 3 things for each bot list.
|
This section of the file is so you can easily have your bot's statistics updated
|
||||||
|
on all the bot lists out there to help you grow your bot. The code is already
|
||||||
|
written to handle this but you will need to do 3 things for each bot list.
|
||||||
|
|
||||||
1. Go to the bot list and add your bot to their website. (Each site has it's own instructions)
|
1. Go to the bot list and add your bot to their website. (Each site has it's own
|
||||||
|
instructions)
|
||||||
2. Create a token for yourself on each website and paste it in your configs.
|
2. Create a token for yourself on each website and paste it in your configs.
|
||||||
3. Enjoy!
|
3. Enjoy!
|
||||||
|
|
||||||
If you wish to customize the code, you will find the bot list tasks in the tasks folder. Don't worry we will discuss this when we get to the tasks section of the guide.
|
If you wish to customize the code, you will find the bot list tasks in the tasks
|
||||||
|
folder. Don't worry we will discuss this when we get to the tasks section of the
|
||||||
|
guide.
|
||||||
|
|
||||||
For now just remember, that Discordeno provides you a built in way to update most discord bot lists.
|
For now just remember, that Discordeno provides you a built in way to update
|
||||||
|
most discord bot lists.
|
||||||
|
|
||||||
#### Channel IDs
|
#### Channel IDs
|
||||||
|
|
||||||
The channelIDs section holds the channel IDs that are useful for specific features to help give you alerts/notifications. For example, the `missingTranslation` channel will be where messages are sent alerting you that somewhere in your code you are trying to use a translation key that you never created.
|
The channelIDs section holds the channel IDs that are useful for specific
|
||||||
|
features to help give you alerts/notifications. For example, the
|
||||||
|
`missingTranslation` channel will be where messages are sent alerting you that
|
||||||
|
somewhere in your code you are trying to use a translation key that you never
|
||||||
|
created.
|
||||||
|
|
||||||
When you get to around 25,000 Discord servers on your bot, you may want to convert these channel IDs to webhooks.
|
When you get to around 25,000 Discord servers on your bot, you may want to
|
||||||
|
convert these channel IDs to webhooks.
|
||||||
|
|
||||||
#### Role IDs
|
#### Role IDs
|
||||||
|
|
||||||
The roleIDs section holds the role IDs that are useful for specific features. For example, the `patreonVIPRoleID` role can be used easily to enable vip features later in this guide.
|
The roleIDs section holds the role IDs that are useful for specific features.
|
||||||
|
For example, the `patreonVIPRoleID` role can be used easily to enable vip
|
||||||
|
features later in this guide.
|
||||||
|
|
||||||
#### User IDs
|
#### User IDs
|
||||||
|
|
||||||
The userIDs section holds the user IDs that are useful for specific features. For example, the `botDevs` and such are useful for permission levels as you will see in the Permission Level part of the guide.
|
The userIDs section holds the user IDs that are useful for specific features.
|
||||||
|
For example, the `botDevs` and such are useful for permission levels as you will
|
||||||
|
see in the Permission Level part of the guide.
|
||||||
|
|
||||||
## It's Alive!
|
## It's Alive!
|
||||||
|
|
||||||
Oh my god! You now have a bot with a bunch of features already! You don't believe me? Well, seeing is believing, so start the bot.
|
Oh my god! You now have a bot with a bunch of features already! You don't
|
||||||
|
believe me? Well, seeing is believing, so start the bot.
|
||||||
|
|
||||||
1. Invite your bot to a discord server.
|
1. Invite your bot to a discord server.
|
||||||
2. Open up the integrated terminal in VSC using **CTRL + `**. If you use a Mac, replace CTRL with CMD.
|
2. Open up the integrated terminal in VSC using **CTRL + `**. If you use a Mac,
|
||||||
|
replace CTRL with CMD.
|
||||||
3. Run the script below:
|
3. Run the script below:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno run -A --no-check mod.ts
|
deno run -A --no-check mod.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
> The `-A` flag will grant it all permissions to run the bot. If you would like to specify specific ones you may do so!
|
> The `-A` flag will grant it all permissions to run the bot. If you would like
|
||||||
|
> to specify specific ones you may do so!
|
||||||
|
|
||||||
> The `--no-check` flag is used for module augmentation support as Deno still does not provide a clean way to have it. If you don't use custom structures, this is not needed.
|
> The `--no-check` flag is used for module augmentation support as Deno still
|
||||||
|
> does not provide a clean way to have it. If you don't use custom structures,
|
||||||
|
> this is not needed.
|
||||||
|
|
||||||
The first time you run it, you may see a lot of files being loaded. This is preparing all the magic behind the scene. Once it is ready, you will see something like this:
|
The first time you run it, you may see a lot of files being loaded. This is
|
||||||
|
preparing all the magic behind the scene. Once it is ready, you will see
|
||||||
|
something like this:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Understanding What Discordeno Did
|
## Understanding What Discordeno Did
|
||||||
|
|
||||||
Discordeno includes these commands/folders as they are essential for any discord bot to have in order to meet the Discord Bot Best Practices. It also adds a few things that will help make some things easier to build a bot.
|
Discordeno includes these commands/folders as they are essential for any discord
|
||||||
|
bot to have in order to meet the Discord Bot Best Practices. It also adds a few
|
||||||
|
things that will help make some things easier to build a bot.
|
||||||
|
|
||||||
We will dive into these deeper in this guide. Let's take it step by step.
|
We will dive into these deeper in this guide. Let's take it step by step.
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
# Creating A Command
|
# Creating A Command
|
||||||
|
|
||||||
Really great job. Now, lets dive into trying to use some of the commands and try to make our very own command.
|
Really great job. Now, lets dive into trying to use some of the commands and try
|
||||||
|
to make our very own command.
|
||||||
|
|
||||||
## Editing Invite Command
|
## Editing Invite Command
|
||||||
|
|
||||||
Let's first start by taking an existing command and slightly modifying it to your needs. Let's use the `Invite` command as our example. When you open the command, you will see something like this:
|
Let's first start by taking an existing command and slightly modifying it to
|
||||||
|
your needs. Let's use the `Invite` command as our example. When you open the
|
||||||
|
command, you will see something like this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
import { createCommand } from "../utils/helpers.ts"
|
import { createCommand } from "../utils/helpers.ts";
|
||||||
|
|
||||||
createCommand({
|
createCommand({
|
||||||
name: "invite",
|
name: "invite",
|
||||||
@@ -21,20 +24,32 @@ createCommand({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's break this down. The first two lines are importing the necessary things from their files so we can use them in this command. Don't worry if this doesn't make sense, most of the time, this will all be done automatically for you if you use a good code editor like Visual Studio Code. We create a command by doing:
|
Let's break this down. The first two lines are importing the necessary things
|
||||||
|
from their files so we can use them in this command. Don't worry if this doesn't
|
||||||
|
make sense, most of the time, this will all be done automatically for you if you
|
||||||
|
use a good code editor like Visual Studio Code. We create a command by doing:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
createCommand({
|
createCommand({
|
||||||
name: "commandname",
|
name: "commandname",
|
||||||
})
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The command name here must be unique. If it is not unique, your commands will not work properly as you wish. For example, you can't have 2 commands with the `ping` name. In this case, our command is called `invite`.
|
The command name here must be unique. If it is not unique, your commands will
|
||||||
|
not work properly as you wish. For example, you can't have 2 commands with the
|
||||||
|
`ping` name. In this case, our command is called `invite`.
|
||||||
|
|
||||||
Next is the `execute`. This is where the magic happens. This is the function that is triggered when the command is ran by a user. In this case, the bot simply sends a message to a channel where the command was ran and sends an invite link. Let's take a minute and optimize this for our case. We don't **NEED** admin permissions for our bot. Let's go to the [permission calculator](https://discordapi.com/permissions.html) and figure out the permissions we need for our bot.
|
Next is the `execute`. This is where the magic happens. This is the function
|
||||||
|
that is triggered when the command is ran by a user. In this case, the bot
|
||||||
Once you have decided which permissions you want to request, you can copy the number and replace it here. For example, if we want `68640` as our permissions. We can modify our command to be:
|
simply sends a message to a channel where the command was ran and sends an
|
||||||
|
invite link. Let's take a minute and optimize this for our case. We don't
|
||||||
|
**NEED** admin permissions for our bot. Let's go to the
|
||||||
|
[permission calculator](https://discordapi.com/permissions.html) and figure out
|
||||||
|
the permissions we need for our bot.
|
||||||
|
|
||||||
|
Once you have decided which permissions you want to request, you can copy the
|
||||||
|
number and replace it here. For example, if we want `68640` as our permissions.
|
||||||
|
We can modify our command to be:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
`https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=68640`,
|
`https://discordapp.com/oauth2/authorize?client_id=${botID}&scope=bot&permissions=68640`,
|
||||||
@@ -44,9 +59,18 @@ Lastly, let's get rid of the comment there as now it is customized to our needs.
|
|||||||
|
|
||||||
### Command Descriptions
|
### Command Descriptions
|
||||||
|
|
||||||
Right now, if you were to go to Discord and type `!help invite`, you would see quite a bit of information. But where does all that text come from? It's not in this file. The description option tells Discordeno what to show the user when they use the help command. In that help message, each command has a small description next to it. The magic is happening because of Discordeno's translations feature.
|
Right now, if you were to go to Discord and type `!help invite`, you would see
|
||||||
|
quite a bit of information. But where does all that text come from? It's not in
|
||||||
|
this file. The description option tells Discordeno what to show the user when
|
||||||
|
they use the help command. In that help message, each command has a small
|
||||||
|
description next to it. The magic is happening because of Discordeno's
|
||||||
|
translations feature.
|
||||||
|
|
||||||
> Discordeno has built in multi-lingual support. You can support as many languages as you wish. We will learn more about languages when we create our own language but for now we just want to see how to customize the command. What we are going to do below isn't the correct way but it's the easy way to learn. We will see the correct way to do it when we get to translations.
|
> Discordeno has built in multi-lingual support. You can support as many
|
||||||
|
> languages as you wish. We will learn more about languages when we create our
|
||||||
|
> own language but for now we just want to see how to customize the command.
|
||||||
|
> What we are going to do below isn't the correct way but it's the easy way to
|
||||||
|
> learn. We will see the correct way to do it when we get to translations.
|
||||||
|
|
||||||
Let's add a custom description to our invite command.
|
Let's add a custom description to our invite command.
|
||||||
|
|
||||||
@@ -55,19 +79,26 @@ name: "invite",
|
|||||||
description: "Like the bot? Use this link to add it to your server!",
|
description: "Like the bot? Use this link to add it to your server!",
|
||||||
```
|
```
|
||||||
|
|
||||||
🎉 It's that simple. So let's restart the bot and see how it changed. Use **CTRL + C** to shut down the bot. Then run the command from earlier.
|
🎉 It's that simple. So let's restart the bot and see how it changed. Use
|
||||||
|
**CTRL + C** to shut down the bot. Then run the command from earlier.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno run -A --no-check mod.ts
|
deno run -A --no-check mod.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
To access this easily, most likely all you need to do is press the **UP ARROW** key. Feel free to copy paste this if it doesn't work.
|
To access this easily, most likely all you need to do is press the **UP ARROW**
|
||||||
|
key. Feel free to copy paste this if it doesn't work.
|
||||||
|
|
||||||
Once the bot is started try `!help invite` again and you will see the change! 🎉
|
Once the bot is started try `!help invite` again and you will see the change! 🎉
|
||||||
|
|
||||||
## Command Aliases
|
## Command Aliases
|
||||||
|
|
||||||
Now, let's make this a little bit easier. What if we wanted this command to also work with other names besides `invite`. For example, as a shortcut what if users could just type `!inv` or as an alias type `!join`. Let's go ahead and set that up. Add a `aliases` property, I usually recommend under the name property. Aliases takes an array of strings that will be the names you would like to use as aliases.
|
Now, let's make this a little bit easier. What if we wanted this command to also
|
||||||
|
work with other names besides `invite`. For example, as a shortcut what if users
|
||||||
|
could just type `!inv` or as an alias type `!join`. Let's go ahead and set that
|
||||||
|
up. Add a `aliases` property, I usually recommend under the name property.
|
||||||
|
Aliases takes an array of strings that will be the names you would like to use
|
||||||
|
as aliases.
|
||||||
|
|
||||||
Let's add in the two aliases we wanted.
|
Let's add in the two aliases we wanted.
|
||||||
|
|
||||||
@@ -77,35 +108,55 @@ aliases: ["inv", "join"],
|
|||||||
description: "Like the bot? Use this link to add it to your server!",
|
description: "Like the bot? Use this link to add it to your server!",
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice, I added 2 aliases here. You can add as many aliases as you like. If you see a lot of users typing it wrong by accident you can add those typos as aliases as well.
|
Notice, I added 2 aliases here. You can add as many aliases as you like. If you
|
||||||
|
see a lot of users typing it wrong by accident you can add those typos as
|
||||||
|
aliases as well.
|
||||||
|
|
||||||
Let's start testing this out. But first, let's understand something important.
|
Let's start testing this out. But first, let's understand something important.
|
||||||
|
|
||||||
## Reloading
|
## Reloading
|
||||||
|
|
||||||
Okay, we need to first understand a core concept of developing discord bots. If you had to restart your bot every time you made a tiny change, it would get really annoying/tedious to do. Also, if you had restarted your bot 1000 times, you would be in big trouble because Discord would ban your bot for 24 hours. The bigger your bot gets, the faster it is to hit this limit of 1000 identify calls to the Discord API.
|
Okay, we need to first understand a core concept of developing discord bots. If
|
||||||
|
you had to restart your bot every time you made a tiny change, it would get
|
||||||
|
really annoying/tedious to do. Also, if you had restarted your bot 1000 times,
|
||||||
|
you would be in big trouble because Discord would ban your bot for 24 hours. The
|
||||||
|
bigger your bot gets, the faster it is to hit this limit of 1000 identify calls
|
||||||
|
to the Discord API.
|
||||||
|
|
||||||
To solve this issue, Discordeno provides you with a really cool `reload` command. Whenever you make a change to any command, event, monitor, inhibitor, task, language or argument you can simply reload it. You don't need to restart your bot every time.
|
To solve this issue, Discordeno provides you with a really cool `reload`
|
||||||
|
command. Whenever you make a change to any command, event, monitor, inhibitor,
|
||||||
|
task, language or argument you can simply reload it. You don't need to restart
|
||||||
|
your bot every time.
|
||||||
|
|
||||||
Now since we added the aliases above, let's test it out.
|
Now since we added the aliases above, let's test it out.
|
||||||
|
|
||||||
1. `!inv` this will not work
|
1. `!inv` this will not work
|
||||||
2. `!reload commands` this will reload the files and the aliases will be created.
|
2. `!reload commands` this will reload the files and the aliases will be
|
||||||
|
created.
|
||||||
3. `!inv` This will now work. 🎉
|
3. `!inv` This will now work. 🎉
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Perfect! You can take some time and customize the invite command to your needs if you wish. For example, this is my bot's invite command.
|
Perfect! You can take some time and customize the invite command to your needs
|
||||||
|
if you wish. For example, this is my bot's invite command.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Nice! You can now customize any of the commands from Discordeno as you wish. Then we will continue to making our very own command so we can learn about all the options that Discordeno gives us when it comes to making commands. As you learn more in this guide, you can keep improving it.
|
Nice! You can now customize any of the commands from Discordeno as you wish.
|
||||||
|
Then we will continue to making our very own command so we can learn about all
|
||||||
|
the options that Discordeno gives us when it comes to making commands. As you
|
||||||
|
learn more in this guide, you can keep improving it.
|
||||||
|
|
||||||
If you get stuck, don't worry. When you are ready, let's continue to the next step.
|
If you get stuck, don't worry. When you are ready, let's continue to the next
|
||||||
|
step.
|
||||||
|
|
||||||
## Creating A Command
|
## Creating A Command
|
||||||
|
|
||||||
Let's make a command that will allow guild admins to give or take roles from a member. Since, we are creating a `Moderation` command to give or take roles, let's go ahead and create a category folder called `Moderation` and then create a file called `role.ts`. Once the file is made, you can paste this following base snippet to make our first command.
|
Let's make a command that will allow guild admins to give or take roles from a
|
||||||
|
member. Since, we are creating a `Moderation` command to give or take roles,
|
||||||
|
let's go ahead and create a category folder called `Moderation` and then create
|
||||||
|
a file called `role.ts`. Once the file is made, you can paste this following
|
||||||
|
base snippet to make our first command.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -131,82 +182,125 @@ createCommand({
|
|||||||
arguments: [],
|
arguments: [],
|
||||||
execute: function (message, args, guild) {
|
execute: function (message, args, guild) {
|
||||||
// The code for your command goes here
|
// The code for your command goes here
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Understanding Command Options
|
## Understanding Command Options
|
||||||
|
|
||||||
Woah! We just added a massive file but most of that stuff is new to us. Don't worry, let's break everything down step by step.
|
Woah! We just added a massive file but most of that stuff is new to us. Don't
|
||||||
|
worry, let's break everything down step by step.
|
||||||
|
|
||||||
> **Note:** Any options that are not changed from the snippet above can actually be deleted as Discordeno will use the default option if you do not provide anything for that option. This can help keep your files cleaner.
|
> **Note:** Any options that are not changed from the snippet above can actually
|
||||||
|
> be deleted as Discordeno will use the default option if you do not provide
|
||||||
|
> anything for that option. This can help keep your files cleaner.
|
||||||
|
|
||||||
Before we start, quickly update the command name and description.
|
Before we start, quickly update the command name and description.
|
||||||
|
|
||||||
## dmOnly & guildOnly Options
|
## dmOnly & guildOnly Options
|
||||||
|
|
||||||
The `dmOnly` option is available if you want to make sure this command only runs in a direct message. If this command is ran in a server, the command will immediately exit out.
|
The `dmOnly` option is available if you want to make sure this command only runs
|
||||||
|
in a direct message. If this command is ran in a server, the command will
|
||||||
|
immediately exit out.
|
||||||
|
|
||||||
On the other hand, `guildOnly` is the opposite. To easily make sure that this command is never allowed in a private message you can simple enable this.
|
On the other hand, `guildOnly` is the opposite. To easily make sure that this
|
||||||
|
command is never allowed in a private message you can simple enable this.
|
||||||
|
|
||||||
If you want to allow a command to be able to run both in a server and in a direct message, simply set them both to false.
|
If you want to allow a command to be able to run both in a server and in a
|
||||||
|
direct message, simply set them both to false.
|
||||||
|
|
||||||
Remember if you want either of them as false, you can simply delete them and it will default to `false` allowing your bot to be cleaner and easier to read. However, if your prefer keeping it you can.
|
Remember if you want either of them as false, you can simply delete them and it
|
||||||
|
will default to `false` allowing your bot to be cleaner and easier to read.
|
||||||
|
However, if your prefer keeping it you can.
|
||||||
|
|
||||||
For the purpose of this guide, we want our `role` comamnd to only be run in a server, so we can set `guildOnly` to be **true** and delete the `dmOnly` option.
|
For the purpose of this guide, we want our `role` comamnd to only be run in a
|
||||||
|
server, so we can set `guildOnly` to be **true** and delete the `dmOnly` option.
|
||||||
|
|
||||||
## NSFW Option
|
## NSFW Option
|
||||||
|
|
||||||
NSFW stands for **Not Safe For Work**. One of Discord's rules is that you enforce that NSFW content is sent only in NSFW channels. Discordeno has this built in. You simply tell Discordeno, that you want a command to be considered `nsfw` or not.
|
NSFW stands for **Not Safe For Work**. One of Discord's rules is that you
|
||||||
|
enforce that NSFW content is sent only in NSFW channels. Discordeno has this
|
||||||
|
built in. You simply tell Discordeno, that you want a command to be considered
|
||||||
|
`nsfw` or not.
|
||||||
|
|
||||||
If this option is enabled, this command will only be able to be used in a `nsfw` channel on a server.
|
If this option is enabled, this command will only be able to be used in a `nsfw`
|
||||||
|
channel on a server.
|
||||||
|
|
||||||
> Discord does not consider Direct Messages as nsfw safe!
|
> Discord does not consider Direct Messages as nsfw safe!
|
||||||
|
|
||||||
## Permission Level Option
|
## Permission Level Option
|
||||||
|
|
||||||
Permission levels are a very powerful feature that easily allow you to set what permission is required to run a command. We will cover this in detail, in a separate section dedicated to permission levels. For now, just understand that this will restrict certain users from using a command.
|
Permission levels are a very powerful feature that easily allow you to set what
|
||||||
|
permission is required to run a command. We will cover this in detail, in a
|
||||||
|
separate section dedicated to permission levels. For now, just understand that
|
||||||
|
this will restrict certain users from using a command.
|
||||||
|
|
||||||
For example, the role command we only want to be used by moderators or server admins.
|
For example, the role command we only want to be used by moderators or server
|
||||||
|
admins.
|
||||||
|
|
||||||
Discordeno makes this pretty simple for us.
|
Discordeno makes this pretty simple for us.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
permissionLevels: [PermissionLevels.MODERATOR, PermissionLevels.ADMIN]
|
permissionLevels:
|
||||||
|
[PermissionLevels.MODERATOR, PermissionLevels.ADMIN];
|
||||||
```
|
```
|
||||||
|
|
||||||
We're not going to cover this in detail here because we will have an entire in depth guide for permission levels. If you want to pause and learn it now, feel free: [Permission Levels Advanced Guide](https://discordeno.mod.land/advanced/permlevels.html)
|
We're not going to cover this in detail here because we will have an entire in
|
||||||
|
depth guide for permission levels. If you want to pause and learn it now, feel
|
||||||
|
free:
|
||||||
|
[Permission Levels Advanced Guide](https://discordeno.mod.land/advanced/permlevels.html)
|
||||||
|
|
||||||
## Permissions Check Options
|
## Permissions Check Options
|
||||||
|
|
||||||
Discordeno provides you 4 different types of permission handling that will be done before the command function is executed. This allows you to make sure that the permissions are available before running a command. For example, the role command will require the bot to have **MANAGE ROLES** permission. This permission comes from the server settings so we can require this in the `botServerPermissions`.
|
Discordeno provides you 4 different types of permission handling that will be
|
||||||
|
done before the command function is executed. This allows you to make sure that
|
||||||
|
the permissions are available before running a command. For example, the role
|
||||||
|
command will require the bot to have **MANAGE ROLES** permission. This
|
||||||
|
permission comes from the server settings so we can require this in the
|
||||||
|
`botServerPermissions`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botServerPermissions: ["MANAGE_ROLES"],
|
botServerPermissions: ["MANAGE_ROLES"],
|
||||||
```
|
```
|
||||||
|
|
||||||
Discordeno will provide you easy to use auto-completion as soon as you type a single letter or two. This will help make it easier to use and will warn you when you make a typo and provide a wrong permission by accident.
|
Discordeno will provide you easy to use auto-completion as soon as you type a
|
||||||
|
single letter or two. This will help make it easier to use and will warn you
|
||||||
|
when you make a typo and provide a wrong permission by accident.
|
||||||
|
|
||||||
We also want to make sure that the bot can send a message in this channel so that we can respond to the user. This will check the correct permission heirarchy to make sure that the bot can indeed send a message in this channel.
|
We also want to make sure that the bot can send a message in this channel so
|
||||||
|
that we can respond to the user. This will check the correct permission
|
||||||
|
heirarchy to make sure that the bot can indeed send a message in this channel.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botChannelPermissions: ["SEND_MESSAGES"],
|
botChannelPermissions: ["SEND_MESSAGES"],
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** If you want to send an embed response, you should also make sure it has the `EMBED_LINKS` permission.
|
> **Note:** If you want to send an embed response, you should also make sure it
|
||||||
|
> has the `EMBED_LINKS` permission.
|
||||||
|
|
||||||
Already we have prevented two of the largest errors that can get our bot banned if it is not checked. Discordeno handles this internally so you don't have to worry about it.
|
Already we have prevented two of the largest errors that can get our bot banned
|
||||||
|
if it is not checked. Discordeno handles this internally so you don't have to
|
||||||
|
worry about it.
|
||||||
|
|
||||||
Sometimes, you may also want to make sure that the user using the command has a certain permission to run the command. To do this, you can use the `userServerPermissions` or the `userChannelPermissions`.
|
Sometimes, you may also want to make sure that the user using the command has a
|
||||||
|
certain permission to run the command. To do this, you can use the
|
||||||
|
`userServerPermissions` or the `userChannelPermissions`.
|
||||||
|
|
||||||
## Cooldown Options
|
## Cooldown Options
|
||||||
|
|
||||||
The cooldown options go hand in hand together.
|
The cooldown options go hand in hand together.
|
||||||
|
|
||||||
- `seconds` is how long to make the user wait in seconds before they can use the command again. By default, there is no wait time aka 0 seconds.
|
- `seconds` is how long to make the user wait in seconds before they can use the
|
||||||
- `allowedUses` is how many times a user is allowed to use a command before they are placed on cooldown.
|
command again. By default, there is no wait time aka 0 seconds.
|
||||||
|
- `allowedUses` is how many times a user is allowed to use a command before they
|
||||||
|
are placed on cooldown.
|
||||||
|
|
||||||
Let's use our command as an example. We don't want someone to start spamming the role command so we can add a cooldown of 60 seconds. However, this would mean every time you give a role to someone, you would need to wait a whole minute to give a role to someone else. This is where `allowedUses` shines! Let's set it to 5 so that a user can use this command 5 times in a row before being asked to wait for 60 seconds.
|
Let's use our command as an example. We don't want someone to start spamming the
|
||||||
|
role command so we can add a cooldown of 60 seconds. However, this would mean
|
||||||
|
every time you give a role to someone, you would need to wait a whole minute to
|
||||||
|
give a role to someone else. This is where `allowedUses` shines! Let's set it to
|
||||||
|
5 so that a user can use this command 5 times in a row before being asked to
|
||||||
|
wait for 60 seconds.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
cooldown: {
|
cooldown: {
|
||||||
@@ -215,7 +309,10 @@ cooldown: {
|
|||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** It is important to understand the way these two interact. The cooldown starts as soon as the first command is used. So, if a user uses the role command 5 times in 59 seconds, they only have to wait 1 second to do another 5 times.
|
> **Note:** It is important to understand the way these two interact. The
|
||||||
|
> cooldown starts as soon as the first command is used. So, if a user uses the
|
||||||
|
> role command 5 times in 59 seconds, they only have to wait 1 second to do
|
||||||
|
> another 5 times.
|
||||||
|
|
||||||
Let's give this a try shall we.
|
Let's give this a try shall we.
|
||||||
|
|
||||||
@@ -224,40 +321,58 @@ Let's give this a try shall we.
|
|||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
Arguments is an incredibly powerful feature in Discordeno. Like Permission Levels we will have an in depth look at arguments later in the guide. For now, let's try and understand this in a simple manner.
|
Arguments is an incredibly powerful feature in Discordeno. Like Permission
|
||||||
|
Levels we will have an in depth look at arguments later in the guide. For now,
|
||||||
|
let's try and understand this in a simple manner.
|
||||||
|
|
||||||
- `name`: The name of the argument. Useful for when you need to alert the user X arg is missing or when you want to use the arg in your command.
|
- `name`: The name of the argument. Useful for when you need to alert the user X
|
||||||
- `type`: The type of the argument you would like. Defaults to string. Some of the ones available by default are "number", "string", "...string", "boolean", "subcommand", "member".
|
arg is missing or when you want to use the arg in your command.
|
||||||
- `missing`: a function that will be run when this argument is not provided by the user or if the provided argument is not valid. By default, it does nothing.
|
- `type`: The type of the argument you would like. Defaults to string. Some of
|
||||||
- `required`: Whether this argument is required or optional. By default, this is true.
|
the ones available by default are "number", "string", "...string", "boolean",
|
||||||
|
"subcommand", "member".
|
||||||
|
- `missing`: a function that will be run when this argument is not provided by
|
||||||
|
the user or if the provided argument is not valid. By default, it does
|
||||||
|
nothing.
|
||||||
|
- `required`: Whether this argument is required or optional. By default, this is
|
||||||
|
true.
|
||||||
- `lowercase`: If the type is a string, this can forcibly lowercase the string.
|
- `lowercase`: If the type is a string, this can forcibly lowercase the string.
|
||||||
- `literals`: If the type is string or subcommand you can provide literals. The argument MUST be exactly the same as the literals to be accepted. For example, if you want to make sure the argument is `a` or `b` only and not `c`.
|
- `literals`: If the type is string or subcommand you can provide literals. The
|
||||||
- `defaultValue`: The default value for this argument/subcommand. If the user does not provide an argument or it is invalid, it can be defaulted to a certain value.
|
argument MUST be exactly the same as the literals to be accepted. For example,
|
||||||
|
if you want to make sure the argument is `a` or `b` only and not `c`.
|
||||||
|
- `defaultValue`: The default value for this argument/subcommand. If the user
|
||||||
|
does not provide an argument or it is invalid, it can be defaulted to a
|
||||||
|
certain value.
|
||||||
|
|
||||||
For our role command, we need a `member` and a `role`.
|
For our role command, we need a `member` and a `role`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
arguments: [
|
arguments:
|
||||||
|
[
|
||||||
{
|
{
|
||||||
name: "member",
|
name: "member",
|
||||||
type: "member",
|
type: "member",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
message.sendResponse(`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
message.sendResponse(
|
||||||
}
|
`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`,
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "role",
|
name: "role",
|
||||||
type: "role",
|
type: "role",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
message.sendResponse(`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
message.sendResponse(
|
||||||
}
|
`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`,
|
||||||
}
|
);
|
||||||
]
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
## Execute Option
|
## Execute Option
|
||||||
|
|
||||||
The execute option is the code that will run when the command is triggered. The execute function is passed 3 parameters that you can use in your command.
|
The execute option is the code that will run when the command is triggered. The
|
||||||
|
execute function is passed 3 parameters that you can use in your command.
|
||||||
|
|
||||||
- `message`: The message object itself that triggered this command.
|
- `message`: The message object itself that triggered this command.
|
||||||
- `args`: The args that were provided by the user.
|
- `args`: The args that were provided by the user.
|
||||||
@@ -269,14 +384,18 @@ execute: function (message, args, guild) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** I prefer writing code using the function keyword, but you can easily change to the fat arrow function if you prefer.
|
> **Note:** I prefer writing code using the function keyword, but you can easily
|
||||||
|
> change to the fat arrow function if you prefer.
|
||||||
|
|
||||||
> ```ts
|
> ```ts
|
||||||
> execute: (message, args, guild) => {
|
> execute:
|
||||||
|
> (message, args, guild) => {
|
||||||
> // The code that is to be executed goes here
|
> // The code that is to be executed goes here
|
||||||
> }
|
> };
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
Let's start out by writing some pseudo-code (comments that will help us plan the code).
|
Let's start out by writing some pseudo-code (comments that will help us plan the
|
||||||
|
code).
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
execute: function (message, args, guild) {
|
execute: function (message, args, guild) {
|
||||||
@@ -304,7 +423,8 @@ execute: function (message, args, guild) {
|
|||||||
|
|
||||||
Nice! The plan for the code is complete. Now, let's add in the code.
|
Nice! The plan for the code is complete. Now, let's add in the code.
|
||||||
|
|
||||||
The first thing we are going to try is to check if the role the user provided is the same as the everyone role.
|
The first thing we are going to try is to check if the role the user provided is
|
||||||
|
the same as the everyone role.
|
||||||
|
|
||||||
> **Note:** The everyone role ID is the same as the server guild ID.
|
> **Note:** The everyone role ID is the same as the server guild ID.
|
||||||
|
|
||||||
@@ -312,11 +432,16 @@ To do this, we are going to want something like this:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
if (args.role.id === message.guildID) {
|
if (args.role.id === message.guildID) {
|
||||||
return message.sendResponse("The everyone role can not be given to anyone because everyone has the everyone role already. *Keep calm and let Carter figure it out*!");
|
return message.sendResponse(
|
||||||
|
"The everyone role can not be given to anyone because everyone has the everyone role already. *Keep calm and let Carter figure it out*!",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You might be seeing an error since we didn't provide the accurate typings for the `args` parameter. The message and guild parameter is automated but the args is dynamic depending on your `arguments` option for your command. So let's do this real quick.
|
You might be seeing an error since we didn't provide the accurate typings for
|
||||||
|
the `args` parameter. The message and guild parameter is automated but the args
|
||||||
|
is dynamic depending on your `arguments` option for your command. So let's do
|
||||||
|
this real quick.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Role, Member } from "../../deps.ts";
|
import { Role, Member } from "../../deps.ts";
|
||||||
@@ -341,7 +466,6 @@ interface RoleArgs {
|
|||||||
|
|
||||||
Awesome! Let's keep going.
|
Awesome! Let's keep going.
|
||||||
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// Make the function asynchronous
|
// Make the function asynchronous
|
||||||
execute: async function (message, args: RoleArgs, guild) {
|
execute: async function (message, args: RoleArgs, guild) {
|
||||||
@@ -392,7 +516,14 @@ execute: async function (message, args: RoleArgs, guild) {
|
|||||||
The final version of the command should look something like this:
|
The final version of the command should look something like this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache, highestRole, higherRolePosition, botID, Role, Member } from "../../deps.ts";
|
import {
|
||||||
|
botCache,
|
||||||
|
botID,
|
||||||
|
higherRolePosition,
|
||||||
|
highestRole,
|
||||||
|
Member,
|
||||||
|
Role,
|
||||||
|
} from "../../deps.ts";
|
||||||
import { PermissionLevels } from "../types/commands.ts";
|
import { PermissionLevels } from "../types/commands.ts";
|
||||||
import { createCommand } from "../utils/helpers.ts";
|
import { createCommand } from "../utils/helpers.ts";
|
||||||
|
|
||||||
@@ -410,16 +541,20 @@ createCommand({
|
|||||||
name: "member",
|
name: "member",
|
||||||
type: "member",
|
type: "member",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
message.sendResponse(`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`)
|
message.sendResponse(
|
||||||
}
|
`You did not provide a member to give the role to. You can provide a @member mention, a member ID, or try using their nickname/username. The nickname/username will only work if they have been active in your server recently.`,
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "role",
|
name: "role",
|
||||||
type: "role",
|
type: "role",
|
||||||
missing: (message) => {
|
missing: (message) => {
|
||||||
message.sendResponse(`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`)
|
message.sendResponse(
|
||||||
}
|
`You did not provide a role to give. You can provide a @role mention, a role ID, or it's name. If the role name did not work, try to use the **roleinfo** command to get the role ID.`,
|
||||||
}
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
execute: async function (message, args: RoleArgs) {
|
execute: async function (message, args: RoleArgs) {
|
||||||
// If this was the everyone role alert with a silly error
|
// If this was the everyone role alert with a silly error
|
||||||
@@ -440,22 +575,32 @@ createCommand({
|
|||||||
const botsHighestRole = await highestRole(message.guildID, botID);
|
const botsHighestRole = await highestRole(message.guildID, botID);
|
||||||
|
|
||||||
// Check if the bot has a role higher than the role that it will try to give. If the role is too high alert the user.
|
// Check if the bot has a role higher than the role that it will try to give. If the role is too high alert the user.
|
||||||
if (!botsHighestRole || !(await higherRolePosition(
|
if (
|
||||||
|
!botsHighestRole || !(await higherRolePosition(
|
||||||
message.guildID,
|
message.guildID,
|
||||||
botsHighestRole.id,
|
botsHighestRole.id,
|
||||||
args.role.id,
|
args.role.id,
|
||||||
))) {
|
))
|
||||||
|
) {
|
||||||
return message.sendResponse(
|
return message.sendResponse(
|
||||||
"Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.",
|
"Okay look, asking me give a role that is higher than my highest role is ridiculous! I am the first bot to admit I don't know who these people are nor do I care to. Look, if you'd like I could take you down the hall and just point at the people who annoy me more than the rest. But that's about as useful as I get.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the command author's highest role
|
// Check the command author's highest role
|
||||||
const membersHighestRole = await highestRole(message.guildID, message.author.id);
|
const membersHighestRole = await highestRole(
|
||||||
|
message.guildID,
|
||||||
|
message.author.id,
|
||||||
|
);
|
||||||
|
|
||||||
// If the author does not have a role high enough to give this role alert
|
// If the author does not have a role high enough to give this role alert
|
||||||
if (!membersHighestRole ||
|
if (
|
||||||
!(await higherRolePosition(message.guildID, membersHighestRole.id, args.role.id))
|
!membersHighestRole ||
|
||||||
|
!(await higherRolePosition(
|
||||||
|
message.guildID,
|
||||||
|
membersHighestRole.id,
|
||||||
|
args.role.id,
|
||||||
|
))
|
||||||
) {
|
) {
|
||||||
return message.sendResponse(
|
return message.sendResponse(
|
||||||
"In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.",
|
"In my culture, whenever someone tries to give a role that is higher than their highest role, I would be well within my rights to dismember you.",
|
||||||
@@ -463,7 +608,9 @@ createCommand({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the user has this role already we should remove it
|
// If the user has this role already we should remove it
|
||||||
if (message.member?.guilds.get(message.guildID)?.roles.includes(args.role.id)) {
|
if (
|
||||||
|
message.member?.guilds.get(message.guildID)?.roles.includes(args.role.id)
|
||||||
|
) {
|
||||||
message.member.removeRole(
|
message.member.removeRole(
|
||||||
message.guildID,
|
message.guildID,
|
||||||
args.role.id,
|
args.role.id,
|
||||||
@@ -476,7 +623,11 @@ createCommand({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the role to the user.
|
// Add the role to the user.
|
||||||
message.member?.addRole(message.guildID, args.role.id, `${message.author.username} used the role command to give this role.`);
|
message.member?.addRole(
|
||||||
|
message.guildID,
|
||||||
|
args.role.id,
|
||||||
|
`${message.author.username} used the role command to give this role.`,
|
||||||
|
);
|
||||||
|
|
||||||
// Alert the user that used the command that the user has been give the role.
|
// Alert the user that used the command that the user has been give the role.
|
||||||
return message.sendResponse(
|
return message.sendResponse(
|
||||||
@@ -491,19 +642,38 @@ interface RoleArgs {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** The response strings are a play on some funny Stargate (my favorite tv show) moments. Feel free to change as you wish to fit your bot's personality.
|
> **Note:** The response strings are a play on some funny Stargate (my favorite
|
||||||
|
> tv show) moments. Feel free to change as you wish to fit your bot's
|
||||||
|
> personality.
|
||||||
|
|
||||||
## Dynamic (Advanced Level) Command Creation
|
## Dynamic (Advanced Level) Command Creation
|
||||||
|
|
||||||
This is some advanced level super magical command creation skills. Discordeno gives you the power to dynamically create commands. What that means is you can write the code for one command but dynamically create like 50 commands using that same code.
|
This is some advanced level super magical command creation skills. Discordeno
|
||||||
|
gives you the power to dynamically create commands. What that means is you can
|
||||||
|
write the code for one command but dynamically create like 50 commands using
|
||||||
|
that same code.
|
||||||
|
|
||||||
For example, in my main bot I have a lot of "fun" commands like `.hug` or `.kiss` etc... All of these files have the exact same code except for 1 word being the command name. Would it not be better to simply write the code once and dynamically create every single command. Let me show you how that works.
|
For example, in my main bot I have a lot of "fun" commands like `.hug` or
|
||||||
|
`.kiss` etc... All of these files have the exact same code except for 1 word
|
||||||
|
being the command name. Would it not be better to simply write the code once and
|
||||||
|
dynamically create every single command. Let me show you how that works.
|
||||||
|
|
||||||
- Create a file in the commands folder called `fun.ts` which will create all our fun commands.
|
- Create a file in the commands folder called `fun.ts` which will create all our
|
||||||
|
fun commands.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache, Member, sendMessage, chooseRandom, avatarURL } from "../../deps.ts";
|
import {
|
||||||
import { createCommand, createCommandAliases, sendEmbed } from "../utils/helpers.ts";
|
avatarURL,
|
||||||
|
botCache,
|
||||||
|
chooseRandom,
|
||||||
|
Member,
|
||||||
|
sendMessage,
|
||||||
|
} from "../../deps.ts";
|
||||||
|
import {
|
||||||
|
createCommand,
|
||||||
|
createCommandAliases,
|
||||||
|
sendEmbed,
|
||||||
|
} from "../utils/helpers.ts";
|
||||||
import { configs } from "../../configs.ts";
|
import { configs } from "../../configs.ts";
|
||||||
import { Embed } from "../utils/Embed.ts";
|
import { Embed } from "../utils/Embed.ts";
|
||||||
import { translate } from "../utils/i18next.ts";
|
import { translate } from "../utils/i18next.ts";
|
||||||
@@ -549,11 +719,10 @@ funCommandData.forEach((data) => {
|
|||||||
const member = args.member || message.member!;
|
const member = args.member || message.member!;
|
||||||
|
|
||||||
const type = member.user.id === message.author.id
|
const type = member.user.id === message.author.id
|
||||||
// Silly response like if user tries to hug themself
|
? // Silly response like if user tries to hug themself
|
||||||
? "SELF"
|
"SELF"
|
||||||
// Response for when user tries to hug another user
|
: // Response for when user tries to hug another user
|
||||||
: "OTHER";
|
"OTHER";
|
||||||
|
|
||||||
|
|
||||||
// Create an embed
|
// Create an embed
|
||||||
const embed = new Embed()
|
const embed = new Embed()
|
||||||
@@ -577,14 +746,26 @@ interface FunArgs {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** This is only an example of dynamic command creation and won't work if you try using this code since you won't have the configs necessary. Since this is an advanced topic we're not going to cover this in more detail here, because we will have an entire in depth guide for dynamic command creation. If you want to pause and learn it now, feel free: [Dynamic Command Creation Advanced Guide](https://discordeno.mod.land/advanced/dynamiccommands.html)
|
> **Note:** This is only an example of dynamic command creation and won't work
|
||||||
|
> if you try using this code since you won't have the configs necessary. Since
|
||||||
|
> this is an advanced topic we're not going to cover this in more detail here,
|
||||||
|
> because we will have an entire in depth guide for dynamic command creation. If
|
||||||
|
> you want to pause and learn it now, feel free:
|
||||||
|
> [Dynamic Command Creation Advanced Guide](https://discordeno.mod.land/advanced/dynamiccommands.html)
|
||||||
|
|
||||||
Take a minute to realize what just happened. This has made 18 different unique commands dynamically. In 1 file, using the same piece of code, we created so many commands. You can easily add more commands to this. For example, if you wanted to add weeb (animated) versions of these commands. Then you are at 36 commands with 1 simple command file.
|
Take a minute to realize what just happened. This has made 18 different unique
|
||||||
|
commands dynamically. In 1 file, using the same piece of code, we created so
|
||||||
|
many commands. You can easily add more commands to this. For example, if you
|
||||||
|
wanted to add weeb (animated) versions of these commands. Then you are at 36
|
||||||
|
commands with 1 simple command file.
|
||||||
|
|
||||||
**That ladies and gentleman is the power and magic of Discordeno!**
|
**That ladies and gentleman is the power and magic of Discordeno!**
|
||||||
|
|
||||||
Take a minute to stand up, step back and make some room around you. Now start dancing because you just acheived one of the most complex things in bot development. You have already created about 36 commands!!!!
|
Take a minute to stand up, step back and make some room around you. Now start
|
||||||
|
dancing because you just acheived one of the most complex things in bot
|
||||||
|
development. You have already created about 36 commands!!!!
|
||||||
|
|
||||||
> **Note:** The command above uses translations which we will cover in depth in a later section of this guide.
|
> **Note:** The command above uses translations which we will cover in depth in
|
||||||
|
> a later section of this guide.
|
||||||
|
|
||||||
Once you are ready, let's proceed to making our events.
|
Once you are ready, let's proceed to making our events.
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
# Creating Events!
|
# Creating Events!
|
||||||
|
|
||||||
Woah! You are almost half way done with understanding all of Discordeno. Amazing isn't it? Something you may have noticed in the last section was when the bot started up you could see a bunch of messages like `Loaded X Commands` and such. Let's jump into this part now and understand how event handling works in Discordeno.
|
Woah! You are almost half way done with understanding all of Discordeno. Amazing
|
||||||
|
isn't it? Something you may have noticed in the last section was when the bot
|
||||||
|
started up you could see a bunch of messages like `Loaded X Commands` and such.
|
||||||
|
Let's jump into this part now and understand how event handling works in
|
||||||
|
Discordeno.
|
||||||
|
|
||||||
## What Is An Event?
|
## What Is An Event?
|
||||||
|
|
||||||
An event in Discordeno is a function that can be called when a specific event occurs. For the most part, events will usually be the ones that are available from Discordeno. However, you can create your own custom events as well if you wish.
|
An event in Discordeno is a function that can be called when a specific event
|
||||||
|
occurs. For the most part, events will usually be the ones that are available
|
||||||
|
from Discordeno. However, you can create your own custom events as well if you
|
||||||
|
wish.
|
||||||
|
|
||||||
## Understanding The Events
|
## Understanding The Events
|
||||||
|
|
||||||
Go ahead and open up the `src/events/ready.ts` file. When you open this file, you will see the code that is triggered on the `ready` event. Whenever the bot completely starts up, Discordeno emits the `ready` event. This is when this code will be run allowing you to log these messages.
|
Go ahead and open up the `src/events/ready.ts` file. When you open this file,
|
||||||
|
you will see the code that is triggered on the `ready` event. Whenever the bot
|
||||||
|
completely starts up, Discordeno emits the `ready` event. This is when this code
|
||||||
|
will be run allowing you to log these messages.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache, cache } from "../../deps.ts";
|
import { botCache, cache } from "../../deps.ts";
|
||||||
@@ -27,13 +37,18 @@ botCache.eventHandlers.ready = function () {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** Some of the code from the ready.ts file was removed here to make it easier to understand.
|
> **Note:** Some of the code from the ready.ts file was removed here to make it
|
||||||
|
> easier to understand.
|
||||||
|
|
||||||
Overall, this code is pretty self-explanatory. When the bot is ready, it logs all these things to the console for you.
|
Overall, this code is pretty self-explanatory. When the bot is ready, it logs
|
||||||
|
all these things to the console for you.
|
||||||
|
|
||||||
## Creating A Custom Event
|
## Creating A Custom Event
|
||||||
|
|
||||||
Make a new file in the events folder called `discordLog.ts` that will send a message to a discord channel whenever we get an error so we don't need to always be watching the console to see errors. Once you made the file, go ahead and paste the base event snippet below.
|
Make a new file in the events folder called `discordLog.ts` that will send a
|
||||||
|
message to a discord channel whenever we get an error so we don't need to always
|
||||||
|
be watching the console to see errors. Once you made the file, go ahead and
|
||||||
|
paste the base event snippet below.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -44,7 +59,8 @@ botCache.eventHandlers.eventname = function () {
|
|||||||
```
|
```
|
||||||
|
|
||||||
- Change the event name to `discordLog`
|
- Change the event name to `discordLog`
|
||||||
- Go to `src/types/events.ts` and add in the following code so it looks like this:
|
- Go to `src/types/events.ts` and add in the following code so it looks like
|
||||||
|
this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// This interface is a placeholder that allows you to easily add on custom events for your need.
|
// This interface is a placeholder that allows you to easily add on custom events for your need.
|
||||||
@@ -55,7 +71,7 @@ export interface CustomEvents extends EventHandlers {
|
|||||||
|
|
||||||
Awesome, now we can get started on adding the code.
|
Awesome, now we can get started on adding the code.
|
||||||
|
|
||||||
```ts
|
````ts
|
||||||
import { botCache, cache, sendMessage } from "../../deps.ts";
|
import { botCache, cache, sendMessage } from "../../deps.ts";
|
||||||
import { Embed } from "../utils/Embed.ts";
|
import { Embed } from "../utils/Embed.ts";
|
||||||
import { configs } from "../../configs.ts";
|
import { configs } from "../../configs.ts";
|
||||||
@@ -66,7 +82,7 @@ botCache.eventHandlers.discordLog = function (error) {
|
|||||||
.setDescription([
|
.setDescription([
|
||||||
"```ts",
|
"```ts",
|
||||||
error,
|
error,
|
||||||
"```"
|
"```",
|
||||||
].join("\n"))
|
].join("\n"))
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
@@ -76,9 +92,14 @@ botCache.eventHandlers.discordLog = function (error) {
|
|||||||
// Send the message
|
// Send the message
|
||||||
return sendEmbed(errorChannel, embed);
|
return sendEmbed(errorChannel, embed);
|
||||||
};
|
};
|
||||||
```
|
````
|
||||||
|
|
||||||
Now that we have fully covered events, it would be a good time to get some practice here. Feel free to make more events that you would like in your bot. The following is a list of all the events available to you by the library at the time of writing this guide. There may be more or some may have been removed. I'll try to keep this updated but either way, VSC will let you know through autocompletion what is and isn't available.
|
Now that we have fully covered events, it would be a good time to get some
|
||||||
|
practice here. Feel free to make more events that you would like in your bot.
|
||||||
|
The following is a list of all the events available to you by the library at the
|
||||||
|
time of writing this guide. There may be more or some may have been removed.
|
||||||
|
I'll try to keep this updated but either way, VSC will let you know through
|
||||||
|
autocompletion what is and isn't available.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botUpdate?: (user: UserPayload) => unknown;
|
botUpdate?: (user: UserPayload) => unknown;
|
||||||
|
|||||||
@@ -1,25 +1,41 @@
|
|||||||
# Creating Inhibitors!
|
# Creating Inhibitors!
|
||||||
|
|
||||||
Woah! You are almost half way done with understanding all of Discordeno. Amazing isn't it? Something you may have noticed in the last section was there were some options that prevented some commands from running like `dmOnly` or the permission options. We created a setting to prevent the monitor from running in certain channels. What if we wanted to do prevent commands from happening? How would we prevent commands from running?
|
Woah! You are almost half way done with understanding all of Discordeno. Amazing
|
||||||
|
isn't it? Something you may have noticed in the last section was there were some
|
||||||
|
options that prevented some commands from running like `dmOnly` or the
|
||||||
|
permission options. We created a setting to prevent the monitor from running in
|
||||||
|
certain channels. What if we wanted to do prevent commands from happening? How
|
||||||
|
would we prevent commands from running?
|
||||||
|
|
||||||
## What is an Inhibitor?
|
## What is an Inhibitor?
|
||||||
|
|
||||||
Inhibitors are very similar to how monitors work. A monitor runs on every message but an inhibitor runs on every command. Remember all those command options like cooldown, permissions, permissionLevel, nsfw, etc... each and every one of these options has an inhibitor that checks commands for these settings.
|
Inhibitors are very similar to how monitors work. A monitor runs on every
|
||||||
|
message but an inhibitor runs on every command. Remember all those command
|
||||||
|
options like cooldown, permissions, permissionLevel, nsfw, etc... each and every
|
||||||
|
one of these options has an inhibitor that checks commands for these settings.
|
||||||
|
|
||||||
Let's create our own inhibitor that would prevent commands from being used if the user is not a VIP user.
|
Let's create our own inhibitor that would prevent commands from being used if
|
||||||
|
the user is not a VIP user.
|
||||||
|
|
||||||
> **Note:** It is important to remember that everything below can be done with a simple permission level as well. We will create our own custom permission levels but for the purposes of this guide and to be able to learn about Inhibitors, we will be using an inhibitor.
|
> **Note:** It is important to remember that everything below can be done with a
|
||||||
|
> simple permission level as well. We will create our own custom permission
|
||||||
|
> levels but for the purposes of this guide and to be able to learn about
|
||||||
|
> Inhibitors, we will be using an inhibitor.
|
||||||
|
|
||||||
## Creating the File
|
## Creating the File
|
||||||
|
|
||||||
Let's start by creating a file inside the inhibitors folder called `boosted.ts` and paste the following snippet of code:
|
Let's start by creating a file inside the inhibitors folder called `boosted.ts`
|
||||||
|
and paste the following snippet of code:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
|
|
||||||
botCache.inhibitors.set("inhibitorname", async function (message, command, guild) {
|
botCache.inhibitors.set(
|
||||||
|
"inhibitorname",
|
||||||
|
async function (message, command, guild) {
|
||||||
// Your code goes here
|
// Your code goes here
|
||||||
});
|
},
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Inhibitors can take up to 3 arguments.
|
Inhibitors can take up to 3 arguments.
|
||||||
@@ -31,10 +47,10 @@ Inhibitors can take up to 3 arguments.
|
|||||||
## Understanding How Inhibitors Function
|
## Understanding How Inhibitors Function
|
||||||
|
|
||||||
To block a command you have to return a truthy value.
|
To block a command you have to return a truthy value.
|
||||||
- **return true;** If you return true the inhibitor will block the execution of the command.
|
|
||||||
To allow a command return a falsey value.
|
|
||||||
- **return false;** If you return false the inhibitor will not block the error.
|
|
||||||
|
|
||||||
|
- **return true;** If you return true the inhibitor will block the execution of
|
||||||
|
the command. To allow a command return a falsey value.
|
||||||
|
- **return false;** If you return false the inhibitor will not block the error.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// If the command is not VIP only we can allow this command to execute
|
// If the command is not VIP only we can allow this command to execute
|
||||||
@@ -43,38 +59,56 @@ if (!command.vipOnly) return false;
|
|||||||
// The bot's support server
|
// The bot's support server
|
||||||
const guild = cache.guilds.get(configs.supportServerID);
|
const guild = cache.guilds.get(configs.supportServerID);
|
||||||
// If the command author is not in the server they won't have the vip role
|
// If the command author is not in the server they won't have the vip role
|
||||||
const member = message.member || await getMember(guild.id, message.author.id).catch(console.error);
|
const member = message.member ||
|
||||||
|
await getMember(guild.id, message.author.id).catch(console.error);
|
||||||
// Member doesn't exist so cancel the command
|
// Member doesn't exist so cancel the command
|
||||||
if (!member) {
|
if (!member) {
|
||||||
message.sendResponse(`Sorry, but you can not use this command until you become VIP. **Close the IRIS!!!**`)
|
message.sendResponse(
|
||||||
|
`Sorry, but you can not use this command until you become VIP. **Close the IRIS!!!**`,
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has the vip role on the support server given by patreon allow the command
|
// If the user has the vip role on the support server given by patreon allow the command
|
||||||
if (member.guilds.get(message.guildID)?.roles.includes(configs.roleIDs.patreonVIPRoleID)) return false;
|
if (
|
||||||
|
member.guilds.get(message.guildID)?.roles.includes(
|
||||||
|
configs.roleIDs.patreonVIPRoleID,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Alert the user they don't have vip and can't use the command
|
// Alert the user they don't have vip and can't use the command
|
||||||
message.sendResponse(`Sorry, but you can not use this command until you become a VIP. I'm sorry, Teal'c. We'll go to Disneyland next year. I promise.`)
|
message.sendResponse(
|
||||||
|
`Sorry, but you can not use this command until you become a VIP. I'm sorry, Teal'c. We'll go to Disneyland next year. I promise.`,
|
||||||
|
);
|
||||||
// Cancel the command since the user does not have vip
|
// Cancel the command since the user does not have vip
|
||||||
return true;
|
return true;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Updating Command Typings
|
## Updating Command Typings
|
||||||
|
|
||||||
Since we need a new option on our commands we need to add that. Open the `src/types/commands.ts` file and add in the following
|
Since we need a new option on our commands we need to add that. Open the
|
||||||
|
`src/types/commands.ts` file and add in the following
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
vipOnly?: boolean;
|
vipOnly?: boolean;
|
||||||
```
|
```
|
||||||
|
|
||||||
Once that is added, you can go into any command and mark them as vip only commands.
|
Once that is added, you can go into any command and mark them as vip only
|
||||||
|
commands.
|
||||||
|
|
||||||
## Challenges
|
## Challenges
|
||||||
|
|
||||||
Hell yes! We just got through the entire inhibitor section. You have now mastered everything related to inhibitors.
|
Hell yes! We just got through the entire inhibitor section. You have now
|
||||||
|
mastered everything related to inhibitors.
|
||||||
|
|
||||||
You can now try and get a little more practice with inhibitors by trying to challenge yourself and make your own inhibitors. Don't worry if you can't think of any good use cases for inhibitors.
|
You can now try and get a little more practice with inhibitors by trying to
|
||||||
|
challenge yourself and make your own inhibitors. Don't worry if you can't think
|
||||||
|
of any good use cases for inhibitors.
|
||||||
|
|
||||||
Majority of Discordeno bots do not use many custom inhibitors because most of the necessary/important ones already come built for you in the inhibitors folder.
|
Majority of Discordeno bots do not use many custom inhibitors because most of
|
||||||
|
the necessary/important ones already come built for you in the inhibitors
|
||||||
|
folder.
|
||||||
|
|
||||||
Next we will try our hands on creating tasks.
|
Next we will try our hands on creating tasks.
|
||||||
|
|||||||
@@ -1,28 +1,45 @@
|
|||||||
# Creating Languages!
|
# Creating Languages!
|
||||||
|
|
||||||
Woot! You have mastered Discordeno events already. Now it's time to finally make our bot multi-lingual. Vàmanos!
|
Woot! You have mastered Discordeno events already. Now it's time to finally make
|
||||||
|
our bot multi-lingual. Vàmanos!
|
||||||
|
|
||||||
## What Is A Discordeno Language?
|
## What Is A Discordeno Language?
|
||||||
|
|
||||||
A Discordeno language is a folder that will hold all our responses that the bot sends. By having various different language files you can have a multi-lingual bot that can be used in different languages.
|
A Discordeno language is a folder that will hold all our responses that the bot
|
||||||
|
sends. By having various different language files you can have a multi-lingual
|
||||||
|
bot that can be used in different languages.
|
||||||
|
|
||||||
## i18next
|
## i18next
|
||||||
|
|
||||||
By default, Discordeno comes built with support for i18next (one of if not the best localization libraries). If you want to learn more, go to [i18next website](https://www.i18next.com/). For now, there is probably not going to be anything you will need to learn there. As most of the functionality has already been created for you right here in Discordeno.
|
By default, Discordeno comes built with support for i18next (one of if not the
|
||||||
|
best localization libraries). If you want to learn more, go to
|
||||||
|
[i18next website](https://www.i18next.com/). For now, there is probably not
|
||||||
|
going to be anything you will need to learn there. As most of the functionality
|
||||||
|
has already been created for you right here in Discordeno.
|
||||||
|
|
||||||
## Default Language
|
## Default Language
|
||||||
|
|
||||||
The default language with Discordeno is American English which uses the name `en_US`. So when you open the `src/languages/` folder you will find a folder called `en_US`. This is where all the strings can be kept for your bot that can be easily translated by other translators.
|
The default language with Discordeno is American English which uses the name
|
||||||
|
`en_US`. So when you open the `src/languages/` folder you will find a folder
|
||||||
|
called `en_US`. This is where all the strings can be kept for your bot that can
|
||||||
|
be easily translated by other translators.
|
||||||
|
|
||||||
## Understanding The Folder Structure
|
## Understanding The Folder Structure
|
||||||
|
|
||||||
The first folder inside the languages folder must be a language folder following the name pattern like `en_US`. So for example, if we wanted to add a Spanish (Spain) language to our bot we would create a new folder called `es_ES`.
|
The first folder inside the languages folder must be a language folder following
|
||||||
|
the name pattern like `en_US`. So for example, if we wanted to add a Spanish
|
||||||
|
(Spain) language to our bot we would create a new folder called `es_ES`.
|
||||||
|
|
||||||
You can have as many folder in here as you like. For example I can do something like `src/languages/en_US/commands/fun/hug.json`. Notice that I have created categories to help keep them categorized and easier to find. You can do it however you wish to have them. For now, just remember that files must always be `.json` files in these folders. **JSON is required.**
|
You can have as many folder in here as you like. For example I can do something
|
||||||
|
like `src/languages/en_US/commands/fun/hug.json`. Notice that I have created
|
||||||
|
categories to help keep them categorized and easier to find. You can do it
|
||||||
|
however you wish to have them. For now, just remember that files must always be
|
||||||
|
`.json` files in these folders. **JSON is required.**
|
||||||
|
|
||||||
## Adding Hug Strings
|
## Adding Hug Strings
|
||||||
|
|
||||||
Earlier in the guide, we made a hug command. So let's make that commands translations work properly now.
|
Earlier in the guide, we made a hug command. So let's make that commands
|
||||||
|
translations work properly now.
|
||||||
|
|
||||||
- Create the `hug.json` file in the `src/languages/en_US/commands/fun/` folder.
|
- Create the `hug.json` file in the `src/languages/en_US/commands/fun/` folder.
|
||||||
|
|
||||||
@@ -32,9 +49,12 @@ Earlier in the guide, we made a hug command. So let's make that commands transla
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Most of the time, you should start with this base. The `DESCRIPTION` key, is used in the help command and provides the description for the command. When someone types `!help hug` they would see this description you typed.
|
Most of the time, you should start with this base. The `DESCRIPTION` key, is
|
||||||
|
used in the help command and provides the description for the command. When
|
||||||
|
someone types `!help hug` they would see this description you typed.
|
||||||
|
|
||||||
In our hug command we also had 2 other keys we used. `SELF` and `OTHER` so let's add those in.
|
In our hug command we also had 2 other keys we used. `SELF` and `OTHER` so let's
|
||||||
|
add those in.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -44,36 +64,41 @@ In our hug command we also had 2 other keys we used. `SELF` and `OTHER` so let's
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now the `"SELF"` is pretty easy to understand but the `OTHER` has some interesting things in it so let's jump into that.
|
Now the `"SELF"` is pretty easy to understand but the `OTHER` has some
|
||||||
|
interesting things in it so let's jump into that.
|
||||||
|
|
||||||
## Translate Function
|
## Translate Function
|
||||||
|
|
||||||
Discordeno provides you a built in function called `translate`. It takes in 3 different arguments.
|
Discordeno provides you a built in function called `translate`. It takes in 3
|
||||||
|
different arguments.
|
||||||
|
|
||||||
- `guildID` the id of the server. This is used to determine which language to use.
|
- `guildID` the id of the server. This is used to determine which language to
|
||||||
- `key` the unique folderpath:KEY string that will determine which string to translate.
|
use.
|
||||||
|
- `key` the unique folderpath:KEY string that will determine which string to
|
||||||
|
translate.
|
||||||
- `options` the variables that the strings have available to them.
|
- `options` the variables that the strings have available to them.
|
||||||
|
|
||||||
i18next allows you to pass in variables that you can use when you want in your strings. If you recall from our guide ealier we passed in 2 variables.
|
i18next allows you to pass in variables that you can use when you want in your
|
||||||
|
strings. If you recall from our guide ealier we passed in 2 variables.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
translate(
|
translate(
|
||||||
message.guildID,
|
message.guildID,
|
||||||
`commands/fun/${data.name}:${type}`,
|
`commands/fun/${data.name}:${type}`,
|
||||||
{ mention: message.member!.mention, user: member.mention },
|
{ mention: message.member!.mention, user: member.mention },
|
||||||
)
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we can see that we passed in:
|
Here we can see that we passed in:
|
||||||
|
|
||||||
- `mention`: The user mention who used this command. `!hug`
|
- `mention`: The user mention who used this command. `!hug`
|
||||||
- `user`: The user mention of the member who was @ by the command author. `!hug @o'neill`
|
- `user`: The user mention of the member who was @ by the command author.
|
||||||
|
`!hug @o'neill`
|
||||||
|
|
||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
::: v-pre
|
::: v-pre Variables in i18next use the `{{}}` format. So the variable `mention`
|
||||||
Variables in i18next use the `{{}}` format. So the variable `mention` would be used by doing `{{mention}}`
|
would be used by doing `{{mention}}` :::
|
||||||
:::
|
|
||||||
|
|
||||||
## Key Rules
|
## Key Rules
|
||||||
|
|
||||||
@@ -83,17 +108,22 @@ When you create keys in the files there are a couple rules to follow.
|
|||||||
- ALWAYS USE UPPERCASE **OPTIONAL**
|
- ALWAYS USE UPPERCASE **OPTIONAL**
|
||||||
- Words are separated by `_` **OPTIONAL**
|
- Words are separated by `_` **OPTIONAL**
|
||||||
|
|
||||||
The first one is the only one that is mandatory. The other two are recommended for you.
|
The first one is the only one that is mandatory. The other two are recommended
|
||||||
|
for you.
|
||||||
|
|
||||||
## Missing Keys
|
## Missing Keys
|
||||||
|
|
||||||
Every developer forgets stuff sometimes. When you forget to translate a key, it will be marked as a missing key and you would be alerted if you have provided a channelID in the configs file, and it will also be logged in your console.
|
Every developer forgets stuff sometimes. When you forget to translate a key, it
|
||||||
|
will be marked as a missing key and you would be alerted if you have provided a
|
||||||
|
channelID in the configs file, and it will also be logged in your console.
|
||||||
|
|
||||||
## Spanish Version
|
## Spanish Version
|
||||||
|
|
||||||
Let's just create a spanish version of the hug command from above to see an example of different languages.
|
Let's just create a spanish version of the hug command from above to see an
|
||||||
|
example of different languages.
|
||||||
|
|
||||||
- Create a file called `hug.json` in the folder `src/languages/es_ES/commands/fun/`
|
- Create a file called `hug.json` in the folder
|
||||||
|
`src/languages/es_ES/commands/fun/`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -103,17 +133,22 @@ Let's just create a spanish version of the hug command from above to see an exam
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice, that there are 2 thing that were **NOT** translated. The `KEY` names and the `VARIABLES`. These 2 things should never be translated. Anything else can be translated upon your needs.
|
Notice, that there are 2 thing that were **NOT** translated. The `KEY` names and
|
||||||
|
the `VARIABLES`. These 2 things should never be translated. Anything else can be
|
||||||
|
translated upon your needs.
|
||||||
|
|
||||||
## Localization Platform
|
## Localization Platform
|
||||||
|
|
||||||
i18next works perfectly with localization platforms. For example, you can easily plug in `crowdin` or `transifex` to your project.
|
i18next works perfectly with localization platforms. For example, you can easily
|
||||||
|
plug in `crowdin` or `transifex` to your project.
|
||||||
|
|
||||||
- [Transifex](https://www.transifex.com/) *This is the one I use in my bot but you can use anything you like.*
|
- [Transifex](https://www.transifex.com/) _This is the one I use in my bot but
|
||||||
|
you can use anything you like._
|
||||||
- [Crowdin](https://crowdin.com/)
|
- [Crowdin](https://crowdin.com/)
|
||||||
|
|
||||||
## Challenge
|
## Challenge
|
||||||
|
|
||||||
Wow! You have even masted languages. Go ahead and jump back to the role command we made earlier and add translation support to it. Give it some love!
|
Wow! You have even masted languages. Go ahead and jump back to the role command
|
||||||
|
we made earlier and add translation support to it. Give it some love!
|
||||||
|
|
||||||
Once you are ready, let's go make our first monitor.
|
Once you are ready, let's go make our first monitor.
|
||||||
|
|||||||
@@ -1,20 +1,31 @@
|
|||||||
# Creating Monitors!
|
# Creating Monitors!
|
||||||
|
|
||||||
Holy bananza! You even got the entire languages complete. You are well on your way to mastering 23 different languages. Now we are going to down and dirty with monitors.
|
Holy bananza! You even got the entire languages complete. You are well on your
|
||||||
|
way to mastering 23 different languages. Now we are going to down and dirty with
|
||||||
|
monitors.
|
||||||
|
|
||||||
## What is a monitor?
|
## What is a monitor?
|
||||||
|
|
||||||
Monitors are functions that will run on every single message that is sent in every channel that your bot has permissions to read. When you want to do something on every single message that is sent, the best way to do that is to create a new monitor.
|
Monitors are functions that will run on every single message that is sent in
|
||||||
|
every channel that your bot has permissions to read. When you want to do
|
||||||
|
something on every single message that is sent, the best way to do that is to
|
||||||
|
create a new monitor.
|
||||||
|
|
||||||
## Command Handler Monitor
|
## Command Handler Monitor
|
||||||
|
|
||||||
Discordeno come built with a monitor called `commandHandler`. This monitor runs on every message sent and figures out if it is a command and executes the command. If it is a valid command with a valid prefix, Discordeno runs that command.
|
Discordeno come built with a monitor called `commandHandler`. This monitor runs
|
||||||
|
on every message sent and figures out if it is a command and executes the
|
||||||
|
command. If it is a valid command with a valid prefix, Discordeno runs that
|
||||||
|
command.
|
||||||
|
|
||||||
Let's try and create our own monitor so we can understand it better. Suppose we wanted to build a filter that would delete any message which included a discord invite link.
|
Let's try and create our own monitor so we can understand it better. Suppose we
|
||||||
|
wanted to build a filter that would delete any message which included a discord
|
||||||
|
invite link.
|
||||||
|
|
||||||
## Creating Invite Filter Monitor
|
## Creating Invite Filter Monitor
|
||||||
|
|
||||||
To start, we make a new file in the monitors folder called `inviteFilter.ts`. Then you can paste in the following base monitor snippet.
|
To start, we make a new file in the monitors folder called `inviteFilter.ts`.
|
||||||
|
Then you can paste in the following base monitor snippet.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -41,7 +52,8 @@ The monitor options are very similar in functionality with the command options.
|
|||||||
|
|
||||||
### Monitor Name
|
### Monitor Name
|
||||||
|
|
||||||
Similar to the command name, we will specify a unique name for the monitors. In this case let's call it inviteFilter
|
Similar to the command name, we will specify a unique name for the monitors. In
|
||||||
|
this case let's call it inviteFilter
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botCache.monitors.set("inviteFilter", {
|
botCache.monitors.set("inviteFilter", {
|
||||||
@@ -50,21 +62,42 @@ botCache.monitors.set("inviteFilter", {
|
|||||||
|
|
||||||
### Ignore Options
|
### Ignore Options
|
||||||
|
|
||||||
The ignore options are filters that you can use to enable/disable the monitor from running in those specific circumstances. The default option for each monitor is shown in the base snippet above.
|
The ignore options are filters that you can use to enable/disable the monitor
|
||||||
|
from running in those specific circumstances. The default option for each
|
||||||
|
monitor is shown in the base snippet above.
|
||||||
|
|
||||||
- **ignoreBots**: Should this monitor run on a message sent by a bot. In our example, if we set this false then no bot would be allowed to post links in the server. Since we don't want other bot's posting invite links either we can simply just set this to `false`.
|
- **ignoreBots**: Should this monitor run on a message sent by a bot. In our
|
||||||
- **ignoreOthers**: Should this monitor run on other USERS. If we set this as false, then any user will not be allowed to post discord links. Since the default option for this is already `false`, let's go ahead and just delete this line.
|
example, if we set this false then no bot would be allowed to post links in
|
||||||
- **ignoreEdits**: Should this monitor run on edited messaged. If we set this as false, then it would also be filtering messages that are edited. It would be important to prevent users from editing a message and posting a discord invite link so let's keep this `false`. Since the default is false, we can just delete this line.
|
the server. Since we don't want other bot's posting invite links either we can
|
||||||
- **ignoreDM**: Should this monitor run on direct messages. If we set this as false, then this monitor would not run when the bot is sent a dm. Since we don't care if the users send our bot a dm with an invite link we can just simply set this to `true` Since the default is `true` for this option we can simple delete this line.
|
simply just set this to `false`.
|
||||||
|
- **ignoreOthers**: Should this monitor run on other USERS. If we set this as
|
||||||
|
false, then any user will not be allowed to post discord links. Since the
|
||||||
|
default option for this is already `false`, let's go ahead and just delete
|
||||||
|
this line.
|
||||||
|
- **ignoreEdits**: Should this monitor run on edited messaged. If we set this as
|
||||||
|
false, then it would also be filtering messages that are edited. It would be
|
||||||
|
important to prevent users from editing a message and posting a discord invite
|
||||||
|
link so let's keep this `false`. Since the default is false, we can just
|
||||||
|
delete this line.
|
||||||
|
- **ignoreDM**: Should this monitor run on direct messages. If we set this as
|
||||||
|
false, then this monitor would not run when the bot is sent a dm. Since we
|
||||||
|
don't care if the users send our bot a dm with an invite link we can just
|
||||||
|
simply set this to `true` Since the default is `true` for this option we can
|
||||||
|
simple delete this line.
|
||||||
|
|
||||||
The default options were chosen for what the majority of monitors will use to help keep your code clean and clear.
|
The default options were chosen for what the majority of monitors will use to
|
||||||
|
help keep your code clean and clear.
|
||||||
|
|
||||||
## Permission Options
|
## Permission Options
|
||||||
|
|
||||||
The permission options are the exact same from the commands guide. These options first make sure that either the bot or user has those necessary permissions to run this monitor. For example, our invite filter would mean we need **MANAGE MESSAGES** permission so we can delete messages sent with an invite URL.
|
The permission options are the exact same from the commands guide. These options
|
||||||
|
first make sure that either the bot or user has those necessary permissions to
|
||||||
|
run this monitor. For example, our invite filter would mean we need **MANAGE
|
||||||
|
MESSAGES** permission so we can delete messages sent with an invite URL.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botChannelPermissions: ["MANAGE_MESSAGES"]
|
botChannelPermissions:
|
||||||
|
["MANAGE_MESSAGES"];
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding The Code
|
## Adding The Code
|
||||||
@@ -92,13 +125,19 @@ botCache.monitors.set("inviteFilter", {
|
|||||||
translate(message.guildID, `monitors/invitefilter:DELETE_REASON`),
|
translate(message.guildID, `monitors/invitefilter:DELETE_REASON`),
|
||||||
);
|
);
|
||||||
// Send a message to the user so they know why the message was deleted. Then delete the response after 5 seconds to prevent spam.
|
// Send a message to the user so they know why the message was deleted. Then delete the response after 5 seconds to prevent spam.
|
||||||
message.alertResponse(translate(message.guildID, "monitors/invitefilter:DELETE_ALERT_MESSAGE"), 5)
|
message.alertResponse(
|
||||||
|
translate(
|
||||||
|
message.guildID,
|
||||||
|
"monitors/invitefilter:DELETE_ALERT_MESSAGE",
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return botCache.eventHandlers.discordLog(error)
|
return botCache.eventHandlers.discordLog(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Nice! Now take some time and add these translation keys to their appropriate files.
|
Nice! Now take some time and add these translation keys to their appropriate
|
||||||
Once, you are ready, let's jump into creating some command inhibitors.
|
files. Once, you are ready, let's jump into creating some command inhibitors.
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
# Creating Tasks
|
# Creating Tasks
|
||||||
|
|
||||||
Phenomenal! Now that you have mastered inhibitors, let's move on to one of the final parts, Tasks. A lot of times, you will want to do something over and over again. To do this, you can use tasks. You could technically just do this with `setInterval` but, with tasks you gain a little more advantage like having reloadability. The functions that are run can be easily reloaded meaning, you don't need to restart your whole bot just for little change.
|
Phenomenal! Now that you have mastered inhibitors, let's move on to one of the
|
||||||
|
final parts, Tasks. A lot of times, you will want to do something over and over
|
||||||
|
again. To do this, you can use tasks. You could technically just do this with
|
||||||
|
`setInterval` but, with tasks you gain a little more advantage like having
|
||||||
|
reloadability. The functions that are run can be easily reloaded meaning, you
|
||||||
|
don't need to restart your whole bot just for little change.
|
||||||
|
|
||||||
## What is a Task?
|
## What is a Task?
|
||||||
|
|
||||||
A task is used to conduct a function every so often. For example, Discordeno comes with a built in task called `botlists`. This task runs every so often and updates your data in all the bot lists apis. Let's create a new task to really understand the gist of it. Let's create a task that will run every 5 minutes which will keep our cache clean and minimal allowing your bot to grow and scale much larger without needing a much bigger server to host it on. Start by pasting the following snippet below:
|
A task is used to conduct a function every so often. For example, Discordeno
|
||||||
|
comes with a built in task called `botlists`. This task runs every so often and
|
||||||
|
updates your data in all the bot lists apis. Let's create a new task to really
|
||||||
|
understand the gist of it. Let's create a task that will run every 5 minutes
|
||||||
|
which will keep our cache clean and minimal allowing your bot to grow and scale
|
||||||
|
much larger without needing a much bigger server to host it on. Start by pasting
|
||||||
|
the following snippet below:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../deps.ts";
|
import { botCache } from "../../deps.ts";
|
||||||
@@ -21,16 +32,24 @@ botCache.tasks.set(`taskname`, {
|
|||||||
|
|
||||||
## Understanding The Options
|
## Understanding The Options
|
||||||
|
|
||||||
- `name`: The name property is just the name of the task. Useful for logs and such to tell you which task is running.
|
- `name`: The name property is just the name of the task. Useful for logs and
|
||||||
- `interval`: The amount of millisconds to wait before executing this function again.
|
such to tell you which task is running.
|
||||||
|
- `interval`: The amount of millisconds to wait before executing this function
|
||||||
|
again.
|
||||||
- `execute`: The function to execute.
|
- `execute`: The function to execute.
|
||||||
|
|
||||||
## What is Cache and Why Should We Sweep It?
|
## What is Cache and Why Should We Sweep It?
|
||||||
|
|
||||||
Your bot's cache is the memory it is holding on to since it started. To understand this slightly easier we have an example: when you buy a server to host your bot on you can check how much RAM (memory) it has. That amount of memory can be expensive $$$ so it is best to build a bot that does not use a ton of memory. You can use this task to tell your bot to delete any memory it is holding on to that is not necessary.
|
Your bot's cache is the memory it is holding on to since it started. To
|
||||||
|
understand this slightly easier we have an example: when you buy a server to
|
||||||
|
host your bot on you can check how much RAM (memory) it has. That amount of
|
||||||
|
memory can be expensive $$$ so it is best to build a bot that does not use a ton
|
||||||
|
of memory. You can use this task to tell your bot to delete any memory it is
|
||||||
|
holding on to that is not necessary.
|
||||||
|
|
||||||
- Suppose we don't want to keep any presence cached.
|
- Suppose we don't want to keep any presence cached.
|
||||||
- Remove any members from cache that have not been active in the last 30 minutes.
|
- Remove any members from cache that have not been active in the last 30
|
||||||
|
minutes.
|
||||||
- Removes any messages sent over 10 minutes ago.
|
- Removes any messages sent over 10 minutes ago.
|
||||||
|
|
||||||
## Adding Pseudo Code
|
## Adding Pseudo Code
|
||||||
@@ -62,55 +81,64 @@ Now that we have a plan in place, let's add the code.
|
|||||||
import { botCache, cache } from "../../deps.ts";
|
import { botCache, cache } from "../../deps.ts";
|
||||||
import { Milliseconds } from "../utils/constants/time.ts";
|
import { Milliseconds } from "../utils/constants/time.ts";
|
||||||
|
|
||||||
const MESSAGE_LIFETIME = Milliseconds.MINUTE * 10
|
const MESSAGE_LIFETIME = Milliseconds.MINUTE * 10;
|
||||||
const MEMBER_LIFETIME = Milliseconds.MINUTE * 30
|
const MEMBER_LIFETIME = Milliseconds.MINUTE * 30;
|
||||||
|
|
||||||
botCache.tasks.set(`sweeper`, {
|
botCache.tasks.set(`sweeper`, {
|
||||||
name: `sweeper`,
|
name: `sweeper`,
|
||||||
interval: Milliseconds.MINUTE * 5,
|
interval: Milliseconds.MINUTE * 5,
|
||||||
execute: async function () {
|
execute: async function () {
|
||||||
const now = Date.now()
|
const now = Date.now();
|
||||||
// Delete presences from the bots cache.
|
// Delete presences from the bots cache.
|
||||||
cache.presences.clear()
|
cache.presences.clear();
|
||||||
|
|
||||||
// For every server, we will clean the cache
|
// For every server, we will clean the cache
|
||||||
cache.guilds.forEach(guild => {
|
cache.guilds.forEach((guild) => {
|
||||||
// Delete presences from the server caches.
|
// Delete presences from the server caches.
|
||||||
guild.presences.clear()
|
guild.presences.clear();
|
||||||
// Delete any member who has not been active in the last 30 minutes and is not currently in a voice channel
|
// Delete any member who has not been active in the last 30 minutes and is not currently in a voice channel
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
// For ever, message we will delete if necessary
|
// For ever, message we will delete if necessary
|
||||||
cache.messages.forEach(message => {
|
cache.messages.forEach((message) => {
|
||||||
// Delete any messages over 10 minutes old
|
// Delete any messages over 10 minutes old
|
||||||
if (now - message.timestamp > MESSAGE_LIFETIME) cache.messages.delete(message.id)
|
if (now - message.timestamp > MESSAGE_LIFETIME) {
|
||||||
})
|
cache.messages.delete(message.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Alrighty, we managed to add most of the code. But we hit a small obstacle. There is no way to tell when the last time a member was active was. In order to accomplish this we will need to add this functionality. No problem, let's get cracking.
|
Alrighty, we managed to add most of the code. But we hit a small obstacle. There
|
||||||
|
is no way to tell when the last time a member was active was. In order to
|
||||||
|
accomplish this we will need to add this functionality. No problem, let's get
|
||||||
|
cracking.
|
||||||
|
|
||||||
## Adding Member Activity Tracker
|
## Adding Member Activity Tracker
|
||||||
|
|
||||||
Let's go to the botCache in `cache.ts` file and add a `new Collection` that will hold the member's id + server id as the key since a member can be in multiple servers. The value for the map will be a timestamp showing when they were last active. We will be able to use this check, how long ago they were last active.
|
Let's go to the botCache in `cache.ts` file and add a `new Collection` that will
|
||||||
|
hold the member's id + server id as the key since a member can be in multiple
|
||||||
|
servers. The value for the map will be a timestamp showing when they were last
|
||||||
|
active. We will be able to use this check, how long ago they were last active.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
memberLastActive: new Collection<string, number>()
|
memberLastActive:
|
||||||
|
new Collection<string, number>();
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, let's go to our `messageCreate`, event and add 1 line to update this value.
|
Now, let's go to our `messageCreate`, event and add 1 line to update this value.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
botCache.memberLastActive.set(message.author.id, message.timestamp)
|
botCache.memberLastActive.set(message.author.id, message.timestamp);
|
||||||
```
|
```
|
||||||
|
|
||||||
This will store the time when the message was sent by the member using their id.
|
This will store the time when the message was sent by the member using their id.
|
||||||
|
|
||||||
## Cleaning Out Inactive Members
|
## Cleaning Out Inactive Members
|
||||||
|
|
||||||
Now, we can get back to cleaning out the members from the cache. The final code should look something like this:
|
Now, we can get back to cleaning out the members from the cache. The final code
|
||||||
|
should look something like this:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { botCache } from "../../mod.ts";
|
import { botCache } from "../../mod.ts";
|
||||||
@@ -157,12 +185,24 @@ botCache.tasks.set(`sweeper`, {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
If you reload/restart your bot now, you will begin to see massive improvements in cache/RAM usage. Feel free to take some time and customize this to your needs for your bot. For example, in this case, I opted to keep voice states but if your bot does not do anything voice related, you can save even more memory by deleting voice states.
|
If you reload/restart your bot now, you will begin to see massive improvements
|
||||||
|
in cache/RAM usage. Feel free to take some time and customize this to your needs
|
||||||
|
for your bot. For example, in this case, I opted to keep voice states but if
|
||||||
|
your bot does not do anything voice related, you can save even more memory by
|
||||||
|
deleting voice states.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Remember, optimizing cache is a very difficult thing to do so don't worry if it doesnt make too much sense. The main thing to take away from this guide is how to create a function that runs every X amount of time. When your bot grows enough where it reaches a point where you need to worry about optimzing the cache, join the Discord server and we can chat.
|
Remember, optimizing cache is a very difficult thing to do so don't worry if it
|
||||||
|
doesnt make too much sense. The main thing to take away from this guide is how
|
||||||
|
to create a function that runs every X amount of time. When your bot grows
|
||||||
|
enough where it reaches a point where you need to worry about optimzing the
|
||||||
|
cache, join the Discord server and we can chat.
|
||||||
|
|
||||||
Cheers 🎉
|
Cheers 🎉
|
||||||
|
|
||||||
Thank you for reading this step by step guide to creating a Discord bot. There are many more powerful things in Discordeno, but they take a little more advanced skills to master. Once you have gone through all this, feel free to jump into the advanced section of the guide to become an advanced level Discord Bot Developer!
|
Thank you for reading this step by step guide to creating a Discord bot. There
|
||||||
|
are many more powerful things in Discordeno, but they take a little more
|
||||||
|
advanced skills to master. Once you have gone through all this, feel free to
|
||||||
|
jump into the advanced section of the guide to become an advanced level Discord
|
||||||
|
Bot Developer!
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
# Hosting Your Bot Online 24/7!
|
# Hosting Your Bot Online 24/7!
|
||||||
|
|
||||||
Once you feel that you are ready to get your bot to stay online 24/7, follow the guide below. This guide, is going to use [Hyper Expert](https://p.hyper.expert/aff.php?aff=125) as our hosting provider. You can use any you wish, but I recommend using [Hyper Expert](https://p.hyper.expert/aff.php?aff=125) for the following reasons.
|
Once you feel that you are ready to get your bot to stay online 24/7, follow the
|
||||||
|
guide below. This guide, is going to use
|
||||||
|
[Hyper Expert](https://p.hyper.expert/aff.php?aff=125) as our hosting provider.
|
||||||
|
You can use any you wish, but I recommend using
|
||||||
|
[Hyper Expert](https://p.hyper.expert/aff.php?aff=125) for the following
|
||||||
|
reasons.
|
||||||
|
|
||||||
- I host all my bots on it. That's currently 6 bots with over 2 million users and 10K servers. They could grow to another 3-5 times their current size without issues.
|
- I host all my bots on it. That's currently 6 bots with over 2 million users
|
||||||
|
and 10K servers. They could grow to another 3-5 times their current size
|
||||||
|
without issues.
|
||||||
- Launch a high performance server in seconds.
|
- Launch a high performance server in seconds.
|
||||||
- One of the cheapest VPS options, I have ever seen!
|
- One of the cheapest VPS options, I have ever seen!
|
||||||
- The best part in my opinion is: **They have a Discord Server where you can get help if you need help with hosting your bot!!!**
|
- The best part in my opinion is: **They have a Discord Server where you can get
|
||||||
|
help if you need help with hosting your bot!!!**
|
||||||
|
|
||||||
## Local Computer 24/7
|
## Local Computer 24/7
|
||||||
|
|
||||||
If you have a computer that you can keep running 24/7, you can actually run the bot on it. Just use the PM2 module to run the bot in the background.
|
If you have a computer that you can keep running 24/7, you can actually run the
|
||||||
|
bot on it. Just use the PM2 module to run the bot in the background.
|
||||||
|
|
||||||
## Virtual Private Server
|
## Virtual Private Server
|
||||||
|
|
||||||
@@ -17,45 +26,58 @@ For most people, this is where you are going to want to be.
|
|||||||
|
|
||||||
### Buying The VPS
|
### Buying The VPS
|
||||||
|
|
||||||
Go to the [Hyper Expert Website](https://p.hyper.expert/aff.php?aff=125) and get a VPS of your choice. Depending on your needs, you can choose a larger more powerful VPS, but I recommend starting out with the base model and then raise it when you need to.
|
Go to the [Hyper Expert Website](https://p.hyper.expert/aff.php?aff=125) and get
|
||||||
|
a VPS of your choice. Depending on your needs, you can choose a larger more
|
||||||
|
powerful VPS, but I recommend starting out with the base model and then raise it
|
||||||
|
when you need to.
|
||||||
|
|
||||||
### Setting Up The VPS
|
### Setting Up The VPS
|
||||||
|
|
||||||
Once your ready and logged into the server, the first thing you should do is change your password!
|
Once your ready and logged into the server, the first thing you should do is
|
||||||
|
change your password!
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo passwd
|
sudo passwd
|
||||||
```
|
```
|
||||||
|
|
||||||
Enter the password twice and make sure you do not forget this password. Save it somewhere safe!
|
Enter the password twice and make sure you do not forget this password. Save it
|
||||||
|
somewhere safe!
|
||||||
|
|
||||||
Next, let's make sure you have the latest updates on your server so all bug fixes and features are available for your server.
|
Next, let's make sure you have the latest updates on your server so all bug
|
||||||
|
fixes and features are available for your server.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt update && sudo apt upgrade -y
|
sudo apt update && sudo apt upgrade -y
|
||||||
```
|
```
|
||||||
|
|
||||||
In case there is some bug that let's someone access your machine, you always want to have an extra layer of protection. So let's create a separate user on your server where your bot will be.
|
In case there is some bug that let's someone access your machine, you always
|
||||||
|
want to have an extra layer of protection. So let's create a separate user on
|
||||||
|
your server where your bot will be.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
adduser [USERNAME]
|
adduser [USERNAME]
|
||||||
```
|
```
|
||||||
|
|
||||||
You will be prompted to enter a password. Use a different password for this for increased security. Save this password as well!
|
You will be prompted to enter a password. Use a different password for this for
|
||||||
|
increased security. Save this password as well!
|
||||||
|
|
||||||
Once your user has been created, you now should give this user sudo powers so it can do everything your root access could.
|
Once your user has been created, you now should give this user sudo powers so it
|
||||||
|
can do everything your root access could.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
usermod -aG sudo [USERNAME]
|
usermod -aG sudo [USERNAME]
|
||||||
```
|
```
|
||||||
|
|
||||||
Now let's install the required stuff for our bot. The following command will install Deno to your server.
|
Now let's install the required stuff for our bot. The following command will
|
||||||
|
install Deno to your server.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -fsSL https://deno.land/x/install/install.sh | sh
|
curl -fsSL https://deno.land/x/install/install.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that we have Deno installed let's install Denon which will keep the process running even after you log out or close your VPS terminal. For Node.JS devs, Denon is like Nodemon/PM2 for Deno.
|
Now that we have Deno installed let's install Denon which will keep the process
|
||||||
|
running even after you log out or close your VPS terminal. For Node.JS devs,
|
||||||
|
Denon is like Nodemon/PM2 for Deno.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
deno install --allow-read --allow-run --allow-write --allow-net -f -q --unstable https://deno.land/x/denon/denon.ts
|
deno install --allow-read --allow-run --allow-write --allow-net -f -q --unstable https://deno.land/x/denon/denon.ts
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ export async function createMessage(data: MessageCreateOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Discord doesnt give guild id for getMessage() so this will fill it in
|
// Discord doesnt give guild id for getMessage() so this will fill it in
|
||||||
const guildIDFinal = guildID || (await cacheHandlers.get("channels", channelID))?.guildID || "";
|
const guildIDFinal = guildID ||
|
||||||
|
(await cacheHandlers.get("channels", channelID))?.guildID || "";
|
||||||
|
|
||||||
const message = Object.create(baseMessage, {
|
const message = Object.create(baseMessage, {
|
||||||
...restProps,
|
...restProps,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# Discordeno Interactions
|
# Discordeno Interactions
|
||||||
|
|
||||||
`interactions` is a standalone submodule that supports the webhooks through HTTP server to add support for Discord's interactions feature.
|
`interactions` is a standalone submodule that supports the webhooks through HTTP
|
||||||
This is a barebones interface that will create and handle requests from Discord API.
|
server to add support for Discord's interactions feature. This is a barebones
|
||||||
|
interface that will create and handle requests from Discord API.
|
||||||
|
|
||||||
- Complete and extremely fast security and verification checks
|
- Complete and extremely fast security and verification checks
|
||||||
- First-class TypeScript Support
|
- First-class TypeScript Support
|
||||||
|
|||||||
+14
-6
@@ -1,13 +1,21 @@
|
|||||||
# Discordeno Rest
|
# Discordeno Rest
|
||||||
|
|
||||||
A standalone and server-less REST module with functionality of REST, independently.
|
A standalone and server-less REST module with functionality of REST,
|
||||||
|
independently.
|
||||||
|
|
||||||
- Easily host on any serverless infrastructure.
|
- Easily host on any serverless infrastructure.
|
||||||
- Easy to use and setup with Cloudflare Workers (FREE for 100K requests per day!)
|
- Easy to use and setup with Cloudflare Workers (FREE for 100K requests per
|
||||||
|
day!)
|
||||||
- Freedom from global rate limit errors
|
- Freedom from global rate limit errors
|
||||||
- As your bot grows, you want to handle global rate limits better. Shards don't communicate fast enough to truly handle it properly so this allows 1 rest handler across the entire bot.
|
- As your bot grows, you want to handle global rate limits better. Shards
|
||||||
- In fact, you can host multiple instances of your bot and all connect to the same rest server.
|
don't communicate fast enough to truly handle it properly so this allows 1
|
||||||
|
rest handler across the entire bot.
|
||||||
|
- In fact, you can host multiple instances of your bot and all connect to the
|
||||||
|
same rest server.
|
||||||
- REST does not rest!
|
- REST does not rest!
|
||||||
- Separate rest means if your bot for whatever reason crashes, your requests that are queued will still keep going and will not be lost.
|
- Separate rest means if your bot for whatever reason crashes, your requests
|
||||||
- Seamless updates! When you want to update and reboot the bot, you could potentially lose tons of messages or responses that are in queue. Using this you could restart your bot without ever worrying about losing any responses.
|
that are queued will still keep going and will not be lost.
|
||||||
|
- Seamless updates! When you want to update and reboot the bot, you could
|
||||||
|
potentially lose tons of messages or responses that are in queue. Using this
|
||||||
|
you could restart your bot without ever worrying about losing any responses.
|
||||||
- Scalability! Scalability! Scalability!
|
- Scalability! Scalability! Scalability!
|
||||||
|
|||||||
Reference in New Issue
Block a user