# discord.js v12

{% hint style="warning" %}
This guide is for the v12 branch of discord.js, which is the current stable version. v11 users are encouraged to move entirely before it stops working. in October, 2020.
{% endhint %}

{% hint style="info" %}
This guide assumes you have reasonable knowledge about the basics of discord.js and node.js in particular. If you don't, it might be best to learn before following this guide.
{% endhint %}

## Scaffold

We'll start with some basic code to scaffold the start of our reaction pages. Here's the main file, in my case being named `index.js`:

```javascript
const Discord = require('discord.js');
const { token, prefix } = require('./config.json');

const client = new Discord.Client();

// functions

client.on('ready', () => {
    console.log(`Logged in as ${client.user.tag}!`);
});

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

    if (message.content === `${prefix}help`) {
        // variables
        
        // command
    }
});

client.login(token);
```

And here's the `config.json` file:

```javascript
{
    "token": "your-bot-token-here",
    "prefix": "!"
}
```

## Variables and Setup

To setup a reaction collector, we'll need some basic variables and the like set up. Head to the `// variables` comment and define some variables there:

```javascript
const emojis = {
    firstPage: '⏮️',
    previousPage: '⬅️',
    stop: '🛑',
    delete: '🗑️',
    nextPage: '➡️',
    lastPage: '⏩'
}

const pages = [
    'This is page one',
    'This is page two',
    'This is page three'
]

const defaultPage = 0;

const timeLimit = 15;

const maximumRetries = 3;

const allowOtherUserReactions = false;

// note the use of 'let' over 'const'
let currentPage = 0;
let currentRetries = 0;
```

Here's what the variables will be used for:

* `emojis` defines what emojis to use for all the specific reactions. It's possible to change any of them to another unicode emoji or a custom emoji without issues.
* `pages` is the array mentioned in the Concept page. It's an array of content or pages to use for your command. This is easily customizable, in this example they're just simple strings but they can use whatever content you want.
* `defaultPage` indicates which page to start on. This is a zero-index based number as the `pages` are an array. Putting `1` would send the second page, not the first.
* `timeLimit` is how long your bot will wait, in seconds, before it stops listening for new reactions. This limit expires and starts again when a user reacts.
* `maximumRetries` indicates how many times you bot should relisten for reactions on timeout before giving up and stopping listening altogether.
* `allowOtherUserReactions` defines whether you want only the message author to be able to react (set it to `false`) or whether you want all users to be able to react (set it to `true`).
* `currentPage` is what your bot will use to work out what page it's on. As mentioned, we're using `let` instead of `const`. Why? The difference is that with `let`, it can be updated and changed, where if you try update a variable using `const`, it'll throw an error.
* `currentRetries` relies on the same `let/const` logic as `currentPage`, however this is used to keep track of how many retries it's used up.

## Command

Now we're starting at the `// command` comment. We'll send the first page as defined by `defaultPage` to get it started.

```javascript
const msg = await message.channel.send(pages[defaultPage]);
```

Straight after, we'll need to add all the reactions your bot will need to let users navigate through.

```javascript
await msg.react(emojis.firstPage);
await msg.react(emojis.previousPage);
await msg.react(emojis.stop);
await msg.react(emojis.delete);
await msg.react(emojis.nextPage);
await msg.react(emojis.lastPage);
```

At this point, try loading your bot up and running the command. It should send the first page you defined and add all the reactions. If it didn't, head back and check your code over, otherwise good job! Keep going!

![Success! It's going well on my end!](https://i.imgur.com/9yR5epC.png)

Under the block of reactions, add a few more lines, then we're done with the command.

```javascript
const options = {
    emojis,
    pages,
    timeLimit,
    maximumRetries,
    allowOtherUserReactions
}

// the difference:
// 'msg' is the message we sent from the bot
// 'message.author' is who sent the command initially
await reactionPages(msg, message.author, options, currentPage, currentRetries);
```

## Functionality

Scroll up to the `// functions` comment at the top of your page and paste this function frame there:

```javascript
const reactionPages = async (message, author, options, page, retries) => {
    // code incoming!
}
```

Now we'll start building up the code inside this to make our reaction pages work. Let's add this code under that comment and learn what it'll do:

```javascript
const filter = (reaction, user) => {
    if (options.allowOtherUserReactions) {
        return Object.values(options.emojis).includes(reaction.emoji.name);
    } else {
        return Object.values(options.emojis).includes(reaction.emoji.name) && user.id === author.id;
    }
}

const collectorOptions = {
    max: 1,
    time: (options.timeLimit * 1000),
    errors: ['time']
}

message.awaitReactions(filter, collectorOptions)
    .then(async (collected) => {
        // more code here
    })
    .catch(async (error) => {
        // and some more here too
    });
```

* The `filter` checks whether the reaction it received is correct or not, checking if it's one of the emojis from the `emojis` object defined earlier, and checking if the user that reacted was the command author, if `allowOtherUserReactions` was set to `false`.
* The `collectorOptions` tells the collector the following:
  * Set the `max` amount of reactions before finishing to `1`;
  * Set the `time` limit to the defined amount of seconds, multiplied by 1,000 to get the time in milliseconds;
  * Set the `errors` to only trigger on running out of time.

Sounds good. First, we'll head to the `.then` loop, adding the following under the comment:

```javascript
const reaction = collected.first();
const minPage = 0;
const maxPage = (options.pages.length - 1);
const restartLoop = async () => { await reactionPages(message, author, options, page, retries); }

if (reaction.emoji.name === options.emojis.firstPage) {
    // head back to the first page
}

if (reaction.emoji.name === options.emojis.previousPage) {
    // move to the previous page
}

if (reaction.emoji.name === options.emojis.stop) {
    // stop listening
}

if (reaction.emoji.name === options.emojis.delete) {
    // delete the message (also stops listening)   
}

if (reaction.emoji.name === options.emojis.nextPage) {
    // move to the next page
}

if (reaction.emoji.name === options.emojis.lastPage) {
    // head forward to the last page       
}
```

* First, we're defining `reaction` as the reaction clicked, being the first in the `collected` Map returned, and `minPage/maxPage` as guides for the code below. We're also defining `restartLoop` as a shortcut for restarting the loop.
* Then, there's 5 if blocks. One for each reaction. We can also use a switch-case block here if you'd like, but for this case an if block is fine.

Now we'll add the functions for each reaction.

#### First Page

Clicking this reaction will take the user back to the first page defined. We'll need some simple logic to check what page they're on, then we'll change to that page by editing the message sent, then restarting the entire reaction loop.

```javascript
// head back to the first page
if (page === minPage) return restartLoop();

page = minPage;
message = await message.edit(options.pages[minPage]);
return restartLoop();
```

#### Previous Page

Clicking this reaction will move user to the page before the one they're on. We'll need some logic here too to check if they actually can move back or not.

```javascript
// move to the previous page
if (page === minPage) return restartLoop();

page--;
message = await message.edit(options.pages[page]);
return restartLoop();
```

#### Stop

Clicking this reaction will stop the listener altogether. And it's a really simple one too.

```javascript
// stop listening
return true;
```

#### Delete

Clicking this reaction will stop the listener as well, but the difference is that it'll delete the message that your bot sent. Nice and clean, and needs no permissions as bots can delete their own messages.

```javascript
// delete the message (also stops listening)
await message.delete();
return true;
```

#### Next Page

Clicking this reaction will move the user to the page after the one they're on. This uses nearly the exact same code as the previous page function, just moving ahead, not back; note the use of `maxPage` and `++` instead of `minPage` and `--`.

```javascript
// move to the next page
if (page === maxPage) return restartLoop();

page++;
message = await message.edit(options.pages[page]);
return restartLoop();
```

#### Last Page

Clicking this reaction will take the user forward to the last page defined. Again, nearly the same code as the first page function, try spot the differences.

```javascript
// head forward to the last page
page = maxPage;
message = await message.edit(options.pages[maxPage]);
return restartLoop();
```

Looking good! Give it a go now and see if it's working or not. If not, read over the code again, otherwise keep going.

![Still going well on my end!](https://i.imgur.com/LRTAKX5.png)

## Extras

There's a few more things we can do before this can be considered 100% complete.

#### Retry Looping

Head down to the `.catch` part of the `awaitReactions` block and add this code:

```javascript
if (collected.size === 0) {
    if (retries >= options.maximumRetries) {
        return true;
    } else {
        retries++;
        return restartLoop();
    }
}
```

This will check if no reactions were added, and if so, either add one to the attempted retries and start listening again, or if it's hit the limit, finish the loop and stop listening to reactions.

#### Embeds

Everyone likes embeds. Hopefully. They're super easy to add as well. Here's how to tell your reaction menu to use them.

{% hint style="info" %}
We'll be using raw embed objects, not the `MessageEmbed` constructor. If you're not sure about what they are or how to use them, [discordjs.guide](https://discordjs.guide) has a great example found [here](https://discordjs.guide/popular-topics/embeds.html#using-an-embed-object).
{% endhint %}

There's only one step: change your `pages` array from an array of strings to an array of embed objects:

```javascript
const pages = [
    {  embed: { title: 'Page 1', description: 'This is page one' } },
    {  embed: { title: 'Page 2', description: 'This is page two' } },
    {  embed: { title: 'Page 3', description: 'This is page three' } }
]
```

![Embeds look awesome. 😎](https://i.imgur.com/A7Mue5G.png)

And we're done! If you had any issues, look back, or join the #support-here channel on my [development Discord server](https://discord.gg/FnK2a9R).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://thattonybo2.gitbook.io/reaction-pages/guides/discord.js.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
