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.
1import {2 Button,3 ActionRow,4 Command,5 Declare,6 type CommandContext7} from 'seyfert';8
9import { ButtonStyle } from 'seyfert/lib/types';10
11@Declare({12 name: 'hello',13 description: 'I will send you a hello world message'14})15export 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 true25 const message = await ctx.write(26 {27 content: 'You want a hello world. Click the button below.',28 components: [row]29 },30 true31 );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:
1import {2 Button,3 ActionRow,4 Command,5 Declare,6 type CommandContext7} from 'seyfert';8
9import { ButtonStyle } from 'seyfert/lib/types';10
11@Declare({12 name: 'hello',13 description: 'I will send you a hello world message'14})15export 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 true30 );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:
1if (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.
1import {2 Button,3 ActionRow,4 Command,5 Declare,6 type CommandContext7} from 'seyfert';8
9import { ButtonStyle } from 'seyfert/lib/types';10
11@Declare({12 name: 'hello',13 description: 'I will send you a hello world message'14})15export 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 true30 );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 istimeout
oridle
if we added the timeout or idle property to our collector. You can set the reason when you manually stop the collector within thecollector.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.
1import {2 Button,3 ActionRow,4 Command,5 Declare,6 type CommandContext7} from 'seyfert';8
9import { ButtonStyle } from 'seyfert/lib/types';10
11@Declare({12 name: 'hello',13 description: 'I will send you a hello world message'14})15export 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 true30 );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 timeout36 if (reason === 'idle') return refresh();37 },38 idle: 1e3 //1000ms39 });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:
1import {2 Modal,3 Command,4 Declare,5 type ModalSubmitInteraction,6 type CommandContext7} from 'seyfert';8
9@Declare({10 name: 'hello',11 description: 'I will send you a hello world message'12})13export 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}