Popii - v0.5.1
    Preparing search index...

    Snaps (Persistent Components)

    Snaps handle Buttons, Select Menus, and Modals by their customId. Unlike one-shot collectors, Snaps survive bot restarts because the handler is registered by pattern, not by a per-message listener.

    // src/snaps/confirm.ts
    import { snap } from "popii";

    export default snap({
    customId: "confirm",
    async do(pop) {
    await pop.reply({ content: "Confirmed!", ephemeral: true });
    }
    });

    Any button or component with customId: "confirm" will trigger this handler.

    Use a RegExp for dynamic IDs that carry data:

    // src/snaps/delete-message.ts
    import { snap } from "popii";

    export default snap({
    customId: /^delete:(\d+)$/,
    async do(pop) {
    const messageId = pop.snapMatches?.[1];
    // delete the message with that ID
    await pop.reply({ content: "Deleted!", ephemeral: true });
    }
    });

    Use prefix instead of a regex for a simpler pattern:

    import { snap } from "popii";

    export default snap({
    prefix: "vote:", // matches "vote:yes", "vote:no", etc.
    async do(pop) {
    const choice = pop.interaction.customId.split(":")[1];
    await pop.reply(`You voted: ${choice}`);
    }
    });

    Use pop.pack() in your command to embed data, and read it back in your snap:

    // In a command:
    const button = {
    type: 2, // Button
    style: 1, // Primary
    label: "Confirm",
    custom_id: pop.pack("confirm-action", { targetId: "123", action: "ban" })
    };

    // In the snap:
    export default snap({
    prefix: "confirm-action",
    async do(pop) {
    // pop.snapData is the decoded object: { targetId: "123", action: "ban" }
    }
    });
    // src/snaps/role-select.ts
    import { snap } from "popii";

    export default snap({
    customId: "role-picker",
    async do(pop) {
    const interaction = pop.interaction as any;
    const selected: string[] = interaction.values;
    await pop.reply(`You selected: ${selected.join(", ")}`);
    }
    });
    // src/snaps/feedback-modal.ts
    import { snap } from "popii";

    export default snap({
    customId: "feedback-modal",
    async do(pop) {
    const interaction = pop.interaction as any;
    const feedback = interaction.fields.getTextInputValue("feedback");
    await pop.reply({ content: `Thanks for your feedback: "${feedback}"`, ephemeral: true });
    }
    });

    Set expiresIn (in ms) to auto-remove the handler after a period:

    export default snap({
    customId: "one-time-action",
    expiresIn: 300_000, // 5 minutes
    async do(pop) {
    await pop.reply("Done!");
    }
    });

    Snaps support the same guards as commands:

    export default snap({
    customId: "admin-action",
    ownerOnly: true,
    permissions: ["Administrator"],
    cooldown: 5000,
    async do(pop) {
    // only bot owners with Administrator can trigger this
    }
    });