Skip to content

Collectors

Now that you have learned how to handle components in a static way, you could have asked yourself how I get more context about what had happened before sending the component.

Seyfert includes message components collectors which are an easy way to handle does interactions received from an specific message and make you able to get more context about what had happened before sending the component.

Building collectors

Collectors are built using createComponentCollector method in a message, which is inherited by BaseMessage. This method returns an object representing a collector.

Here is an example of how to build a simple collector after sending a message with one button attached to it in a hello world command.

1
import {
2
Button,
3
ActionRow,
4
Command,
5
Declare,
6
type CommandContext
7
} from 'seyfert';
8
9
import { ButtonStyle } from 'seyfert/lib/types';
10
11
@Declare({
12
name: 'hello',
13
description: 'I will send you a hello world message'
14
})
15
export default class HelloWorldCommand extends Command {
16
async run(ctx: CommandContext) {
17
const button = new Button()
18
.setCustomId('hello')
19
.setLabel('Hello')
20
.setStyle(ButtonStyle.Primary);
21
22
const row = new ActionRow<Button>().setComponents([button]);
23
24
// get the message by setting fetchReply to true
25
const message = await ctx.write(
26
{
27
content: 'You want a hello world. Click the button below.',
28
components: [row]
29
},
30
true
31
);
32
33
const collector = message.createComponentCollector();
34
}
35
}

Handling interactions within a collector

Having created a collector from a message we are going to handle the interaction of the button with the run function of the collector.

Here is an example:

1
import {
2
Button,
3
ActionRow,
4
Command,
5
Declare,
6
type CommandContext
7
} from 'seyfert';
8
9
import { ButtonStyle } from 'seyfert/lib/types';
10
11
@Declare({
12
name: 'hello',
13
description: 'I will send you a hello world message'
14
})
15
export default class HelloWorldCommand extends Command {
16
async run(ctx: CommandContext) {
17
const button = new Button()
18
.setCustomId('hello')
19
.setLabel('Hello')
20
.setStyle(ButtonStyle.Primary);
21
22
const row = new ActionRow<Button>().setComponents([button]);
23
24
const message = await ctx.write(
25
{
26
content: 'You want a hello world. Click the button below.',
27
components: [row]
28
},
29
true
30
);
31
32
const collector = message.createComponentCollector();
33
34
// we are putting the custom id we have set into the button in the first param of the function.
35
collector.run('hello', async (i) => {
36
if (i.isButton()) return i.write({ content: 'Hello World πŸ‘‹' });
37
});
38
}
39
}

Filtering interactions

You might have thought about filtering the interaction received in the run function for limiting, for example the user who is interacting with the button.

You would have added a condition inside the run function like this:

1
if (i.user.id === ctx.author.id)
2
return i.write({ content: 'Do not touch the button' });

This will limit the use of the button to only the one which run the command.

But seyfert implements just a simply filter option when creating the collector which expects a callback that returns a boolean.

We are going to implement the filter for filtering the user who ran the interaction and filter the interaction only for button interactions.

1
import {
2
Button,
3
ActionRow,
4
Command,
5
Declare,
6
type CommandContext
7
} from 'seyfert';
8
9
import { ButtonStyle } from 'seyfert/lib/types';
10
11
@Declare({
12
name: 'hello',
13
description: 'I will send you a hello world message'
14
})
15
export default class HelloWorldCommand extends Command {
16
async run(ctx: CommandContext) {
17
const button = new Button()
18
.setCustomId('hello')
19
.setLabel('Hello')
20
.setStyle(ButtonStyle.Primary);
21
22
const row = new ActionRow<Button>().setComponents([button]);
23
24
const message = await ctx.write(
25
{
26
content: 'You want a hello world. Click the button below.',
27
components: [row]
28
},
29
true
30
);
31
32
const collector = message.createComponentCollector({
33
filter: (i) => i.user.id === ctx.author.id && i.isButton()
34
});
35
36
collector.run('hello', async (i) => {
37
return i.write({ content: 'Hello World πŸ‘‹' });
38
});
39
}
40
}

Handling collector onStop

A collector could stop this mean the collector won’t be collecting more interactions of the message. To handle the stop we have to pass a callback into onStop option when creating the collector.

The callback will take two parameters:

  • reason. A string which indicates the reason of why the collector has stopped. The most common is timeout or idle if we added the timeout or idle property to our collector. You can set the reason when you manually stop the collector within the collector.stop() function.

  • refresh. A function which you can execute to refresh the collector, making it collecting interactions as it did before.

Here is an example of how we add an idle to the collector of 1000ms and then everytime it enters into onStop callback we refresh it.

1
import {
2
Button,
3
ActionRow,
4
Command,
5
Declare,
6
type CommandContext
7
} from 'seyfert';
8
9
import { ButtonStyle } from 'seyfert/lib/types';
10
11
@Declare({
12
name: 'hello',
13
description: 'I will send you a hello world message'
14
})
15
export default class HelloWorldCommand extends Command {
16
async run(ctx: CommandContext) {
17
const button = new Button()
18
.setCustomId('hello')
19
.setLabel('Hello')
20
.setStyle(ButtonStyle.Primary);
21
22
const row = new ActionRow<Button>().setComponents([button]);
23
24
const message = await ctx.write(
25
{
26
content: 'You want a hello world. Click the button below.',
27
components: [row]
28
},
29
true
30
);
31
32
const collector = message.createComponentCollector({
33
filter: (i) => i.user.id === ctx.author.id && i.isButton(),
34
onStop(reason, refresh) {
35
//this will refresh the collector everytime it stops by timeout
36
if (reason === 'idle') return refresh();
37
},
38
idle: 1e3 //1000ms
39
});
40
41
collector.run('hello', async (i) => {
42
return i.write({ content: 'Hello World πŸ‘‹' });
43
});
44
}
45
}

Handling Modals with collectors

As modals aren’t message components there is not possibility to create a message components collector but Seyfert introduces the possiblity to create it by using the run method within the modal builder which expects a callback that will handle the interactions.

Here is an example using the run within the modal builder:

1
import {
2
Modal,
3
Command,
4
Declare,
5
type ModalSubmitInteraction,
6
type CommandContext
7
} from 'seyfert';
8
9
@Declare({
10
name: 'hello',
11
description: 'I will send you a hello world message'
12
})
13
export default class HelloWorldCommand extends Command {
14
async run(ctx: CommandContext) {
15
const modal = new Modal()
16
.setCustomId('hello')
17
.setTitle('Hello')
18
.run(this.handleModal);
19
20
await ctx.interaction.modal(modal);
21
}
22
23
async handleModal(i: ModalSubmitInteraction) {
24
return i.write({ content: 'Hello World πŸ‘‹' });
25
}
26
}