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 🙏

© 2024 – Pkg Stats / Ryan Hefner

discordjs-diy

v1.6.1

Published

[![npm](https://img.shields.io/npm/v/discordjs-diy)]()

Downloads

48

Readme

Discord.js - DIY

npm

Easy to use, do-it-yourself Discord.js mini-framework

You can find the full reference wiki here.

What can I use it for

Making Discord.JS bots when you're in a run. You can get started with only 2 lines! (incluiding the import)!

How do I use it

All you need to get started is install it using npm install discordjs-diy and import it into your project.

import { Bot } from "discordjs-diy";

const bot = new Bot("<your Discord API token>", { prefix: "!" });
bot.registerAction("ping", "pong"); //!ping => pong

What if I want my bot to do cool stuff?

import { Bot } from "discordjs-diy";
const bot = new Bot("<your Discord API token>", { prefix: "?" });
bot.registerAction(
  "rate",
  ({ args }) =>
    `You want me to rate ${args}? Ok... ${Math.floor(Math.random() * 10)}/10`
); //?rate something => The bot replies!
import { Bot } from "discordjs-diy";
const bot = new Bot("<your Discord API token>", { prefix: "*" });
bot.registerAction("reactAndReply", {
  response: "Hello!",
  reaction: () => "🤓",
}); //*reactAndReply => The bot can also react to messages and reply!
import { Bot } from "discordjs-diy";
const bot = new Bot("<your Discord API token>", { prefix: "*", ignoreCaps });
bot.registerAction("await", {
  response: async ({ msg }) => await waitForResponse(msg.author),
}); //*AWAIT => bot can ignore caps and use async/await!

Updating Discord presence

The bot can also easily update presence information

const bot = new Bot("<you know the deal>", { prefix: "!" });
bot.setPresence(["a game", "PLAYING"]);
//or, in case you want it to change every 10 minutes
bot.setPresence([
  ["a game", "PLAYING"],
  ["a movie", "WATCHING"],
]);
//you can also set your own time (in ms)
bot.setPresence(
  [
    ["a game", "PLAYING"],
    ["a movie", "WATCHING"],
  ],
  5 * 60 * 1000
);

What if I want to do my own thing

You can always access the normal client object from Discord.JS

const bot = new Bot("<you know the deal>", { prefix: "!" });
bot.client.on("<some action>", () => "<do something>");

Using embeds

Discordjs-diy tries to make using embeds a little easier. You first need to create an Embed object

import { Embed } from "discordjs-diy";

const embed = new Embed({
  //you can customize the Embed here, all parameters are optional
  color: "#0000FF", //blue
});

To use your embeds in your bot you can pass the object to your bot's object

const embed = new Embed({});
const bot = new Bot("<token>", { prefix: "!", embed });
bot.registerAction("test", ({ args, createEmbed }) =>
  createEmbed({ desc: args })
);
//!test hello => embed containing hello as a description

Bots will usually use a collection of images to represent emotions, you can use them easily with the embed.registerImage method

const embed = new Embed({});
const bot = new Bot("<token>", { prefix: "!", embed });
embed.registerImage("happy", "<url to image>");
bot.registerAction("test", ({ args, createEmbed }) =>
  createEmbed({ desc: args, sideImage: "happy" })
);
//!test hello => embed containing hello as a description and the image "test"

In case your bot requires it, you can set a custom format for the embed description and the footer

const embed = new Embed({
  descTransform: (desc: string) => `${desc}, hello!`, //hello => hello, hello!
  refTransform: (user: User) => [
    `User: ${user.username}`,
    user.avatarURL() ?? undefined,
  ],
});
const bot = new Bot("<token>", { prefix: "!", embed });
bot.registerAction("test", ({ msg, args, createEmbed }) =>
  createEmbed({ desc: args, reference: msg.author })
);
//!test hello => embed containing "hello, hello!" as a description and the footer containing "User: <name>"

Also your embeds can contain the avatar and name of the bot

const embed = new Embed({
  author: bot.user,
});
const bot = new Bot("<token>", { prefix: "!", embed });
bot.registerAction("test", ({ args, createEmbed }) =>
  createEmbed({ desc: args })
);

Handling typos

Your users can be overwhelmed and confused by your bot's syntax. To aid them in the process, djs-diy offers a way to immediately point out which options they might have meant to type instead.

const bot = new Bot("<token>", { prefix: "!", embed });
bot.on("test", "hi there!");
bot.onTypo(
  ({ author }, [first, ...rest]) =>
    `Hey there, ${
      author.username
    }! Did you mean to type !${first}? Other options: ${rest.join(", ")}`
);

Bot#onTypo can set a callback for an scenario where an user types "tsst" or something similar as any other trigger. Should be noted that onTypo is available router-wise and will always attempt to fetch a callback from any parent router (incluiding the Bot object's)

onTypo can take a second argument in the form of an object

{
  maxDistance: number;
  maxSuggestions: number;
}

maxDistance: Maximum Levenshtein distance allowed maxSuggestions: Max amount of suggestions to be provided to the callback

Routing

Sometimes you may want a command to contain a subcommand. This is where routers come in. To use them, create a new Router object then assign commands to it. Finally assign it as an action in your main Bot object. Don't worry about the constructor parameters, they'll be filled in for you.

const bot = new Bot("<token>", { prefix: "!" });

const helpRouter = new Router();
helpRouter.on("info", "lorem ipsum");

bot.on("help", helpRouter);
//Bot will now respond to `!help info` with "lorem ipsum"

Routers have their own error handling too.

helpRouter.onError("Oh no!");
//if any of the commands under help router fail, "Oh no!" will be sent instead

Routers also have full support for slash commands.

Expecting replies

You can easily do a 2 part command, expecting a reply from the same user

For example, a simple conversation could go like

(user) => "!wakeMeUp";
(bot) => "When should I wake you up?";
(user) => "When September ends";
(bot) => "Ok, I'll wake you up When September ends";

This can be easily achieved with the expectReply method the action toolkit provides

bot.registerAction("wakeMeUp", async ({ expectReply }) => {
  //built in Promise handling!
  const reply = await expectReply("When should I wake you up?", true); //You can choose if the bot should delete this message or not by setting the second parameter
  return `Ok, I'll wake you up ${reply?.content}`;
});

The expectReply() promise will resolve to undefined if there's a timeout

Automatic Slash Command Support

Discordjs-DIY comes with integrated slash command generation. When you execute the .registerAction method, the library automatically generates a slash command JSON to be sent to the API.

The default is a command with no parameters and a description of "A command"

To specify parameters manually, add a parameters property or a third parameter to .registerAction.

Example parameters array:

[
  {
    name: "key",
    type: "STRING" /*optional, defaults to STRING*/,
    description: "Hello" /*optional, defaults to "A command"*/,
  },
  { name: "value" },
];

The Bot object now provides a Bot#commands.register method. On its own, it'll register the commands globally (Read about the implications here). You can pass an array of strings if you only want to register the commands on certain guild IDs (for development, etc).

bot.on("debug_register", async ({ guild }) =>
  (await bot.commands.register(guild.id)) ? "Done" : "Something went wrong"
); //registers all the available slash commands , in the guild this message was sent in

Be sure to call this method AFTER all of your commands and routers have been registered. For troubleshooting, Bot#compileCommands will return the objects that will then be passed to the Discord API (via Bot#commands.overwriteCommands)

DJS-diy will automatically create subcommands in the case of routers. Be mindful of the nesting limitations.

Slash command timeout prevention

Routers (and the Bot object) support having a loading action be executed for any slash command interactions that may take longer than 2.5s to execute (Discord's official timeout is 3s).

This action is a sendable message, meaning it can be a string or a function which receives ActionParameters. Do note that for performance reasons, this action can't be async

bot.onLoading(({ author }) => `I'm working on it, ${author.username}!`);
router1.onLoading("Hold tight!!!");

Async Jobs

DJS-diy offers a small addon for running jobs after a response has been issued. You can enqueue these jobs by calling asyncEffect from the ActionParameters received by an action. Should be noted that msg in the passed ActionParameters object will contain the newly created response.

bot.registerAction("image", async ({ createEmbed, asyncEffect }) => {
  asyncEffect(async ({ msg }) => {
    await msg.edit({
      content: "Hello there",
    });
  });

  return "This message will change";
});

Triggering actions inside actions

ActionParameters include a runAction callback which can be used to manually invoke an action through the DJS pipeline, thus fully supporting asyncEffect and message component subscriptions.

You need a ResponseAction and ActionParameters to call this. You may reuse the action's ActionParameters, be aware that reusing asyncEffect or subscribe's ActionParameters will have the side-effect of making it seem like the Bot is the one calling the action (ergo, making components unclickable to users)

//test action can include asyncEffect calls and components!
const testAction = () => "hello!";
bot.on("coolAction", (params) => {
  const { subscribe, createEmbed } = params;
  /* `runAction` is also available here! ^*/

  const buttonRow = subscribe(
    {
      label: "Press me",
      style: "PRIMARY",
    },
    ({ runAction }) => {
      runAction(testAction, params);
    }
  );
  return createEmbed({
    desc: "Press the button to be greeted",
    components: [buttonRow],
  });
});

Error handling

DJS-diy offers per-action and global approaches to error handling.

To handle any exception using the same action:

bot.setErrorAction({
  reaction: "😭",
  response({ args }) {
    //args will contain the value of `e.message`
    return `Error ocurred => ${args}`;
  },
});

To handle an exception for a specific action, pass in an object and include the onError ActionObject:

bot.registerAction("hello", {
  response() {
    throw new Error("No hellos for you today");
  },
  onError: {
    reaction: "😭",
  },
});

Middleware

Discordjs-diy provides support for custom middleware.

interface MyCustomMW{
  myMW: {
    currentTime: number
  }
}

const bot = new Bot("<token>", { prefix: "!", embed });
function myMiddleware(params: ActionParameters): ActionParameters<MyCustomMW>{
  return {
    ...params,
    middleware: {
      ...params.middleware,
      myMW: {
        currentTime: Date.now()
      }
    }
  }
}
bot.useMiddleware<MyCustomMW>(myMiddleware)
bot.registerAction(({middleware}) => //now you can use middleware.myMW in every action execution)

Session Middleware

Discordjs-diy provides a solution for sessions.

Documentation is available here.