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

itsmybot

v0.3.2

Published

SDK for building ItsMyBot plugins.

Readme

itsmybot

Small SDK for authoring ItsMyBot plugins.

Install

npm install itsmybot

Bundle plugin metadata from src/index.ts exports:

npx itsmybot bundle .

Publish generated artifact with Discord browser auth:

npx itsmybot publish .

Discord app must allow CLI callback URL. Default:

http://127.0.0.1:8976/callback

Core shape

  • index.ts default plugin export is source of truth for both publish metadata and runtime logic
  • Command<Config, Options> holds slash-command metadata and typed options
  • MessageCreateEvent, GuildMemberAddEvent, MessageReactionAddEvent, VoiceStateUpdateEvent give typed event hooks
  • defineConfig(...) keeps defaultConfig typed without extra schema DSL
  • plugin metadata lives directly on Plugin class fields
  • defineCommandOptions(...) defines slash-command options once

itsmybot bundle reads default plugin export from index.ts and turns it into .itsmybot/manifest.json. Commands and events are derived from plugin class arrays. Capabilities, storage, and outbound allowlist come from plugin class fields. Capabilities used at runtime must be declared there too.

Optional presentation assets bundle automatically too:

  • README.md
  • icon.png (256x256)
  • screenshots/*.png|jpg|jpeg (max 5, max 1920x1080)

Command plugin

src/index.ts

import {
  Command,
  Plugin,
  CommandContext,
  defineConfig,
  defineCommandOptions,
  PluginCapability,
  PluginCategory,
  stringOption,
  integerOption,
} from "itsmybot";

export interface Config {
  greeting: string;
}

export const defaultConfig = defineConfig<Config>({
  greeting: "Hello",
});

export const greeterOptions = defineCommandOptions({
  target: stringOption({
    description: "Who should receive greeting",
    required: true,
    minLength: 1,
    maxLength: 32,
  }),
  count: integerOption({
    description: "How many times to repeat",
    minValue: 1,
    maxValue: 5,
  }),
});

export class GreeterCommand extends Command<Config, typeof greeterOptions> {
  readonly name = "greeter";
  readonly description = "Send greeting";
  readonly options = greeterOptions;

  run(ctx: CommandContext<Config, typeof greeterOptions>) {
    const count = ctx.event.options.count ?? 1;
    const content = Array.from({ length: count }, () => `${ctx.config.greeting} ${ctx.event.options.target}`).join(" ");
    return ctx.reply(content);
  }
}

export default new (class GreeterPlugin extends Plugin<Config> {
  readonly id = "greeter";
  readonly version = "0.1.0";
  readonly name = "Greeter";
  readonly summary = "Send greeting";
  readonly category = PluginCategory.Utilities;
  readonly compatibilityDate = "2026-04-18";
  readonly capabilities = [PluginCapability.DiscordInteractionReply] as const;
  readonly storage = {
    greetings: {
      indexes: ["target"],
    },
  };
  readonly commands = [new GreeterCommand()];
})();

Config widgets

Config UI stays schema-driven. Add JSDoc tags on Config fields to hint richer dashboard widgets.

export interface Config {
  /**
   * @title Announcement channel
   * @widget channel-select
   * @channelTypes text,announcement
   */
  announcementChannelId?: string;

  /**
   * @title Moderator roles
   * @widget role-select
   */
  moderatorRoleIds: string[];

  /**
   * @title Welcome message
   * @widget message-builder
   * @rows 8
   */
  welcomeMessage: string;

  theme: "light" | "dark";
}

Supported widget tags now:

  • @widget textarea
  • @widget message-builder
  • @widget channel-select
  • @widget role-select

Useful extra tags:

  • @channelTypes text,announcement,voice,stage,forum,media,category,thread
  • @placeholder Your value here
  • @rows 8

Message event plugin

import {
  Plugin,
  MessageCreateEvent,
  MessageCreateContext,
  PluginCategory,
} from "itsmybot";

export class MentionResponderEvent extends MessageCreateEvent {
  run(ctx: MessageCreateContext) {
    if (!ctx.event.mentionedBot) {
      return ctx.ok();
    }

    return ctx.sendMessage("Need help? Run /help");
  }
}

export default new (class HelpPlugin extends Plugin {
  readonly id = "help-plugin";
  readonly version = "0.1.0";
  readonly name = "Help Plugin";
  readonly summary = "Reply when bot mentioned";
  readonly category = PluginCategory.Support;
  readonly compatibilityDate = "2026-04-18";
  readonly events = [new MentionResponderEvent()];
})();

Mixed plugin

import {
  Command,
  GuildMemberAddEvent,
  GuildMemberAddContext,
  MessageCreateEvent,
  MessageCreateContext,
  PluginCapability,
  PluginCategory,
} from "itsmybot";

class PingCommand extends Command {
  readonly name = "ping";
  readonly description = "Reply pong";

  run(ctx) {
    return ctx.reply("pong");
  }
}

class WelcomeEvent extends GuildMemberAddEvent {
  run(ctx: GuildMemberAddContext) {
    return ctx.ok([], [ctx.info(`Member joined: ${ctx.event.userId}`)]);
  }
}

class MentionEvent extends MessageCreateEvent {
  run(ctx: MessageCreateContext) {
    if (!ctx.event.mentionedBot) {
      return ctx.ok();
    }

    return ctx.sendMessage("Bot online.");
  }
}

export default new (class MixedPlugin extends Plugin {
  readonly id = "mixed-plugin";
  readonly version = "0.1.0";
  readonly name = "Mixed Plugin";
  readonly summary = "Command and event example";
  readonly category = PluginCategory.Utilities;
  readonly compatibilityDate = "2026-04-18";
  readonly capabilities = [PluginCapability.DiscordMessageWrite] as const;
  readonly commands = [new PingCommand()];
  readonly events = [new WelcomeEvent(), new MentionEvent()];
})();

Testing helpers

import { runPlugin, createCommandInvocation, assertOk } from "itsmybot/testing";

const result = await runPlugin(
  plugin,
  createCommandInvocation({
    commandName: "greeter",
    config: { greeting: "Hello" },
    options: { target: "Theo", count: 2 },
  }),
);

assertOk(result);

Stable imports

  • itsmybot
  • itsmybot/commands
  • itsmybot/config
  • itsmybot/discord
  • itsmybot/events
  • itsmybot/testing
  • itsmybot/types