Skip to content

Commit

Permalink
feat(sharding): Update sharding page to v13 syntax (#695)
Browse files Browse the repository at this point in the history
  • Loading branch information
WiseDevHelper committed Jul 24, 2021
1 parent df823fe commit 5063ab3
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
const Discord = require('discord.js');
const client = new Discord.Client();
const { Client, Intents } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
const prefix = '!';

function findEmoji(nameOrID) {
return this.emojis.cache.get(nameOrID) || this.emojis.cache.find(e => e.name.toLowerCase() === nameOrID.toLowerCase());
function findEmoji(c, { nameOrId }) {
return c.emojis.cache.get(nameOrId) || c.emojis.cache.find(e => e.name.toLowerCase() === nameOrId.toLowerCase());
}

client.on('message', message => {
client.on('messageCreate', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;

const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();

if (command === 'stats') {
return client.shard.broadcastEval('this.guilds.cache.size')
.then(results => {
return message.channel.send(`Server count: ${results.reduce((acc, val) => acc + val, 0)}`);
})
.catch(console.error);
}

if (command === 'send') {
if (!args.length) return message.reply('please specify a destination channel id.');

return client.shard.broadcastEval(`
const channel = this.channels.cache.get('${args[0]}');
return client.shard.broadcastEval(async (c, { channelId }) => {
const channel = c.channels.cache.get(channelId);
if (channel) {
channel.send('This is a message from shard ${this.shard.id}!');
true;
}
else {
false;
await channel.send(`This is a message from shard ${client.shard.ids.join(',')}!`);
return true;
}
`)
return false;
}, { context: { channelId: args[0] } })
.then(sentArray => {
if (!sentArray.includes(true)) {
return message.reply('I could not find such a channel.');
Expand All @@ -45,11 +35,12 @@ client.on('message', message => {
if (command === 'emoji') {
if (!args.length) return message.reply('please specify an emoji ID or name to search for.');

return client.shard.broadcastEval(`(${findEmoji}).call(this, '${args[0]}')`)
return client.shard.broadcastEval(findEmoji, { context: { nameOrId: args[0] } })
.then(emojiArray => {
// Locate a non falsy result, which will be the emoji in question
const foundEmoji = emojiArray.find(emoji => emoji);
if (!foundEmoji) return message.reply('I could not find such an emoji.');
return message.reply(`I have found the ${foundEmoji.animated ? `<${foundEmoji.identifier}>` : `<:${foundEmoji.identifier}>`} emoji!`);
return message.reply(`I have found the ${foundEmoji.animated ? `<${foundEmoji.identifier}>` : `<:${foundEmoji.identifier}> emoji!`}!`);
});
}
});
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const { Client } = require('discord.js');
const client = new Client();
const { Client, Intents } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
const prefix = '!';


client.on('message', message => {
client.on('messageCreate', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;

const args = message.content.slice(prefix.length).trim().split(/ +/);
Expand All @@ -12,9 +11,8 @@ client.on('message', message => {
if (command === 'stats') {
const promises = [
client.shard.fetchClientValues('guilds.cache.size'),
client.shard.broadcastEval('this.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)'),
client.shard.broadcastEval(c => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)),
];

return Promise.all(promises)
.then(results => {
const totalGuilds = results[0].reduce((acc, guildCount) => acc + guildCount, 0);
Expand Down
17 changes: 10 additions & 7 deletions guide/sharding/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const { Client } = require('discord.js');
const client = new Client();
const prefix = '!';

client.on('message', message => {
client.on('messageCreate', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;

const args = message.content.slice(prefix.length).trim().split(/ +/);
Expand All @@ -67,7 +67,7 @@ Let's say your bot is in a total of 3,600 guilds. Using the recommended shard co

## FetchClientValues

<DocsLink path="class/ShardClientUtil?scrollTo=fetchClientValues">Shard#fetchClientValues</DocsLink> is one of the most common sharding utility methods you'll be using. This method retrieves a property on the Client object of all shards.
One of the most common sharding utility methods you'll be using is <DocsLink path="class/ShardClientUtil?scrollTo=fetchClientValues">Shard#fetchClientValues</DocsLink>. This method retrieves a property on the Client object of all shards.

Take the following snippet of code:

Expand Down Expand Up @@ -108,16 +108,19 @@ client.on('message', message => {

## BroadcastEval

Next, check out another handy sharding method known as <DocsLink path="class/ShardClientUtil?scrollTo=broadcastEval">`Shard#broadcastEval`</DocsLink>. This method makes all of the shards evaluate a given script, where `this` is the `client` once each shard gets to evaluating it. You can read more about the `this` keyword [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this). For now, essentially understand that it is the shard's Client object.
Next, check out another handy sharding method known as <DocsLink path="class/ShardClientUtil?scrollTo=broadcastEval">`Shard#broadcastEval`</DocsLink>. This method makes all of the shards evaluate a given method, which recieves a `client` and a `context` argument. The `client` argument refers to the Client object of the shard evaluating it. You can read about the `context` argument [here](/sharding/additional-information.md#eval-arguments).

```js
client.shard.broadcastEval('this.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)').then(console.log);
client.shard
.broadcastEval(c => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0))
.then(console.log);
```

This will run the code given to `broadcastEval` on each shard and return the results to the Promise as an array, once again. You should see something like `[9001, 16658, 13337, 15687]` logged. The code sent to each shard adds up the `memberCount` property of every guild that shard is handling and returns it, so each shard's total guild member count. Of course, if you want to total up the member count of *every* shard, you can do the same thing again on the Promise results.

```js
client.shard.broadcastEval('this.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)')
client.shard
.broadcastEval(c => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0))
.then(results => {
return message.channel.send(`Total member count: ${results.reduce((acc, memberCount) => acc + memberCount, 0)}`);
})
Expand All @@ -131,7 +134,7 @@ You'd likely want to output both pieces of information in the stats command. You
```js
const promises = [
client.shard.fetchClientValues('guilds.cache.size'),
client.shard.broadcastEval('this.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)'),
client.shard.broadcastEval(c => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)),
];

Promise.all(promises)
Expand All @@ -151,7 +154,7 @@ client.on('message', message => {
if (command === 'stats') {
const promises = [
client.shard.fetchClientValues('guilds.cache.size'),
client.shard.broadcastEval('this.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)'),
client.shard.broadcastEval(c => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)),
];

return Promise.all(promises)
Expand Down
61 changes: 18 additions & 43 deletions guide/sharding/additional-information.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For shards to communicate, they have to send messages to one another, as they ea
manager.spawn()
.then(shards => {
shards.forEach(shard => {
shard.on('message', message => {
shard.on('messageCreate', message => {
console.log(`Shard[${shard.id}] : ${message._eval} : ${message._result}`);
});
});
Expand All @@ -36,11 +36,13 @@ You can also send messages via `process.send('hello')`, which would not contain
There might be times where you want to target a specific shard. An example would be to kill a specific shard that isn't working as intended. You can achieve this by taking the following snippet (in a command, preferably):

::: tip
In discord.js v12, <DocsLink path="class/ShardClientUtil?scrollTo=ids">`client.shard`</DocsLink> can hold multiple ids. If you use the default sharding manager, the `.ids` array will only have one entry.
In discord.js v13, <DocsLink path="class/ShardClientUtil?scrollTo=ids">`client.shard`</DocsLink> can hold multiple ids. If you use the default sharding manager, the `.ids` array will only have one entry.
:::

```js
client.shard.broadcastEval('if (this.shard.ids.includes(0)) process.exit();');
client.shard.broadcastEval(c => {
if (c.shard.ids.includes(0)) process.exit();
});
```

If you're using something like [PM2](http://pm2.keymetrics.io/) or [Forever](https://github.com/foreverjs/forever), this is an easy way to restart a specific shard. Remember, <DocsLink path="class/ShardClientUtil?scrollTo=broadcastEval">Shard#broadcastEval</DocsLink> sends a message to **all** shards, so you have to check if it's on the shard you want.
Expand Down Expand Up @@ -75,50 +77,23 @@ You can access them later as usual via `process.argv`, which contains an array o

## Eval arguments

There may come the point where you will want to pass functions or arguments from the outer scope into a `.broadcastEval()` call.
There may come the point where you will want to pass arguments from the outer scope into a `.broadcastEval()` call.

```js
client.shard.broadcastEval(`(${funcName})('${arg}')`);
```

In this small snippet, an entire function is passed to the eval. It needs to be encased in parenthesis; it will throw errors on its way there otherwise. Another set of parenthesis is required so that the function gets called. Finally, the passing of the argument itself, which slightly varies, depending on the type of argument you are passing. If it's a string, you must wrap it in quotes, or it will be interpreted as is and will throw a syntax error because it won't be a string by the time it gets there.

Now, what if you wanted to call a function from *within* the client context? That is to say, you are not passing a function. It would look something like this:

```js
client.shard.broadcastEval(`this.${funcName}(${args});`);
```
function funcName(c, { arg }) {
// ...
}

This would become `client.funcName(args)` once it gets through. This is handy if you, for example, have extended your client object with your class and wish to call some of its methods manually.

### Asynchronous functions

There may be a time when you want to have your shard process an asynchronous function. Here's how you can do that!

```js
client.shard.broadcastEval(`
let channel = this.channels.cache.get('id');
let msg;
if (channel) {
msg = channel.messages.fetch('id').then(m => m.id);
}
msg;
`);
client.shard.broadcastEval(funcName, { context: { arg: 'arg' } });
```

This snippet allows you to return fetched messages outside of the `broadcastEval`, letting you know whether or not you were able to retrieve a message, for example. Remember, you aren't able to return entire objects outside. Now, what if we wanted to use `async/await` syntax inside?
The <DocsLink path="typedef/BroadcastEvalOptions">`BroadcastEvalOptions`</DocsLink> typedef was introduced in discord.js v13 as the second parameter in `.broadcastEval()`.
It accepts two properties: `shard` and `context`. The `context` property will be sent as the second argument to your function.

```js
client.shard.broadcastEval(`
(async () => {
let channel = this.channels.cache.get('id');
let msg;
if (channel) {
msg = await channel.messages.fetch('id').then(m => m.id);
}
return msg;
})();
`);
```
In this small snippet, an argument is passed to the `funcName` function through this parameter.
The function will recieve the arguments as an object as the second parameter.

This example will work the same, but you can produce cleaner code with `async/await`. Additionally, what this does is declare an asynchronous function and then immediately call it. As it is also the last declared line, it is effectively being returned. Remember that you need to `return` an item inside a function one way or another.
::: warning
The `context` option only accepts properties which are JSON-serializable. This means you cannot pass complex data types in the context directly.
For example, if you sent a `User` instance, the function would recieve the raw data object.
:::

0 comments on commit 5063ab3

Please sign in to comment.