Asking before deleting something
Some commands are hard to undo. By the end of this page, /todo delete will
reply with a Delete button that only the person who asked can press, and nothing
is deleted until they do.
It uses data in custom IDs to carry the todo and its owner, and a guard to check the clicker.
Steps
-
Describe what the button carries. It sits next to the button handler, so the command can import it too:
// src/buttons/delete-todo.ts export const deleteTodo = defineParams({ id: "todo.delete", keys: ["todoId", "owner"], schema: v.object({ todoId: v.string(), owner: v.string() }), }); -
Write the button handler in the same file. The template is the button people see, and
paramOwner("owner")turns everyone else away:export default defineButton({ params: deleteTodo, builder: new DangerButtonBuilder() .setCustomId(deleteTodo.id) .setLabel("Delete"), guards: [paramOwner("owner", "Only you can delete your todo.")], run: async ctx => { const [issues, params] = ctx.params; if (issues) { await ctx.interaction.update({ content: "This button is out of date.", components: [], }); return; } const deleted = todos.delete({ id: params.todoId }); await ctx.interaction.update({ content: deleted ? "Deleted." : "That todo was already gone.", components: [], }); }, });The handler deletes by ID only after the guard has checked the owner. If your todos can change hands, also check the stored owner before deleting: the custom ID only tells you who the button was built for.
-
In the command, reply with a copy of the button, built for this todo and this user. Making the reply ephemeral keeps the button out of everyone else's sight, and ignoring it is the same as cancelling:
// In /todo delete's run() const button = ctx.client.buttons.build(deleteTodo, { todoId: id, owner: ctx.interaction.user.id, }); await ctx.interaction.reply({ content: `Delete "${todo.title}"? This can't be undone.`, components: [new ActionRowBuilder().addComponents(button)], flags: MessageFlags.Ephemeral, });
Check it worked
Run /todo delete and press Delete: the message changes to "Deleted." and the
todo is gone. To check the guard, send the reply without
MessageFlags.Ephemeral and have someone else press the button. They get "Only
you can delete your todo." privately, and the todo stays.
Watch out
Change keys or their order after you've sent buttons, and the old buttons
decode wrongly. Give the new version a new id instead, as
changing params later explains.