Button Interaction Pass Data

Hello im traying to make a coinflip game which is simple, but I need to pass some data to the button handler and I'm not sure how to do it, and I even need to send and embed to an other channel and interact with 3 differents button which modify the embeds in different ways, if someone can answer all of that I will be happy where some code if something is wrong let me know.
export class GameCoinFlipButton extends InteractionHandler {
public constructor(ctx: InteractionHandler.LoaderContext, options: InteractionHandler.Options) {
super(ctx, {
...options,
interactionHandlerType: InteractionHandlerTypes.Button
});
}

public override parse(interaction: ButtonInteraction) {
if (interaction.customId !== 'confirm') return this.none();

return this.some();
}

public async run(interaction: ButtonInteraction) {
// edit current the message
const newEmbed = successEmbed(`${knowIcon} Coin Flip Confirmed`, `The coin flip has been created. Please wait for someone to join.`);
await interaction.update({ embeds: [newEmbed], components: [] });

// create a new message
const embed = createEmbed(`${knowIcon} Coin Flip Created`, `${gemsIcon} **Gems Amount:** ...\n`);
}
}
export class GameCoinFlipButton extends InteractionHandler {
public constructor(ctx: InteractionHandler.LoaderContext, options: InteractionHandler.Options) {
super(ctx, {
...options,
interactionHandlerType: InteractionHandlerTypes.Button
});
}

public override parse(interaction: ButtonInteraction) {
if (interaction.customId !== 'confirm') return this.none();

return this.some();
}

public async run(interaction: ButtonInteraction) {
// edit current the message
const newEmbed = successEmbed(`${knowIcon} Coin Flip Confirmed`, `The coin flip has been created. Please wait for someone to join.`);
await interaction.update({ embeds: [newEmbed], components: [] });

// create a new message
const embed = createEmbed(`${knowIcon} Coin Flip Created`, `${gemsIcon} **Gems Amount:** ...\n`);
}
}
@ApplyOptions<Command.Options>({
description: 'The upgrader game',
preconditions: ['RoomsOnly', 'HasRegistered']
})
export class GameCoinFlip extends Command {
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand((builder) => {
builder
.setName(this.name)
.setDescription(this.description)
.addStringOption((option) =>
option.setName('choice').setDescription('The choice to flip').setRequired(true).addChoices(
{
name: 'Head',
value: 'head'
},
{
name: 'Tails',
value: 'tails'
}
)
)
.addStringOption((option) => option.setName('amount').setDescription('The amount to flip').setRequired(true));
});
}
public override async chatInputRun(interaction: ChatInputCommandInteraction, _context: ChatInputCommand.RunContext) {
try {
await this.sendInfo(interaction);
} catch (error) {
console.error('Error in chatInputRun:', error);
await interaction.reply({ content: 'An error occurred while processing your request.', ephemeral: true });
}
}

private async sendInfo(interaction: ChatInputCommandInteraction) {
const choice = interaction.options.getString('choice');
const amountOption = interaction.options.getString('amount');

if (!amountOption || !choice) {
return await interaction.reply({ content: 'Amount and choice are required options.', ephemeral: true });
}

const amount = suffixToAmount(amountOption);

const user = await prisma.userStakes.findUnique({ where: { userId: Number(interaction.user.id) } });
if (!user || !checkIfUserHasBalance(user.userId, amount, interaction)) {
return await interaction.reply({ content: 'insufficient balance.', ephemeral: true });
}

const embed = createEmbed(
`${knowIcon} Coin Flip Confirmation`,
`${gemsIcon} **Gems Amount:** ${amountToSuffix(amount)}\n` + `${diceIcon} **Side:** ${choice}\n`
);
const confirmButton = new ButtonBuilder().setCustomId('confirm').setLabel('Confirm').setStyle(ButtonStyle.Secondary);

const row = new ActionRowBuilder<ButtonBuilder>().addComponents(confirmButton);

return await interaction.reply({ embeds: [embed], components: [row] });
}
}
@ApplyOptions<Command.Options>({
description: 'The upgrader game',
preconditions: ['RoomsOnly', 'HasRegistered']
})
export class GameCoinFlip extends Command {
public override registerApplicationCommands(registry: Command.Registry) {
registry.registerChatInputCommand((builder) => {
builder
.setName(this.name)
.setDescription(this.description)
.addStringOption((option) =>
option.setName('choice').setDescription('The choice to flip').setRequired(true).addChoices(
{
name: 'Head',
value: 'head'
},
{
name: 'Tails',
value: 'tails'
}
)
)
.addStringOption((option) => option.setName('amount').setDescription('The amount to flip').setRequired(true));
});
}
public override async chatInputRun(interaction: ChatInputCommandInteraction, _context: ChatInputCommand.RunContext) {
try {
await this.sendInfo(interaction);
} catch (error) {
console.error('Error in chatInputRun:', error);
await interaction.reply({ content: 'An error occurred while processing your request.', ephemeral: true });
}
}

private async sendInfo(interaction: ChatInputCommandInteraction) {
const choice = interaction.options.getString('choice');
const amountOption = interaction.options.getString('amount');

if (!amountOption || !choice) {
return await interaction.reply({ content: 'Amount and choice are required options.', ephemeral: true });
}

const amount = suffixToAmount(amountOption);

const user = await prisma.userStakes.findUnique({ where: { userId: Number(interaction.user.id) } });
if (!user || !checkIfUserHasBalance(user.userId, amount, interaction)) {
return await interaction.reply({ content: 'insufficient balance.', ephemeral: true });
}

const embed = createEmbed(
`${knowIcon} Coin Flip Confirmation`,
`${gemsIcon} **Gems Amount:** ${amountToSuffix(amount)}\n` + `${diceIcon} **Side:** ${choice}\n`
);
const confirmButton = new ButtonBuilder().setCustomId('confirm').setLabel('Confirm').setStyle(ButtonStyle.Secondary);

const row = new ActionRowBuilder<ButtonBuilder>().addComponents(confirmButton);

return await interaction.reply({ embeds: [embed], components: [row] });
}
}
7 Replies
✞
OP3d ago
The data must be the following: - balance - choice - userid (owner of the created confilp) - opposite_userid (the member who joined the coinflip) There will be differents games and I even need to handle that too, but I'm not sure if someone can help me out
Favna
Favna3d ago
you have 3 options: - Use an in memory Map with unique keys that you store in the custom_id - Use an external fast database like Redis with unique keys that you store in the custom_id - Store the data entirely in the custom_id
✞
OP3d ago
and about the section section?
Favna
Favna3d ago
what do you mean?
✞
OP3d ago
and I even need to send and embed to an other channel and interact with 3 differents button which modify the embeds in different ways
Favna
Favna3d ago
The answer I gave is still the correct one. You'll need to store the data either in the custom_id, or some in memory storage (be that in nodejs memory or redis or a persistent database like sqlite, postgres, mysql, etc). how you solve that exactly is up to you and how to you determine what data to pass in what way is also up to you. there is no one correct way For my bot Bloombot I pass an identifier as well as a database id throug the custom id in the format of prefix-job|databaseid then I extract the first part in my parse function (https://github.com/favware/bloombot/blob/main/src/interaction-handlers/buttons/jobs/melee/dragoon.ts) and the second part in the handler (https://github.com/favware/bloombot/blob/main/src/lib/util/functions/handleJobOrRoleButtonClick.ts#L11) On the other hand, for @Dragonite I dump everything in the custom_id using a custom compression algorithm to ensure it is never above the 100 character limit that Discord permits https://github.com/favware/dragonite/blob/main/src/commands/Pokemon/pokemon.ts#L107 https://github.com/favware/dragonite/blob/main/src/interaction-handlers/select-menus/pokemonSelectMenu.ts#L61
✞
OP2d ago
instead I made a list, passed the id from the button and retrieved what I needed it's working sooo

Did you find this page helpful?