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

discord-scene

v1.0.1

Published

A simple scene and session system for Discord bots, inspired by Telegraf Scenes

Readme

discord-scene

A simple library to implement multi-step conversations ("scenes") and session storage in a Discord bot, inspired by Telegraf Scenes.

Features

  • Scene-based workflow: Define multiple scenes, each with onEnter, onLeave, and onMessage callbacks.
  • Session storage: Store and retrieve data for each user. By default, it's in-memory, but you can easily extend it to Redis or a database.
  • Stage manager: A central Stage that keeps track of user states (which scene they're in, their session data, etc.) and handles transitions between scenes.

Installation

npm install discord-scene

Below is a minimal example of how to set up a Discord bot using discord-scene. In this scenario, the bot will walk the user through collecting their name and age.


// index.js
const { Client, GatewayIntentBits } = require('discord.js');
const {
  Stage,
  Scene,
  MemorySessionStore,
} = require('discord-scene');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

// 1) Create a session store and a Stage
const sessionStore = new MemorySessionStore();
const stage = new Stage(sessionStore);

// 2) Define a scene
const userInfoScene = new Scene('userInfoScene');

// Called when the user enters the scene
userInfoScene.onEnter(async (ctx) => {
  ctx.sessionData.step = 1;
  await ctx.message.reply('Hello! What is your name?');
});

// Called when the user leaves the scene
userInfoScene.onLeave(async (ctx) => {
  await ctx.message.reply('Leaving the scene. Have a nice day!');
});

// Called for each message while in this scene
userInfoScene.onMessage(async (ctx) => {
  const step = ctx.sessionData.step;

  if (step === 1) {
    ctx.sessionData.name = ctx.message.content.trim();
    ctx.sessionData.step = 2;
    await ctx.message.reply(`Nice to meet you, ${ctx.sessionData.name}! How old are you?`);
    return;
  }

  if (step === 2) {
    const ageNum = parseInt(ctx.message.content.trim(), 10);
    if (isNaN(ageNum)) {
      await ctx.message.reply('Please enter a valid number for your age.');
      return;
    }
    ctx.sessionData.age = ageNum;
    ctx.sessionData.step = 3;
    await ctx.message.reply(
      `So, your name is ${ctx.sessionData.name}, and you are ${ctx.sessionData.age} years old. Is that correct? (yes/no)`
    );
    return;
  }

  if (step === 3) {
    const answer = ctx.message.content.toLowerCase();
    if (answer === 'yes') {
      await ctx.message.reply('Great! Your data has been recorded. Thanks!');
      // Exit the scene
      await stage.enter(null, ctx);
    } else if (answer === 'no') {
      await ctx.message.reply('Let’s start over. Type `!start` again if you want to retry.');
      // Exit the scene
      await stage.enter(null, ctx);
    } else {
      await ctx.message.reply('Please answer "yes" or "no".');
    }
  }
});

// 3) Register the scene
stage.register(userInfoScene);

// 4) Handle Discord messages
client.on('messageCreate', async (message) => {
  if (message.author.bot) return;

  const ctx = {
    message,
    userId: message.author.id,
  };

  // Command to start the scene
  if (message.content === '!start') {
    await stage.enter('userInfoScene', ctx);
    return;
  }

  // Otherwise, let the stage handle the message
  await stage.handleMessage(ctx);
});

client.once('ready', () => {
  console.log(`Bot is ready as ${client.user.tag}`);
});

client.login('YOUR_BOT_TOKEN');
  1. Replace YOUR_BOT_TOKEN with your real token from the Discord Developer Portal.

  2. Run the bot: bash node index.js

  3. Invite your bot to a server and type !start. You will be guided through a multi-step conversation.

API Reference

MemorySessionStore

  • getSession(userId): Returns the user's session object or null if none exists.
  • setSession(userId, sessionData): Saves or updates the session data.
  • deleteSession(userId): Removes the session data for the user.

class Stage

Orchestrates scene management and user state.

  • Constructor:
const stage = new Stage(sessionStore);

Accepts an instance of a SessionStore.

  • register(...scenes): Register one or more Scene objects.
  • enter(sceneName, ctx): Moves a user to the given scene. If sceneName is null, the user leaves any current scene.
  • handleMessage(ctx): Dispatches the incoming message to the correct scene's onMessage callback.

class Scene

Represents a multi-step conversation flow.

Constructor:

const myScene = new Scene('myScene');

Takes a name for identification.

  • onEnter(fn): Registers a callback for when a user enters the scene.
  • onLeave(fn): Registers a callback for when a user leaves the scene.
  • onMessage(fn): Registers a callback for when the user sends a message in this scene.

All callbacks receive a context object ctx:

ctx.message - The Discord.Message object ctx.userId - The user's Discord ID (message.author.id) ctx.sessionData - An object for storing data relevant to the current scene

Extending

Custom Session Storage

Create a class that inherits SessionStore and implement getSession, setSession, and deleteSession using your preferred database (e.g., Redis or MongoDB).

Slash Commands

Instead of messageCreate and !start, you could handle interactionCreate for slash commands in Discord. The logic for Stage and Scene remains the same.

Multiple Scenes

If you have various flows (e.g., collecting feedback, ordering, or other steps), simply create additional scenes and stage.register(themAll).

License

MIT – use, modify, and distribute as needed.