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:
ITOH
2022-02-04 15:00:04 +01:00
committed by GitHub
parent 637e2c2a90
commit 81f8e0377c
259 changed files with 1731 additions and 1795 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
};
+3 -3
View File
@@ -1,4 +1,4 @@
{
"label": "Big Bot Guide",
"position": 2
}
"label": "Big Bot Guide",
"position": 2
}
+31 -14
View File
@@ -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.
+82 -43
View File
@@ -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.
+86 -69
View File
@@ -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.
+29 -23
View File
@@ -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,
})
}),
);
}
}
+18 -11
View File
@@ -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.
+3 -3
View File
@@ -1,4 +1,4 @@
{
"label": "General",
"position": 1
}
"label": "General",
"position": 1
}
+52 -79
View File
@@ -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";
+19 -26
View File
@@ -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)
+41 -67
View File
@@ -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
View File
@@ -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
View File
@@ -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: [
+1 -3
View File
@@ -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
View File
@@ -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 />