npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tomcrack/discord-easy-interactions

v1.1.3

Published

A simple and modular library for managing interactive flows of Discord.js v14 (buttons, selects, V2 modals and Wizard Forms sequentially).

Readme

discord-easy-interactions

A lightweight, fluent, and promise-based wrapper for Discord.js v14 designed to manage interactive UI flows (buttons, select menus, V2 modals) and sequential wizard forms in a linear, inline fashion. Say goodbye to "callback hell" and fragmented command folders!

Features

  • 🧙 FormBuilder (Sequential Wizard Forms): Gather string selects, free text inputs, file uploads, and V2 modals sequentially using async/await.
  • 🕸️ InteractionMesh (Callback-driven Layouts): Bind onClick and onSelect callbacks directly to buttons and menus, bypassing global interaction listeners.
  • 🔄 Activity-aware Listeners: Utilizes idle timeouts instead of absolute limits. Collectors remain active as long as the user keeps interacting.
  • 🎴 Components V2 Support: Fluent builders for Discord's new modular message components (Container, Section, TextDisplay, Button) with color hex-to-decimal conversion and type safety.
  • 🛡️ Production Ready: Zero third-party runtime dependencies, strict validation of styles, deep-cloned component structures to prevent memory leaks, and native UUID generation.

Installation

npm install @tomcrack/discord-easy-interactions

Note: discord.js (v14.0.0+) is required as a peer dependency.


Usage Examples

1. Sequential Wizard Form (FormBuilder)

Perfect for applications, surveys, or setups where steps must run in a specific order.

const { FormBuilder } = require('@tomcrack/discord-easy-interactions');
const { TextInputStyle } = require('discord.js');

// Inside your command execute function:
const form = new FormBuilder(interactionOrMessage, {
    timeoutMessage: '⏳ Time limit expired! Please run the command again.',
    unauthorizedMessage: '❌ Only the command executor can interact here.',
    modalSubmittedMessage: '✅ Form received. Thank you!'
})
    .addTextStep('What is your GitHub username?', {
        validate: (m) => m.content.startsWith('https://github.com/'),
        retryMessage: 'Please provide a valid GitHub profile link.'
    })
    .addSelectStep('What is your primary programming language?', [
        'JavaScript', 'TypeScript', 'Python', 'Go'
    ], { placeholder: 'Choose a language...' })
    .addModalStep('Tell us about yourself', {
        title: 'Staff Application Form',
        buttonLabel: 'Open Form',
        inputs: [
            { customId: 'experience', label: 'Work experience:', style: TextInputStyle.Paragraph },
            { customId: 'why', label: 'Why do you want to join?:', style: TextInputStyle.Short }
        ]
    });

const results = await form.start({ timeout: 300000 }); // 5 minutes total

if (results) {
    const githubLink = results.get(0);
    const primaryLang = results.get(1);
    const modalInputs = results.get(2); // Returns an object with input customIds as keys
    
    console.log('Form completed:', { githubLink, primaryLang, modalInputs });
} else {
    console.log('The form expired or was cancelled by the user.');
}

2. Callback-driven Button Layouts (InteractionMesh)

Perfect for pagination (e.g. catalog or inventory) and confirmation prompts.

const { InteractionMesh, MeshButton } = require('@tomcrack/discord-easy-interactions');
const { ButtonStyle } = require('discord.js');

const mesh = new InteractionMesh();

const confirmBtn = new MeshButton()
    .setCustomId('confirm_action')
    .setLabel('Confirm')
    .setStyle(ButtonStyle.Success)
    .onClick(async (interaction) => {
        await interaction.update({ content: '✅ Action confirmed successfully!', components: [] });
    });

const cancelBtn = new MeshButton()
    .setCustomId('cancel_action')
    .setLabel('Cancel')
    .setStyle(ButtonStyle.Danger)
    .onClick(async (interaction) => {
        await interaction.update({ content: '❌ Action cancelled.', components: [] });
    });

mesh.addButton(confirmBtn).addButton(cancelBtn);

const msg = await message.reply({
    content: 'Are you sure you want to proceed?',
    components: mesh.compile()
});

// Starts listening for clicks. The idle timer resets on every interaction!
mesh.listen(msg, { timeout: 60000 });

3. Modular Message Layouts (Components V2)

Replace old embeds with modular and structured layouts using the new Components V2 standard.

const { Container, Section, TextDisplay, Button } = require('@tomcrack/discord-easy-interactions');

const layout = new Container()
    .setAccentColor('#5865F2') // Supports hex string color conversion
    .addComponent(
        new Section()
            .addComponent(
                new TextDisplay('**Welcome!** This message uses Components V2.')
            )
            .setAccessory(
                new Button()
                    .setCustomId('btn_docs')
                    .setLabel('Read Docs')
                    .setStyle('success') // Throws RangeError if style string is invalid
            )
    )
    .compile();

await message.reply({ components: [layout] });

Best Practices

  1. Text Step Collectors: Since .addTextStep() relies on a message collector bound to the channel, any message sent by the target user will be consumed as an answer. It is best to run sequential forms in Direct Messages (DMs) or dedicated private channels.
  2. Backwards Compatibility: The InteractionMesh.listen() method uses idle timeouts under the hood, but transparently maps 'idle' events back to 'time' reasons when the collector ends, so your existing event handlers won't break.

License

This project is licensed under the ISC License.