sabr
v0.1.15
Published
Klasa Discord API Library Framework
Downloads
6
Maintainers
Readme
Sabr
A Klasa Discord API Library Framework.
Note: The official Klasa framework is a lot more powerful, in-depth and a lot more feature packed.
If you still interested in using this framework keep reading below:
Usage Getting Started
npm i sabr @klasa/core
Create an index.ts file and paste the code below:
import BotClient from "sabr";
// This is your config file to help keep your bot token and other info you want SECRET. Never hard code your tokens.
import config from "./config";
export const client = new BotClient(config.token, {
rest: {
offset: 0,
}
})
// To set a default prefix for the bot
.setDefaultPrefix("%")
// To load your commands, monitors, tasks etc... These paths will be cached and reloaded when you use the reload command
.loadDirectories([
`${__dirname}/commands/`,
`${dirname}/monitors/`,
`${dirname}/tasks`,
]);
These are all the client options available from Sabr.
/** Whether the core commands and library should respond use embeds. By default this is enabled. */
useEmbeds?: boolean;
/** The core commands that you would like to disable */
disabledCoreCommands?: ("info" | "invite" | "ping" | "reload" | "stats")[];
As soon as you start your bot, you will realize that Klasa's framework is different. For example, Klasa will make folders like monitors/tasks/arguments/providers etc... Sabr will not create these. Sabr will only use what you provide it.
Commands
Commands are a bit different in Sabr as well. In Sabr, they are added by running a function as opposed to creating a new class that extends the command class. First, let's review all the command options available:
/** The command names that can also trigger this command. */
aliases?: string[];
/** Whether or not this command can be used in DM. */
dm?: boolean;
/** List of modules that you can use to enable/disable certain modules of your bots on a server. */
modules?: string[];
/** The permission level required to run this command. */
permissionLevel?: (message: Message) => boolean;
/** The description of the command. Useful for a help command to provide information on the command. */
description?: string;
/** The permissions you want to check if the message author has from their roles. */
requiredServerPermissions?: PermissionsFlags[];
/** The permissions you want to check if the message author has in this channel where the command is used. */
requiredChannelPermissions?: PermissionsFlags[];
/** The permissions the BOT must have from it's roles. */
botServerPermissions?: PermissionsFlags[];
/** The permissions the BOT must have in the current channel. */
botChannelPermissions?: PermissionsFlags[];
/** The arguments that a command should have with arg parsing built in. */
arguments?: Argument[];
/** The main code that will be run when this command is triggered. */
execute: (message: Message, parameters: unknown) => unknown;
These are the options for the command arguments:
/** The name of the argument. Useful for when you need to alert the user X arg is missing. */
name: string;
/** The type of the argument you would like. Defaults to string. */
type?: "number" | "string" | "boolean" | "subcommand";
/** The function that runs if this argument is required and is missing. */
missing?: (message: Message) => unknown;
/** Whether or not this argument is required. Defaults to true. */
required?: boolean;
/** If the type is string, this will force this argument to be lowercase. */
lowercase?: boolean;
/** If the type is string or subcommand you can provide literals. The argument MUST be exactly the same as the literals to be accepted. For example, you can list the subcommands here to make sure it matches. */
literals?: string[];
/** The default value for this argument/subcommand. */
defaultValue?: string | boolean | number
Here is a very basic example:
client.createCommand("ping", {
aliases: ["pong"],
description: "Hmm some silly description",
botChannelPermissions: [PermissionsFlags.SendMessages],
execute: (message) => {
client.sendMessage(message.channel.id, { content: "Shut up" });
},
})
Here is a bit more complex example, with a subcommand example. Notice how you can simply chain subcommands 1 after another. Each subcommand is it's own command instance allowing you to have custom aliases per subcommand, arguments per subcommand, unique permission levels, required permissions etc...
client.createCommand("ping", {
aliases: ["pong"],
dm: false,
description: "Hmm some silly description",
permissionLevel: (message) => message.member!.permissions.has(PermissionsFlags.Administrator),
botChannelPermissions: [PermissionsFlags.SendMessages],
arguments: [
{
name: "subcommand",
type: "subcommand",
literals: ["subcommand"],
},
],
execute: (message) => {
client.sendMessage(message.channel.id, { content: "Shut up" });
},
})
// Add a subcommand example with a custom alias, permissions and description
.createSubcommand("ping", "subcommand", {
aliases: ["sub"],
description: "Hmm some silly description for the sub",
botChannelPermissions: [PermissionsFlags.SendMessages],
execute: (message) => {
client.sendMessage(message.channel.id, { content: "Shut up, from subway eat freshs" });
},
});
Let's try and note a few more differences. Notice how you no longer need to explicitly type the execute: (message)
as execute: (message: Message)
saving you imports. Sabr can automatically infer these because it is not using extended classes but pure functions. Also notice that permissionLevel is a function as opposed to a number like in Klasa. Subcommands are done by a function as well, meaning you can even refactor code enough so that every subcommand has its own file or u can have multiple related commands in the same file. Another difference that is not entirely obvious in the example above is that the command args are provided as an object instead of an array. To see this in action, let's see another example:
interface PingCommandArgs {
amount?: number;
reason: string;
}
client.createCommand("ping", {
aliases: ["pong"],
dm: false,
description: "Hmm some silly description",
botChannelPermissions: [PermissionsFlags.SendMessages],
arguments: [
{
name: "amount",
type: "number"
},
{
name: "reason",
type: "string",
required: true,
lowercase: true
}
],
execute: (message, args: PingCommandArgs) => {
/**
* Assuming we use the following command usage: `.ping REASON`
* args.amount will be undefined
* args.reason will be `test reason`
*
* Assuming we use the following command usage: `.ping 5 REASON`
* args.amount will be 5
* and args.reason will be `test reason`
*
*/
if (!args.amount) {
// Handle when amount is not provided.
}
client.sendMessage(message.channel.id, { content: `Shut up, ${args.reason}` });
},
})
Monitors
Monitors in Sabr are also done through a functional approach as opposed to a class based approach. Sabr also has a few different default options that rely on most used values as opposed to ignoring everything by default.
client.createMonitor("name", {
execute: (message) => {
client.sendMessage(message.channel.id, { content: "Monitor is working for sure for sure" });
},
});
/** Monitor Options */
/** Whether this monitor should ignore messages that are sent by bots. By default this is true. */
ignoreBots?: boolean;
/** Whether this monitor should ignore messages that are sent by others. By default this is false.*/
ignoreOthers?: boolean;
/** Whether this monitor should ignore messages that are edited. By default this is false.*/
ignoreEdits?: boolean;
/** Whether this monitor should ignore messages that are sent in DM. By default this is true. */
ignoreDM?: boolean;
/** The permissions you want to check if the message author has from their roles. */
requiredServerPermissions?: PermissionsFlags[];
/** The permissions you want to check if the message author has in this channel where the command is used. */
requiredChannelPermissions?: PermissionsFlags[];
/** The permissions the BOT must have from it's roles. */
botServerPermissions?: PermissionsFlags[];
/** The permissions the BOT must have in the current channel. */
botChannelPermissions?: PermissionsFlags[];
Sabr has almost all the same ignore options except the blacklisted ones as those require settings and Sabr stays away from creating default settings so that you can control your database as you wish. There are also permission options similar to commands allowing you to handle permissions much easier.
Tasks
Tasks are a bit different from Klasa Tasks in that Sabr tasks are repeating. Doing a task for a 1 time unmute is not an option(klasa is able to do this because it has access to settings that it controls). Instead, the same functionality can be accomplished by just handling unmutes in a simple unmute task that handles all ur unmute needs.
client.createTask('unmute', {
interval: 12000,
execute: () => {
const now = Date.now()
client.mutedUsers.forEach(mutedUser => {
if (now < mutedUser.timestamp) return;
// Remove the muted role from use
handleUnmute(mutedUser)
})
}
})
Notice that in that example we provided a interval of 120,000 ms which is 2 minutes. However, this is just to show an example of how to provide an interval. By default the interval is 60000
ms. By not having access to settings like Klasa does, there is a small downside of not being able to accurately time tasks to run. For example, if you had a task that ran every 20 minutes and it ran at 5:00. Then you restarted the bot at 5:05. The task would run at 5:25. To solve this, Sabr provides you the ability to customize the task initilization once you have your settings prepared as you like. When the "READY" event is fired, Sabr runs the client.loadTasks()
function. By overriding this function you can enhance this functionality if you so desire. For the average bot developer, this is not usually needed for most use cases in my experience.