Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Micro Base

Discord bots just got easier

About

Micro Base is a pre-built bot base for Discord.js that makes creating Discord bots easier. We provide all the essentials a bot needs (handlers, logging, hot reload) so you can focus on the fun stuff. We also provide a variety of features that make creating bots more fun and interactive, taking inspiration from react and developer advice.

Our Goals

Discord bot are in an ever changing landscape of standards and features, no two bots are the same and often most of your time is spent simply debugging broken code. Our goal is to provide a stable and feature rich base that you can build off of, setting you off on the right foot with something you can trust - battle tested, active development, and plenty of features to stand out of the crowd - Micro is among the best for you. We make it a point to give the developer full control over what their bot does and how.

Spotlight

Check out some other great bases!

Installation

Micro Base is a standalone project meaning that you control every aspect of it. There are no dependencies to install besides Discord.js, everything else is on you. To get started, simply clone the repository and run npm install to get started.

git clone https://github.com/MusicMakerOwO/MicroBase
cd MicroBase
npm install

Next you should go check out the config.json, this is where all your configurations will go. You can set your token, prefix, and other settings here. If you need to abtain your bot token head over to the Discord Developer Portal. Do the same for the application ID as well while you are at it. You are now ready to start your bot!

npm run start

Configuration

This section covers the different features in config.json - If you are looking for setup direction see the Installation section.

A lot of features in Micro Base can see confusing or may not even work in all enviorments such as dockers. This is where the config.json comes in, here you can disable certain features of the base with ease. Here is a list of all the features you can disable in the config.json.

OptionDescriptionDefault
HOT_RELOADEnables hot reload for the bot, this uses fs.watch() and may not be supported in all enviormentstrue
PROCESS_HANDLERSEnables the anti-crash system for the bot, this will not automatically restart but any errors will be ignoredtrue
CHECK_INTENTSThis is only a partial safety net, it checks what events you have added and takes a good guesstrue
CHECK_EVENT_NAMESJust a quick check that all of your event names are correct, can be annoying if you have lots of custom eventstrue
REGISTER_COMMANDSRegisters all the commands in the commands folder, this can be disabled if you want to manually register commandstrue
FANCY_ERRORSErrors will be displayed in a more readable format on discord. Will show a snippet of code - not recommended for productiontrue

Usage

Being flexible brings a lot of power to the table, and Micro Base is no exception, however with it comes a lot of complexity. Many new users get confused on this part who aren't used to seeing this method of bot creation. We will go over the different parts of the bot and how to use them.

Slash Commands

Slash commands are the key point of most all bots after the lockdown of the MessageCreate intent. They are not to be trifled with however as the various options bring plenty of versaility for both users and devs, some of which were never possible for prefix commands. With slash commands the only thing to worry about is the data property, this is a SlashCommandBuilder that you can use to define your command. The execute function is the same as prefix commands, this is where you put your code.

module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Replies with Pong!'),asyncexecute(interaction){awaitinteraction.reply('Pong!');},};

NOTE:
Slash commands are global by default, this means that they will be available in all guilds. If you want to make a command guild only you can use the dev property, see the Component Restrictions section for more information. If the command does not show up after registering you may need to restart your discord client.

Prefix Commands

Prefix commands are of the old world how bots used to be created. They used to rule the entire industry and in hindsight there is a certain level of simplicity and ease of use to them. They are definitely easier then slash however they are not as flexible when it comes to options. Here there are a couple fields to consider: name, description, and aliases are all self explanatory. The execute function is where you put your code.

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,args,client){constuser=message.mentions.users.first()||message.author;constrank=awaitclient.levels.getRank(user.id);// or however you get your rank lolawaitmessage.channel.send(`${user.tag} is rank #${rank}`);}};

Buttons

Buttons are probably the easiest component to wrap your head around. There is only one property you need to worry about: customID. This is the ID that Discord will send when the button is clicked, you can use this to determine what action to take.

module.exports={customID: 'myButton',asyncexecute(interaction){awaitinteraction.reply('Button clicked!');}};

HINT:
Buttons are extremely versatile when you can link them together, we will go into more detail about that in a later section however, see Advanced Components.

Select Menus

Slightly less powerful than buttons but still very useful, select menus allow you to create dropdowns for your users to select from. The customID is the same as buttons, this is the ID that Discord will send when the select menu is used. All of your options are contained within the interaction.values as per usual.

module.exports={customID: 'mySelectMenu',asyncexecute(interaction){constselection=interaction.values[0];awaitinteraction.reply(`You selected ${selection}`);}};

Modals

Finally we have modals which are the most complex of the components but for all the wrong reasons. Again the customID is the only property you need to worry about. This is the ID of the Modal itself - Not the text input ID.

module.exports={customID: 'myModal',asyncexecute(interaction){constfirstAnswer=interaction.fields.getTextInputValue('questionID');constsecondAnswer=interaction.fields.getTextInputValue('another_question');awaitinteraction.reply(`You answered "${firstAnswer}" and "${secondAnswer}"`);}};

Context Menus

Context menus are often the odd one out, they are not as powerful as the other components but they are still very useful, they pick up where the others fall short like fetching stickers from a message. Here they are built similar to the Slash Commands but with a few key differences, namely being the data is a ContextMenuCommandBuilder instead of a SlashCommandBuilder. The rest is the same as slash commands however.

module.exports={data: newContextMenuCommandBuilder().setName('stealsticker').setType(ApplicationCommandType.Message),asyncexecute(interaction,client){constmessage=interaction.targetMessage;conststicker=message.stickers.first();if(!sticker)returninteraction.reply({content: 'This message does not have any stickers.',ephemeral: true});awaitinteraction.reply({content: sticker.url,ephemeral: true});}}

Events

Events are fairly straight forward, but are also the most unique of the components. They are standard JS events that are emitted by the client, however they differ in that they are bound functions and the hot reload has to do some funny trickery to handle them. Here you have 2 different properties to worry about: name and execute. The name is the name of the event you want to listen for, things like ready, guildMemberAdd, interactionCreate, etc. The execute function on the other hand is a bound function, this means the client is always the first argument passed in, no matter what. This is to make sure you have access to the client at all times but can also trick some people up if not used to it.

module.exports={name: 'guildMemberUpdate',execute(client,oldMember,newMember){constoldRoles=oldMember.roles.cache;constnewRoles=newMember.roles.cache;constaddedRoles=newRoles.filter(role=>!oldRoles.has(role.id));constremovedRoles=oldRoles.filter(role=>!newRoles.has(role.id));// do something with the roles lol}};

Hot Reload

Initially inspired by NextJS, Micro Base has a hot reload feature that allows you to make changes to your bot without having to restart it. This is great for testing new features or debugging on the fly, but can be a bottleneck for static hosting, to disable it see the Configuration section.

NOTE:
Not all systems support fs.watch() so things may not work as expected. Please submit an issue if you have any problems.

So how does it work?
Micro Base uses fs.watch() to watch for changes in the files, when a change is detected it will reload the bot. When a change is detected a bunch of things all fire off at once. At it's core we only have to update the appropriate client cache, sounds easy, but we have to fight against JS itself to make this work. Unfortunately the require() cache is not updated when a file is changed, so we have to purge it and force JS to recompile our new file.

WARNING:
This can lead to memory leaks in some cases so it is advised to not directly require() components, go through the client cache whenever possible.

Why not use nodemon?
Nodemon is a great tool for hot reloading, but it is not perfect. Nodemon is a bit overkill and has some other more subtle issues. It's less commonly known but Discord enforces a limit on the number of times you can log in per day. This can be a problem if you are constantly restarting your bot, reaching that limit is not too hard if you are grinding for a few hours and constantly making edits, reaching that limit will lock you out of your bot for the rest of the day. Micro Base is a little more complex but overall safeter and more reliable, no restart is needed as it only modifies the cache, so thus faster results and less risk.

Why don't my commands show up?
This is a common issue with Discord and not necessarily Micro Base. Discord is a little lazy on their clients and tries to avoid loading new commands when possible. The only way to fix this is to restart your Discord client (or refresh if you are using the web client). If the command still doesn't show up you probably missed an error in the console.

File Presets

Inspired by Mid Base, Micro Base has a file preset system that pre-fills a file when created, this is a great time saver and lowers the learning curve for new users who may not be familiar with the idea of components. These are all blank templates to autofill the most basic stuff and just hop right into writing the actual code. Below is an example of the command preset, this is the most common one you will use.

const{ SlashCommandBuilder }=require('@discordjs/builders');module.exports={data: newSlashCommandBuilder().setName('ping').setDescription('Pong!'),autocomplete: asyncfunction(interaction,client){// this is optional, called on any autocomplete stuff},execute: asyncfunction(interaction,client){}}

Alongside the command preset there are also presets for buttons, select menus, modals, context menus, and events. Each has their own unique setup but share the same general format. These are all configurable within the Presets folder, find the file for the desired component and edit it to your liking. These are not connecte to Hot Reload so you will have to restart your bot for changes to take affect.

Advanced Components

This is where Micro Base really shines from the rest of the pack, using handlers unlocks some performance gains that you can't really find anywhere else. Component args, linking, restrictions, and caching are only a few ways that help bring the most out of your production code. The entire goal of this is to reuse as much code as possible, this is not only efficient for the compiler but also saves you time debugging.

Aliases

Most wouldn't count this as a performance gain but in Micro it is. Aliases are a way to reuse the same component in multiple places, Micro Base takes this a step further in that aliases can be used not only in prefix but also slash commands. Why is this useful? Say you have a leveling system, you might want to add /rank and /level an alias, however you would have to duplicate the code in some compacity. With aliases you can simply duplicate the component data with a new name.

Caveats: Aliases are only supported in prefix and slash commands. They count as a separate commands so be mindful with bots that have a lot of commands. Aliases are full duplicates internally, they are only here to make things easier for you.


Prefix

module.exports={name: 'rank',description: 'Shows your rank',aliases: ['level','xp'],//optionalasyncexecute(message,client,args){// your code here}};

Slash

module.exports={aliases: ['level','xp'],//optionaldata: newSlashCommandBuilder().setName('rank').setDescription('Shows your rank').addStringOption(option=>option.setName('user').setDescription('The user to get the rank of')),asyncexecute(interaction,client){// your code here}};

HINT: This will register 3 commands: /rank, /level, and /xp. Be mindful of your command count!

Component Args

The roots of this come from way back before Micro Base was even conceptualized. The idea is simple, adding state to your buttons so you don't have to, this will keep a small amount of data even after restarting, no need for cache. In the older days before Micro this was done with many nested switch statements.

// ban_userID// export_channelID_guildID_timestamp// modPanel_edit_roleconstargs=interaction.customId.split('_');switch(args[0]){case: 'ban':
constuserID=args[1];// ban the userbreak;case: 'export':
constchannelID=args[1];constguildID=args[2];consttimestamp=args[3];// export the channelbreak;case: 'modPanel':
constaction=args[1];switch(action){case'edit':
constrole=args[2];// edit the rolebreak;case'delete':
// delete the rolebreak;case'create':
// create the rolebreak;}default:
thrownewError('Unknown button');}

However this is very long winded and extremely hard to maintain. It's useful for buttons that share a lot of similarities to reduce code repition but there is surely a better way to go aboout it. So this led to a full handler system, and where the customID fields comes in. This is NOT the full customID, only the start of it. Micro is built assuming you are using args so any _ used in the ID get removed. This effectively turns ban_guild into ['ban', 'guild'] where ban is the customID and guild is the arg. This unfortunately confuses a lot of new people, _ is a reserved symbol for the args but - is better for compound IDs: ban-guild.

module.exports={customID: 'ban',asyncexecute(interaction,client,args){consttype=args[0];constid=args[1];// guildID or userID dependending on the typeswitch(type){// CustomID: ban_user_1234567890case'user':
// ban the userawaitinteraction.guild.bans.create(id);break;// CustomID: ban_guild_1234567890case'guild':
// ban the guild in a databasebreak;}}};

With a system like this you can pass data around the bot without any caching or database, as long as the button exists so does your data. This same system applies to all components, not just buttons, so you can run a command that spawns a modal, that sends a select menu, and then the buttons still remember the data. This is a powerful system that can be used in many ways, but it is not without it's downsides. This system is not perfect and can be a little confusing at first, but once you get the hang of it you will never want to go back.

Component Restrictions

Micro Base has a ton of built-in restrictions you can apply to components. Everything from required permissions to cooldowns to server owner. It is worth noting however that these are all single-guilded. This means that if you want users to set their own roles on a dashboard you have to do the check logic yourself. If that doesn't apply to you then all of these will work perfectly for you!

PropertyDescriptionTypeDefault
devThis command will not be registered to any other servers (slash commands only)Booleanfalse
ownerOnly the server owner can use this commandBooleanfalse
cooldownThe cooldown in seconds, using it again before the time has passed will deny accessNumber0
guildsAn array of guild IDs that can use this commandArray[]
rolesAn array of role IDs that can use this commandArray[]
channelsAn array of channel IDs that can use this commandArray[]
usersAn array of user IDs that can use this commandArray[]
userPermsAn array of permissions that the user must have to use this commandArray[]
botPermsAn array of permissions that the bot must have (current server) to use this commandArray[]
module.exports={botPerms: ['BanMembers'],// bot must have ban members permissionuserPerms: ['BanMembers'],// user must also have itdata: newSlashCommandBuilder().setName('ban').setDescription('Bans a user').addUserOption(option=>option.setName('user').setDescription('The user to ban').setRequired(true)),asyncexecute(interaction){// ban the user, no permission checks needed}};

Also under the same category there are a couple of extra options that don't restrict access but simply make life easier

PropertyDescriptionTypeDefault
deferDefer the reply automatically, the value will set it as ephemeral or not, no value will make it not deferBoolean | Nullnull
cacheCache the response, this is only useful for static components such as /helpBooleanfalse
aliasJust another aliases field with a different name, they do the same thing, see Aliases for more infoArray[]

Linking Components

HINT:
This section plays perfectly with Component Args, if you are not familiar with that section you may want to read it first. This takes the ideas from there and goes another step further.

Did you know you can make a command run a button automatically? This is not a feature specific to Micro Base but the first that I have seen use it. This can technically be done in any base but Micro capitalizes on it. There are a couple of caveats to note however. When you transfer an interaction it does not change types - If it started as a command and transfer to a button it is still a command interaction. This can be a little confusing at first but it opens the doors to a lot of stuff.

// Buttonmodule.exports={customID: 'poke',asyncexecute(interaction){awaitinteraction.reply('Ouch!');}}// Commandmodule.exports={data: newSlashCommandBuilder().setName('poke').setDescription('Poke the bot'),asyncexecute(interaction,client){constbutton=client.buttons.get('poke');awaitbutton.execute(interaction,client);// 'Ouch!'}}

Caching

This is a first-ever for Discord bot bases - Response caching! (seriously why has no one thought of this?) This entire feature, like Hot Reload, was inspired by NextJS. The idea is simple, cache the response of a command so you don't have to run the same code over and over again. This is especially useful for commands that don't change often, like /help. The execution of this is actually very simple, we will save the last response emitted and reuse it if the command gets ran again. This is a huge performance gain for bots that have a lot of commands that don't change often. However this is only for static commands so if you reply on user input, time of day, or a guild setting this will cause more harm than good.

module.exports={cache: true,// The first time you run this it will take 5 seconds, the second time is instantdata: newSlashCommandBuilder().setName('slow-command').setDescription('This is very slooooowwww'),asyncexecute(interaction){awaitinteraction.deferReply();awaitnewPromise(resolve=>setTimeout(resolve,5000));// wait 5 secondsawaitinteraction.editReply('Done!');}}

Contributing

Contributions are always welcome! If you have an idea for a new feature or a bug fix, feel free to open a pull request - We want this to be the best it can possibly be! On that note however, we do have some guidelines to follow for commits. This is only to make maintaining the project easier for us, and to make sure we can understand what you are trying to do.

  1. Keep your commits short and to the point, don't commit 300 lines across 6 different files.
  2. Make sure your commit message is clear. We use a standard format inspired by Angular - type: message The type is a general idea of what it is, refer to the table below for a list of types and how they are used.
  3. Make sure your code is clean and readable, we don't want to spend hours deciphering why or how it works.
  4. Make sure your code works, if it doesn't compile and run then don't commit. We will reject it.
  5. Try to only commit full features, make sure you are content with what you have before submitting.
TypeDescription
featA new feature
fixA bug fix
docsDocumentation changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactorA code change that neither fixes a bug nor adds a feature
perfA code change that improves performance

License

Micro Base falls under the Apache 2.0 License. You can view the full license here.
This project is not affiliated with Discord or Discord.js in any way. Discord and Discord.js are registered trademarks of their respective owners. We do not claim ownership of any of the trademarks, logos, names, or any other intellectual property related to Discord or Discord.js.

About

No description, website, or topics provided.

Resources

Stars

24 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages