Skip to content

A Typescript NodeJS library to interact with Discord's API, both Rest and Gateway.

License

Notifications You must be signed in to change notification settings

detritusjs/client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Detritus

npm

A wheels-attached, pure-TypeScript library for the Discord API.

Installation

Detritus is distributed via npm. A high-level wrapper over the Discord API is provided in this package, detritus-client. Low-level wrappers over Discord's REST API and Gateway are provided through detritus-client-rest and detritus-client-socket.

  • $ npm i detritus-client
  • $ yarn add detritus-client

Usage

Detritus is operated through the Clients classes:

  • ShardClient provides a base client for connecting to the Discord API and receiving events.
  • ClusterClient provides a client that creates ShardClient classes inside of it for easier sharding
  • CommandClient wraps over the ClusterClient or ShardClient to provide support for bot commands.
  • InteractionCommandClient wraps over the ClusterClient or ShardClient to provide support for interaction commands.
  • ClusterManager provides a manager that'll spawn in multiple ClusterClient processes for big shardings

More Examples are provided under the examples/ directory.

Command Client Sample

const { CommandClient } = require('detritus-client');

// Note: it is not advised to hard-code your bot token directly into the bot source.
//
// Tokens should be considered secrets and stored in a configuration file that is not
// part of your version control system, or an environment variable.
// By default, the CommandClient will use the ClusterClient
// The ShardClient/ClusterClient will be under CommandClient.client as soon as you create the object
const token = '';
const commandClient = new CommandClient(token, {
  // Prefix `..`, if you want multiple prefixes pass in `prefixes: ['..', '...']`
  prefix: '..',
});

// Simple ping/pong command
commandClient.add({
  // name describes the command trigger; in this case, ..ping
  name: 'ping',
  run: (context, args) => {
    // Commands should return a promise to ensure that errors are handled
    return context.reply('pong!');
  },
});

// Command demonstrating command pipelines
commandClient.add({
  name: 'owner',
  // onBefore should return a boolean to indicate whether or not the command should proceed
  onBefore: (context) => context.client.isOwner(context.userId),
  onCancel: (context) => context.reply('This command is only available to the bot owner.'),
  run: async (context) => {
    // Commands may also run asynchronously.
    await context.reply('You are the owner of the bot!');
  },
});

// Spawn the client in an async context
//
// Note: Due to how Node handles tasks, the script will block until the Detritus client
// is killed.
(async () => {
  const client = await commandClient.run();
  console.log(`Client has loaded with a shard count of ${client.shardCount}`);
})();

InteractionCommand Client Sample

const { Constants, InteractionCommandClient } = require('detritus-client');
const { ApplicationCommandTypes, InteractionCallbackTypes, MessageFlags } = Constants;

// Note: it is not advised to hard-code your bot token directly into the bot source.
//
// Tokens should be considered secrets and stored in a configuration file that is not
// part of your version control system, or an environment variable.
// By default, the InteractionCommandClient will use the ClusterClient
// The ShardClient/ClusterClient will be under InteractionCommandClient.client as soon as you create the object
const token = '';
const interactionClient = new InteractionCommandClient(token);

// Simple ping/pong command
interactionClient.add({
  description: 'Ping!',
  name: 'ping',
  run: (context, args) => {
    // Commands should return a promise to ensure that errors are handled
    return context.respond(InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, 'pong!');
  },
});

// Command demonstrating command pipelines
interactionClient.add({
  description: 'Are you the owner or part of the team for this application?',
  name: 'owner',
  // onBefore should return a boolean to indicate whether or not the command should proceed
  onBefore: (context) => context.client.isOwner(context.userId),
  // we want the error to only show to the user to not clunk up the chat
  onCancel: (context) => {
    return context.respond(InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, {
      content: 'This command is only available to the bot owner.'),
      flags: MessageFlags.EPHEMERAL,
    });
  },
  run: async (context) => {
    // Commands may also run asynchronously.
    await context.respond(InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, 'You are the owner of the bot!');
  },
});

// Context Menu User Command
interactionClient.add({
  name: 'Poke',
  type: ApplicationCommandTypes.USER,
  run: async (context, args) => {
    await context.respond(InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, {
      content: `You poked ${args.member || args.user}`,
      flags: MessageFlags.EPHEMERAL,
    });
  },
});

// Context Menu Message Command (tells you when the message was created)
interactionClient.add({
  name: 'Creation Date',
  guildIds: [], // you can make it a guild command
  type: ApplicationCommandTypes.MESSAGE,
  run: async (context, args) => {
    const { message } = args;
    await context.respond(InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, {
      content: `${message.id} was made at ${message.createdAt}`,
      flags: MessageFlags.EPHEMERAL,
    });
  },
});

// Spawn the client in an async context
//
// Note: Due to how Node handles tasks, the script will block until the Detritus client
// is killed.
(async () => {
  // Interaction Client will compare the local commands w/ commands stored on discord
  // If any of them differ, it will call `.bulkOverwriteApplicationCommands()` with the local commands
  // Guild-specific Interaction commands are not supported right now
  const client = await interactionClient.run();
  console.log(`Client has loaded with a shard count of ${client.shardCount}`);
})();

Shard Client Sample

const { ShardClient } = require('detritus-client');

// Note: it is not advised to hard-code your bot token directly into the bot source.
//
// Tokens should be considered secrets and stored in a configuration file that is not
// part of your version control system, or an environment variable.
const token = '';
const client = new ShardClient(token, {
  gateway: {
    // This will tell our client to fill our Members cache on any of our guilds that are larger than the large threshold you pass in (default 250)
    loadAllMembers: true,
  },
});

// listen to our client's eventemitter
client.on('guildCreate', async ({fromUnavailable, guild}) => {
  if (fromUnavailable) {
    console.log(`Guild ${guild.name} has just came back from being unavailable`);
  } else {
    console.log(`Joined Guild ${guild.name}, bringing us up to ${client.guilds.length} guilds.`);
  }
});

// listen to our client's eventemitter
client.on('messageCreate', async ({message}) => {
  if (message.content === '!ping') {
    const reply = await message.reply('pong!, deleting message in 5 seconds...');
    setTimeout(async () => {
      await reply.delete();
    }, 5000);
  }
});

(async () => {
  await client.run();
  console.log('Successfully connected to Discord!');
  console.log(`Currently have ${client.guilds.length} guilds in cache.`);
  // set our presence, we can pass this into the client's options too under `gateway.presence`
  client.gateway.setPresence({
    activity: {
      // What comes after our activity type, x.
      name: 'with Detritus',
      // Type 0 sets our message to `Playing x`
      type: 0,
    },
    // do-not-disturb us
    status: 'dnd',
  });
})();

Cluster Client Sample

const { ClusterClient } = require('detritus-client');

// Note: it is not advised to hard-code your bot token directly into the bot source.
//
// Tokens should be considered secrets and stored in a configuration file that is not
// part of your version control system, or an environment variable.
const token = '';
const cluster = new ClusterClient(token, {
  gateway: {
    // Pass in a presence we will send with the identify payload
    presence: {
      activity: {
        // What comes after our activity type, x.
        name: 'with Detritus ClusterClient',
        // Type 0 sets our message to `Playing x`
        type: 0,
      },
      // do-not-disturb us
      status: 'dnd',
    },
  },
});

// listen to our client's eventemitter
// `shard` (which is the ShardClient the event originated from) is added onto EVERY event that you listen to on the cluster client
cluster.on('guildCreate', async ({fromUnavailable, guild, shard}) => {
  if (fromUnavailable) {
    console.log(`Shard #${shard.shardId}:`, `Guild ${guild.name} has just came back from being unavailable`);
  } else {
    console.log(`Shard #${shard.shardId}:`, `Joined Guild ${guild.name}, bringing us up to ${client.guilds.length} guilds.`);
  }
});

// listen to our client's eventemitter
// `shard` (which is the ShardClient the event originated from) is added onto EVERY event that you listen to on the cluster client
cluster.on('messageCreate', async ({message, shard}) => {
  if (message.content === '!ping') {
    const reply = await message.reply(`pong on shard #${shard.shardId}!, deleting message in 5 seconds...`);
    setTimeout(async () => {
      await reply.delete();
    }, 5000);
  }
});

(async () => {
  // shards are made after the cluster is ran, found in `ClusterClient.shards`.
  await cluster.run();
  console.log(`Successfully launched shards ${cluster.shardStart} to ${cluster.shardEnd} with a shardCount of ${cluster.shardCount}`);
})();

Contributing

Detritus is licensed under the BSD-2 license; see the LICENSE.

To contribute, please first open an issue describing your requested changes, and then open a pull request.