mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
style: move to deno fmt (#1992)
* Create deno.json * run format * run format * ci: only check formatting * f * Update settings.json * Update settings.json
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"label": "Big Bot Guide",
|
||||
"position": 2
|
||||
}
|
||||
"label": "Big Bot Guide",
|
||||
"position": 2
|
||||
}
|
||||
|
||||
@@ -5,28 +5,42 @@ sidebar_label: Step 3 - Cache
|
||||
|
||||
# Step 3: Standalone Cache Process
|
||||
|
||||
The next part of this is going to be about making a standalone cache process. By now, you should have both a REST and a Gateway process ready. Before, we start handling events we should build a Cache handler.
|
||||
The next part of this is going to be about making a standalone cache process. By now, you should have both a REST and a
|
||||
Gateway process ready. Before, we start handling events we should build a Cache handler.
|
||||
|
||||
## Why Use Standalone Cache Process?
|
||||
|
||||
A standalone cache process allows you to retain cached data even after bot restarts. For example, if you are caching member roles to track when a role was added or removed, you may want to cache the members. The question then comes to play, when deciding where to keep your cache. Another reason to use this is, whether or not you are using a standalone gateway process.
|
||||
A standalone cache process allows you to retain cached data even after bot restarts. For example, if you are caching
|
||||
member roles to track when a role was added or removed, you may want to cache the members. The question then comes to
|
||||
play, when deciding where to keep your cache. Another reason to use this is, whether or not you are using a standalone
|
||||
gateway process.
|
||||
|
||||
- Start rest process
|
||||
- Start event handler process(bot)
|
||||
- Start gateway process.
|
||||
- Guild create events arrive providing all the data needed to cache in the bot process.
|
||||
- Guild create events arrive providing all the data needed to cache in the bot process.
|
||||
- Restart event handler process(maybe for an update/reboot)
|
||||
- You lost all guilds/channels/permissions etc and can not get them again without restarting gateway. This defeats the entire point of the standalone gateway.
|
||||
- You lost all guilds/channels/permissions etc and can not get them again without restarting gateway. This defeats the
|
||||
entire point of the standalone gateway.
|
||||
|
||||
If your cache is tied to the bot processes which is not tied to the gateway you lose all this info. The next thought is to just keep the Cache entirely in the gateway process however, I do not like this personally however, should you desire this you can do this as well. The reason I prefer not to do this is when your bot needs to make requests to your cache, you do not want it occupying the thread for processing other gateway events arriving from discord. A separate cache process makes it so it uses an entirely separate thread and will not slow down anything else.
|
||||
If your cache is tied to the bot processes which is not tied to the gateway you lose all this info. The next thought is
|
||||
to just keep the Cache entirely in the gateway process however, I do not like this personally however, should you desire
|
||||
this you can do this as well. The reason I prefer not to do this is when your bot needs to make requests to your cache,
|
||||
you do not want it occupying the thread for processing other gateway events arriving from discord. A separate cache
|
||||
process makes it so it uses an entirely separate thread and will not slow down anything else.
|
||||
|
||||
## Understand Cache Types
|
||||
|
||||
When I use the term cache process, this is interchangeable with any similar term such as "custom cache", "redis cache", "pgsql cache", etc... The fact is you can keep this "cache" anywhere. For this guide, we will implement a very simple cache using pgsql. Feel free to modify this any way you like as advanced as you like. The point is Discordeno cache is flexible enough to let you use anything for your Cache storage.
|
||||
When I use the term cache process, this is interchangeable with any similar term such as "custom cache", "redis cache",
|
||||
"pgsql cache", etc... The fact is you can keep this "cache" anywhere. For this guide, we will implement a very simple
|
||||
cache using pgsql. Feel free to modify this any way you like as advanced as you like. The point is Discordeno cache is
|
||||
flexible enough to let you use anything for your Cache storage.
|
||||
|
||||
## Setting Up The Cache
|
||||
|
||||
This step is for you to create the base schema for your cache. For example, if you want to implement a pgsql or redis cache perhaps you want to prepare the tables/schema. For this guide, we are just going to do a quick little hack to get a custom cache working.
|
||||
This step is for you to create the base schema for your cache. For example, if you want to implement a pgsql or redis
|
||||
cache perhaps you want to prepare the tables/schema. For this guide, we are just going to do a quick little hack to get
|
||||
a custom cache working.
|
||||
|
||||
Create a file in a path like `src/bot/cache/schema.sql`
|
||||
|
||||
@@ -55,14 +69,16 @@ Cache Tables:
|
||||
|
||||
Once you are finished continue forward, for the purpose of keeping this guide short we wont cover each table.
|
||||
|
||||
> You should also run this file to prepare your pgsql and have your pgsql database running by now. Or whatever, cache service you use.
|
||||
> You should also run this file to prepare your pgsql and have your pgsql database running by now. Or whatever, cache
|
||||
> service you use.
|
||||
|
||||
### Cache Handler
|
||||
|
||||
Now we will initiate our cache service. This may be different for you based on your choice of cache type. Since we are using PGSQL for our cache layer, we will now instantiate it.
|
||||
Now we will initiate our cache service. This may be different for you based on your choice of cache type. Since we are
|
||||
using PGSQL for our cache layer, we will now instantiate it.
|
||||
|
||||
```ts
|
||||
import { postgres } from '../../../deps.ts'
|
||||
import { postgres } from "../../../deps.ts";
|
||||
|
||||
// YOU CUSTOM PGSQL INFO GOES HERE
|
||||
const DATABASE_USERNAME = "";
|
||||
@@ -85,16 +101,17 @@ export const psql = postgres({
|
||||
types: {
|
||||
bigint: postgres.BigInt,
|
||||
},
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
To use the PGSQL driver we are using in this guide you can insert this into your `deps.ts`.
|
||||
To use the PGSQL driver we are using in this guide you can insert this into your `deps.ts`.
|
||||
|
||||
```ts
|
||||
// @deno-types="https://denopkg.com/porsager/postgres@e2a8595d7aa8c3c838b83b9bca7b890c1707ad2c/types/index.d.ts"
|
||||
export { default as postgres } from "https://denopkg.com/porsager/postgres@e2a8595d7aa8c3c838b83b9bca7b890c1707ad2c/deno/lib/index.js";
|
||||
```
|
||||
|
||||
> Note: Remember you can use any driver you like. For deno users we prefer to use this library for PGSQL because it is more stable and more performant.
|
||||
> Note: Remember you can use any driver you like. For deno users we prefer to use this library for PGSQL because it is
|
||||
> more stable and more performant.
|
||||
|
||||
Now that the cache layer is ready, we can proceed to begin creating our bot.
|
||||
Now that the cache layer is ready, we can proceed to begin creating our bot.
|
||||
|
||||
@@ -5,13 +5,19 @@ sidebar_label: Step 4 - Event Handler
|
||||
|
||||
# Step 4: Creating Standalone Event Handler
|
||||
|
||||
Now we are about to start working on the bot code itself. The last 3 steps should be completed by the time you reach this. The event handler process will be listening for events from any number of gateway instances and be ready to handle them.
|
||||
Now we are about to start working on the bot code itself. The last 3 steps should be completed by the time you reach
|
||||
this. The event handler process will be listening for events from any number of gateway instances and be ready to handle
|
||||
them.
|
||||
|
||||
In this guide, we may use the term `Bot` or the term `event handler`, remember that these refer to the same thing. This is your main bot code.
|
||||
In this guide, we may use the term `Bot` or the term `event handler`, remember that these refer to the same thing. This
|
||||
is your main bot code.
|
||||
|
||||
## Why Use Standalone Event Handler Process?
|
||||
|
||||
The standalone event handler is the portion of your bot code that you will be changing the most. The three previous steps created processes that are intended to never be turned off. This process is designed to let you restart whenever you wish and be incredibly quick to restart. Since we don't have the delay to start up shards anymore, your code becomes reloaded instantly.
|
||||
The standalone event handler is the portion of your bot code that you will be changing the most. The three previous
|
||||
steps created processes that are intended to never be turned off. This process is designed to let you restart whenever
|
||||
you wish and be incredibly quick to restart. Since we don't have the delay to start up shards anymore, your code becomes
|
||||
reloaded instantly.
|
||||
|
||||
## Creating Event Handlers
|
||||
|
||||
@@ -19,7 +25,7 @@ Create a file path like `src/bot/mod.ts`.
|
||||
|
||||
```ts
|
||||
import { DISCORD_TOKEN } from "../../configs.ts";
|
||||
import { createBot, Collection } from "../../deps.ts";
|
||||
import { Collection, createBot } from "../../deps.ts";
|
||||
import { psql } from "./cache/mod.ts";
|
||||
|
||||
export const bot = createBot({
|
||||
@@ -28,13 +34,13 @@ export const bot = createBot({
|
||||
// applicationId: 270010330782892032,
|
||||
intents: ["Guilds", "GuildMessages"],
|
||||
events: {
|
||||
messageCreate: function(bot, message) {
|
||||
messageCreate: function (bot, message) {
|
||||
console.log("message arrived");
|
||||
},
|
||||
},
|
||||
cache: {
|
||||
isAsync: true,
|
||||
customTableCreator: function(table) {
|
||||
customTableCreator: function (table) {
|
||||
const tables = {
|
||||
users: "users",
|
||||
channels: "channels",
|
||||
@@ -51,9 +57,11 @@ export const bot = createBot({
|
||||
return {
|
||||
/** Get a single item from the table */
|
||||
async get(key) {
|
||||
return await psql`SELECT * FROM ${psql(
|
||||
tables[table]
|
||||
)} WHERE "id" = ${psql.types.bigint(key)}`;
|
||||
return await psql`SELECT * FROM ${
|
||||
psql(
|
||||
tables[table],
|
||||
)
|
||||
} WHERE "id" = ${psql.types.bigint(key)}`;
|
||||
},
|
||||
/** Completely empty this table. */
|
||||
async clear() {
|
||||
@@ -61,17 +69,21 @@ export const bot = createBot({
|
||||
},
|
||||
/** Delete the data related to this key from table. */
|
||||
async delete(key) {
|
||||
await psql`DELETE FROM ${psql(
|
||||
tables[table]
|
||||
)} WHERE "id" = ${psql.types.bigint(key)}`;
|
||||
await psql`DELETE FROM ${
|
||||
psql(
|
||||
tables[table],
|
||||
)
|
||||
} WHERE "id" = ${psql.types.bigint(key)}`;
|
||||
return true;
|
||||
},
|
||||
/** Check if there is data assigned to this key. */
|
||||
async has(key) {
|
||||
return Boolean(
|
||||
await psql`SELECT 1 FROM ${psql(
|
||||
tables[table]
|
||||
)} WHERE "id" = ${psql.types.bigint(key)}`
|
||||
await psql`SELECT 1 FROM ${
|
||||
psql(
|
||||
tables[table],
|
||||
)
|
||||
} WHERE "id" = ${psql.types.bigint(key)}`,
|
||||
);
|
||||
},
|
||||
/** Check how many items are stored in this table. */
|
||||
@@ -81,10 +93,12 @@ export const bot = createBot({
|
||||
},
|
||||
/** Store new data to this table. */
|
||||
async set(key, data) {
|
||||
await psql`INSERT INTO ${psql(tables[table])} ${psql(
|
||||
data,
|
||||
...Object.keys(data)
|
||||
)}`;
|
||||
await psql`INSERT INTO ${psql(tables[table])} ${
|
||||
psql(
|
||||
data,
|
||||
...Object.keys(data),
|
||||
)
|
||||
}`;
|
||||
return true;
|
||||
},
|
||||
// THESE TWO ARE USELESS FOR CUSTOM CACHE BUT NEED TO SHUT UP TS ERRORS
|
||||
@@ -105,15 +119,23 @@ Alright that was a lot of code. Now let's break it down little by little.
|
||||
**Basic Keys**
|
||||
|
||||
- `token` if you can't figure this out stop reading and find another guide please. Thanks.
|
||||
- `botId` This is going to be your bot id. The reason we require this here is because we are going to set up a standalone gateway process. With most other libs, they can fill this information using the READY event. However, since our gateway is designed not to reboot, we are not going to get the READY event whenever we restart our bot. This means we won't be able to fill this information later. Another method to get the id is to use the `token` but discord developers have mentioned that this behavior is not documented and not supposed to be relied on to remain stable. Due to these reasons, we chose to just require the bot id be passed here.
|
||||
- `botId` This is going to be your bot id. The reason we require this here is because we are going to set up a
|
||||
standalone gateway process. With most other libs, they can fill this information using the READY event. However, since
|
||||
our gateway is designed not to reboot, we are not going to get the READY event whenever we restart our bot. This means
|
||||
we won't be able to fill this information later. Another method to get the id is to use the `token` but discord
|
||||
developers have mentioned that this behavior is not documented and not supposed to be relied on to remain stable. Due
|
||||
to these reasons, we chose to just require the bot id be passed here.
|
||||
- `applicationId` is an optional choice if your bot is old and has a unique id different from it's bot id.
|
||||
- `intents`: Provide the intents you like using strings or a number. String form supports autocomplete and type safety.
|
||||
- `events`: These are your event handler functions. When a MESSAGE_CREATE event arrives from Discord it will be processed here. We will set up the routing to run these functions later in the guide but for now you can see how to set it up. Note, you can create these functions in separate files and just import them here as you wish.
|
||||
- `events`: These are your event handler functions. When a MESSAGE_CREATE event arrives from Discord it will be
|
||||
processed here. We will set up the routing to run these functions later in the guide but for now you can see how to
|
||||
set it up. Note, you can create these functions in separate files and just import them here as you wish.
|
||||
- `cache`: This is going to be the cache part. We will discuss this more below.
|
||||
|
||||
### Understanding Cache Option
|
||||
|
||||
Since we are using a standalone gateway a custom cache is essentially required as explained in step 3 of this guide. Please remember, to mark the cache as `async`
|
||||
Since we are using a standalone gateway a custom cache is essentially required as explained in step 3 of this guide.
|
||||
Please remember, to mark the cache as `async`
|
||||
|
||||
```ts
|
||||
cache: {
|
||||
@@ -121,7 +143,9 @@ cache: {
|
||||
}
|
||||
```
|
||||
|
||||
When you opt into the async cache, you must also provide a table creator function. This will not actually create any tables but it will create an object with methods to manage your "tables". Man we need a better name for this. Please send recommendations to @Skillz4Killz in discord. Thanks. Until then, please blame wolf for the terrible name. :)
|
||||
When you opt into the async cache, you must also provide a table creator function. This will not actually create any
|
||||
tables but it will create an object with methods to manage your "tables". Man we need a better name for this. Please
|
||||
send recommendations to @Skillz4Killz in discord. Thanks. Until then, please blame wolf for the terrible name. :)
|
||||
|
||||
Alrighty, now let's dig deeper into this function.
|
||||
|
||||
@@ -140,7 +164,8 @@ const tables = {
|
||||
if (!tables[table]) throw new Error("I HACKED ITOH!");
|
||||
```
|
||||
|
||||
This part of the code is only going to make sense if you are used to PGSQL. To prevent any attacks here we will forcibly control which table will be used.
|
||||
This part of the code is only going to make sense if you are used to PGSQL. To prevent any attacks here we will forcibly
|
||||
control which table will be used.
|
||||
|
||||
This function must return an object with several methods on it. You can see the methods above.
|
||||
|
||||
@@ -151,30 +176,38 @@ async get(key) {
|
||||
}
|
||||
```
|
||||
|
||||
You can insert any code you desire for your cache system here. Since we were using PGSQL, we used sql queries to make these requests. However, should you need to communicate to Redis or anything else of your choice, you can do so here.
|
||||
You can insert any code you desire for your cache system here. Since we were using PGSQL, we used sql queries to make
|
||||
these requests. However, should you need to communicate to Redis or anything else of your choice, you can do so here.
|
||||
|
||||
> Note: The .filter() and .forEach() methods are unnecessary and should not be used for your bot as they are not optimized for performance. These are made for smaller bot users who would not leave itoh alone and in order to please them itoh gave them their hearts desire! LMAO!
|
||||
> Note: The .filter() and .forEach() methods are unnecessary and should not be used for your bot as they are not
|
||||
> optimized for performance. These are made for smaller bot users who would not leave itoh alone and in order to please
|
||||
> them itoh gave them their hearts desire! LMAO!
|
||||
|
||||
## Customizing Internal Code
|
||||
|
||||
One of the best parts about discordeno is the flexibility. In order to show this off, we will use the `user` example but you can apply this to any part of the library.
|
||||
One of the best parts about discordeno is the flexibility. In order to show this off, we will use the `user` example but
|
||||
you can apply this to any part of the library.
|
||||
|
||||
### Why Is Customizing Important?
|
||||
|
||||
At large scale, every single property can become expensive to store in your cache. For example, if your bot does not make use of a `channel.topic` why storing potentially millions of strings in your memory for something you never need/user. This could save you potentially GBs of memory to just remove this one property.
|
||||
At large scale, every single property can become expensive to store in your cache. For example, if your bot does not
|
||||
make use of a `channel.topic` why storing potentially millions of strings in your memory for something you never
|
||||
need/user. This could save you potentially GBs of memory to just remove this one property.
|
||||
|
||||
### Customizing Process
|
||||
|
||||
First, let's create a file in some path like `src/bot/internals/mod.ts`. Note that we will create quite a few files below simply to keep code cleaner and simpler, in expectation that it will grow more complex later. You can merge them as you wish.
|
||||
First, let's create a file in some path like `src/bot/internals/mod.ts`. Note that we will create quite a few files
|
||||
below simply to keep code cleaner and simpler, in expectation that it will grow more complex later. You can merge them
|
||||
as you wish.
|
||||
|
||||
```ts
|
||||
import { Bot } from "../../../deps.ts";
|
||||
import { customizeBotTransformers } from "./transformers/mod.ts";
|
||||
|
||||
export function customizeBotInternals(bot: Bot) {
|
||||
bot = customizeBotTransformers(bot);
|
||||
// ADD AS MANY MORE CUSTOMIZATIONS HERE AS YOU LIKE TO HANDLERS, HELPERS, UTILS ETC...
|
||||
return bot;
|
||||
bot = customizeBotTransformers(bot);
|
||||
// ADD AS MANY MORE CUSTOMIZATIONS HERE AS YOU LIKE TO HANDLERS, HELPERS, UTILS ETC...
|
||||
return bot;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -182,19 +215,19 @@ We also need to add another file now at `src/bot/internals/transformers/mod.ts`
|
||||
|
||||
```ts
|
||||
import { Bot } from "../../../../deps.ts";
|
||||
import { customizeUserTransformer } from './user.ts'
|
||||
import { customizeUserTransformer } from "./user.ts";
|
||||
|
||||
export function customizeBotTransformers(bot: Bot) {
|
||||
bot = customizeUserTransformer(bot)
|
||||
bot = customizeUserTransformer(bot);
|
||||
// ADD ANY MORE CUSTOM TRANSFORMERS HERE
|
||||
return bot
|
||||
return bot;
|
||||
}
|
||||
```
|
||||
|
||||
One more file at `src/bot/internals/transformers/user.ts`
|
||||
|
||||
```ts
|
||||
import { Bot, DiscordenoUser, transformUser } from '../../../../deps.ts'
|
||||
import { Bot, DiscordenoUser, transformUser } from "../../../../deps.ts";
|
||||
|
||||
export function customizeUserTransformer(bot: Bot) {
|
||||
bot.transformers.user = function (bot, payload) {
|
||||
@@ -202,21 +235,27 @@ export function customizeUserTransformer(bot: Bot) {
|
||||
const { system, locale, verified, email, flags, mfaEnabled, premiumType, publicFlags, ...user } = transformUser(
|
||||
bot,
|
||||
payload,
|
||||
)
|
||||
);
|
||||
|
||||
// RETURN ONLY USEFUL PROPS WE NEED TO USE AND CACHE IF NECESSARY
|
||||
return user as DiscordenoUser
|
||||
}
|
||||
return user as DiscordenoUser;
|
||||
};
|
||||
|
||||
return bot;
|
||||
}
|
||||
```
|
||||
|
||||
First we override the internal transformer for the `user` object. What's cool is the typings will be automatically provided :) Next, we use the `transformUser` function from the lib itself to make it create the internal user version. The reason I do this is so when I update the library and a new property is added or removed i can simply update and get it. Should you desire maximum control you can remove this entirely and only have what you want no matter what discord sends. Discordeno gives you the ability to stay in control.
|
||||
First we override the internal transformer for the `user` object. What's cool is the typings will be automatically
|
||||
provided :) Next, we use the `transformUser` function from the lib itself to make it create the internal user version.
|
||||
The reason I do this is so when I update the library and a new property is added or removed i can simply update and get
|
||||
it. Should you desire maximum control you can remove this entirely and only have what you want no matter what discord
|
||||
sends. Discordeno gives you the ability to stay in control.
|
||||
|
||||
This method can be applied to any transformer, helper function, gateway event handler, util function or any part of the library. Anything and everything is possible to override. You do NOT need to fork and modify the library ever and give yourself a headache trying to maintain your fork with updates.
|
||||
This method can be applied to any transformer, helper function, gateway event handler, util function or any part of the
|
||||
library. Anything and everything is possible to override. You do NOT need to fork and modify the library ever and give
|
||||
yourself a headache trying to maintain your fork with updates.
|
||||
|
||||
## Handling Incoming Gateway Events
|
||||
|
||||
Remember, this is a separate process we need to make sure we are listening to incoming events from our gateway instances. Since we used http in our Gateway step, we can create an http listener here as well.
|
||||
|
||||
Remember, this is a separate process we need to make sure we are listening to incoming events from our gateway
|
||||
instances. Since we used http in our Gateway step, we can create an http listener here as well.
|
||||
|
||||
@@ -5,7 +5,8 @@ sidebar_label: Step 2 - Gateway
|
||||
|
||||
# Step 2: Creating A Standalone Gateway Process
|
||||
|
||||
If you are reading this, you should have your REST process completed. We are going to need it here. This process will be connecting to discord's websockets which will send you all the events.
|
||||
If you are reading this, you should have your REST process completed. We are going to need it here. This process will be
|
||||
connecting to discord's websockets which will send you all the events.
|
||||
|
||||
Before, we dive into how, here is a quick summary of why you will want a standalone gateway process.
|
||||
|
||||
@@ -13,63 +14,51 @@ Before, we dive into how, here is a quick summary of why you will want a standal
|
||||
|
||||
- **Zero Downtime Updates**:
|
||||
|
||||
- Your bot can be updated in a matter of seconds. With normal sharding, you
|
||||
have to restart which also has to process identifying all your shards with a
|
||||
1/~5s rate limit. With WS handling moved to a proxy process, this allows you
|
||||
to instantly get the bot code restarted without any concerns of delays. If
|
||||
you have a bot on 200,000 servers normally this would mean a 20 minute delay
|
||||
to restart your bot if you made a small change and restarted.
|
||||
- Your bot can be updated in a matter of seconds. With normal sharding, you have to restart which also has to process
|
||||
identifying all your shards with a 1/~5s rate limit. With WS handling moved to a proxy process, this allows you to
|
||||
instantly get the bot code restarted without any concerns of delays. If you have a bot on 200,000 servers normally
|
||||
this would mean a 20 minute delay to restart your bot if you made a small change and restarted.
|
||||
|
||||
- **Zero Downtime Resharding**:
|
||||
|
||||
- Discord stops letting your bot get added to new servers at certain points in
|
||||
time. For example, suppose you had 150,000 servers running 150 shards. The
|
||||
maximum amount of servers your shards could hold is 150 \* 2500 = 375,000. If
|
||||
your bot reaches this, it can no longer join new servers until it re-shards.
|
||||
- DD proxy provides 2 types of re-sharding. Automated and manual. You can also
|
||||
have both.
|
||||
- `Automated`: This system will automatically begin a Zero-downtime
|
||||
resharding process behind the scenes when you reach 80% of your maximum
|
||||
servers allowed by your shards. For example, since 375,000 was the max, at
|
||||
300,000 we would begin re-sharding behind the scenes with `ZERO DOWNTIME`.
|
||||
- Discord stops letting your bot get added to new servers at certain points in time. For example, suppose you had
|
||||
150,000 servers running 150 shards. The maximum amount of servers your shards could hold is 150 \* 2500 = 375,000.
|
||||
If your bot reaches this, it can no longer join new servers until it re-shards.
|
||||
- DD proxy provides 2 types of re-sharding. Automated and manual. You can also have both.
|
||||
- `Automated`: This system will automatically begin a Zero-downtime resharding process behind the scenes when you
|
||||
reach 80% of your maximum servers allowed by your shards. For example, since 375,000 was the max, at 300,000 we
|
||||
would begin re-sharding behind the scenes with `ZERO DOWNTIME`.
|
||||
- 80% of maximum servers reached (The % of 80% is customizable.)
|
||||
- Identify limits have room to allow re-sharding. (Also customizable)
|
||||
- `Manual`: You can also trigger this manually should you choose.
|
||||
|
||||
- **Horizontal Scaling**:
|
||||
|
||||
- The proxy system allows you to scale the bot horizontally. When you reach a
|
||||
huge size, you can either keep spending more money to keep beefing up your
|
||||
server or you can buy several cheaper servers and scale horizontally. The
|
||||
proxy means you can have WS handling on a completely separate system.
|
||||
- The proxy system allows you to scale the bot horizontally. When you reach a huge size, you can either keep spending
|
||||
more money to keep beefing up your server or you can buy several cheaper servers and scale horizontally. The proxy
|
||||
means you can have WS handling on a completely separate system.
|
||||
|
||||
- **No Loss Restarts**:
|
||||
|
||||
- When you restart a bot without the proxy system, normally you would lose
|
||||
many events. Users may be using commands or messages are sent that will not
|
||||
be filtered. As your bot's grow this number rises dramatically. Users may
|
||||
join who wont get the auto-roles or any other actions your bot should take.
|
||||
With the proxy system, you can keep restarting your bot and never lose any
|
||||
events. Events will be put into a queue while your bot is down(max size of
|
||||
queue is customizable), once the bot is available the queue will begin
|
||||
processing all events.
|
||||
- When you restart a bot without the proxy system, normally you would lose many events. Users may be using commands or
|
||||
messages are sent that will not be filtered. As your bot's grow this number rises dramatically. Users may join who
|
||||
wont get the auto-roles or any other actions your bot should take. With the proxy system, you can keep restarting
|
||||
your bot and never lose any events. Events will be put into a queue while your bot is down(max size of queue is
|
||||
customizable), once the bot is available the queue will begin processing all events.
|
||||
|
||||
- **Controllers**:
|
||||
|
||||
- The controller aspect gives you full control over everything inside the
|
||||
proxy. You can provide a function to simply override the handler. For
|
||||
example, if you would like a certain function to do something different,
|
||||
instead of having to fork and maintain your fork, you can just provide a
|
||||
function to override.
|
||||
- The controller aspect gives you full control over everything inside the proxy. You can provide a function to simply
|
||||
override the handler. For example, if you would like a certain function to do something different, instead of having
|
||||
to fork and maintain your fork, you can just provide a function to override.
|
||||
|
||||
- **Clustering With Workers**:
|
||||
- Take full advantage of all your CPU cores by using workers to spread the
|
||||
load. Control how many shards per worker and how many workers to maximize
|
||||
efficiency!
|
||||
- Take full advantage of all your CPU cores by using workers to spread the load. Control how many shards per worker
|
||||
and how many workers to maximize efficiency!
|
||||
|
||||
## Creating Gateway Manager
|
||||
|
||||
Create a file under some path like `src/gateway/mod.ts`.
|
||||
Create a file under some path like `src/gateway/mod.ts`.
|
||||
|
||||
```ts
|
||||
import { DISCORD_TOKEN, REST_AUTHORIZATION, REST_PORT } from "../../configs.ts";
|
||||
@@ -82,13 +71,17 @@ const rest = createRestManager({
|
||||
});
|
||||
```
|
||||
|
||||
Throw another rest manager here which will be responsible for calling the main REST process we created in Step 1. This will allow your gateway to communicate to the other process. Remember this is just to communicate outwards, this file should not have the http listener.
|
||||
Throw another rest manager here which will be responsible for calling the main REST process we created in Step 1. This
|
||||
will allow your gateway to communicate to the other process. Remember this is just to communicate outwards, this file
|
||||
should not have the http listener.
|
||||
|
||||
> Feel free to refactor and optimize this should you wish to move `const rest...` to a separate file and reuse in both steps.
|
||||
> Feel free to refactor and optimize this should you wish to move `const rest...` to a separate file and reuse in both
|
||||
> steps.
|
||||
|
||||
### Getting Gateway Bot Data
|
||||
|
||||
Now we need to use this rest manager to call the api to get information about how to connect to discord's gateway for your bot.
|
||||
Now we need to use this rest manager to call the api to get information about how to connect to discord's gateway for
|
||||
your bot.
|
||||
|
||||
```ts
|
||||
const rest = createRestManager({
|
||||
@@ -98,7 +91,7 @@ const rest = createRestManager({
|
||||
});
|
||||
|
||||
// CALL THE REST PROCESS TO GET GATEWAY DATA
|
||||
const result = await rest.runMethod(rest, 'get', endpoints.GATEWAY_BOT).then((res) => ({
|
||||
const result = await rest.runMethod(rest, "get", endpoints.GATEWAY_BOT).then((res) => ({
|
||||
url: res.url,
|
||||
shards: res.shards,
|
||||
sessionStartLimit: {
|
||||
@@ -118,7 +111,7 @@ With this info, we can now create our gateway manager.
|
||||
const gateway = createGatewayManager({
|
||||
secretKey: EVENT_HANDLER_SECRET_KEY,
|
||||
token: DISCORD_TOKEN,
|
||||
intents: ['GuildMessages', 'Guilds'],
|
||||
intents: ["GuildMessages", "Guilds"],
|
||||
shardsRecommended: result.shards,
|
||||
sessionStartLimitTotal: result.sessionStartLimit.total,
|
||||
sessionStartLimitRemaining: result.sessionStartLimit.remaining,
|
||||
@@ -131,7 +124,7 @@ const gateway = createGatewayManager({
|
||||
await fetch(`${EVENT_HANDLER_URL}:${EVENT_HANDLER_PORT}`, {
|
||||
headers: {
|
||||
Authorization: gateway.secretKey,
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
shardId,
|
||||
data,
|
||||
@@ -140,61 +133,78 @@ const gateway = createGatewayManager({
|
||||
})
|
||||
// BELOW IS FOR SOLVING DENO MEMORY LEAK. Node users do your thing.
|
||||
.then((res) => res.text())
|
||||
.catch(() => null)
|
||||
.catch(() => null);
|
||||
},
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
**Basic Keys**
|
||||
|
||||
- `EVENT_HANDLER_SECRET_KEY` is from your configs that will be used to make sure requests sent to your event handler process are indeed from you.
|
||||
- `EVENT_HANDLER_SECRET_KEY` is from your configs that will be used to make sure requests sent to your event handler
|
||||
process are indeed from you.
|
||||
- `DISCORD_TOKEN` if you can't figure this out, this guide isn't for you. Please find another.
|
||||
- `intents` pass in a number or a string of intents. Autocomplete/type-safety is provided for strings :)
|
||||
|
||||
**Discord Data Keys**: These keys will be the data you got from the gateway request we made earlier.
|
||||
**Discord Data Keys**: These keys will be the data you got from the gateway request we made earlier.
|
||||
|
||||
- `shardsRecommended`
|
||||
- `sessionStartLimitTotal`
|
||||
- `sessionStartLimitTotal`
|
||||
- `sessionStartLimitRemaining`
|
||||
- `sessionStartLimitResetAfter`
|
||||
- `maxConcurrency`
|
||||
|
||||
**Powerful Keys**
|
||||
|
||||
If your bot is going to be run on one process, you can re-use the data that discord gave you to connect.
|
||||
If your bot is going to be run on one process, you can re-use the data that discord gave you to connect.
|
||||
|
||||
- `maxShards`: is the maximum number of shards you want to use for connecting your bot. Should you think Discord is not smart enough to recommend a good amount, use this to override their choice. Highly recommend just using theirs.
|
||||
- `maxShards`: is the maximum number of shards you want to use for connecting your bot. Should you think Discord is not
|
||||
smart enough to recommend a good amount, use this to override their choice. Highly recommend just using theirs.
|
||||
- `lastShardId`: is the last shard you want to connect in this process.
|
||||
- Using a combination of `lastShardId` & `firstShardId`, you can create several processes or even several servers to handle different amounts of shards should your bot get that big to require horizontal scaling. You can control how many shards each gateway manager will be responsible for.
|
||||
- `reshard`: Whether or not to automatically reshard the bot when necessary with zero downtime deployment strategy. Default: true.
|
||||
- Using a combination of `lastShardId` & `firstShardId`, you can create several processes or even several servers to
|
||||
handle different amounts of shards should your bot get that big to require horizontal scaling. You can control how
|
||||
many shards each gateway manager will be responsible for.
|
||||
- `reshard`: Whether or not to automatically reshard the bot when necessary with zero downtime deployment strategy.
|
||||
Default: true.
|
||||
- `reshardPercentage`: The % of servers to trigger a reshard. Default: 80%.
|
||||
- `spawnShardDelay`: The delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 2500. YOU DON"T WANT TO HIT THE RATE LIMIT!!! This is mainly if you are changing internals a lot and need to modify this behavior.
|
||||
- `useOptimalLargeBotSharding`: Whether or not the resharder should automatically switch to LARGE BOT SHARDING when you are above 100K servers.
|
||||
- `spawnShardDelay`: The delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 2500. YOU DON"T WANT
|
||||
TO HIT THE RATE LIMIT!!! This is mainly if you are changing internals a lot and need to modify this behavior.
|
||||
- `useOptimalLargeBotSharding`: Whether or not the resharder should automatically switch to LARGE BOT SHARDING when you
|
||||
are above 100K servers.
|
||||
- `shardsPerCluster`: The amount of shards to load per worker. Discussed in detail below.
|
||||
- `maxClusters`: The maximum amount of workers to use for your bot.
|
||||
|
||||
#### Gateway Cache
|
||||
|
||||
There is a few things that we cache in the gateway process directly, because sending them across the network is not ideal. This is done to support custom cache functionality.
|
||||
There is a few things that we cache in the gateway process directly, because sending them across the network is not
|
||||
ideal. This is done to support custom cache functionality.
|
||||
|
||||
- `guildIds`: Used for determining what type of GUILD_CREATE event is received.
|
||||
- `loadingGuildIds`: Used for determining if all guilds have arrived when initially connecting.
|
||||
- `editedMessages`: Used to prevent spam of events across the network. MESSAGE_UPDATE are an extremely heavy event. Any embed or link that is in a message will unfurl triggerring a message update event. This is undesired behavior for 99% of bots out there. If someone sends a message with 5 urls, in there you will get a MESSAGE_CREATE and 5 MESSAGE_UPDATE events. If that user edits a single letter on it you now get 6 MESSAGE_UPDATE events, 1 for the content change and 5 more for each url being unfurled. The editedMessages cache checks if the content of the message changed or not before sending the event downstream. Override this behavior if you need different behavior.
|
||||
- `editedMessages`: Used to prevent spam of events across the network. MESSAGE_UPDATE are an extremely heavy event. Any
|
||||
embed or link that is in a message will unfurl triggerring a message update event. This is undesired behavior for 99%
|
||||
of bots out there. If someone sends a message with 5 urls, in there you will get a MESSAGE_CREATE and 5 MESSAGE_UPDATE
|
||||
events. If that user edits a single letter on it you now get 6 MESSAGE_UPDATE events, 1 for the content change and 5
|
||||
more for each url being unfurled. The editedMessages cache checks if the content of the message changed or not before
|
||||
sending the event downstream. Override this behavior if you need different behavior.
|
||||
|
||||
#### Gateway Method Overriding
|
||||
|
||||
One of the benefits of Discordeno is that you can override/customize anything from the library. Should you desire to change the logic in any method it is as simple as:
|
||||
One of the benefits of Discordeno is that you can override/customize anything from the library. Should you desire to
|
||||
change the logic in any method it is as simple as:
|
||||
|
||||
```ts
|
||||
// TYPINGS WILL BE AUTOMATICALLY PROVIDED
|
||||
gateway.heartbeat = function(gateway, shardId, interval) {
|
||||
// YOUR CUSTOM HANDLING CODE HERE
|
||||
}
|
||||
gateway.heartbeat = function (gateway, shardId, interval) {
|
||||
// YOUR CUSTOM HANDLING CODE HERE
|
||||
};
|
||||
```
|
||||
|
||||
### Handle Discord Payloads
|
||||
|
||||
One of the big things we didn't cover yet is the handler for discord payloads. This is the main sauce of your gateway process here. This is going to take the events that the gateway manager processed and send it to your event handler. How you wish to communicate with your event handler is up to you. For this guide, we will use http, but you can replace that with anything you like.
|
||||
One of the big things we didn't cover yet is the handler for discord payloads. This is the main sauce of your gateway
|
||||
process here. This is going to take the events that the gateway manager processed and send it to your event handler. How
|
||||
you wish to communicate with your event handler is up to you. For this guide, we will use http, but you can replace that
|
||||
with anything you like.
|
||||
|
||||
```ts
|
||||
handleDiscordPayload: async function (_, data, shardId) {
|
||||
@@ -215,27 +225,34 @@ handleDiscordPayload: async function (_, data, shardId) {
|
||||
},
|
||||
```
|
||||
|
||||
You can change this function to use a WS or any form of communication you prefer to use to send this to your event handler.
|
||||
You can change this function to use a WS or any form of communication you prefer to use to send this to your event
|
||||
handler.
|
||||
|
||||
## Spawning Shards
|
||||
|
||||
Once you are ready and the gateway has been created as you desired, we can begin spawning the shards.
|
||||
|
||||
```ts
|
||||
gateway.spawnShards(gateway)
|
||||
gateway.spawnShards(gateway);
|
||||
```
|
||||
|
||||
## Workers
|
||||
|
||||
Now, we should take a minute here to talk about workers. Workers are just Clusters in Node.js
|
||||
|
||||
When you have a big bot and you are processing millions of events, you need to speed up that processing. Keeping it in 1 thread is not very nice since JavaScript is single threaded. This means it can only process 1 event at a time. With workers, you can make it process several events at the same time. We mentioned the `shardsPerCluster` earlier. This option was added to allow you to choose how many shards should be managed by each worker.
|
||||
When you have a big bot and you are processing millions of events, you need to speed up that processing. Keeping it in 1
|
||||
thread is not very nice since JavaScript is single threaded. This means it can only process 1 event at a time. With
|
||||
workers, you can make it process several events at the same time. We mentioned the `shardsPerCluster` earlier. This
|
||||
option was added to allow you to choose how many shards should be managed by each worker.
|
||||
|
||||
When shards are spawn they are triggered by a method on gateway.
|
||||
|
||||
```ts
|
||||
gateway.tellClusterToIdentify = async function(gateway, workerId, shardId, bucketId) {
|
||||
gateway.tellClusterToIdentify = async function (gateway, workerId, shardId, bucketId) {
|
||||
await gateway.identify(gateway, shardId, gateway.maxShards);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can choose to replace the handler with any desired functionality you like. For example, should should you want to create a new worker for each new workerId that appears and have that worker trigger the identify functionaly. How you choose to handler workers is left in your care.
|
||||
You can choose to replace the handler with any desired functionality you like. For example, should should you want to
|
||||
create a new worker for each new workerId that appears and have that worker trigger the identify functionaly. How you
|
||||
choose to handler workers is left in your care.
|
||||
|
||||
@@ -5,7 +5,8 @@ sidebar_label: Step 1 - REST
|
||||
|
||||
# Creating A Standalone REST Process
|
||||
|
||||
The first thing we want to make is our standalone REST process. This process will be used by almost every other process, so it is going to be the foundation of the bot.
|
||||
The first thing we want to make is our standalone REST process. This process will be used by almost every other process,
|
||||
so it is going to be the foundation of the bot.
|
||||
|
||||
Before, we dive into how, here is a quick summary of why you will want a standalone REST process.
|
||||
|
||||
@@ -13,19 +14,18 @@ Before, we dive into how, here is a quick summary of why you will want a standal
|
||||
|
||||
- Easily host on any serverless infrastructure.
|
||||
- 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.
|
||||
- In fact, you can host multiple instances of your bot and all connect to the
|
||||
same rest server.
|
||||
- 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.
|
||||
- In fact, you can host multiple instances of your bot and all connect to the same rest server.
|
||||
- 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.
|
||||
- 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.
|
||||
- Separate rest means if your bot for whatever reason crashes, your requests 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.
|
||||
- Single source of contact to Discord API
|
||||
- This will allow you to make requests to discord from anywhere including a bot dashboard. You no longer need to have to communicate to your bot processes just to make a request or anything. Free up your bot process for processing bot events.
|
||||
- This will allow you to make requests to discord from anywhere including a bot dashboard. You no longer need to have
|
||||
to communicate to your bot processes just to make a request or anything. Free up your bot process for processing bot
|
||||
events.
|
||||
- Scalability! Scalability! Scalability!
|
||||
|
||||
## Preparations
|
||||
@@ -55,20 +55,24 @@ const rest = createRestManager({
|
||||
|
||||
- `createRestManager` is imported from your deps file which should have exported everything from discordeno.
|
||||
- `DISCORD_TOKEN` is the bots token itself.
|
||||
- `REST_AUTHORIZATION` is a special password you want to use to authenticate that requests being sent to your port are indeed from you.
|
||||
- `customUrl` the url where this rest process will be running. This can be localhost which we are using in this guide if you want all processes on same VPS or separate them to different servers for horizontal scaling. `REST_PORT` is just the port where you want the process hosted.
|
||||
- `REST_AUTHORIZATION` is a special password you want to use to authenticate that requests being sent to your port are
|
||||
indeed from you.
|
||||
- `customUrl` the url where this rest process will be running. This can be localhost which we are using in this guide if
|
||||
you want all processes on same VPS or separate them to different servers for horizontal scaling. `REST_PORT` is just
|
||||
the port where you want the process hosted.
|
||||
|
||||
Now you have an entire Rest manager ready and waiting. Only thing you need now, is to listen for requests.
|
||||
|
||||
## Creating HTTP Listener
|
||||
|
||||
Since this is not a beginner guide, I am assuming you know already how to create a HTTP listener. There are enough guides on this out there. I will only cover the rough functionality.
|
||||
Since this is not a beginner guide, I am assuming you know already how to create a HTTP listener. There are enough
|
||||
guides on this out there. I will only cover the rough functionality.
|
||||
|
||||
```ts
|
||||
// START LISTENING TO THE URL(localhost)
|
||||
const server = Deno.listen({ port: REST_PORT });
|
||||
console.info(
|
||||
`HTTP webserver running. Access it at: http://localhost:${REST_PORT}/`
|
||||
`HTTP webserver running. Access it at: http://localhost:${REST_PORT}/`,
|
||||
);
|
||||
|
||||
// Connections to the server will be yielded up as an async iterable.
|
||||
@@ -91,7 +95,7 @@ async function handleRequest(conn: Deno.Conn) {
|
||||
return requestEvent.respondWith(
|
||||
new Response(JSON.stringify({ error: "Invalid authorization key." }), {
|
||||
status: 401,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,10 +109,12 @@ async function handleRequest(conn: Deno.Conn) {
|
||||
// USE THE SAME METHOD THAT CAME IN. IF DELETE CAME IN WE SEND DELETE OUT
|
||||
requestEvent.request.method as any,
|
||||
// OVERWRITE THE CUSTOM URL WITH DISCORDS BASE URL
|
||||
`${BASE_URL}/v${rest.version}${requestEvent.request.url.substring(
|
||||
rest.customUrl.length
|
||||
)}`,
|
||||
json
|
||||
`${BASE_URL}/v${rest.version}${
|
||||
requestEvent.request.url.substring(
|
||||
rest.customUrl.length,
|
||||
)
|
||||
}`,
|
||||
json,
|
||||
);
|
||||
|
||||
// RETURN DISCORDS RESPONSE BACK TO THE PROCESS MAKING THE REQUEST
|
||||
@@ -116,13 +122,13 @@ async function handleRequest(conn: Deno.Conn) {
|
||||
requestEvent.respondWith(
|
||||
new Response(JSON.stringify(result), {
|
||||
status: 200,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
requestEvent.respondWith(
|
||||
new Response(undefined, {
|
||||
status: 204,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,33 @@ THIS IS A WORK IN PROGRESS GUIDE USING THE NEW v13 OF DISCORDENO.
|
||||
|
||||
## Understanding The Goals of This Guide
|
||||
|
||||
This guide is a quick-paced walkthrough meant for big bot developers. It is expected that you have a decent amount of understanding of how to code your bots.
|
||||
This guide is a quick-paced walkthrough meant for big bot developers. It is expected that you have a decent amount of
|
||||
understanding of how to code your bots.
|
||||
|
||||
## Is This Guide Meant For You?
|
||||
|
||||
If your goal is not to have a bot in millions of discord servers, please find another guide/library. Discordeno is heavily opinionated towards optimizing for bots at scale. If you do not know what a Map or a Set is without having to google it, you are at the wrong place.
|
||||
If your goal is not to have a bot in millions of discord servers, please find another guide/library. Discordeno is
|
||||
heavily opinionated towards optimizing for bots at scale. If you do not know what a Map or a Set is without having to
|
||||
google it, you are at the wrong place.
|
||||
|
||||
## Why You Should Use Discordeno?
|
||||
|
||||
The best way I can describe why you should use Discordeno, is from the words of the biggest bot developers themselves. After speaking to some of the developers of the biggest JS/TS bots, you begin to see a pattern of users unhappy with the current state of JS/TS libraries. They are no longer able to help them scale easily and are starting to move away to other libraries or having to make their own libraries because they need to be able to make their bot distributed.
|
||||
The best way I can describe why you should use Discordeno, is from the words of the biggest bot developers themselves.
|
||||
After speaking to some of the developers of the biggest JS/TS bots, you begin to see a pattern of users unhappy with the
|
||||
current state of JS/TS libraries. They are no longer able to help them scale easily and are starting to move away to
|
||||
other libraries or having to make their own libraries because they need to be able to make their bot distributed.
|
||||
|
||||
The following quotes are from developers who have bot's in atleast 1 million+ discord servers.
|
||||
|
||||
- Flexibility like no other library.
|
||||
- One of the big bot developers found that when their bot got too big, Eris was just very painful to optimize.
|
||||
- "A pretty large hassle, I had to fork eris and modify it. There was a lot of interdependency on the values from caches that made it difficult to remove properties "safely" without searching the entire codebase"
|
||||
- When discovering how easy it was to do the same thing in Discordeno:
|
||||
- "the convenience of being able to do so puts confidence in me that the lib is versatile so it'd certainly draw me towards it"
|
||||
- One of the big bot developers found that when their bot got too big, Eris was just very painful to optimize.
|
||||
- "A pretty large hassle, I had to fork eris and modify it. There was a lot of interdependency on the values from
|
||||
caches that made it difficult to remove properties "safely" without searching the entire codebase"
|
||||
- When discovering how easy it was to do the same thing in Discordeno:
|
||||
- "the convenience of being able to do so puts confidence in me that the lib is versatile so it'd certainly draw me
|
||||
towards it"
|
||||
- Scalability: Standalone Gateway, Rest, Event Handler, Commands, Cache and much more.
|
||||
- "All this sound like a dream (especially when you currently use eris)"
|
||||
- "All this sound like a dream (especially when you currently use eris)"
|
||||
|
||||
Discordeno provides you all the tools that you need to make bot development
|
||||
really easy. 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.
|
||||
Discordeno provides you all the tools that you need to make bot development really easy. 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,4 +1,4 @@
|
||||
{
|
||||
"label": "General",
|
||||
"position": 1
|
||||
}
|
||||
"label": "General",
|
||||
"position": 1
|
||||
}
|
||||
|
||||
@@ -6,57 +6,44 @@ sidebar_position: 1
|
||||
|
||||
## 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?
|
||||
|
||||
One of the biggest issues with almost every library (that 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 maintaining 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 (that 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 maintaining 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?
|
||||
|
||||
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/templates 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/templates 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/)
|
||||
- [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
|
||||
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
|
||||
@@ -66,18 +53,14 @@ EventEmitter.emit("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
|
||||
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.
|
||||
In Discordeno, this is extremely simple, you just simply give it the new event handlers.
|
||||
|
||||
```typescript
|
||||
updateEventHandlers(newEventHandlers);
|
||||
@@ -85,45 +68,35 @@ updateEventHandlers(newEventHandlers);
|
||||
|
||||
## 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
|
||||
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?
|
||||
|
||||
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
|
||||
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"?
|
||||
|
||||
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
|
||||
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
|
||||
import { Errors, Message } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||
|
||||
@@ -4,10 +4,9 @@ sidebar_position: 2
|
||||
|
||||
# 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. This guide serves as the purpose for introducing
|
||||
Discordeno to developers.
|
||||
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 guide serves as the purpose for
|
||||
introducing Discordeno to developers.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -17,23 +16,20 @@ Discordeno to developers.
|
||||
|
||||
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!
|
||||
2. After creating an application. Save the **Client ID.** Thats the unique
|
||||
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!!!**
|
||||
1. [Creating an Application](https://discord.com/developers/applications) on the Developer Portal, name something cool
|
||||
and pick a sweet icon!
|
||||
2. After creating an application. Save the **Client ID.** Thats the unique 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.
|
||||
**[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
|
||||
|
||||
@@ -45,8 +41,8 @@ import { startBot } from "https://deno.land/x/discordeno@10.0.0/mod.ts";
|
||||
|
||||
## Example Usage
|
||||
|
||||
Starting with Discordeno is very simple, you can start from scratch without any
|
||||
templates/frameworks: Add this snippet of code into a new TypeScript file:
|
||||
Starting with Discordeno is very simple, you can start from scratch without any templates/frameworks: Add this snippet
|
||||
of code into a new TypeScript file:
|
||||
|
||||
```ts
|
||||
import { startBot } from "https://deno.land/x/discordeno/mod.ts";
|
||||
@@ -69,11 +65,8 @@ startBot({
|
||||
|
||||
## 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.
|
||||
|
||||
- [Making a Discord bot with Deno and
|
||||
Discordeno](https://web-mystery.com/articles/making-discord-bot-deno-and-discordeno)
|
||||
- [Running a Discord bot written using 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 using Deno in Docker](https://web-mystery.com/articles/running-discord-bot-written-deno-docker)
|
||||
- [Discordeno Bot Tutorials (YouTube)](https://youtu.be/rIph9-BGsuQ)
|
||||
|
||||
@@ -1,49 +1,44 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Migrating
|
||||
|
||||
## Migrating from Discord.js
|
||||
|
||||
This migration guide is not intended to discredit Discord.js authors/maintainers
|
||||
or Discord.js itself. In fact, Discord.js is the most popular Node.js library,
|
||||
admired and praised by a lot of JavaScript developers.
|
||||
This migration guide is not intended to discredit Discord.js authors/maintainers or Discord.js itself. In fact,
|
||||
Discord.js is the most popular Node.js library, admired and praised by a lot of JavaScript developers.
|
||||
|
||||
## Finding an Open-Source Discord 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)
|
||||
|
||||
## Preparations
|
||||
|
||||
- First, create a Discordeno Bot using the
|
||||
[Generator Template](https://github.com/discordeno/template) I will name
|
||||
it Zodiac.
|
||||
- First, create a Discordeno Bot using the [Generator Template](https://github.com/discordeno/template) I will name it
|
||||
Zodiac.
|
||||
|
||||
- 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
|
||||
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!
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -73,9 +68,7 @@ fs.readdir("./src/events/", (err, files) => {
|
||||
const { once } = eventFunction;
|
||||
try {
|
||||
emitter[
|
||||
once
|
||||
? "once"
|
||||
: "on"
|
||||
once ? "once" : "on"
|
||||
](event, (...args) => eventFunction.run(...args));
|
||||
} catch (error) {
|
||||
console.error(error.stack);
|
||||
@@ -175,22 +168,14 @@ 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.
|
||||
|
||||
```ts
|
||||
import {
|
||||
ActivityType,
|
||||
botCache,
|
||||
cache,
|
||||
chooseRandom,
|
||||
editBotsStatus,
|
||||
StatusTypes,
|
||||
} from "../../deps.ts";
|
||||
import { ActivityType, botCache, cache, chooseRandom, editBotsStatus, StatusTypes } from "../../deps.ts";
|
||||
import { registerTasks } from "./../utils/taskHelper.ts";
|
||||
|
||||
botCache.eventHandlers.ready = function () {
|
||||
@@ -225,19 +210,15 @@ botCache.eventHandlers.ready = function () {
|
||||
};
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -330,9 +311,8 @@ createCommand({
|
||||
});
|
||||
```
|
||||
|
||||
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 template 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 template and have something amazing.
|
||||
|
||||
Discord.JS Kick Command Version
|
||||
|
||||
@@ -455,9 +435,7 @@ createCommand({
|
||||
.addField("Reason >", args.reason)
|
||||
.addField("Time", message.timestamp.toString());
|
||||
|
||||
const reportchannel = message.guild?.channels.find((channel) =>
|
||||
channel.name === "report"
|
||||
);
|
||||
const reportchannel = message.guild?.channels.find((channel) => channel.name === "report");
|
||||
if (!reportchannel) {
|
||||
return message.reply("*`Report channel cannot be found!`*");
|
||||
}
|
||||
@@ -477,20 +455,16 @@ 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
|
||||
|
||||
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.
|
||||
|
||||
+9
-12
@@ -11,16 +11,13 @@ sidebar_position: 1
|
||||
|
||||
## Features
|
||||
|
||||
- **Secure & stable**: Discordeno is secure and stable. 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. Moreover, it
|
||||
internally checks all missing permissions before forwarding a request to the
|
||||
Discord API so that the client does not get globally-banned by Discord.
|
||||
- **Simple, Efficient, & Lightweight**: Discordeno is simplistic, easy-to-use,
|
||||
versatile while being efficient and lightweight.
|
||||
- [**Functional API**](https://en.wikipedia.org/wiki/Functional_programming):
|
||||
Functional API ensures an overall concise yet performant code while removing
|
||||
the difficulties of extending built-in classes and inheritance.
|
||||
- **Secure & stable**: Discordeno is secure and stable. 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. Moreover, it internally checks all missing permissions before forwarding a request to the Discord API
|
||||
so that the client does not get globally-banned by Discord.
|
||||
- **Simple, Efficient, & Lightweight**: Discordeno is simplistic, easy-to-use, versatile while being efficient and
|
||||
lightweight.
|
||||
- [**Functional API**](https://en.wikipedia.org/wiki/Functional_programming): Functional API ensures an overall concise
|
||||
yet performant code while removing the difficulties of extending built-in classes and inheritance.
|
||||
[Learn more about class-free JavaScript](https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/)
|
||||
|
||||
+1
-2
@@ -14,8 +14,7 @@
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||
|
||||
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
|
||||
@@ -85,9 +85,7 @@ export default function HomepageFeatures(): JSX.Element {
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
{FeatureList.map((props, idx) => <Feature key={idx} {...props} />)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+14
-12
@@ -1,22 +1,23 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Layout from '@theme/Layout';
|
||||
import Link from '@docusaurus/Link';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import styles from './index.module.css';
|
||||
import HomepageFeatures from '../components/HomepageFeatures';
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import Layout from "@theme/Layout";
|
||||
import Link from "@docusaurus/Link";
|
||||
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
||||
import styles from "./index.module.css";
|
||||
import HomepageFeatures from "../components/HomepageFeatures";
|
||||
|
||||
function HomepageHeader() {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<header className={clsx('hero hero--primary', styles.heroBanner)}>
|
||||
<header className={clsx("hero hero--primary", styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
className="button button--secondary button--lg"
|
||||
to="/docs/intro">
|
||||
to="/docs/intro"
|
||||
>
|
||||
Discordeno Tutorial
|
||||
</Link>
|
||||
</div>
|
||||
@@ -26,11 +27,12 @@ function HomepageHeader() {
|
||||
}
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
const {siteConfig} = useDocusaurusContext();
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
return (
|
||||
<Layout
|
||||
title={`Hello from ${siteConfig.title}`}
|
||||
description="Description will go into a meta tag in <head />">
|
||||
description="Description will go into a meta tag in <head />"
|
||||
>
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
|
||||
Reference in New Issue
Block a user